8 #include <security/pam_appl.h>
17 #define LOGIN_PROMPT "login:"
19 static int console_fd = -1;
21 static GList *user_entries = NULL;
22 static GList *getpwent_link = NULL;
24 static GList *group_entries = NULL;
26 static int active_vt = 7;
36 struct pam_conv conversation;
52 initgroups (const char *user, gid_t group)
63 getgroups (int size, gid_t list[])
65 const gchar *group_list;
69 /* Get groups we are a member of */
70 group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
73 groups = g_strsplit (group_list, ",", -1);
74 groups_length = g_strv_length (groups);
80 if (groups_length > size)
85 for (i = 0; groups[i]; i++)
86 list[i] = atoi (groups[i]);
94 setgroups (size_t size, const gid_t *list)
99 group_list = g_string_new ("");
100 for (i = 0; i < size; i++)
103 g_string_append (group_list, ",");
104 g_string_append_printf (group_list, "%d", list[i]);
106 g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
107 g_string_free (group_list, TRUE);
125 setresgid (gid_t rgid, gid_t ugid, gid_t sgid)
143 setresuid (uid_t ruid, uid_t uuid, uid_t suid)
149 redirect_path (const gchar *path)
151 if (g_str_has_prefix (path, g_getenv ("LIGHTDM_TEST_ROOT")))
152 return g_strdup (path);
153 else if (strcmp (path, CONFIG_DIR "/lightdm.conf") == 0)
154 return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "lightdm", "lightdm.conf", NULL);
155 else if (g_str_has_prefix (path, "/tmp/"))
156 return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", path + 5, NULL);
158 return g_strdup (path);
163 open_wrapper (const char *func, const char *pathname, int flags, mode_t mode)
165 int (*_open) (const char *pathname, int flags, mode_t mode);
166 gchar *new_path = NULL;
169 _open = (int (*)(const char *pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, func);
171 if (strcmp (pathname, "/dev/console") == 0)
175 console_fd = _open ("/dev/null", flags, mode);
176 fcntl (console_fd, F_SETFD, FD_CLOEXEC);
181 new_path = redirect_path (pathname);
182 fd = _open (new_path, flags, mode);
189 open (const char *pathname, int flags, ...)
195 va_start (ap, flags);
196 mode = va_arg (ap, int);
199 return open_wrapper ("open", pathname, flags, mode);
203 open64 (const char *pathname, int flags, ...)
209 va_start (ap, flags);
210 mode = va_arg (ap, int);
213 return open_wrapper ("open64", pathname, flags, mode);
217 ioctl (int d, int request, void *data)
219 int (*_ioctl) (int d, int request, void *data);
221 _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
222 if (d > 0 && d == console_fd)
224 struct vt_stat *console_state;
230 console_state = data;
231 console_state->v_active = active_vt;
234 active_vt = GPOINTER_TO_INT (data);
242 return _ioctl (d, request, data);
248 int (*_close) (int fd);
250 if (fd > 0 && fd == console_fd)
253 _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
259 free_user (gpointer data)
261 struct passwd *entry = data;
263 g_free (entry->pw_name);
264 g_free (entry->pw_passwd);
265 g_free (entry->pw_gecos);
266 g_free (entry->pw_dir);
267 g_free (entry->pw_shell);
274 gchar *path, *data = NULL, **lines;
276 GError *error = NULL;
278 g_list_free_full (user_entries, free_user);
280 getpwent_link = NULL;
282 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
283 g_file_get_contents (path, &data, NULL, &error);
286 g_warning ("Error loading passwd file: %s", error->message);
287 g_clear_error (&error);
292 lines = g_strsplit (data, "\n", -1);
295 for (i = 0; lines[i]; i++)
297 gchar *line, **fields;
299 line = g_strstrip (lines[i]);
300 fields = g_strsplit (line, ":", -1);
301 if (g_strv_length (fields) == 7)
303 struct passwd *entry = malloc (sizeof (struct passwd));
305 entry->pw_name = g_strdup (fields[0]);
306 entry->pw_passwd = g_strdup (fields[1]);
307 entry->pw_uid = atoi (fields[2]);
308 entry->pw_gid = atoi (fields[3]);
309 entry->pw_gecos = g_strdup (fields[4]);
310 entry->pw_dir = g_strdup (fields[5]);
311 entry->pw_shell = g_strdup (fields[6]);
312 user_entries = g_list_append (user_entries, entry);
322 if (getpwent_link == NULL)
325 if (user_entries == NULL)
327 getpwent_link = user_entries;
331 if (getpwent_link->next == NULL)
333 getpwent_link = getpwent_link->next;
336 return getpwent_link->data;
342 getpwent_link = NULL;
348 getpwent_link = NULL;
352 getpwnam (const char *name)
361 for (link = user_entries; link; link = link->next)
363 struct passwd *entry = link->data;
364 if (strcmp (entry->pw_name, name) == 0)
380 for (link = user_entries; link; link = link->next)
382 struct passwd *entry = link->data;
383 if (entry->pw_uid == uid)
393 free_group (gpointer data)
395 struct group *entry = data;
397 g_free (entry->gr_name);
398 g_free (entry->gr_passwd);
399 g_strfreev (entry->gr_mem);
406 gchar *path, *data = NULL, **lines;
408 GError *error = NULL;
410 g_list_free_full (group_entries, free_group);
411 group_entries = NULL;
413 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
414 g_file_get_contents (path, &data, NULL, &error);
417 g_warning ("Error loading group file: %s", error->message);
418 g_clear_error (&error);
423 lines = g_strsplit (data, "\n", -1);
426 for (i = 0; lines[i]; i++)
428 gchar *line, **fields;
430 line = g_strstrip (lines[i]);
431 fields = g_strsplit (line, ":", -1);
432 if (g_strv_length (fields) == 4)
434 struct group *entry = malloc (sizeof (struct group));
436 entry->gr_name = g_strdup (fields[0]);
437 entry->gr_passwd = g_strdup (fields[1]);
438 entry->gr_gid = atoi (fields[2]);
439 entry->gr_mem = g_strsplit (fields[3], ",", -1);
440 group_entries = g_list_append (group_entries, entry);
448 getgrnam (const char *name)
454 for (link = group_entries; link; link = link->next)
456 struct group *entry = link->data;
457 if (strcmp (entry->gr_name, name) == 0)
473 for (link = group_entries; link; link = link->next)
475 struct group *entry = link->data;
476 if (entry->gr_gid == gid)
486 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
488 pam_handle_t *handle;
490 if (service_name == NULL || conversation == NULL || pamh == NULL)
491 return PAM_SYSTEM_ERR;
493 handle = *pamh = malloc (sizeof (pam_handle_t));
497 handle->service_name = strdup (service_name);
498 handle->user = user ? strdup (user) : NULL;
499 handle->authtok = NULL;
500 handle->ruser = NULL;
502 handle->conversation.conv = conversation->conv;
503 handle->conversation.appdata_ptr = conversation->appdata_ptr;
504 handle->envlist = malloc (sizeof (char *) * 1);
505 handle->envlist[0] = NULL;
511 send_info (pam_handle_t *pamh, const char *message)
513 struct pam_message **msg;
514 struct pam_response *resp = NULL;
516 msg = calloc (1, sizeof (struct pam_message *));
517 msg[0] = malloc (sizeof (struct pam_message));
518 msg[0]->msg_style = PAM_TEXT_INFO;
519 msg[0]->msg = message;
520 pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
532 pam_authenticate (pam_handle_t *pamh, int flags)
534 struct passwd *entry;
535 gboolean password_matches = FALSE;
538 return PAM_SYSTEM_ERR;
540 if (strcmp (pamh->service_name, "test-remote") == 0)
543 struct pam_message **msg;
544 struct pam_response *resp = NULL;
546 msg = malloc (sizeof (struct pam_message *) * 1);
547 msg[0] = malloc (sizeof (struct pam_message));
548 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
549 msg[0]->msg = "remote-login:";
550 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
553 if (result != PAM_SUCCESS)
558 if (resp[0].resp == NULL)
566 pamh->ruser = strdup (resp[0].resp);
570 msg = malloc (sizeof (struct pam_message *) * 1);
571 msg[0] = malloc (sizeof (struct pam_message));
572 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
573 msg[0]->msg = "remote-password:";
574 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
577 if (result != PAM_SUCCESS)
582 if (resp[0].resp == NULL)
589 free (pamh->authtok);
590 pamh->authtok = strdup (resp[0].resp);
594 password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
596 if (password_matches)
602 /* Prompt for username */
603 if (pamh->user == NULL)
606 struct pam_message **msg;
607 struct pam_response *resp = NULL;
609 msg = malloc (sizeof (struct pam_message *) * 1);
610 msg[0] = malloc (sizeof (struct pam_message));
611 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
612 msg[0]->msg = LOGIN_PROMPT;
613 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
616 if (result != PAM_SUCCESS)
621 if (resp[0].resp == NULL)
627 pamh->user = strdup (resp[0].resp);
632 if (strcmp (pamh->user, "log-pam") == 0)
633 send_info (pamh, "pam_authenticate");
635 /* Crash on authenticate */
636 if (strcmp (pamh->user, "crash-authenticate") == 0)
637 kill (getpid (), SIGSEGV);
639 /* Look up password database */
640 entry = getpwnam (pamh->user);
642 /* Prompt for password if required */
643 if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
644 password_matches = TRUE;
647 int i, n_messages = 0, password_index, result;
648 struct pam_message **msg;
649 struct pam_response *resp = NULL;
651 msg = malloc (sizeof (struct pam_message *) * 5);
652 if (strcmp (pamh->user, "info-prompt") == 0)
654 msg[n_messages] = malloc (sizeof (struct pam_message));
655 msg[n_messages]->msg_style = PAM_TEXT_INFO;
656 msg[n_messages]->msg = "Welcome to LightDM";
659 if (strcmp (pamh->user, "multi-info-prompt") == 0)
661 msg[n_messages] = malloc (sizeof (struct pam_message));
662 msg[n_messages]->msg_style = PAM_TEXT_INFO;
663 msg[n_messages]->msg = "Welcome to LightDM";
665 msg[n_messages] = malloc (sizeof (struct pam_message));
666 msg[n_messages]->msg_style = PAM_ERROR_MSG;
667 msg[n_messages]->msg = "This is an error";
669 msg[n_messages] = malloc (sizeof (struct pam_message));
670 msg[n_messages]->msg_style = PAM_TEXT_INFO;
671 msg[n_messages]->msg = "You should have seen three messages";
674 if (strcmp (pamh->user, "multi-prompt") == 0)
676 msg[n_messages] = malloc (sizeof (struct pam_message));
677 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
678 msg[n_messages]->msg = "Favorite Color:";
681 msg[n_messages] = malloc (sizeof (struct pam_message));
682 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
683 msg[n_messages]->msg = "Password:";
684 password_index = n_messages;
686 result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
687 for (i = 0; i < n_messages; i++)
690 if (result != PAM_SUCCESS)
695 if (resp[password_index].resp == NULL)
702 password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
704 if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
705 password_matches = strcmp ("blue", resp[0].resp) == 0;
707 for (i = 0; i < n_messages; i++)
714 /* Do two factor authentication */
715 if (password_matches && strcmp (pamh->user, "two-factor") == 0)
717 msg = malloc (sizeof (struct pam_message *) * 1);
718 msg[0] = malloc (sizeof (struct pam_message));
719 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
720 msg[0]->msg = "OTP:";
722 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
728 if (resp[0].resp == NULL)
733 password_matches = strcmp (resp[0].resp, "otp") == 0;
739 /* Special user has home directory created on login */
740 if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
741 g_mkdir_with_parents (entry->pw_dir, 0755);
743 /* Special user 'change-user1' changes user on authentication */
744 if (password_matches && strcmp (pamh->user, "change-user1") == 0)
747 pamh->user = g_strdup ("change-user2");
750 /* Special user 'change-user-invalid' changes to an invalid user on authentication */
751 if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
754 pamh->user = g_strdup ("invalid-user");
757 if (password_matches)
764 get_env_value (const char *name_value, const char *name)
768 for (j = 0; name[j] && name[j] != '=' && name[j] == name_value[j]; j++);
769 if (name_value[j] == '=')
770 return &name_value[j + 1];
776 pam_putenv (pam_handle_t *pamh, const char *name_value)
780 if (pamh == NULL || name_value == NULL)
781 return PAM_SYSTEM_ERR;
783 for (i = 0; pamh->envlist[i]; i++)
785 if (get_env_value (pamh->envlist[i], name_value))
789 if (pamh->envlist[i])
791 free (pamh->envlist[i]);
792 pamh->envlist[i] = strdup (name_value);
796 pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
797 pamh->envlist[i] = strdup (name_value);
798 pamh->envlist[i + 1] = NULL;
805 pam_getenv (pam_handle_t *pamh, const char *name)
809 if (pamh == NULL || name == NULL)
812 for (i = 0; pamh->envlist[i]; i++)
815 value = get_env_value (pamh->envlist[i], name);
824 pam_getenvlist (pam_handle_t *pamh)
829 return pamh->envlist;
833 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
835 if (pamh == NULL || item == NULL)
836 return PAM_SYSTEM_ERR;
843 pamh->tty = strdup ((const char *) item);
852 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
854 if (pamh == NULL || item == NULL)
855 return PAM_SYSTEM_ERR;
860 *item = pamh->service_name;
868 *item = pamh->authtok;
875 case PAM_USER_PROMPT:
876 *item = LOGIN_PROMPT;
884 *item = &pamh->conversation;
893 pam_open_session (pam_handle_t *pamh, int flags)
896 return PAM_SYSTEM_ERR;
898 if (strcmp (pamh->user, "session-error") == 0)
899 return PAM_SESSION_ERR;
901 if (strcmp (pamh->user, "log-pam") == 0)
902 send_info (pamh, "pam_open_session");
904 if (strcmp (pamh->user, "make-home-dir") == 0)
906 struct passwd *entry;
907 entry = getpwnam (pamh->user);
908 g_mkdir_with_parents (entry->pw_dir, 0755);
915 pam_close_session (pam_handle_t *pamh, int flags)
918 return PAM_SYSTEM_ERR;
920 if (strcmp (pamh->user, "log-pam") == 0)
921 send_info (pamh, "pam_close_session");
927 pam_acct_mgmt (pam_handle_t *pamh, int flags)
930 return PAM_SYSTEM_ERR;
933 return PAM_USER_UNKNOWN;
935 if (strcmp (pamh->user, "log-pam") == 0)
936 send_info (pamh, "pam_acct_mgmt");
938 if (strcmp (pamh->user, "denied") == 0)
939 return PAM_PERM_DENIED;
940 if (strcmp (pamh->user, "expired") == 0)
941 return PAM_ACCT_EXPIRED;
942 if (strcmp (pamh->user, "new-authtok") == 0)
943 return PAM_NEW_AUTHTOK_REQD;
949 pam_chauthtok (pam_handle_t *pamh, int flags)
951 struct passwd *entry;
953 struct pam_message **msg;
954 struct pam_response *resp = NULL;
957 return PAM_SYSTEM_ERR;
959 if (strcmp (pamh->user, "log-pam") == 0)
960 send_info (pamh, "pam_chauthtok");
962 msg = malloc (sizeof (struct pam_message *) * 1);
963 msg[0] = malloc (sizeof (struct pam_message));
964 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
965 msg[0]->msg = "Enter new password:";
966 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
969 if (result != PAM_SUCCESS)
974 if (resp[0].resp == NULL)
980 /* Update password database */
981 entry = getpwnam (pamh->user);
982 free (entry->pw_passwd);
983 entry->pw_passwd = resp[0].resp;
990 pam_setcred (pam_handle_t *pamh, int flags)
995 return PAM_SYSTEM_ERR;
997 if (strcmp (pamh->user, "log-pam") == 0)
998 send_info (pamh, "pam_setcred");
1000 /* Put the test directories into the path */
1001 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"));
1002 pam_putenv (pamh, e);
1005 if (strcmp (pamh->user, "cred-error") == 0)
1006 return PAM_CRED_ERR;
1007 if (strcmp (pamh->user, "cred-expired") == 0)
1008 return PAM_CRED_EXPIRED;
1009 if (strcmp (pamh->user, "cred-unavail") == 0)
1010 return PAM_CRED_UNAVAIL;
1012 /* Join special groups if requested */
1013 if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
1015 struct group *group;
1019 group = getgrnam ("test-group");
1022 groups_length = getgroups (0, NULL);
1023 groups = malloc (sizeof (gid_t) * (groups_length + 1));
1024 groups_length = getgroups (groups_length, groups);
1025 groups[groups_length] = group->gr_gid;
1027 setgroups (groups_length, groups);
1031 /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
1032 pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
1039 pam_end (pam_handle_t *pamh, int pam_status)
1042 return PAM_SYSTEM_ERR;
1044 free (pamh->service_name);
1048 free (pamh->authtok);
1059 pam_strerror (pam_handle_t *pamh, int errnum)
1069 return "Critical error - immediate abort";
1071 return "Failed to load module";
1072 case PAM_SYMBOL_ERR:
1073 return "Symbol not found";
1074 case PAM_SERVICE_ERR:
1075 return "Error in service module";
1076 case PAM_SYSTEM_ERR:
1077 return "System error";
1079 return "Memory buffer error";
1080 case PAM_PERM_DENIED:
1081 return "Permission denied";
1083 return "Authentication failure";
1084 case PAM_CRED_INSUFFICIENT:
1085 return "Insufficient credentials to access authentication data";
1086 case PAM_AUTHINFO_UNAVAIL:
1087 return "Authentication service cannot retrieve authentication info";
1088 case PAM_USER_UNKNOWN:
1089 return "User not known to the underlying authentication module";
1091 return "Have exhausted maximum number of retries for service";
1092 case PAM_NEW_AUTHTOK_REQD:
1093 return "Authentication token is no longer valid; new one required";
1094 case PAM_ACCT_EXPIRED:
1095 return "User account has expired";
1096 case PAM_SESSION_ERR:
1097 return "Cannot make/remove an entry for the specified session";
1098 case PAM_CRED_UNAVAIL:
1099 return "Authentication service cannot retrieve user credentials";
1100 case PAM_CRED_EXPIRED:
1101 return "User credentials expired";
1103 return "Failure setting user credentials";
1104 case PAM_NO_MODULE_DATA:
1105 return "No module specific data is present";
1107 return "Bad item passed to pam_*_item()";
1109 return "Conversation error";
1110 case PAM_AUTHTOK_ERR:
1111 return "Authentication token manipulation error";
1112 case PAM_AUTHTOK_RECOVERY_ERR:
1113 return "Authentication information cannot be recovered";
1114 case PAM_AUTHTOK_LOCK_BUSY:
1115 return "Authentication token lock busy";
1116 case PAM_AUTHTOK_DISABLE_AGING:
1117 return "Authentication token aging disabled";
1119 return "Failed preliminary check by password service";
1121 return "The return value should be ignored by PAM dispatch";
1122 case PAM_MODULE_UNKNOWN:
1123 return "Module is unknown";
1124 case PAM_AUTHTOK_EXPIRED:
1125 return "Authentication token expired";
1126 case PAM_CONV_AGAIN:
1127 return "Conversation is waiting for event";
1128 case PAM_INCOMPLETE:
1129 return "Application needs to call libpam again";
1131 return "Unknown PAM error";
1141 pututxline (struct utmp *ut)