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;
33 struct pam_conv conversation;
49 initgroups (const char *user, gid_t group)
60 getgroups (int size, gid_t list[])
62 const gchar *group_list;
66 /* Get groups we are a member of */
67 group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
70 groups = g_strsplit (group_list, ",", -1);
71 groups_length = g_strv_length (groups);
77 if (groups_length > size)
82 for (i = 0; groups[i]; i++)
83 list[i] = atoi (groups[i]);
91 setgroups (size_t size, const gid_t *list)
96 group_list = g_string_new ("");
97 for (i = 0; i < size; i++)
100 g_string_append (group_list, ",");
101 g_string_append_printf (group_list, "%d", list[i]);
103 g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
104 g_string_free (group_list, TRUE);
122 setresgid (gid_t rgid, gid_t ugid, gid_t sgid)
140 setresuid (uid_t ruid, uid_t uuid, uid_t suid)
147 open_wrapper (const char *func, const char *pathname, int flags, mode_t mode)
149 int (*_open) (const char * pathname, int flags, mode_t mode);
151 _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, func);
152 if (strcmp (pathname, "/dev/console") == 0)
156 console_fd = _open ("/dev/null", flags, mode);
157 fcntl (console_fd, F_SETFD, FD_CLOEXEC);
161 else if (strcmp (pathname, CONFIG_DIR "/lightdm.conf") == 0)
166 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "lightdm", "lightdm.conf", NULL);
167 fd = _open (path, flags, mode);
173 return _open (pathname, flags, mode);
177 open (const char *pathname, int flags, ...)
183 va_start (ap, flags);
184 mode = va_arg (ap, int);
187 return open_wrapper ("open", pathname, flags, mode);
191 open64 (const char *pathname, int flags, ...)
197 va_start (ap, flags);
198 mode = va_arg (ap, int);
201 return open_wrapper ("open64", pathname, flags, mode);
205 ioctl (int d, int request, void *data)
207 int (*_ioctl) (int d, int request, void *data);
209 _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
210 if (d > 0 && d == console_fd)
212 struct vt_stat *console_state;
217 console_state = data;
218 console_state->v_active = 7;
226 return _ioctl (d, request, data);
232 int (*_close) (int fd);
234 if (fd > 0 && fd == console_fd)
237 _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
243 free_user (gpointer data)
245 struct passwd *entry = data;
247 g_free (entry->pw_name);
248 g_free (entry->pw_passwd);
249 g_free (entry->pw_gecos);
250 g_free (entry->pw_dir);
251 g_free (entry->pw_shell);
258 gchar *path, *data = NULL, **lines;
260 GError *error = NULL;
262 g_list_free_full (user_entries, free_user);
264 getpwent_link = NULL;
266 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
267 g_file_get_contents (path, &data, NULL, &error);
270 g_warning ("Error loading passwd file: %s", error->message);
271 g_clear_error (&error);
276 lines = g_strsplit (data, "\n", -1);
279 for (i = 0; lines[i]; i++)
281 gchar *line, **fields;
283 line = g_strstrip (lines[i]);
284 fields = g_strsplit (line, ":", -1);
285 if (g_strv_length (fields) == 7)
287 struct passwd *entry = malloc (sizeof (struct passwd));
289 entry->pw_name = g_strdup (fields[0]);
290 entry->pw_passwd = g_strdup (fields[1]);
291 entry->pw_uid = atoi (fields[2]);
292 entry->pw_gid = atoi (fields[3]);
293 entry->pw_gecos = g_strdup (fields[4]);
294 entry->pw_dir = g_strdup (fields[5]);
295 entry->pw_shell = g_strdup (fields[6]);
296 user_entries = g_list_append (user_entries, entry);
306 if (getpwent_link == NULL)
309 if (user_entries == NULL)
311 getpwent_link = user_entries;
315 if (getpwent_link->next == NULL)
317 getpwent_link = getpwent_link->next;
320 return getpwent_link->data;
326 getpwent_link = NULL;
332 getpwent_link = NULL;
336 getpwnam (const char *name)
345 for (link = user_entries; link; link = link->next)
347 struct passwd *entry = link->data;
348 if (strcmp (entry->pw_name, name) == 0)
364 for (link = user_entries; link; link = link->next)
366 struct passwd *entry = link->data;
367 if (entry->pw_uid == uid)
377 free_group (gpointer data)
379 struct group *entry = data;
381 g_free (entry->gr_name);
382 g_free (entry->gr_passwd);
383 g_strfreev (entry->gr_mem);
390 gchar *path, *data = NULL, **lines;
392 GError *error = NULL;
394 g_list_free_full (group_entries, free_group);
395 group_entries = NULL;
397 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
398 g_file_get_contents (path, &data, NULL, &error);
401 g_warning ("Error loading group file: %s", error->message);
402 g_clear_error (&error);
407 lines = g_strsplit (data, "\n", -1);
410 for (i = 0; lines[i]; i++)
412 gchar *line, **fields;
414 line = g_strstrip (lines[i]);
415 fields = g_strsplit (line, ":", -1);
416 if (g_strv_length (fields) == 4)
418 struct group *entry = malloc (sizeof (struct group));
420 entry->gr_name = g_strdup (fields[0]);
421 entry->gr_passwd = g_strdup (fields[1]);
422 entry->gr_gid = atoi (fields[2]);
423 entry->gr_mem = g_strsplit (fields[3], ",", -1);
424 group_entries = g_list_append (group_entries, entry);
432 getgrnam (const char *name)
438 for (link = group_entries; link; link = link->next)
440 struct group *entry = link->data;
441 if (strcmp (entry->gr_name, name) == 0)
457 for (link = group_entries; link; link = link->next)
459 struct group *entry = link->data;
460 if (entry->gr_gid == gid)
470 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
472 pam_handle_t *handle;
474 if (service_name == NULL || conversation == NULL || pamh == NULL)
475 return PAM_SYSTEM_ERR;
477 handle = *pamh = malloc (sizeof (pam_handle_t));
481 handle->service_name = strdup (service_name);
482 handle->user = user ? strdup (user) : NULL;
483 handle->authtok = NULL;
484 handle->ruser = NULL;
486 handle->conversation.conv = conversation->conv;
487 handle->conversation.appdata_ptr = conversation->appdata_ptr;
488 handle->envlist = malloc (sizeof (char *) * 1);
489 handle->envlist[0] = NULL;
495 send_info (pam_handle_t *pamh, const char *message)
497 struct pam_message **msg;
498 struct pam_response *resp = NULL;
500 msg = calloc (1, sizeof (struct pam_message *));
501 msg[0] = malloc (sizeof (struct pam_message));
502 msg[0]->msg_style = PAM_TEXT_INFO;
503 msg[0]->msg = message;
504 pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
516 pam_authenticate (pam_handle_t *pamh, int flags)
518 struct passwd *entry;
519 gboolean password_matches = FALSE;
522 return PAM_SYSTEM_ERR;
524 if (strcmp (pamh->service_name, "test-remote") == 0)
527 struct pam_message **msg;
528 struct pam_response *resp = NULL;
530 msg = malloc (sizeof (struct pam_message *) * 1);
531 msg[0] = malloc (sizeof (struct pam_message));
532 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
533 msg[0]->msg = "remote-login:";
534 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
537 if (result != PAM_SUCCESS)
542 if (resp[0].resp == NULL)
550 pamh->ruser = strdup (resp[0].resp);
554 msg = malloc (sizeof (struct pam_message *) * 1);
555 msg[0] = malloc (sizeof (struct pam_message));
556 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
557 msg[0]->msg = "remote-password:";
558 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
561 if (result != PAM_SUCCESS)
566 if (resp[0].resp == NULL)
573 free (pamh->authtok);
574 pamh->authtok = strdup (resp[0].resp);
578 password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
580 if (password_matches)
586 /* Prompt for username */
587 if (pamh->user == NULL)
590 struct pam_message **msg;
591 struct pam_response *resp = NULL;
593 msg = malloc (sizeof (struct pam_message *) * 1);
594 msg[0] = malloc (sizeof (struct pam_message));
595 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
596 msg[0]->msg = LOGIN_PROMPT;
597 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
600 if (result != PAM_SUCCESS)
605 if (resp[0].resp == NULL)
611 pamh->user = strdup (resp[0].resp);
616 if (strcmp (pamh->user, "log-pam") == 0)
617 send_info (pamh, "pam_authenticate");
619 /* Crash on authenticate */
620 if (strcmp (pamh->user, "crash-authenticate") == 0)
621 kill (getpid (), SIGSEGV);
623 /* Look up password database */
624 entry = getpwnam (pamh->user);
626 /* Prompt for password if required */
627 if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
628 password_matches = TRUE;
631 int i, n_messages = 0, password_index, result;
632 struct pam_message **msg;
633 struct pam_response *resp = NULL;
635 msg = malloc (sizeof (struct pam_message *) * 5);
636 if (strcmp (pamh->user, "info-prompt") == 0)
638 msg[n_messages] = malloc (sizeof (struct pam_message));
639 msg[n_messages]->msg_style = PAM_TEXT_INFO;
640 msg[n_messages]->msg = "Welcome to LightDM";
643 if (strcmp (pamh->user, "multi-info-prompt") == 0)
645 msg[n_messages] = malloc (sizeof (struct pam_message));
646 msg[n_messages]->msg_style = PAM_TEXT_INFO;
647 msg[n_messages]->msg = "Welcome to LightDM";
649 msg[n_messages] = malloc (sizeof (struct pam_message));
650 msg[n_messages]->msg_style = PAM_ERROR_MSG;
651 msg[n_messages]->msg = "This is an error";
653 msg[n_messages] = malloc (sizeof (struct pam_message));
654 msg[n_messages]->msg_style = PAM_TEXT_INFO;
655 msg[n_messages]->msg = "You should have seen three messages";
658 if (strcmp (pamh->user, "multi-prompt") == 0)
660 msg[n_messages] = malloc (sizeof (struct pam_message));
661 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
662 msg[n_messages]->msg = "Favorite Color:";
665 msg[n_messages] = malloc (sizeof (struct pam_message));
666 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
667 msg[n_messages]->msg = "Password:";
668 password_index = n_messages;
670 result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
671 for (i = 0; i < n_messages; i++)
674 if (result != PAM_SUCCESS)
679 if (resp[password_index].resp == NULL)
686 password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
688 if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
689 password_matches = strcmp ("blue", resp[0].resp) == 0;
691 for (i = 0; i < n_messages; i++)
698 /* Do two factor authentication */
699 if (password_matches && strcmp (pamh->user, "two-factor") == 0)
701 msg = malloc (sizeof (struct pam_message *) * 1);
702 msg[0] = malloc (sizeof (struct pam_message));
703 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
704 msg[0]->msg = "OTP:";
706 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
712 if (resp[0].resp == NULL)
717 password_matches = strcmp (resp[0].resp, "otp") == 0;
723 /* Special user has home directory created on login */
724 if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
725 g_mkdir_with_parents (entry->pw_dir, 0755);
727 /* Special user 'change-user1' changes user on authentication */
728 if (password_matches && strcmp (pamh->user, "change-user1") == 0)
731 pamh->user = g_strdup ("change-user2");
734 /* Special user 'change-user-invalid' changes to an invalid user on authentication */
735 if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
738 pamh->user = g_strdup ("invalid-user");
741 if (password_matches)
748 get_env_value (const char *name_value, const char *name)
752 for (j = 0; name[j] && name[j] != '=' && name[j] == name_value[j]; j++);
753 if (name_value[j] == '=')
754 return &name_value[j + 1];
760 pam_putenv (pam_handle_t *pamh, const char *name_value)
764 if (pamh == NULL || name_value == NULL)
765 return PAM_SYSTEM_ERR;
767 for (i = 0; pamh->envlist[i]; i++)
769 if (get_env_value (pamh->envlist[i], name_value))
773 if (pamh->envlist[i])
775 free (pamh->envlist[i]);
776 pamh->envlist[i] = strdup (name_value);
780 pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
781 pamh->envlist[i] = strdup (name_value);
782 pamh->envlist[i + 1] = NULL;
789 pam_getenv (pam_handle_t *pamh, const char *name)
793 if (pamh == NULL || name == NULL)
796 for (i = 0; pamh->envlist[i]; i++)
799 value = get_env_value (pamh->envlist[i], name);
808 pam_getenvlist (pam_handle_t *pamh)
813 return pamh->envlist;
817 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
819 if (pamh == NULL || item == NULL)
820 return PAM_SYSTEM_ERR;
827 pamh->tty = strdup ((const char *) item);
836 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
838 if (pamh == NULL || item == NULL)
839 return PAM_SYSTEM_ERR;
844 *item = pamh->service_name;
852 *item = pamh->authtok;
859 case PAM_USER_PROMPT:
860 *item = LOGIN_PROMPT;
868 *item = &pamh->conversation;
877 pam_open_session (pam_handle_t *pamh, int flags)
880 return PAM_SYSTEM_ERR;
882 if (strcmp (pamh->user, "session-error") == 0)
883 return PAM_SESSION_ERR;
885 if (strcmp (pamh->user, "log-pam") == 0)
886 send_info (pamh, "pam_open_session");
888 if (strcmp (pamh->user, "make-home-dir") == 0)
890 struct passwd *entry;
891 entry = getpwnam (pamh->user);
892 g_mkdir_with_parents (entry->pw_dir, 0755);
899 pam_close_session (pam_handle_t *pamh, int flags)
902 return PAM_SYSTEM_ERR;
904 if (strcmp (pamh->user, "log-pam") == 0)
905 send_info (pamh, "pam_close_session");
911 pam_acct_mgmt (pam_handle_t *pamh, int flags)
914 return PAM_SYSTEM_ERR;
917 return PAM_USER_UNKNOWN;
919 if (strcmp (pamh->user, "log-pam") == 0)
920 send_info (pamh, "pam_acct_mgmt");
922 if (strcmp (pamh->user, "denied") == 0)
923 return PAM_PERM_DENIED;
924 if (strcmp (pamh->user, "expired") == 0)
925 return PAM_ACCT_EXPIRED;
926 if (strcmp (pamh->user, "new-authtok") == 0)
927 return PAM_NEW_AUTHTOK_REQD;
933 pam_chauthtok (pam_handle_t *pamh, int flags)
935 struct passwd *entry;
937 struct pam_message **msg;
938 struct pam_response *resp = NULL;
941 return PAM_SYSTEM_ERR;
943 if (strcmp (pamh->user, "log-pam") == 0)
944 send_info (pamh, "pam_chauthtok");
946 msg = malloc (sizeof (struct pam_message *) * 1);
947 msg[0] = malloc (sizeof (struct pam_message));
948 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
949 msg[0]->msg = "Enter new password:";
950 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
953 if (result != PAM_SUCCESS)
958 if (resp[0].resp == NULL)
964 /* Update password database */
965 entry = getpwnam (pamh->user);
966 free (entry->pw_passwd);
967 entry->pw_passwd = resp[0].resp;
974 pam_setcred (pam_handle_t *pamh, int flags)
979 return PAM_SYSTEM_ERR;
981 if (strcmp (pamh->user, "log-pam") == 0)
982 send_info (pamh, "pam_setcred");
984 /* Put the test directories into the path */
985 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"));
986 pam_putenv (pamh, e);
989 if (strcmp (pamh->user, "cred-error") == 0)
991 if (strcmp (pamh->user, "cred-expired") == 0)
992 return PAM_CRED_EXPIRED;
993 if (strcmp (pamh->user, "cred-unavail") == 0)
994 return PAM_CRED_UNAVAIL;
996 /* Join special groups if requested */
997 if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
1003 group = getgrnam ("test-group");
1006 groups_length = getgroups (0, NULL);
1007 groups = malloc (sizeof (gid_t) * (groups_length + 1));
1008 groups_length = getgroups (groups_length, groups);
1009 groups[groups_length] = group->gr_gid;
1011 setgroups (groups_length, groups);
1015 /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
1016 pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
1023 pam_end (pam_handle_t *pamh, int pam_status)
1026 return PAM_SYSTEM_ERR;
1028 free (pamh->service_name);
1032 free (pamh->authtok);
1043 pam_strerror (pam_handle_t *pamh, int errnum)
1053 return "Critical error - immediate abort";
1055 return "Failed to load module";
1056 case PAM_SYMBOL_ERR:
1057 return "Symbol not found";
1058 case PAM_SERVICE_ERR:
1059 return "Error in service module";
1060 case PAM_SYSTEM_ERR:
1061 return "System error";
1063 return "Memory buffer error";
1064 case PAM_PERM_DENIED:
1065 return "Permission denied";
1067 return "Authentication failure";
1068 case PAM_CRED_INSUFFICIENT:
1069 return "Insufficient credentials to access authentication data";
1070 case PAM_AUTHINFO_UNAVAIL:
1071 return "Authentication service cannot retrieve authentication info";
1072 case PAM_USER_UNKNOWN:
1073 return "User not known to the underlying authentication module";
1075 return "Have exhausted maximum number of retries for service";
1076 case PAM_NEW_AUTHTOK_REQD:
1077 return "Authentication token is no longer valid; new one required";
1078 case PAM_ACCT_EXPIRED:
1079 return "User account has expired";
1080 case PAM_SESSION_ERR:
1081 return "Cannot make/remove an entry for the specified session";
1082 case PAM_CRED_UNAVAIL:
1083 return "Authentication service cannot retrieve user credentials";
1084 case PAM_CRED_EXPIRED:
1085 return "User credentials expired";
1087 return "Failure setting user credentials";
1088 case PAM_NO_MODULE_DATA:
1089 return "No module specific data is present";
1091 return "Bad item passed to pam_*_item()";
1093 return "Conversation error";
1094 case PAM_AUTHTOK_ERR:
1095 return "Authentication token manipulation error";
1096 case PAM_AUTHTOK_RECOVERY_ERR:
1097 return "Authentication information cannot be recovered";
1098 case PAM_AUTHTOK_LOCK_BUSY:
1099 return "Authentication token lock busy";
1100 case PAM_AUTHTOK_DISABLE_AGING:
1101 return "Authentication token aging disabled";
1103 return "Failed preliminary check by password service";
1105 return "The return value should be ignored by PAM dispatch";
1106 case PAM_MODULE_UNKNOWN:
1107 return "Module is unknown";
1108 case PAM_AUTHTOK_EXPIRED:
1109 return "Authentication token expired";
1110 case PAM_CONV_AGAIN:
1111 return "Conversation is waiting for event";
1112 case PAM_INCOMPLETE:
1113 return "Application needs to call libpam again";
1115 return "Unknown PAM error";
1125 pututxline (struct utmp *ut)