7 #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;
32 struct pam_conv conversation;
48 initgroups (const char *user, gid_t group)
59 getgroups (int size, gid_t list[])
61 const gchar *group_list;
65 /* Get groups we are a member of */
66 group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
69 groups = g_strsplit (group_list, ",", -1);
70 groups_length = g_strv_length (groups);
76 if (groups_length > size)
81 for (i = 0; groups[i]; i++)
82 list[i] = atoi (groups[i]);
90 setgroups (size_t size, const gid_t *list)
95 group_list = g_string_new ("");
96 for (i = 0; i < size; i++)
99 g_string_append (group_list, ",");
100 g_string_append_printf (group_list, "%d", list[i]);
102 g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
103 g_string_free (group_list, TRUE);
122 open (const char *pathname, int flags, ...)
124 int (*_open) (const char * pathname, int flags, mode_t mode);
130 va_start (ap, flags);
131 mode = va_arg (ap, int);
135 _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, "open");
136 if (strcmp (pathname, "/dev/console") == 0)
140 console_fd = _open ("/dev/null", flags, mode);
141 fcntl (console_fd, F_SETFD, FD_CLOEXEC);
145 else if (strcmp (pathname, CONFIG_DIR "/lightdm.conf") == 0)
150 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "lightdm", "lightdm.conf", NULL);
151 fd = _open (path, flags, mode);
157 return _open (pathname, flags, mode);
161 ioctl (int d, int request, void *data)
163 int (*_ioctl) (int d, int request, void *data);
165 _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
166 if (d > 0 && d == console_fd)
168 struct vt_stat *console_state;
173 console_state = data;
174 console_state->v_active = 7;
182 return _ioctl (d, request, data);
188 int (*_close) (int fd);
190 if (fd > 0 && fd == console_fd)
193 _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
199 free_user (gpointer data)
201 struct passwd *entry = data;
203 g_free (entry->pw_name);
204 g_free (entry->pw_passwd);
205 g_free (entry->pw_gecos);
206 g_free (entry->pw_dir);
207 g_free (entry->pw_shell);
214 gchar *path, *data = NULL, **lines;
216 GError *error = NULL;
218 g_list_free_full (user_entries, free_user);
220 getpwent_link = NULL;
222 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
223 g_file_get_contents (path, &data, NULL, &error);
226 g_warning ("Error loading passwd file: %s", error->message);
227 g_clear_error (&error);
232 lines = g_strsplit (data, "\n", -1);
235 for (i = 0; lines[i]; i++)
237 gchar *line, **fields;
239 line = g_strstrip (lines[i]);
240 fields = g_strsplit (line, ":", -1);
241 if (g_strv_length (fields) == 7)
243 struct passwd *entry = malloc (sizeof (struct passwd));
245 entry->pw_name = g_strdup (fields[0]);
246 entry->pw_passwd = g_strdup (fields[1]);
247 entry->pw_uid = atoi (fields[2]);
248 entry->pw_gid = atoi (fields[3]);
249 entry->pw_gecos = g_strdup (fields[4]);
250 entry->pw_dir = g_strdup (fields[5]);
251 entry->pw_shell = g_strdup (fields[6]);
252 user_entries = g_list_append (user_entries, entry);
262 if (getpwent_link == NULL)
265 if (user_entries == NULL)
267 getpwent_link = user_entries;
271 if (getpwent_link->next == NULL)
273 getpwent_link = getpwent_link->next;
276 return getpwent_link->data;
282 getpwent_link = NULL;
288 getpwent_link = NULL;
292 getpwnam (const char *name)
301 for (link = user_entries; link; link = link->next)
303 struct passwd *entry = link->data;
304 if (strcmp (entry->pw_name, name) == 0)
320 for (link = user_entries; link; link = link->next)
322 struct passwd *entry = link->data;
323 if (entry->pw_uid == uid)
333 free_group (gpointer data)
335 struct group *entry = data;
337 g_free (entry->gr_name);
338 g_free (entry->gr_passwd);
339 g_strfreev (entry->gr_mem);
346 gchar *path, *data = NULL, **lines;
348 GError *error = NULL;
350 g_list_free_full (group_entries, free_group);
351 group_entries = NULL;
353 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
354 g_file_get_contents (path, &data, NULL, &error);
357 g_warning ("Error loading group file: %s", error->message);
358 g_clear_error (&error);
363 lines = g_strsplit (data, "\n", -1);
366 for (i = 0; lines[i]; i++)
368 gchar *line, **fields;
370 line = g_strstrip (lines[i]);
371 fields = g_strsplit (line, ":", -1);
372 if (g_strv_length (fields) == 4)
374 struct group *entry = malloc (sizeof (struct group));
376 entry->gr_name = g_strdup (fields[0]);
377 entry->gr_passwd = g_strdup (fields[1]);
378 entry->gr_gid = atoi (fields[2]);
379 entry->gr_mem = g_strsplit (fields[3], ",", -1);
380 group_entries = g_list_append (group_entries, entry);
388 getgrnam (const char *name)
394 for (link = group_entries; link; link = link->next)
396 struct group *entry = link->data;
397 if (strcmp (entry->gr_name, name) == 0)
413 for (link = group_entries; link; link = link->next)
415 struct group *entry = link->data;
416 if (entry->gr_gid == gid)
426 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
428 pam_handle_t *handle;
430 if (service_name == NULL || conversation == NULL || pamh == NULL)
431 return PAM_SYSTEM_ERR;
433 handle = *pamh = malloc (sizeof (pam_handle_t));
437 handle->service_name = strdup (service_name);
438 handle->user = user ? strdup (user) : NULL;
440 handle->conversation.conv = conversation->conv;
441 handle->conversation.appdata_ptr = conversation->appdata_ptr;
442 handle->envlist = malloc (sizeof (char *) * 1);
443 handle->envlist[0] = NULL;
449 send_info (pam_handle_t *pamh, const char *message)
451 struct pam_message **msg;
452 struct pam_response *resp = NULL;
454 msg = calloc (1, sizeof (struct pam_message *));
455 msg[0] = malloc (sizeof (struct pam_message));
456 msg[0]->msg_style = PAM_TEXT_INFO;
457 msg[0]->msg = message;
458 pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
470 pam_authenticate (pam_handle_t *pamh, int flags)
472 struct passwd *entry;
473 gboolean password_matches = FALSE;
476 return PAM_SYSTEM_ERR;
478 /* Prompt for username */
479 if (pamh->user == NULL)
482 struct pam_message **msg;
483 struct pam_response *resp = NULL;
485 msg = malloc (sizeof (struct pam_message *) * 1);
486 msg[0] = malloc (sizeof (struct pam_message));
487 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
488 msg[0]->msg = LOGIN_PROMPT;
489 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
492 if (result != PAM_SUCCESS)
497 if (resp[0].resp == NULL)
503 pamh->user = strdup (resp[0].resp);
508 if (strcmp (pamh->user, "log-pam") == 0)
509 send_info (pamh, "pam_authenticate");
511 /* Crash on authenticate */
512 if (strcmp (pamh->user, "crash-authenticate") == 0)
513 kill (getpid (), SIGSEGV);
515 /* Look up password database */
516 entry = getpwnam (pamh->user);
518 /* Prompt for password if required */
519 if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
520 password_matches = TRUE;
523 int i, n_messages = 0, password_index, result;
524 struct pam_message **msg;
525 struct pam_response *resp = NULL;
527 msg = malloc (sizeof (struct pam_message *) * 5);
528 if (strcmp (pamh->user, "info-prompt") == 0)
530 msg[n_messages] = malloc (sizeof (struct pam_message));
531 msg[n_messages]->msg_style = PAM_TEXT_INFO;
532 msg[n_messages]->msg = "Welcome to LightDM";
535 if (strcmp (pamh->user, "multi-info-prompt") == 0)
537 msg[n_messages] = malloc (sizeof (struct pam_message));
538 msg[n_messages]->msg_style = PAM_TEXT_INFO;
539 msg[n_messages]->msg = "Welcome to LightDM";
541 msg[n_messages] = malloc (sizeof (struct pam_message));
542 msg[n_messages]->msg_style = PAM_ERROR_MSG;
543 msg[n_messages]->msg = "This is an error";
545 msg[n_messages] = malloc (sizeof (struct pam_message));
546 msg[n_messages]->msg_style = PAM_TEXT_INFO;
547 msg[n_messages]->msg = "You should have seen three messages";
550 msg[n_messages] = malloc (sizeof (struct pam_message));
551 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
552 msg[n_messages]->msg = "Password:";
553 password_index = n_messages;
555 result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
556 for (i = 0; i < n_messages; i++)
559 if (result != PAM_SUCCESS)
564 if (resp[password_index].resp == NULL)
571 password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
572 for (i = 0; i < n_messages; i++)
579 /* Do two factor authentication */
580 if (password_matches && strcmp (pamh->user, "two-factor") == 0)
582 msg = malloc (sizeof (struct pam_message *) * 1);
583 msg[0] = malloc (sizeof (struct pam_message));
584 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
585 msg[0]->msg = "OTP:";
587 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
593 if (resp[0].resp == NULL)
598 password_matches = strcmp (resp[0].resp, "otp") == 0;
604 /* Special user has home directory created on login */
605 if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
606 g_mkdir_with_parents (entry->pw_dir, 0755);
608 /* Special user 'change-user1' changes user on authentication */
609 if (password_matches && strcmp (pamh->user, "change-user1") == 0)
612 pamh->user = g_strdup ("change-user2");
615 /* Special user 'change-user-invalid' changes to an invalid user on authentication */
616 if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
619 pamh->user = g_strdup ("invalid-user");
622 if (password_matches)
629 get_env_value (const char *name_value, const char *name)
633 for (j = 0; name[j] && name[j] != '=' && name[j] == name_value[j]; j++);
634 if (name_value[j] == '=')
635 return &name_value[j + 1];
641 pam_putenv (pam_handle_t *pamh, const char *name_value)
645 if (pamh == NULL || name_value == NULL)
646 return PAM_SYSTEM_ERR;
648 for (i = 0; pamh->envlist[i]; i++)
650 if (get_env_value (pamh->envlist[i], name_value))
654 if (pamh->envlist[i])
656 free (pamh->envlist[i]);
657 pamh->envlist[i] = strdup (name_value);
661 pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
662 pamh->envlist[i] = strdup (name_value);
663 pamh->envlist[i + 1] = NULL;
670 pam_getenv (pam_handle_t *pamh, const char *name)
674 if (pamh == NULL || name == NULL)
677 for (i = 0; pamh->envlist[i]; i++)
680 value = get_env_value (pamh->envlist[i], name);
689 pam_getenvlist (pam_handle_t *pamh)
694 return pamh->envlist;
698 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
700 if (pamh == NULL || item == NULL)
701 return PAM_SYSTEM_ERR;
708 pamh->tty = strdup ((const char *) item);
717 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
719 if (pamh == NULL || item == NULL)
720 return PAM_SYSTEM_ERR;
725 *item = pamh->service_name;
732 case PAM_USER_PROMPT:
733 *item = LOGIN_PROMPT;
741 *item = &pamh->conversation;
750 pam_open_session (pam_handle_t *pamh, int flags)
753 return PAM_SYSTEM_ERR;
755 if (strcmp (pamh->user, "session-error") == 0)
756 return PAM_SESSION_ERR;
758 if (strcmp (pamh->user, "log-pam") == 0)
759 send_info (pamh, "pam_open_session");
761 if (strcmp (pamh->user, "make-home-dir") == 0)
763 struct passwd *entry;
764 entry = getpwnam (pamh->user);
765 g_mkdir_with_parents (entry->pw_dir, 0755);
772 pam_close_session (pam_handle_t *pamh, int flags)
775 return PAM_SYSTEM_ERR;
777 if (strcmp (pamh->user, "log-pam") == 0)
778 send_info (pamh, "pam_close_session");
784 pam_acct_mgmt (pam_handle_t *pamh, int flags)
787 return PAM_SYSTEM_ERR;
790 return PAM_USER_UNKNOWN;
792 if (strcmp (pamh->user, "log-pam") == 0)
793 send_info (pamh, "pam_acct_mgmt");
795 if (strcmp (pamh->user, "denied") == 0)
796 return PAM_PERM_DENIED;
797 if (strcmp (pamh->user, "expired") == 0)
798 return PAM_ACCT_EXPIRED;
799 if (strcmp (pamh->user, "new-authtok") == 0)
800 return PAM_NEW_AUTHTOK_REQD;
806 pam_chauthtok (pam_handle_t *pamh, int flags)
808 struct passwd *entry;
810 struct pam_message **msg;
811 struct pam_response *resp = NULL;
814 return PAM_SYSTEM_ERR;
816 if (strcmp (pamh->user, "log-pam") == 0)
817 send_info (pamh, "pam_chauthtok");
819 msg = malloc (sizeof (struct pam_message *) * 1);
820 msg[0] = malloc (sizeof (struct pam_message));
821 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
822 msg[0]->msg = "Enter new password:";
823 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
826 if (result != PAM_SUCCESS)
831 if (resp[0].resp == NULL)
837 /* Update password database */
838 entry = getpwnam (pamh->user);
839 free (entry->pw_passwd);
840 entry->pw_passwd = resp[0].resp;
847 pam_setcred (pam_handle_t *pamh, int flags)
852 return PAM_SYSTEM_ERR;
854 if (strcmp (pamh->user, "log-pam") == 0)
855 send_info (pamh, "pam_setcred");
857 /* Put the test directories into the path */
858 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"));
859 pam_putenv (pamh, e);
862 if (strcmp (pamh->user, "cred-error") == 0)
864 if (strcmp (pamh->user, "cred-expired") == 0)
865 return PAM_CRED_EXPIRED;
866 if (strcmp (pamh->user, "cred-unavail") == 0)
867 return PAM_CRED_UNAVAIL;
869 /* Join special groups if requested */
870 if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
876 group = getgrnam ("test-group");
879 groups_length = getgroups (0, NULL);
880 groups = malloc (sizeof (gid_t) * (groups_length + 1));
881 groups_length = getgroups (groups_length, groups);
882 groups[groups_length] = group->gr_gid;
884 setgroups (groups_length, groups);
888 /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
889 pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
896 pam_end (pam_handle_t *pamh, int pam_status)
899 return PAM_SYSTEM_ERR;
901 free (pamh->service_name);
912 pam_strerror (pam_handle_t *pamh, int errnum)
922 return "Critical error - immediate abort";
924 return "Failed to load module";
926 return "Symbol not found";
927 case PAM_SERVICE_ERR:
928 return "Error in service module";
930 return "System error";
932 return "Memory buffer error";
933 case PAM_PERM_DENIED:
934 return "Permission denied";
936 return "Authentication failure";
937 case PAM_CRED_INSUFFICIENT:
938 return "Insufficient credentials to access authentication data";
939 case PAM_AUTHINFO_UNAVAIL:
940 return "Authentication service cannot retrieve authentication info";
941 case PAM_USER_UNKNOWN:
942 return "User not known to the underlying authentication module";
944 return "Have exhausted maximum number of retries for service";
945 case PAM_NEW_AUTHTOK_REQD:
946 return "Authentication token is no longer valid; new one required";
947 case PAM_ACCT_EXPIRED:
948 return "User account has expired";
949 case PAM_SESSION_ERR:
950 return "Cannot make/remove an entry for the specified session";
951 case PAM_CRED_UNAVAIL:
952 return "Authentication service cannot retrieve user credentials";
953 case PAM_CRED_EXPIRED:
954 return "User credentials expired";
956 return "Failure setting user credentials";
957 case PAM_NO_MODULE_DATA:
958 return "No module specific data is present";
960 return "Bad item passed to pam_*_item()";
962 return "Conversation error";
963 case PAM_AUTHTOK_ERR:
964 return "Authentication token manipulation error";
965 case PAM_AUTHTOK_RECOVERY_ERR:
966 return "Authentication information cannot be recovered";
967 case PAM_AUTHTOK_LOCK_BUSY:
968 return "Authentication token lock busy";
969 case PAM_AUTHTOK_DISABLE_AGING:
970 return "Authentication token aging disabled";
972 return "Failed preliminary check by password service";
974 return "The return value should be ignored by PAM dispatch";
975 case PAM_MODULE_UNKNOWN:
976 return "Module is unknown";
977 case PAM_AUTHTOK_EXPIRED:
978 return "Authentication token expired";
980 return "Conversation is waiting for event";
982 return "Application needs to call libpam again";
984 return "Unknown PAM error";