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