7 #include <security/pam_appl.h>
16 #define LOGIN_PROMPT "login:"
18 static int console_fd = -1;
20 static GList *user_entries = NULL;
21 static GList *getpwent_link = NULL;
23 static GList *group_entries = NULL;
31 struct pam_conv conversation;
47 initgroups (const char *user, gid_t group)
58 getgroups (int size, gid_t list[])
60 const gchar *group_list;
64 /* Get groups we are a member of */
65 group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
68 groups = g_strsplit (group_list, ",", -1);
69 groups_length = g_strv_length (groups);
75 if (groups_length > size)
80 for (i = 0; groups[i]; i++)
81 list[i] = atoi (groups[i]);
89 setgroups (size_t size, const gid_t *list)
94 group_list = g_string_new ("");
95 for (i = 0; i < size; i++)
98 g_string_append (group_list, ",");
99 g_string_append_printf (group_list, "%d", list[i]);
101 g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
102 g_string_free (group_list, TRUE);
121 open (const char *pathname, int flags, ...)
123 int (*_open) (const char * pathname, int flags, mode_t mode);
129 va_start (ap, flags);
130 mode = va_arg (ap, int);
134 _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, "open");
135 if (strcmp (pathname, "/dev/console") == 0)
139 console_fd = _open ("/dev/null", flags, mode);
140 fcntl (console_fd, F_SETFD, FD_CLOEXEC);
144 else if (strcmp (pathname, CONFIG_DIR "/lightdm.conf") == 0)
149 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "lightdm", "lightdm.conf", NULL);
150 fd = _open (path, flags, mode);
156 return _open (pathname, flags, mode);
160 ioctl (int d, int request, void *data)
162 int (*_ioctl) (int d, int request, void *data);
164 _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
165 if (d > 0 && d == console_fd)
167 struct vt_stat *console_state;
172 console_state = data;
173 console_state->v_active = 7;
181 return _ioctl (d, request, data);
187 int (*_close) (int fd);
189 if (fd > 0 && fd == console_fd)
192 _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
198 free_user (gpointer data)
200 struct passwd *entry = data;
202 g_free (entry->pw_name);
203 g_free (entry->pw_passwd);
204 g_free (entry->pw_gecos);
205 g_free (entry->pw_dir);
206 g_free (entry->pw_shell);
213 gchar *path, *data = NULL, **lines;
215 GError *error = NULL;
217 g_list_free_full (user_entries, free_user);
219 getpwent_link = NULL;
221 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
222 g_file_get_contents (path, &data, NULL, &error);
225 g_warning ("Error loading passwd file: %s", error->message);
226 g_clear_error (&error);
231 lines = g_strsplit (data, "\n", -1);
234 for (i = 0; lines[i]; i++)
236 gchar *line, **fields;
238 line = g_strstrip (lines[i]);
239 fields = g_strsplit (line, ":", -1);
240 if (g_strv_length (fields) == 7)
242 struct passwd *entry = malloc (sizeof (struct passwd));
244 entry->pw_name = g_strdup (fields[0]);
245 entry->pw_passwd = g_strdup (fields[1]);
246 entry->pw_uid = atoi (fields[2]);
247 entry->pw_gid = atoi (fields[3]);
248 entry->pw_gecos = g_strdup (fields[4]);
249 entry->pw_dir = g_strdup (fields[5]);
250 entry->pw_shell = g_strdup (fields[6]);
251 user_entries = g_list_append (user_entries, entry);
261 if (getpwent_link == NULL)
264 if (user_entries == NULL)
266 getpwent_link = user_entries;
270 if (getpwent_link->next == NULL)
272 getpwent_link = getpwent_link->next;
275 return getpwent_link->data;
281 getpwent_link = NULL;
287 getpwent_link = NULL;
291 getpwnam (const char *name)
300 for (link = user_entries; link; link = link->next)
302 struct passwd *entry = link->data;
303 if (strcmp (entry->pw_name, name) == 0)
319 for (link = user_entries; link; link = link->next)
321 struct passwd *entry = link->data;
322 if (entry->pw_uid == uid)
332 free_group (gpointer data)
334 struct group *entry = data;
336 g_free (entry->gr_name);
337 g_free (entry->gr_passwd);
338 g_strfreev (entry->gr_mem);
345 gchar *path, *data = NULL, **lines;
347 GError *error = NULL;
349 g_list_free_full (group_entries, free_group);
350 group_entries = NULL;
352 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
353 g_file_get_contents (path, &data, NULL, &error);
356 g_warning ("Error loading group file: %s", error->message);
357 g_clear_error (&error);
362 lines = g_strsplit (data, "\n", -1);
365 for (i = 0; lines[i]; i++)
367 gchar *line, **fields;
369 line = g_strstrip (lines[i]);
370 fields = g_strsplit (line, ":", -1);
371 if (g_strv_length (fields) == 4)
373 struct group *entry = malloc (sizeof (struct group));
375 entry->gr_name = g_strdup (fields[0]);
376 entry->gr_passwd = g_strdup (fields[1]);
377 entry->gr_gid = atoi (fields[2]);
378 entry->gr_mem = g_strsplit (fields[3], ",", -1);
379 group_entries = g_list_append (group_entries, entry);
387 getgrnam (const char *name)
393 for (link = group_entries; link; link = link->next)
395 struct group *entry = link->data;
396 if (strcmp (entry->gr_name, name) == 0)
412 for (link = group_entries; link; link = link->next)
414 struct group *entry = link->data;
415 if (entry->gr_gid == gid)
425 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
427 pam_handle_t *handle;
429 if (service_name == NULL || conversation == NULL || pamh == NULL)
430 return PAM_SYSTEM_ERR;
432 handle = *pamh = malloc (sizeof (pam_handle_t));
436 handle->service_name = strdup (service_name);
437 handle->user = user ? strdup (user) : NULL;
439 handle->conversation.conv = conversation->conv;
440 handle->conversation.appdata_ptr = conversation->appdata_ptr;
441 handle->envlist = malloc (sizeof (char *) * 1);
442 handle->envlist[0] = NULL;
448 send_info (pam_handle_t *pamh, const char *message)
450 struct pam_message **msg;
451 struct pam_response *resp = NULL;
453 msg = calloc (1, sizeof (struct pam_message *));
454 msg[0] = malloc (sizeof (struct pam_message));
455 msg[0]->msg_style = PAM_TEXT_INFO;
456 msg[0]->msg = message;
457 pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
469 pam_authenticate (pam_handle_t *pamh, int flags)
471 struct passwd *entry;
472 gboolean password_matches = FALSE;
475 return PAM_SYSTEM_ERR;
477 /* Prompt for username */
478 if (pamh->user == NULL)
481 struct pam_message **msg;
482 struct pam_response *resp = NULL;
484 msg = malloc (sizeof (struct pam_message *) * 1);
485 msg[0] = malloc (sizeof (struct pam_message));
486 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
487 msg[0]->msg = LOGIN_PROMPT;
488 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
491 if (result != PAM_SUCCESS)
496 if (resp[0].resp == NULL)
502 pamh->user = strdup (resp[0].resp);
507 if (strcmp (pamh->user, "log-pam") == 0)
508 send_info (pamh, "pam_authenticate");
510 /* Crash on authenticate */
511 if (strcmp (pamh->user, "crash-authenticate") == 0)
512 kill (getpid (), SIGSEGV);
514 /* Look up password database */
515 entry = getpwnam (pamh->user);
517 /* Prompt for password if required */
518 if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
519 password_matches = TRUE;
522 int i, n_messages = 0, password_index, result;
523 struct pam_message **msg;
524 struct pam_response *resp = NULL;
526 msg = malloc (sizeof (struct pam_message *) * 5);
527 if (strcmp (pamh->user, "info-prompt") == 0)
529 msg[n_messages] = malloc (sizeof (struct pam_message));
530 msg[n_messages]->msg_style = PAM_TEXT_INFO;
531 msg[n_messages]->msg = "Welcome to LightDM";
534 if (strcmp (pamh->user, "multi-info-prompt") == 0)
536 msg[n_messages] = malloc (sizeof (struct pam_message));
537 msg[n_messages]->msg_style = PAM_TEXT_INFO;
538 msg[n_messages]->msg = "Welcome to LightDM";
540 msg[n_messages] = malloc (sizeof (struct pam_message));
541 msg[n_messages]->msg_style = PAM_ERROR_MSG;
542 msg[n_messages]->msg = "This is an error";
544 msg[n_messages] = malloc (sizeof (struct pam_message));
545 msg[n_messages]->msg_style = PAM_TEXT_INFO;
546 msg[n_messages]->msg = "You should have seen three messages";
549 msg[n_messages] = malloc (sizeof (struct pam_message));
550 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
551 msg[n_messages]->msg = "Password:";
552 password_index = n_messages;
554 result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
555 for (i = 0; i < n_messages; i++)
558 if (result != PAM_SUCCESS)
563 if (resp[password_index].resp == NULL)
570 password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
571 for (i = 0; i < n_messages; i++)
578 /* Do two factor authentication */
579 if (password_matches && strcmp (pamh->user, "two-factor") == 0)
581 msg = malloc (sizeof (struct pam_message *) * 1);
582 msg[0] = malloc (sizeof (struct pam_message));
583 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
584 msg[0]->msg = "OTP:";
586 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
592 if (resp[0].resp == NULL)
597 password_matches = strcmp (resp[0].resp, "otp") == 0;
603 /* Special user has home directory created on login */
604 if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
605 g_mkdir_with_parents (entry->pw_dir, 0755);
607 /* Special user 'change-user1' changes user on authentication */
608 if (password_matches && strcmp (pamh->user, "change-user1") == 0)
611 pamh->user = g_strdup ("change-user2");
614 /* Special user 'change-user-invalid' changes to an invalid user on authentication */
615 if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
618 pamh->user = g_strdup ("invalid-user");
621 if (password_matches)
628 get_env_value (const char *name_value, const char *name)
632 for (j = 0; name[j] && name[j] != '=' && name[j] == name_value[j]; j++);
633 if (name_value[j] == '=')
634 return &name_value[j + 1];
640 pam_putenv (pam_handle_t *pamh, const char *name_value)
644 if (pamh == NULL || name_value == NULL)
645 return PAM_SYSTEM_ERR;
647 for (i = 0; pamh->envlist[i]; i++)
649 if (get_env_value (pamh->envlist[i], name_value))
653 if (pamh->envlist[i])
655 free (pamh->envlist[i]);
656 pamh->envlist[i] = strdup (name_value);
660 pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
661 pamh->envlist[i] = strdup (name_value);
662 pamh->envlist[i + 1] = NULL;
669 pam_getenv (pam_handle_t *pamh, const char *name)
673 if (pamh == NULL || name == NULL)
676 for (i = 0; pamh->envlist[i]; i++)
679 value = get_env_value (pamh->envlist[i], name);
688 pam_getenvlist (pam_handle_t *pamh)
693 return pamh->envlist;
697 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
699 if (pamh == NULL || item == NULL)
700 return PAM_SYSTEM_ERR;
707 pamh->tty = strdup ((const char *) item);
716 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
718 if (pamh == NULL || item == NULL)
719 return PAM_SYSTEM_ERR;
724 *item = pamh->service_name;
731 case PAM_USER_PROMPT:
732 *item = LOGIN_PROMPT;
740 *item = &pamh->conversation;
749 pam_open_session (pam_handle_t *pamh, int flags)
752 return PAM_SYSTEM_ERR;
754 if (strcmp (pamh->user, "session-error") == 0)
755 return PAM_SESSION_ERR;
757 if (strcmp (pamh->user, "log-pam") == 0)
758 send_info (pamh, "pam_open_session");
760 if (strcmp (pamh->user, "make-home-dir") == 0)
762 struct passwd *entry;
763 entry = getpwnam (pamh->user);
764 g_mkdir_with_parents (entry->pw_dir, 0755);
771 pam_close_session (pam_handle_t *pamh, int flags)
774 return PAM_SYSTEM_ERR;
776 if (strcmp (pamh->user, "log-pam") == 0)
777 send_info (pamh, "pam_close_session");
783 pam_acct_mgmt (pam_handle_t *pamh, int flags)
786 return PAM_SYSTEM_ERR;
789 return PAM_USER_UNKNOWN;
791 if (strcmp (pamh->user, "log-pam") == 0)
792 send_info (pamh, "pam_acct_mgmt");
794 if (strcmp (pamh->user, "denied") == 0)
795 return PAM_PERM_DENIED;
796 if (strcmp (pamh->user, "expired") == 0)
797 return PAM_ACCT_EXPIRED;
798 if (strcmp (pamh->user, "new-authtok") == 0)
799 return PAM_NEW_AUTHTOK_REQD;
805 pam_chauthtok (pam_handle_t *pamh, int flags)
807 struct passwd *entry;
809 struct pam_message **msg;
810 struct pam_response *resp = NULL;
813 return PAM_SYSTEM_ERR;
815 if (strcmp (pamh->user, "log-pam") == 0)
816 send_info (pamh, "pam_chauthtok");
818 msg = malloc (sizeof (struct pam_message *) * 1);
819 msg[0] = malloc (sizeof (struct pam_message));
820 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
821 msg[0]->msg = "Enter new password:";
822 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
825 if (result != PAM_SUCCESS)
830 if (resp[0].resp == NULL)
836 /* Update password database */
837 entry = getpwnam (pamh->user);
838 free (entry->pw_passwd);
839 entry->pw_passwd = resp[0].resp;
846 pam_setcred (pam_handle_t *pamh, int flags)
851 return PAM_SYSTEM_ERR;
853 if (strcmp (pamh->user, "log-pam") == 0)
854 send_info (pamh, "pam_setcred");
856 /* Put the test directories into the path */
857 e = g_strdup_printf ("PATH=%s/tests/src/.libs:%s/tests/src:%s/tests/src:%s/src:%s", BUILDDIR, BUILDDIR, SRCDIR, BUILDDIR, pam_getenv (pamh, "PATH"));
858 pam_putenv (pamh, e);
861 if (strcmp (pamh->user, "cred-error") == 0)
863 if (strcmp (pamh->user, "cred-expired") == 0)
864 return PAM_CRED_EXPIRED;
865 if (strcmp (pamh->user, "cred-unavail") == 0)
866 return PAM_CRED_UNAVAIL;
868 /* Join special groups if requested */
869 if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
875 group = getgrnam ("test-group");
878 groups_length = getgroups (0, NULL);
879 groups = malloc (sizeof (gid_t) * (groups_length + 1));
880 groups_length = getgroups (groups_length, groups);
881 groups[groups_length] = group->gr_gid;
883 setgroups (groups_length, groups);
887 /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
888 pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
895 pam_end (pam_handle_t *pamh, int pam_status)
898 return PAM_SYSTEM_ERR;
900 free (pamh->service_name);
911 pam_strerror (pam_handle_t *pamh, int errnum)
921 return "Critical error - immediate abort";
923 return "Failed to load module";
925 return "Symbol not found";
926 case PAM_SERVICE_ERR:
927 return "Error in service module";
929 return "System error";
931 return "Memory buffer error";
932 case PAM_PERM_DENIED:
933 return "Permission denied";
935 return "Authentication failure";
936 case PAM_CRED_INSUFFICIENT:
937 return "Insufficient credentials to access authentication data";
938 case PAM_AUTHINFO_UNAVAIL:
939 return "Authentication service cannot retrieve authentication info";
940 case PAM_USER_UNKNOWN:
941 return "User not known to the underlying authentication module";
943 return "Have exhausted maximum number of retries for service";
944 case PAM_NEW_AUTHTOK_REQD:
945 return "Authentication token is no longer valid; new one required";
946 case PAM_ACCT_EXPIRED:
947 return "User account has expired";
948 case PAM_SESSION_ERR:
949 return "Cannot make/remove an entry for the specified session";
950 case PAM_CRED_UNAVAIL:
951 return "Authentication service cannot retrieve user credentials";
952 case PAM_CRED_EXPIRED:
953 return "User credentials expired";
955 return "Failure setting user credentials";
956 case PAM_NO_MODULE_DATA:
957 return "No module specific data is present";
959 return "Bad item passed to pam_*_item()";
961 return "Conversation error";
962 case PAM_AUTHTOK_ERR:
963 return "Authentication token manipulation error";
964 case PAM_AUTHTOK_RECOVERY_ERR:
965 return "Authentication information cannot be recovered";
966 case PAM_AUTHTOK_LOCK_BUSY:
967 return "Authentication token lock busy";
968 case PAM_AUTHTOK_DISABLE_AGING:
969 return "Authentication token aging disabled";
971 return "Failed preliminary check by password service";
973 return "The return value should be ignored by PAM dispatch";
974 case PAM_MODULE_UNKNOWN:
975 return "Module is unknown";
976 case PAM_AUTHTOK_EXPIRED:
977 return "Authentication token expired";
979 return "Conversation is waiting for event";
981 return "Application needs to call libpam again";
983 return "Unknown PAM error";
993 pututxline (struct utmp *ut)