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);
123 open (const char *pathname, int flags, ...)
125 int (*_open) (const char * pathname, int flags, mode_t mode);
131 va_start (ap, flags);
132 mode = va_arg (ap, int);
136 _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, "open");
137 if (strcmp (pathname, "/dev/console") == 0)
141 console_fd = _open ("/dev/null", flags, mode);
142 fcntl (console_fd, F_SETFD, FD_CLOEXEC);
146 else if (strcmp (pathname, CONFIG_DIR "/lightdm.conf") == 0)
151 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "lightdm", "lightdm.conf", NULL);
152 fd = _open (path, flags, mode);
158 return _open (pathname, flags, mode);
162 ioctl (int d, int request, void *data)
164 int (*_ioctl) (int d, int request, void *data);
166 _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
167 if (d > 0 && d == console_fd)
169 struct vt_stat *console_state;
174 console_state = data;
175 console_state->v_active = 7;
183 return _ioctl (d, request, data);
189 int (*_close) (int fd);
191 if (fd > 0 && fd == console_fd)
194 _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
200 free_user (gpointer data)
202 struct passwd *entry = data;
204 g_free (entry->pw_name);
205 g_free (entry->pw_passwd);
206 g_free (entry->pw_gecos);
207 g_free (entry->pw_dir);
208 g_free (entry->pw_shell);
215 gchar *path, *data = NULL, **lines;
217 GError *error = NULL;
219 g_list_free_full (user_entries, free_user);
221 getpwent_link = NULL;
223 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
224 g_file_get_contents (path, &data, NULL, &error);
227 g_warning ("Error loading passwd file: %s", error->message);
228 g_clear_error (&error);
233 lines = g_strsplit (data, "\n", -1);
236 for (i = 0; lines[i]; i++)
238 gchar *line, **fields;
240 line = g_strstrip (lines[i]);
241 fields = g_strsplit (line, ":", -1);
242 if (g_strv_length (fields) == 7)
244 struct passwd *entry = malloc (sizeof (struct passwd));
246 entry->pw_name = g_strdup (fields[0]);
247 entry->pw_passwd = g_strdup (fields[1]);
248 entry->pw_uid = atoi (fields[2]);
249 entry->pw_gid = atoi (fields[3]);
250 entry->pw_gecos = g_strdup (fields[4]);
251 entry->pw_dir = g_strdup (fields[5]);
252 entry->pw_shell = g_strdup (fields[6]);
253 user_entries = g_list_append (user_entries, entry);
263 if (getpwent_link == NULL)
266 if (user_entries == NULL)
268 getpwent_link = user_entries;
272 if (getpwent_link->next == NULL)
274 getpwent_link = getpwent_link->next;
277 return getpwent_link->data;
283 getpwent_link = NULL;
289 getpwent_link = NULL;
293 getpwnam (const char *name)
302 for (link = user_entries; link; link = link->next)
304 struct passwd *entry = link->data;
305 if (strcmp (entry->pw_name, name) == 0)
321 for (link = user_entries; link; link = link->next)
323 struct passwd *entry = link->data;
324 if (entry->pw_uid == uid)
334 free_group (gpointer data)
336 struct group *entry = data;
338 g_free (entry->gr_name);
339 g_free (entry->gr_passwd);
340 g_strfreev (entry->gr_mem);
347 gchar *path, *data = NULL, **lines;
349 GError *error = NULL;
351 g_list_free_full (group_entries, free_group);
352 group_entries = NULL;
354 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
355 g_file_get_contents (path, &data, NULL, &error);
358 g_warning ("Error loading group file: %s", error->message);
359 g_clear_error (&error);
364 lines = g_strsplit (data, "\n", -1);
367 for (i = 0; lines[i]; i++)
369 gchar *line, **fields;
371 line = g_strstrip (lines[i]);
372 fields = g_strsplit (line, ":", -1);
373 if (g_strv_length (fields) == 4)
375 struct group *entry = malloc (sizeof (struct group));
377 entry->gr_name = g_strdup (fields[0]);
378 entry->gr_passwd = g_strdup (fields[1]);
379 entry->gr_gid = atoi (fields[2]);
380 entry->gr_mem = g_strsplit (fields[3], ",", -1);
381 group_entries = g_list_append (group_entries, entry);
389 getgrnam (const char *name)
395 for (link = group_entries; link; link = link->next)
397 struct group *entry = link->data;
398 if (strcmp (entry->gr_name, name) == 0)
414 for (link = group_entries; link; link = link->next)
416 struct group *entry = link->data;
417 if (entry->gr_gid == gid)
427 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
429 pam_handle_t *handle;
431 if (service_name == NULL || conversation == NULL || pamh == NULL)
432 return PAM_SYSTEM_ERR;
434 handle = *pamh = malloc (sizeof (pam_handle_t));
438 handle->service_name = strdup (service_name);
439 handle->user = user ? strdup (user) : NULL;
440 handle->authtok = NULL;
441 handle->ruser = NULL;
443 handle->conversation.conv = conversation->conv;
444 handle->conversation.appdata_ptr = conversation->appdata_ptr;
445 handle->envlist = malloc (sizeof (char *) * 1);
446 handle->envlist[0] = NULL;
452 send_info (pam_handle_t *pamh, const char *message)
454 struct pam_message **msg;
455 struct pam_response *resp = NULL;
457 msg = calloc (1, sizeof (struct pam_message *));
458 msg[0] = malloc (sizeof (struct pam_message));
459 msg[0]->msg_style = PAM_TEXT_INFO;
460 msg[0]->msg = message;
461 pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
473 pam_authenticate (pam_handle_t *pamh, int flags)
475 struct passwd *entry;
476 gboolean password_matches = FALSE;
479 return PAM_SYSTEM_ERR;
481 if (strcmp (pamh->service_name, "test-remote") == 0)
484 struct pam_message **msg;
485 struct pam_response *resp = NULL;
487 msg = malloc (sizeof (struct pam_message *) * 1);
488 msg[0] = malloc (sizeof (struct pam_message));
489 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
490 msg[0]->msg = "remote-login:";
491 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
494 if (result != PAM_SUCCESS)
499 if (resp[0].resp == NULL)
507 pamh->ruser = strdup (resp[0].resp);
511 msg = malloc (sizeof (struct pam_message *) * 1);
512 msg[0] = malloc (sizeof (struct pam_message));
513 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
514 msg[0]->msg = "remote-password:";
515 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
518 if (result != PAM_SUCCESS)
523 if (resp[0].resp == NULL)
530 free (pamh->authtok);
531 pamh->authtok = strdup (resp[0].resp);
535 password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
537 if (password_matches)
543 /* Prompt for username */
544 if (pamh->user == NULL)
547 struct pam_message **msg;
548 struct pam_response *resp = NULL;
550 msg = malloc (sizeof (struct pam_message *) * 1);
551 msg[0] = malloc (sizeof (struct pam_message));
552 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
553 msg[0]->msg = LOGIN_PROMPT;
554 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
557 if (result != PAM_SUCCESS)
562 if (resp[0].resp == NULL)
568 pamh->user = strdup (resp[0].resp);
573 if (strcmp (pamh->user, "log-pam") == 0)
574 send_info (pamh, "pam_authenticate");
576 /* Crash on authenticate */
577 if (strcmp (pamh->user, "crash-authenticate") == 0)
578 kill (getpid (), SIGSEGV);
580 /* Look up password database */
581 entry = getpwnam (pamh->user);
583 /* Prompt for password if required */
584 if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
585 password_matches = TRUE;
588 int i, n_messages = 0, password_index, result;
589 struct pam_message **msg;
590 struct pam_response *resp = NULL;
592 msg = malloc (sizeof (struct pam_message *) * 5);
593 if (strcmp (pamh->user, "info-prompt") == 0)
595 msg[n_messages] = malloc (sizeof (struct pam_message));
596 msg[n_messages]->msg_style = PAM_TEXT_INFO;
597 msg[n_messages]->msg = "Welcome to LightDM";
600 if (strcmp (pamh->user, "multi-info-prompt") == 0)
602 msg[n_messages] = malloc (sizeof (struct pam_message));
603 msg[n_messages]->msg_style = PAM_TEXT_INFO;
604 msg[n_messages]->msg = "Welcome to LightDM";
606 msg[n_messages] = malloc (sizeof (struct pam_message));
607 msg[n_messages]->msg_style = PAM_ERROR_MSG;
608 msg[n_messages]->msg = "This is an error";
610 msg[n_messages] = malloc (sizeof (struct pam_message));
611 msg[n_messages]->msg_style = PAM_TEXT_INFO;
612 msg[n_messages]->msg = "You should have seen three messages";
615 if (strcmp (pamh->user, "multi-prompt") == 0)
617 msg[n_messages] = malloc (sizeof (struct pam_message));
618 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
619 msg[n_messages]->msg = "Favorite Color:";
622 msg[n_messages] = malloc (sizeof (struct pam_message));
623 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
624 msg[n_messages]->msg = "Password:";
625 password_index = n_messages;
627 result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
628 for (i = 0; i < n_messages; i++)
631 if (result != PAM_SUCCESS)
636 if (resp[password_index].resp == NULL)
643 password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
645 if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
646 password_matches = strcmp ("blue", resp[0].resp) == 0;
648 for (i = 0; i < n_messages; i++)
655 /* Do two factor authentication */
656 if (password_matches && strcmp (pamh->user, "two-factor") == 0)
658 msg = malloc (sizeof (struct pam_message *) * 1);
659 msg[0] = malloc (sizeof (struct pam_message));
660 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
661 msg[0]->msg = "OTP:";
663 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
669 if (resp[0].resp == NULL)
674 password_matches = strcmp (resp[0].resp, "otp") == 0;
680 /* Special user has home directory created on login */
681 if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
682 g_mkdir_with_parents (entry->pw_dir, 0755);
684 /* Special user 'change-user1' changes user on authentication */
685 if (password_matches && strcmp (pamh->user, "change-user1") == 0)
688 pamh->user = g_strdup ("change-user2");
691 /* Special user 'change-user-invalid' changes to an invalid user on authentication */
692 if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
695 pamh->user = g_strdup ("invalid-user");
698 if (password_matches)
705 get_env_value (const char *name_value, const char *name)
709 for (j = 0; name[j] && name[j] != '=' && name[j] == name_value[j]; j++);
710 if (name_value[j] == '=')
711 return &name_value[j + 1];
717 pam_putenv (pam_handle_t *pamh, const char *name_value)
721 if (pamh == NULL || name_value == NULL)
722 return PAM_SYSTEM_ERR;
724 for (i = 0; pamh->envlist[i]; i++)
726 if (get_env_value (pamh->envlist[i], name_value))
730 if (pamh->envlist[i])
732 free (pamh->envlist[i]);
733 pamh->envlist[i] = strdup (name_value);
737 pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
738 pamh->envlist[i] = strdup (name_value);
739 pamh->envlist[i + 1] = NULL;
746 pam_getenv (pam_handle_t *pamh, const char *name)
750 if (pamh == NULL || name == NULL)
753 for (i = 0; pamh->envlist[i]; i++)
756 value = get_env_value (pamh->envlist[i], name);
765 pam_getenvlist (pam_handle_t *pamh)
770 return pamh->envlist;
774 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
776 if (pamh == NULL || item == NULL)
777 return PAM_SYSTEM_ERR;
784 pamh->tty = strdup ((const char *) item);
793 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
795 if (pamh == NULL || item == NULL)
796 return PAM_SYSTEM_ERR;
801 *item = pamh->service_name;
809 *item = pamh->authtok;
816 case PAM_USER_PROMPT:
817 *item = LOGIN_PROMPT;
825 *item = &pamh->conversation;
834 pam_open_session (pam_handle_t *pamh, int flags)
837 return PAM_SYSTEM_ERR;
839 if (strcmp (pamh->user, "session-error") == 0)
840 return PAM_SESSION_ERR;
842 if (strcmp (pamh->user, "log-pam") == 0)
843 send_info (pamh, "pam_open_session");
845 if (strcmp (pamh->user, "make-home-dir") == 0)
847 struct passwd *entry;
848 entry = getpwnam (pamh->user);
849 g_mkdir_with_parents (entry->pw_dir, 0755);
856 pam_close_session (pam_handle_t *pamh, int flags)
859 return PAM_SYSTEM_ERR;
861 if (strcmp (pamh->user, "log-pam") == 0)
862 send_info (pamh, "pam_close_session");
868 pam_acct_mgmt (pam_handle_t *pamh, int flags)
871 return PAM_SYSTEM_ERR;
874 return PAM_USER_UNKNOWN;
876 if (strcmp (pamh->user, "log-pam") == 0)
877 send_info (pamh, "pam_acct_mgmt");
879 if (strcmp (pamh->user, "denied") == 0)
880 return PAM_PERM_DENIED;
881 if (strcmp (pamh->user, "expired") == 0)
882 return PAM_ACCT_EXPIRED;
883 if (strcmp (pamh->user, "new-authtok") == 0)
884 return PAM_NEW_AUTHTOK_REQD;
890 pam_chauthtok (pam_handle_t *pamh, int flags)
892 struct passwd *entry;
894 struct pam_message **msg;
895 struct pam_response *resp = NULL;
898 return PAM_SYSTEM_ERR;
900 if (strcmp (pamh->user, "log-pam") == 0)
901 send_info (pamh, "pam_chauthtok");
903 msg = malloc (sizeof (struct pam_message *) * 1);
904 msg[0] = malloc (sizeof (struct pam_message));
905 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
906 msg[0]->msg = "Enter new password:";
907 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
910 if (result != PAM_SUCCESS)
915 if (resp[0].resp == NULL)
921 /* Update password database */
922 entry = getpwnam (pamh->user);
923 free (entry->pw_passwd);
924 entry->pw_passwd = resp[0].resp;
931 pam_setcred (pam_handle_t *pamh, int flags)
936 return PAM_SYSTEM_ERR;
938 if (strcmp (pamh->user, "log-pam") == 0)
939 send_info (pamh, "pam_setcred");
941 /* Put the test directories into the path */
942 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"));
943 pam_putenv (pamh, e);
946 if (strcmp (pamh->user, "cred-error") == 0)
948 if (strcmp (pamh->user, "cred-expired") == 0)
949 return PAM_CRED_EXPIRED;
950 if (strcmp (pamh->user, "cred-unavail") == 0)
951 return PAM_CRED_UNAVAIL;
953 /* Join special groups if requested */
954 if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
960 group = getgrnam ("test-group");
963 groups_length = getgroups (0, NULL);
964 groups = malloc (sizeof (gid_t) * (groups_length + 1));
965 groups_length = getgroups (groups_length, groups);
966 groups[groups_length] = group->gr_gid;
968 setgroups (groups_length, groups);
972 /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
973 pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
980 pam_end (pam_handle_t *pamh, int pam_status)
983 return PAM_SYSTEM_ERR;
985 free (pamh->service_name);
989 free (pamh->authtok);
1000 pam_strerror (pam_handle_t *pamh, int errnum)
1010 return "Critical error - immediate abort";
1012 return "Failed to load module";
1013 case PAM_SYMBOL_ERR:
1014 return "Symbol not found";
1015 case PAM_SERVICE_ERR:
1016 return "Error in service module";
1017 case PAM_SYSTEM_ERR:
1018 return "System error";
1020 return "Memory buffer error";
1021 case PAM_PERM_DENIED:
1022 return "Permission denied";
1024 return "Authentication failure";
1025 case PAM_CRED_INSUFFICIENT:
1026 return "Insufficient credentials to access authentication data";
1027 case PAM_AUTHINFO_UNAVAIL:
1028 return "Authentication service cannot retrieve authentication info";
1029 case PAM_USER_UNKNOWN:
1030 return "User not known to the underlying authentication module";
1032 return "Have exhausted maximum number of retries for service";
1033 case PAM_NEW_AUTHTOK_REQD:
1034 return "Authentication token is no longer valid; new one required";
1035 case PAM_ACCT_EXPIRED:
1036 return "User account has expired";
1037 case PAM_SESSION_ERR:
1038 return "Cannot make/remove an entry for the specified session";
1039 case PAM_CRED_UNAVAIL:
1040 return "Authentication service cannot retrieve user credentials";
1041 case PAM_CRED_EXPIRED:
1042 return "User credentials expired";
1044 return "Failure setting user credentials";
1045 case PAM_NO_MODULE_DATA:
1046 return "No module specific data is present";
1048 return "Bad item passed to pam_*_item()";
1050 return "Conversation error";
1051 case PAM_AUTHTOK_ERR:
1052 return "Authentication token manipulation error";
1053 case PAM_AUTHTOK_RECOVERY_ERR:
1054 return "Authentication information cannot be recovered";
1055 case PAM_AUTHTOK_LOCK_BUSY:
1056 return "Authentication token lock busy";
1057 case PAM_AUTHTOK_DISABLE_AGING:
1058 return "Authentication token aging disabled";
1060 return "Failed preliminary check by password service";
1062 return "The return value should be ignored by PAM dispatch";
1063 case PAM_MODULE_UNKNOWN:
1064 return "Module is unknown";
1065 case PAM_AUTHTOK_EXPIRED:
1066 return "Authentication token expired";
1067 case PAM_CONV_AGAIN:
1068 return "Conversation is waiting for event";
1069 case PAM_INCOMPLETE:
1070 return "Application needs to call libpam again";
1072 return "Unknown PAM error";
1082 pututxline (struct utmp *ut)