]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/libsystem.c
Fix PAM conversations after authentication from locking up sessions
[sojka/lightdm.git] / tests / src / libsystem.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <sys/types.h>
5 #include <pwd.h>
6 #include <grp.h>
7 #include <security/pam_appl.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #define __USE_GNU
11 #include <dlfcn.h>
12 #ifdef __linux__
13 #include <linux/vt.h>
14 #endif
15 #include <glib.h>
16
17 #define LOGIN_PROMPT "login:"
18
19 static int console_fd = -1;
20
21 static GList *user_entries = NULL;
22 static GList *getpwent_link = NULL;
23
24 static GList *group_entries = NULL;
25
26 struct pam_handle
27 {
28     char *service_name;
29     char *user;
30     char *tty;
31     char **envlist;
32     struct pam_conv conversation;
33 };
34
35 uid_t
36 getuid (void)
37 {
38     return 0;
39 }
40
41 /*uid_t
42 geteuid (void)
43 {
44     return 0;
45 }*/
46
47 int
48 initgroups (const char *user, gid_t group)
49 {
50     gid_t g[1];
51
52     g[0] = group;
53     setgroups (1, g);
54
55     return 0;
56 }
57
58 int
59 getgroups (int size, gid_t list[])
60 {
61     const gchar *group_list;
62     gchar **groups;
63     gint groups_length;
64
65     /* Get groups we are a member of */
66     group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
67     if (!group_list)
68         group_list = "";
69     groups = g_strsplit (group_list, ",", -1);
70     groups_length = g_strv_length (groups);
71
72     if (size != 0)
73     {
74         int i;
75
76         if (groups_length > size)
77         {
78             errno = EINVAL;
79             return -1;
80         }
81         for (i = 0; groups[i]; i++)
82             list[i] = atoi (groups[i]);
83     }
84     g_free (groups);
85
86     return groups_length;
87 }
88
89 int
90 setgroups (size_t size, const gid_t *list)
91 {
92     size_t i;
93     GString *group_list;
94
95     group_list = g_string_new ("");
96     for (i = 0; i < size; i++)
97     {
98         if (i != 0)
99             g_string_append (group_list, ",");
100         g_string_append_printf (group_list, "%d", list[i]);
101     }
102     g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
103     g_string_free (group_list, TRUE);
104
105     return 0;
106 }
107
108 int
109 setgid (gid_t gid)
110 {
111     return 0;
112 }
113
114 int
115 setuid (uid_t uid)
116 {
117     return 0;
118 }
119
120 #ifdef __linux__
121 int
122 open (const char *pathname, int flags, ...)
123 {
124     int (*_open) (const char * pathname, int flags, mode_t mode);
125     int mode = 0;
126   
127     if (flags & O_CREAT)
128     {
129         va_list ap;
130         va_start (ap, flags);
131         mode = va_arg (ap, int);
132         va_end (ap);
133     }
134
135     _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, "open");
136     if (strcmp (pathname, "/dev/console") == 0)
137     {
138         if (console_fd < 0)
139         {
140             console_fd = _open ("/dev/null", flags, mode);
141             fcntl (console_fd, F_SETFD, FD_CLOEXEC);
142         }
143         return console_fd;
144     }
145     else if (strcmp (pathname, CONFIG_DIR "/lightdm.conf") == 0)
146     {
147         gchar *path;
148         int fd;
149
150         path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "lightdm", "lightdm.conf", NULL);
151         fd = _open (path, flags, mode);
152         g_free (path);
153
154         return fd;
155     }
156     else
157         return _open (pathname, flags, mode);
158 }
159
160 int
161 ioctl (int d, int request, void *data)
162 {
163     int (*_ioctl) (int d, int request, void *data);
164
165     _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
166     if (d > 0 && d == console_fd)
167     {
168         struct vt_stat *console_state;
169
170         switch (request)
171         {
172         case VT_GETSTATE:
173             console_state = data;
174             console_state->v_active = 7;
175             break;          
176         case VT_ACTIVATE:
177             break;
178         }
179         return 0;
180     }
181     else
182         return _ioctl (d, request, data);
183 }
184
185 int
186 close (int fd)
187 {
188     int (*_close) (int fd);
189
190     if (fd > 0 && fd == console_fd)
191         return 0;
192
193     _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
194     return _close (fd);
195 }
196 #endif
197
198 static void
199 free_user (gpointer data)
200 {
201     struct passwd *entry = data;
202   
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);
208     g_free (entry);
209 }
210
211 static void
212 load_passwd_file ()
213 {
214     gchar *path, *data = NULL, **lines;
215     gint i;
216     GError *error = NULL;
217
218     g_list_free_full (user_entries, free_user);
219     user_entries = NULL;
220     getpwent_link = NULL;
221
222     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
223     g_file_get_contents (path, &data, NULL, &error);
224     g_free (path);
225     if (error)
226         g_warning ("Error loading passwd file: %s", error->message);
227     g_clear_error (&error);
228
229     if (!data)
230         return;
231
232     lines = g_strsplit (data, "\n", -1);
233     g_free (data);
234
235     for (i = 0; lines[i]; i++)
236     {
237         gchar *line, **fields;
238
239         line = g_strstrip (lines[i]);
240         fields = g_strsplit (line, ":", -1);
241         if (g_strv_length (fields) == 7)
242         {
243             struct passwd *entry = malloc (sizeof (struct passwd));
244
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);
253         }
254         g_strfreev (fields);
255     }
256     g_strfreev (lines);
257 }
258
259 struct passwd *
260 getpwent (void)
261 {
262     if (getpwent_link == NULL)
263     {
264         load_passwd_file ();
265         if (user_entries == NULL)
266             return NULL;
267         getpwent_link = user_entries;
268     }
269     else
270     {
271         if (getpwent_link->next == NULL)
272             return NULL;
273         getpwent_link = getpwent_link->next;
274     }
275
276     return getpwent_link->data;
277 }
278
279 void
280 setpwent (void)
281 {
282     getpwent_link = NULL;
283 }
284
285 void
286 endpwent (void)
287 {
288     getpwent_link = NULL;
289 }
290
291 struct passwd *
292 getpwnam (const char *name)
293 {
294     GList *link;
295   
296     if (name == NULL)
297         return NULL;
298   
299     load_passwd_file ();
300
301     for (link = user_entries; link; link = link->next)
302     {
303         struct passwd *entry = link->data;
304         if (strcmp (entry->pw_name, name) == 0)
305             break;
306     }
307     if (!link)
308         return NULL;
309
310     return link->data;
311 }
312
313 struct passwd *
314 getpwuid (uid_t uid)
315 {
316     GList *link;
317
318     load_passwd_file ();
319
320     for (link = user_entries; link; link = link->next)
321     {
322         struct passwd *entry = link->data;
323         if (entry->pw_uid == uid)
324             break;
325     }
326     if (!link)
327         return NULL;
328
329     return link->data;
330 }
331
332 static void
333 free_group (gpointer data)
334 {
335     struct group *entry = data;
336   
337     g_free (entry->gr_name);
338     g_free (entry->gr_passwd);
339     g_strfreev (entry->gr_mem);
340     g_free (entry);
341 }
342
343 static void
344 load_group_file ()
345 {
346     gchar *path, *data = NULL, **lines;
347     gint i;
348     GError *error = NULL;
349
350     g_list_free_full (group_entries, free_group);
351     group_entries = NULL;
352
353     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
354     g_file_get_contents (path, &data, NULL, &error);
355     g_free (path);
356     if (error)
357         g_warning ("Error loading group file: %s", error->message);
358     g_clear_error (&error);
359
360     if (!data)
361         return;
362
363     lines = g_strsplit (data, "\n", -1);
364     g_free (data);
365
366     for (i = 0; lines[i]; i++)
367     {
368         gchar *line, **fields;
369
370         line = g_strstrip (lines[i]);
371         fields = g_strsplit (line, ":", -1);
372         if (g_strv_length (fields) == 4)
373         {
374             struct group *entry = malloc (sizeof (struct group));
375
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);
381         }
382         g_strfreev (fields);
383     }
384     g_strfreev (lines);
385 }
386
387 struct group *
388 getgrnam (const char *name)
389 {
390     GList *link;
391
392     load_group_file ();
393
394     for (link = group_entries; link; link = link->next)
395     {
396         struct group *entry = link->data;
397         if (strcmp (entry->gr_name, name) == 0)
398             break;
399     }
400     if (!link)
401         return NULL;
402
403     return link->data;
404 }
405
406 struct group *
407 getgrgid (gid_t gid)
408 {
409     GList *link;
410
411     load_group_file ();
412
413     for (link = group_entries; link; link = link->next)
414     {
415         struct group *entry = link->data;
416         if (entry->gr_gid == gid)
417             break;
418     }
419     if (!link)
420         return NULL;
421
422     return link->data;
423 }
424
425 int
426 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
427 {
428     pam_handle_t *handle;
429
430     if (service_name == NULL || conversation == NULL || pamh == NULL)
431         return PAM_SYSTEM_ERR;
432
433     handle = *pamh = malloc (sizeof (pam_handle_t));
434     if (handle == NULL)
435         return PAM_BUF_ERR;
436
437     handle->service_name = strdup (service_name);
438     handle->user = user ? strdup (user) : NULL;
439     handle->tty = 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;
444
445     return PAM_SUCCESS;
446 }
447
448 static void
449 send_info (pam_handle_t *pamh, const char *message)
450 {
451     struct pam_message **msg;
452     struct pam_response *resp = NULL;
453
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);
459     free (msg[0]);
460     free (msg);
461     if (resp)
462     {
463         if (resp[0].resp)
464             free (resp[0].resp);
465         free (resp);
466     }
467 }
468
469 int
470 pam_authenticate (pam_handle_t *pamh, int flags)
471 {
472     struct passwd *entry;
473     gboolean password_matches = FALSE;
474
475     if (pamh == NULL)
476         return PAM_SYSTEM_ERR;
477
478     /* Prompt for username */
479     if (pamh->user == NULL)
480     {
481         int result;
482         struct pam_message **msg;
483         struct pam_response *resp = NULL;
484
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);
490         free (msg[0]);
491         free (msg);
492         if (result != PAM_SUCCESS)
493             return result;
494
495         if (resp == NULL)
496             return PAM_CONV_ERR;
497         if (resp[0].resp == NULL)
498         {
499             free (resp);
500             return PAM_CONV_ERR;
501         }
502       
503         pamh->user = strdup (resp[0].resp);
504         free (resp[0].resp);
505         free (resp);
506     }
507
508     if (strcmp (pamh->user, "log-pam") == 0)
509         send_info (pamh, "pam_authenticate");
510
511     /* Crash on authenticate */
512     if (strcmp (pamh->user, "crash-authenticate") == 0)
513         kill (getpid (), SIGSEGV);
514
515     /* Look up password database */
516     entry = getpwnam (pamh->user);
517
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;
521     else
522     {
523         int i, n_messages = 0, password_index, result;
524         struct pam_message **msg;
525         struct pam_response *resp = NULL;
526
527         msg = malloc (sizeof (struct pam_message *) * 5);
528         if (strcmp (pamh->user, "info-prompt") == 0)
529         {
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";
533             n_messages++;
534         }
535         if (strcmp (pamh->user, "multi-info-prompt") == 0)
536         {
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";
540             n_messages++;
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";
544             n_messages++;
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";
548             n_messages++;
549         }
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;
554         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++)
557             free (msg[i]);
558         free (msg);
559         if (result != PAM_SUCCESS)
560             return result;
561
562         if (resp == NULL)
563             return PAM_CONV_ERR;
564         if (resp[password_index].resp == NULL)
565         {
566             free (resp);
567             return PAM_CONV_ERR;
568         }
569
570         if (entry)
571             password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
572         for (i = 0; i < n_messages; i++)
573         {
574             if (resp[i].resp)
575                 free (resp[i].resp);
576         }
577         free (resp);
578
579         /* Do two factor authentication */
580         if (password_matches && strcmp (pamh->user, "two-factor") == 0)
581         {
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:";
586             resp = NULL;
587             result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
588             free (msg[0]);
589             free (msg);
590
591             if (resp == NULL)
592                 return PAM_CONV_ERR;
593             if (resp[0].resp == NULL)
594             {
595                 free (resp);
596                 return PAM_CONV_ERR;
597             }
598             password_matches = strcmp (resp[0].resp, "otp") == 0;
599             free (resp[0].resp);
600             free (resp);
601         }
602     }
603
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);
607
608     /* Special user 'change-user1' changes user on authentication */
609     if (password_matches && strcmp (pamh->user, "change-user1") == 0)
610     {
611         g_free (pamh->user);
612         pamh->user = g_strdup ("change-user2");
613     }
614
615     /* Special user 'change-user-invalid' changes to an invalid user on authentication */
616     if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
617     {
618         g_free (pamh->user);
619         pamh->user = g_strdup ("invalid-user");
620     }
621
622     if (password_matches)
623         return PAM_SUCCESS;
624     else
625         return PAM_AUTH_ERR;
626 }
627
628 static const char *
629 get_env_value (const char *name_value, const char *name)
630 {
631     int j;
632
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];
636
637     return NULL;
638 }
639
640 int
641 pam_putenv (pam_handle_t *pamh, const char *name_value)
642 {
643     int i;
644
645     if (pamh == NULL || name_value == NULL)
646         return PAM_SYSTEM_ERR;
647
648     for (i = 0; pamh->envlist[i]; i++)
649     {
650         if (get_env_value (pamh->envlist[i], name_value))
651             break;
652     }
653
654     if (pamh->envlist[i])
655     {
656         free (pamh->envlist[i]);
657         pamh->envlist[i] = strdup (name_value);
658     }
659     else
660     {
661         pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
662         pamh->envlist[i] = strdup (name_value);
663         pamh->envlist[i + 1] = NULL;
664     }
665
666     return PAM_SUCCESS;
667 }
668
669 const char *
670 pam_getenv (pam_handle_t *pamh, const char *name)
671 {
672     int i;
673
674     if (pamh == NULL || name == NULL)
675         return NULL;
676
677     for (i = 0; pamh->envlist[i]; i++)
678     {
679         const char *value;
680         value = get_env_value (pamh->envlist[i], name);
681         if (value)
682             return value;
683     }
684
685     return NULL;
686 }
687
688 char **
689 pam_getenvlist (pam_handle_t *pamh)
690 {
691     if (pamh == NULL)
692         return NULL;
693
694     return pamh->envlist;
695 }
696
697 int
698 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
699 {
700     if (pamh == NULL || item == NULL)
701         return PAM_SYSTEM_ERR;
702
703     switch (item_type)
704     {
705     case PAM_TTY:
706         if (pamh->tty)
707             free (pamh->tty);
708         pamh->tty = strdup ((const char *) item);
709         return PAM_SUCCESS;
710
711     default:
712         return PAM_BAD_ITEM;
713     }
714 }
715
716 int
717 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
718 {
719     if (pamh == NULL || item == NULL)
720         return PAM_SYSTEM_ERR;
721   
722     switch (item_type)
723     {
724     case PAM_SERVICE:
725         *item = pamh->service_name;
726         return PAM_SUCCESS;
727       
728     case PAM_USER:
729         *item = pamh->user;
730         return PAM_SUCCESS;
731       
732     case PAM_USER_PROMPT:
733         *item = LOGIN_PROMPT;
734         return PAM_SUCCESS;
735       
736     case PAM_TTY:
737         *item = pamh->tty;
738         return PAM_SUCCESS;
739
740     case PAM_CONV:
741         *item = &pamh->conversation;
742         return PAM_SUCCESS;
743
744     default:
745         return PAM_BAD_ITEM;
746     }
747 }
748
749 int
750 pam_open_session (pam_handle_t *pamh, int flags)
751 {
752     if (pamh == NULL)
753         return PAM_SYSTEM_ERR;
754
755     if (strcmp (pamh->user, "session-error") == 0)
756         return PAM_SESSION_ERR;
757
758     if (strcmp (pamh->user, "log-pam") == 0)
759         send_info (pamh, "pam_open_session");
760
761     if (strcmp (pamh->user, "make-home-dir") == 0)
762     {
763         struct passwd *entry;
764         entry = getpwnam (pamh->user);
765         g_mkdir_with_parents (entry->pw_dir, 0755);
766     }
767
768     return PAM_SUCCESS;
769 }
770
771 int
772 pam_close_session (pam_handle_t *pamh, int flags)
773 {
774     if (pamh == NULL)
775         return PAM_SYSTEM_ERR;
776
777     if (strcmp (pamh->user, "log-pam") == 0)
778         send_info (pamh, "pam_close_session");
779
780     return PAM_SUCCESS;
781 }
782
783 int
784 pam_acct_mgmt (pam_handle_t *pamh, int flags)
785 {
786     if (pamh == NULL)
787         return PAM_SYSTEM_ERR;
788   
789     if (!pamh->user)
790         return PAM_USER_UNKNOWN;
791
792     if (strcmp (pamh->user, "log-pam") == 0)
793         send_info (pamh, "pam_acct_mgmt");
794
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;
801
802     return PAM_SUCCESS;
803 }
804
805 int
806 pam_chauthtok (pam_handle_t *pamh, int flags)
807 {
808     struct passwd *entry;
809     int result;
810     struct pam_message **msg;
811     struct pam_response *resp = NULL;
812
813     if (pamh == NULL)
814         return PAM_SYSTEM_ERR;
815
816     if (strcmp (pamh->user, "log-pam") == 0)
817         send_info (pamh, "pam_chauthtok");
818
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);
824     free (msg[0]);
825     free (msg);
826     if (result != PAM_SUCCESS)
827         return result;
828
829     if (resp == NULL)
830         return PAM_CONV_ERR;
831     if (resp[0].resp == NULL)
832     {
833         free (resp);
834         return PAM_CONV_ERR;
835     }
836
837     /* Update password database */
838     entry = getpwnam (pamh->user);
839     free (entry->pw_passwd);
840     entry->pw_passwd = resp[0].resp;
841     free (resp);
842
843     return PAM_SUCCESS;
844 }
845
846 int
847 pam_setcred (pam_handle_t *pamh, int flags)
848 {
849     gchar *e;
850
851     if (pamh == NULL)
852         return PAM_SYSTEM_ERR;
853
854     if (strcmp (pamh->user, "log-pam") == 0)
855         send_info (pamh, "pam_setcred");
856
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);
860     g_free (e);
861
862     if (strcmp (pamh->user, "cred-error") == 0)
863         return PAM_CRED_ERR;
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;
868
869     /* Join special groups if requested */
870     if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
871     {
872         struct group *group;
873         gid_t *groups;
874         int groups_length;
875
876         group = getgrnam ("test-group");
877         if (group)
878         {
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;
883             groups_length++;
884             setgroups (groups_length, groups);
885             free (groups);
886         }
887
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")));
890     }
891
892     return PAM_SUCCESS;
893 }
894
895 int
896 pam_end (pam_handle_t *pamh, int pam_status)
897 {
898     if (pamh == NULL)
899         return PAM_SYSTEM_ERR;
900   
901     free (pamh->service_name);
902     if (pamh->user)
903         free (pamh->user);
904     if (pamh->tty)
905         free (pamh->tty);
906     free (pamh);
907
908     return PAM_SUCCESS;
909 }
910
911 const char *
912 pam_strerror (pam_handle_t *pamh, int errnum)
913 {
914     if (pamh == NULL)
915         return NULL;
916
917     switch (errnum)
918     {
919     case PAM_SUCCESS:
920         return "Success";
921     case PAM_ABORT:
922         return "Critical error - immediate abort";
923     case PAM_OPEN_ERR:
924         return "Failed to load module";
925     case PAM_SYMBOL_ERR:
926         return "Symbol not found";
927     case PAM_SERVICE_ERR:
928         return "Error in service module";
929     case PAM_SYSTEM_ERR:
930         return "System error";
931     case PAM_BUF_ERR:
932         return "Memory buffer error";
933     case PAM_PERM_DENIED:
934         return "Permission denied";
935     case PAM_AUTH_ERR:
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";
943     case PAM_MAXTRIES:
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";
955     case PAM_CRED_ERR:
956         return "Failure setting user credentials";
957     case PAM_NO_MODULE_DATA:
958         return "No module specific data is present";
959     case PAM_BAD_ITEM:
960         return "Bad item passed to pam_*_item()";
961     case PAM_CONV_ERR:
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";
971     case PAM_TRY_AGAIN:
972         return "Failed preliminary check by password service";
973     case PAM_IGNORE:
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";
979     case PAM_CONV_AGAIN:
980         return "Conversation is waiting for event";
981     case PAM_INCOMPLETE:
982         return "Application needs to call libpam again";
983     default:
984         return "Unknown PAM error";
985     }
986 }