]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/libsystem.c
Merge with trunk
[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 <sys/stat.h>
6 #include <pwd.h>
7 #include <unistd.h>
8 #include <dirent.h>
9 #include <grp.h>
10 #include <security/pam_appl.h>
11 #include <fcntl.h>
12 #define __USE_GNU
13 #include <dlfcn.h>
14 #ifdef __linux__
15 #include <linux/vt.h>
16 #endif
17 #include <glib.h>
18 #include <xcb/xcb.h>
19 #include <gio/gunixsocketaddress.h>
20
21 #define LOGIN_PROMPT "login:"
22
23 static int console_fd = -1;
24
25 static GList *user_entries = NULL;
26 static GList *getpwent_link = NULL;
27
28 static GList *group_entries = NULL;
29
30 static int active_vt = 7;
31
32 struct pam_handle
33 {
34     char *service_name;
35     char *user;
36     char *authtok;
37     char *ruser;
38     char *tty;
39     char **envlist;
40     struct pam_conv conversation;
41 };
42
43 uid_t
44 getuid (void)
45 {
46     return 0;
47 }
48
49 /*uid_t
50 geteuid (void)
51 {
52     return 0;
53 }*/
54
55 int
56 initgroups (const char *user, gid_t group)
57 {
58     gid_t g[1];
59
60     g[0] = group;
61     setgroups (1, g);
62
63     return 0;
64 }
65
66 int
67 getgroups (int size, gid_t list[])
68 {
69     const gchar *group_list;
70     gchar **groups;
71     gint groups_length;
72
73     /* Get groups we are a member of */
74     group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
75     if (!group_list)
76         group_list = "";
77     groups = g_strsplit (group_list, ",", -1);
78     groups_length = g_strv_length (groups);
79
80     if (size != 0)
81     {
82         int i;
83
84         if (groups_length > size)
85         {
86             errno = EINVAL;
87             return -1;
88         }
89         for (i = 0; groups[i]; i++)
90             list[i] = atoi (groups[i]);
91     }
92     g_free (groups);
93
94     return groups_length;
95 }
96
97 int
98 setgroups (size_t size, const gid_t *list)
99 {
100     size_t i;
101     GString *group_list;
102
103     group_list = g_string_new ("");
104     for (i = 0; i < size; i++)
105     {
106         if (i != 0)
107             g_string_append (group_list, ",");
108         g_string_append_printf (group_list, "%d", list[i]);
109     }
110     g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
111     g_string_free (group_list, TRUE);
112
113     return 0;
114 }
115
116 int
117 setgid (gid_t gid)
118 {
119     return 0;
120 }
121
122 int
123 setegid (gid_t gid)
124 {
125     return 0;
126 }
127
128 int
129 setresgid (gid_t rgid, gid_t ugid, gid_t sgid)
130 {
131     return 0;
132 }
133
134 int
135 setuid (uid_t uid)
136 {
137     return 0;
138 }
139
140 int
141 seteuid (uid_t uid)
142 {
143     return 0;
144 }
145
146 int
147 setresuid (uid_t ruid, uid_t uuid, uid_t suid)
148 {
149     return 0;
150 }
151
152 static gchar *
153 redirect_path (const gchar *path)
154 {
155     size_t offset;
156     gboolean matches;
157
158     // Don't redirect if inside the running directory
159     if (g_str_has_prefix (path, g_getenv ("LIGHTDM_TEST_ROOT")))
160         return g_strdup (path);
161
162     if (g_str_has_prefix (path, SYSCONFDIR))
163         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", path + strlen (SYSCONFDIR), NULL);
164
165     if (g_str_has_prefix (path, LOCALSTATEDIR))
166         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "var", path + strlen (LOCALSTATEDIR), NULL);
167
168     if (g_str_has_prefix (path, DATADIR))
169         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "usr", "share", path + strlen (DATADIR), NULL);
170
171     // Don't redirect if inside the build directory
172     if (g_str_has_prefix (path, BUILDDIR))
173         return g_strdup (path);
174
175     if (g_str_has_prefix (path, "/tmp"))
176         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", path + strlen ("tmp"), NULL);
177
178     return g_strdup (path);
179 }
180
181 #ifdef __linux__
182 static int
183 open_wrapper (const char *func, const char *pathname, int flags, mode_t mode)
184 {
185     int (*_open) (const char *pathname, int flags, mode_t mode);
186     gchar *new_path = NULL;
187     int fd;
188
189     _open = (int (*)(const char *pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, func);
190
191     if (strcmp (pathname, "/dev/console") == 0)
192     {
193         if (console_fd < 0)
194         {
195             console_fd = _open ("/dev/null", flags, mode);
196             fcntl (console_fd, F_SETFD, FD_CLOEXEC);
197         }
198         return console_fd;
199     }
200
201     new_path = redirect_path (pathname);
202     fd = _open (new_path, flags, mode);
203     g_free (new_path);
204
205     return fd;
206 }
207
208 int
209 open (const char *pathname, int flags, ...)
210 {
211     int mode = 0;
212     if (flags & O_CREAT)
213     {
214         va_list ap;
215         va_start (ap, flags);
216         mode = va_arg (ap, mode_t);
217         va_end (ap);
218     }
219     return open_wrapper ("open", pathname, flags, mode);
220 }
221
222 int
223 open64 (const char *pathname, int flags, ...)
224 {
225     int mode = 0;
226     if (flags & O_CREAT)
227     {
228         va_list ap;
229         va_start (ap, flags);
230         mode = va_arg (ap, mode_t);
231         va_end (ap);
232     }
233     return open_wrapper ("open64", pathname, flags, mode);
234 }
235
236 FILE *
237 fopen (const char *path, const char *mode)
238 {
239     FILE *(*_fopen) (const char *pathname, const char *mode);
240     gchar *new_path = NULL;
241     FILE *result;
242
243     _fopen = (FILE *(*)(const char *pathname, const char *mode)) dlsym (RTLD_NEXT, "fopen");
244
245     new_path = redirect_path (path);
246     result = _fopen (new_path, mode);
247     g_free (new_path);
248
249     return result;
250 }
251
252 int
253 creat (const char *pathname, mode_t mode)
254 {
255     int (*_creat) (const char *pathname, mode_t mode);
256     gchar *new_path = NULL;
257     int result;
258
259     _creat = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "creat");
260
261     new_path = redirect_path (pathname);
262     result = _creat (new_path, mode);
263     g_free (new_path);
264
265     return result;
266 }
267
268 int
269 creat64 (const char *pathname, mode_t mode)
270 {
271     int (*_creat64) (const char *pathname, mode_t mode);
272     gchar *new_path = NULL;
273     int result;
274
275     _creat64 = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "creat64");
276
277     new_path = redirect_path (pathname);
278     result = _creat64 (new_path, mode);
279     g_free (new_path);
280
281     return result;
282 }
283
284 int
285 access (const char *pathname, int mode)
286 {
287     int (*_access) (const char *pathname, int mode);
288     gchar *new_path = NULL;
289     int ret;
290
291     _access = (int (*)(const char *pathname, int mode)) dlsym (RTLD_NEXT, "access");
292
293     new_path = redirect_path (pathname);
294     ret = _access (new_path, mode);
295     g_free (new_path);
296
297     return ret;
298 }
299
300 int
301 stat (const char *path, struct stat *buf)
302 {
303     int (*_stat) (const char *path, struct stat *buf);
304     gchar *new_path = NULL;
305     int ret;
306   
307     _stat = (int (*)(const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "stat");
308
309     new_path = redirect_path (path);
310     ret = _stat (new_path, buf);
311     g_free (new_path);
312
313     return ret;
314 }
315
316 int
317 stat64 (const char *path, struct stat *buf)
318 {
319     int (*_stat64) (const char *path, struct stat *buf);
320     gchar *new_path = NULL;
321     int ret;
322
323     _stat64 = (int (*)(const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "stat64");
324
325     new_path = redirect_path (path);
326     ret = _stat (new_path, buf);
327     g_free (new_path);
328
329     return ret;
330 }
331
332 int
333 __xstat (int version, const char *path, struct stat *buf)
334 {
335     int (*___xstat) (int version, const char *path, struct stat *buf);
336     gchar *new_path = NULL;
337     int ret;
338   
339     ___xstat = (int (*)(int version, const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "__xstat");
340
341     new_path = redirect_path (path);
342     ret = ___xstat (version, new_path, buf);
343     g_free (new_path);
344
345     return ret;
346 }
347
348 int
349 __xstat64 (int version, const char *path, struct stat *buf)
350 {
351     int (*___xstat64) (int version, const char *path, struct stat *buf);
352     gchar *new_path = NULL;
353     int ret;
354   
355     ___xstat64 = (int (*)(int version, const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "__xstat64");
356
357     new_path = redirect_path (path);
358     ret = ___xstat64 (version, new_path, buf);
359     g_free (new_path);
360
361     return ret;
362 }
363
364 DIR *
365 opendir (const char *name)
366 {
367     DIR *(*_opendir) (const char *name);
368     gchar *new_path = NULL;
369     DIR *result;
370
371     _opendir = (DIR *(*)(const char *name)) dlsym (RTLD_NEXT, "opendir");
372
373     new_path = redirect_path (name);
374     result = _opendir (new_path);
375     g_free (new_path);
376
377     return result; 
378 }
379
380 int
381 mkdir (const char *pathname, mode_t mode)
382 {
383     int (*_mkdir) (const char *pathname, mode_t mode);
384     gchar *new_path = NULL;
385     int result;
386
387     _mkdir = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "mkdir");
388
389     new_path = redirect_path (pathname);
390     result = _mkdir (new_path, mode);
391     g_free (new_path);
392
393     return result;
394 }
395
396 int
397 chown (const char *pathname, uid_t owner, gid_t group)
398 {
399     int (*_chown) (const char *pathname, uid_t owner, gid_t group);
400     gchar *new_path = NULL;
401     int result;
402
403     _chown = (int (*)(const char *pathname, uid_t owner, gid_t group)) dlsym (RTLD_NEXT, "chown");
404
405     new_path = redirect_path (pathname);
406     result = _chown (new_path, owner, group);
407     g_free (new_path);
408
409     return result;
410 }
411
412 int
413 ioctl (int d, int request, void *data)
414 {
415     int (*_ioctl) (int d, int request, void *data);
416
417     _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
418     if (d > 0 && d == console_fd)
419     {
420         struct vt_stat *console_state;
421         int *n;
422
423         switch (request)
424         {
425         case VT_GETSTATE:
426             console_state = data;
427             console_state->v_active = active_vt;
428             break;
429         case VT_ACTIVATE:
430             active_vt = GPOINTER_TO_INT (data);
431             break;
432         case VT_WAITACTIVE:
433             break;
434         }
435         return 0;
436     }
437     else
438         return _ioctl (d, request, data);
439 }
440
441 int
442 close (int fd)
443 {
444     int (*_close) (int fd);
445
446     if (fd > 0 && fd == console_fd)
447         return 0;
448
449     _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
450     return _close (fd);
451 }
452 #endif
453
454 static void
455 free_user (gpointer data)
456 {
457     struct passwd *entry = data;
458   
459     g_free (entry->pw_name);
460     g_free (entry->pw_passwd);
461     g_free (entry->pw_gecos);
462     g_free (entry->pw_dir);
463     g_free (entry->pw_shell);
464     g_free (entry);
465 }
466
467 static void
468 load_passwd_file ()
469 {
470     gchar *path, *data = NULL, **lines;
471     gint i;
472     GError *error = NULL;
473
474     g_list_free_full (user_entries, free_user);
475     user_entries = NULL;
476     getpwent_link = NULL;
477
478     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
479     g_file_get_contents (path, &data, NULL, &error);
480     g_free (path);
481     if (error)
482         g_warning ("Error loading passwd file: %s", error->message);
483     g_clear_error (&error);
484
485     if (!data)
486         return;
487
488     lines = g_strsplit (data, "\n", -1);
489     g_free (data);
490
491     for (i = 0; lines[i]; i++)
492     {
493         gchar *line, **fields;
494
495         line = g_strstrip (lines[i]);
496         fields = g_strsplit (line, ":", -1);
497         if (g_strv_length (fields) == 7)
498         {
499             struct passwd *entry = malloc (sizeof (struct passwd));
500
501             entry->pw_name = g_strdup (fields[0]);
502             entry->pw_passwd = g_strdup (fields[1]);
503             entry->pw_uid = atoi (fields[2]);
504             entry->pw_gid = atoi (fields[3]);
505             entry->pw_gecos = g_strdup (fields[4]);
506             entry->pw_dir = g_strdup (fields[5]);
507             entry->pw_shell = g_strdup (fields[6]);
508             user_entries = g_list_append (user_entries, entry);
509         }
510         g_strfreev (fields);
511     }
512     g_strfreev (lines);
513 }
514
515 struct passwd *
516 getpwent (void)
517 {
518     if (getpwent_link == NULL)
519     {
520         load_passwd_file ();
521         if (user_entries == NULL)
522             return NULL;
523         getpwent_link = user_entries;
524     }
525     else
526     {
527         if (getpwent_link->next == NULL)
528             return NULL;
529         getpwent_link = getpwent_link->next;
530     }
531
532     return getpwent_link->data;
533 }
534
535 void
536 setpwent (void)
537 {
538     getpwent_link = NULL;
539 }
540
541 void
542 endpwent (void)
543 {
544     getpwent_link = NULL;
545 }
546
547 struct passwd *
548 getpwnam (const char *name)
549 {
550     GList *link;
551   
552     if (name == NULL)
553         return NULL;
554   
555     load_passwd_file ();
556
557     for (link = user_entries; link; link = link->next)
558     {
559         struct passwd *entry = link->data;
560         if (strcmp (entry->pw_name, name) == 0)
561             break;
562     }
563     if (!link)
564         return NULL;
565
566     return link->data;
567 }
568
569 struct passwd *
570 getpwuid (uid_t uid)
571 {
572     GList *link;
573
574     load_passwd_file ();
575
576     for (link = user_entries; link; link = link->next)
577     {
578         struct passwd *entry = link->data;
579         if (entry->pw_uid == uid)
580             break;
581     }
582     if (!link)
583         return NULL;
584
585     return link->data;
586 }
587
588 static void
589 free_group (gpointer data)
590 {
591     struct group *entry = data;
592   
593     g_free (entry->gr_name);
594     g_free (entry->gr_passwd);
595     g_strfreev (entry->gr_mem);
596     g_free (entry);
597 }
598
599 static void
600 load_group_file ()
601 {
602     gchar *path, *data = NULL, **lines;
603     gint i;
604     GError *error = NULL;
605
606     g_list_free_full (group_entries, free_group);
607     group_entries = NULL;
608
609     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
610     g_file_get_contents (path, &data, NULL, &error);
611     g_free (path);
612     if (error)
613         g_warning ("Error loading group file: %s", error->message);
614     g_clear_error (&error);
615
616     if (!data)
617         return;
618
619     lines = g_strsplit (data, "\n", -1);
620     g_free (data);
621
622     for (i = 0; lines[i]; i++)
623     {
624         gchar *line, **fields;
625
626         line = g_strstrip (lines[i]);
627         fields = g_strsplit (line, ":", -1);
628         if (g_strv_length (fields) == 4)
629         {
630             struct group *entry = malloc (sizeof (struct group));
631
632             entry->gr_name = g_strdup (fields[0]);
633             entry->gr_passwd = g_strdup (fields[1]);
634             entry->gr_gid = atoi (fields[2]);
635             entry->gr_mem = g_strsplit (fields[3], ",", -1);
636             group_entries = g_list_append (group_entries, entry);
637         }
638         g_strfreev (fields);
639     }
640     g_strfreev (lines);
641 }
642
643 struct group *
644 getgrnam (const char *name)
645 {
646     GList *link;
647
648     load_group_file ();
649
650     for (link = group_entries; link; link = link->next)
651     {
652         struct group *entry = link->data;
653         if (strcmp (entry->gr_name, name) == 0)
654             break;
655     }
656     if (!link)
657         return NULL;
658
659     return link->data;
660 }
661
662 struct group *
663 getgrgid (gid_t gid)
664 {
665     GList *link;
666
667     load_group_file ();
668
669     for (link = group_entries; link; link = link->next)
670     {
671         struct group *entry = link->data;
672         if (entry->gr_gid == gid)
673             break;
674     }
675     if (!link)
676         return NULL;
677
678     return link->data;
679 }
680
681 int
682 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
683 {
684     pam_handle_t *handle;
685
686     if (service_name == NULL || conversation == NULL || pamh == NULL)
687         return PAM_SYSTEM_ERR;
688
689     handle = *pamh = malloc (sizeof (pam_handle_t));
690     if (handle == NULL)
691         return PAM_BUF_ERR;
692
693     handle->service_name = strdup (service_name);
694     handle->user = user ? strdup (user) : NULL;
695     handle->authtok = NULL;
696     handle->ruser = NULL;
697     handle->tty = NULL;
698     handle->conversation.conv = conversation->conv;
699     handle->conversation.appdata_ptr = conversation->appdata_ptr;
700     handle->envlist = malloc (sizeof (char *) * 1);
701     handle->envlist[0] = NULL;
702
703     return PAM_SUCCESS;
704 }
705
706 static void
707 send_info (pam_handle_t *pamh, const char *message)
708 {
709     struct pam_message **msg;
710     struct pam_response *resp = NULL;
711
712     msg = calloc (1, sizeof (struct pam_message *));
713     msg[0] = malloc (sizeof (struct pam_message));
714     msg[0]->msg_style = PAM_TEXT_INFO;
715     msg[0]->msg = message;
716     pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
717     free (msg[0]);
718     free (msg);
719     if (resp)
720     {
721         if (resp[0].resp)
722             free (resp[0].resp);
723         free (resp);
724     }
725 }
726
727 int
728 pam_authenticate (pam_handle_t *pamh, int flags)
729 {
730     struct passwd *entry;
731     gboolean password_matches = FALSE;
732
733     if (pamh == NULL)
734         return PAM_SYSTEM_ERR;
735   
736     if (strcmp (pamh->service_name, "test-remote") == 0)
737     {
738         int result;
739         struct pam_message **msg;
740         struct pam_response *resp = NULL;
741
742         msg = malloc (sizeof (struct pam_message *) * 1);
743         msg[0] = malloc (sizeof (struct pam_message));
744         msg[0]->msg_style = PAM_PROMPT_ECHO_ON; 
745         msg[0]->msg = "remote-login:";
746         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
747         free (msg[0]);
748         free (msg);
749         if (result != PAM_SUCCESS)
750             return result;
751
752         if (resp == NULL)
753             return PAM_CONV_ERR;
754         if (resp[0].resp == NULL)
755         {
756             free (resp);
757             return PAM_CONV_ERR;
758         }
759
760         if (pamh->ruser)
761             free (pamh->ruser);
762         pamh->ruser = strdup (resp[0].resp);
763         free (resp[0].resp);
764         free (resp);
765
766         msg = malloc (sizeof (struct pam_message *) * 1);
767         msg[0] = malloc (sizeof (struct pam_message));
768         msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
769         msg[0]->msg = "remote-password:";
770         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
771         free (msg[0]);
772         free (msg);
773         if (result != PAM_SUCCESS)
774             return result;
775
776         if (resp == NULL)
777             return PAM_CONV_ERR;
778         if (resp[0].resp == NULL)
779         {
780             free (resp);
781             return PAM_CONV_ERR;
782         }
783
784         if (pamh->authtok)
785             free (pamh->authtok);
786         pamh->authtok = strdup (resp[0].resp);
787         free (resp[0].resp);
788         free (resp);
789
790         password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
791
792         if (password_matches)
793             return PAM_SUCCESS;
794         else
795             return PAM_AUTH_ERR;
796     }
797
798     /* Prompt for username */
799     if (pamh->user == NULL)
800     {
801         int result;
802         struct pam_message **msg;
803         struct pam_response *resp = NULL;
804
805         msg = malloc (sizeof (struct pam_message *) * 1);
806         msg[0] = malloc (sizeof (struct pam_message));
807         msg[0]->msg_style = PAM_PROMPT_ECHO_ON; 
808         msg[0]->msg = LOGIN_PROMPT;
809         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
810         free (msg[0]);
811         free (msg);
812         if (result != PAM_SUCCESS)
813             return result;
814
815         if (resp == NULL)
816             return PAM_CONV_ERR;
817         if (resp[0].resp == NULL)
818         {
819             free (resp);
820             return PAM_CONV_ERR;
821         }
822       
823         pamh->user = strdup (resp[0].resp);
824         free (resp[0].resp);
825         free (resp);
826     }
827
828     if (strcmp (pamh->user, "log-pam") == 0)
829         send_info (pamh, "pam_authenticate");
830
831     /* Crash on authenticate */
832     if (strcmp (pamh->user, "crash-authenticate") == 0)
833         kill (getpid (), SIGSEGV);
834
835     /* Look up password database */
836     entry = getpwnam (pamh->user);
837
838     /* Prompt for password if required */
839     if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
840         password_matches = TRUE;
841     else
842     {
843         int i, n_messages = 0, password_index, result;
844         struct pam_message **msg;
845         struct pam_response *resp = NULL;
846
847         msg = malloc (sizeof (struct pam_message *) * 5);
848         if (strcmp (pamh->user, "info-prompt") == 0)
849         {
850             msg[n_messages] = malloc (sizeof (struct pam_message));
851             msg[n_messages]->msg_style = PAM_TEXT_INFO;
852             msg[n_messages]->msg = "Welcome to LightDM";
853             n_messages++;
854         }
855         if (strcmp (pamh->user, "multi-info-prompt") == 0)
856         {
857             msg[n_messages] = malloc (sizeof (struct pam_message));
858             msg[n_messages]->msg_style = PAM_TEXT_INFO;
859             msg[n_messages]->msg = "Welcome to LightDM";
860             n_messages++;
861             msg[n_messages] = malloc (sizeof (struct pam_message));
862             msg[n_messages]->msg_style = PAM_ERROR_MSG;
863             msg[n_messages]->msg = "This is an error";
864             n_messages++;
865             msg[n_messages] = malloc (sizeof (struct pam_message));
866             msg[n_messages]->msg_style = PAM_TEXT_INFO;
867             msg[n_messages]->msg = "You should have seen three messages";
868             n_messages++;
869         }
870         if (strcmp (pamh->user, "multi-prompt") == 0)
871         {
872             msg[n_messages] = malloc (sizeof (struct pam_message));
873             msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
874             msg[n_messages]->msg = "Favorite Color:";
875             n_messages++;
876         }
877         msg[n_messages] = malloc (sizeof (struct pam_message));
878         msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
879         msg[n_messages]->msg = "Password:";
880         password_index = n_messages;
881         n_messages++;
882         result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
883         for (i = 0; i < n_messages; i++)
884             free (msg[i]);
885         free (msg);
886         if (result != PAM_SUCCESS)
887             return result;
888
889         if (resp == NULL)
890             return PAM_CONV_ERR;
891         if (resp[password_index].resp == NULL)
892         {
893             free (resp);
894             return PAM_CONV_ERR;
895         }
896
897         if (entry)
898             password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
899
900         if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
901             password_matches = strcmp ("blue", resp[0].resp) == 0;
902
903         for (i = 0; i < n_messages; i++)
904         {
905             if (resp[i].resp)
906                 free (resp[i].resp);
907         }
908         free (resp);
909
910         /* Do two factor authentication */
911         if (password_matches && strcmp (pamh->user, "two-factor") == 0)
912         {
913             msg = malloc (sizeof (struct pam_message *) * 1);
914             msg[0] = malloc (sizeof (struct pam_message));
915             msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
916             msg[0]->msg = "OTP:";
917             resp = NULL;
918             result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
919             free (msg[0]);
920             free (msg);
921
922             if (resp == NULL)
923                 return PAM_CONV_ERR;
924             if (resp[0].resp == NULL)
925             {
926                 free (resp);
927                 return PAM_CONV_ERR;
928             }
929             password_matches = strcmp (resp[0].resp, "otp") == 0;
930             free (resp[0].resp);
931             free (resp);
932         }
933     }
934
935     /* Special user has home directory created on login */
936     if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
937         g_mkdir_with_parents (entry->pw_dir, 0755);
938
939     /* Special user 'change-user1' changes user on authentication */
940     if (password_matches && strcmp (pamh->user, "change-user1") == 0)
941     {
942         g_free (pamh->user);
943         pamh->user = g_strdup ("change-user2");
944     }
945
946     /* Special user 'change-user-invalid' changes to an invalid user on authentication */
947     if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
948     {
949         g_free (pamh->user);
950         pamh->user = g_strdup ("invalid-user");
951     }
952
953     if (password_matches)
954         return PAM_SUCCESS;
955     else
956         return PAM_AUTH_ERR;
957 }
958
959 static const char *
960 get_env_value (const char *name_value, const char *name)
961 {
962     int j;
963   
964     for (j = 0; name[j] && name_value[j] && name[j] == name_value[j]; j++);
965     if (name[j] == '\0' && name_value[j] == '=')
966         return &name_value[j + 1];
967
968     return NULL;
969 }
970
971 int
972 pam_putenv (pam_handle_t *pamh, const char *name_value)
973 {
974     int i;
975     gchar *name;
976
977     if (pamh == NULL || name_value == NULL)
978         return PAM_SYSTEM_ERR;
979
980     name = strdup (name_value);
981     for (i = 0; name[i]; i++)
982         if (name[i] == '=')
983             name[i] = '\0';
984     for (i = 0; pamh->envlist[i]; i++)
985     {
986         if (get_env_value (pamh->envlist[i], name))
987             break;
988     }
989     free (name);
990
991     if (pamh->envlist[i])
992     {
993         free (pamh->envlist[i]);
994         pamh->envlist[i] = strdup (name_value);
995     }
996     else
997     {
998         pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
999         pamh->envlist[i] = strdup (name_value);
1000         pamh->envlist[i + 1] = NULL;
1001     }
1002
1003     return PAM_SUCCESS;
1004 }
1005
1006 const char *
1007 pam_getenv (pam_handle_t *pamh, const char *name)
1008 {
1009     int i;
1010
1011     if (pamh == NULL || name == NULL)
1012         return NULL;
1013
1014     for (i = 0; pamh->envlist[i]; i++)
1015     {
1016         const char *value;
1017         value = get_env_value (pamh->envlist[i], name);
1018         if (value)
1019             return value;
1020     }
1021
1022     return NULL;
1023 }
1024
1025 char **
1026 pam_getenvlist (pam_handle_t *pamh)
1027 {
1028     if (pamh == NULL)
1029         return NULL;
1030
1031     return pamh->envlist;
1032 }
1033
1034 int
1035 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
1036 {
1037     if (pamh == NULL || item == NULL)
1038         return PAM_SYSTEM_ERR;
1039
1040     switch (item_type)
1041     {
1042     case PAM_TTY:
1043         if (pamh->tty)
1044             free (pamh->tty);
1045         pamh->tty = strdup ((const char *) item);
1046         return PAM_SUCCESS;
1047
1048     default:
1049         return PAM_BAD_ITEM;
1050     }
1051 }
1052
1053 int
1054 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
1055 {
1056     if (pamh == NULL || item == NULL)
1057         return PAM_SYSTEM_ERR;
1058   
1059     switch (item_type)
1060     {
1061     case PAM_SERVICE:
1062         *item = pamh->service_name;
1063         return PAM_SUCCESS;
1064       
1065     case PAM_USER:
1066         *item = pamh->user;
1067         return PAM_SUCCESS;
1068
1069     case PAM_AUTHTOK:
1070         *item = pamh->authtok;
1071         return PAM_SUCCESS;
1072
1073     case PAM_RUSER:
1074         *item = pamh->ruser;
1075         return PAM_SUCCESS;
1076      
1077     case PAM_USER_PROMPT:
1078         *item = LOGIN_PROMPT;
1079         return PAM_SUCCESS;
1080       
1081     case PAM_TTY:
1082         *item = pamh->tty;
1083         return PAM_SUCCESS;
1084
1085     case PAM_CONV:
1086         *item = &pamh->conversation;
1087         return PAM_SUCCESS;
1088
1089     default:
1090         return PAM_BAD_ITEM;
1091     }
1092 }
1093
1094 int
1095 pam_open_session (pam_handle_t *pamh, int flags)
1096 {
1097     if (pamh == NULL)
1098         return PAM_SYSTEM_ERR;
1099
1100     if (strcmp (pamh->user, "session-error") == 0)
1101         return PAM_SESSION_ERR;
1102
1103     if (strcmp (pamh->user, "log-pam") == 0)
1104         send_info (pamh, "pam_open_session");
1105
1106     if (strcmp (pamh->user, "make-home-dir") == 0)
1107     {
1108         struct passwd *entry;
1109         entry = getpwnam (pamh->user);
1110         g_mkdir_with_parents (entry->pw_dir, 0755);
1111     }
1112
1113     return PAM_SUCCESS;
1114 }
1115
1116 int
1117 pam_close_session (pam_handle_t *pamh, int flags)
1118 {
1119     if (pamh == NULL)
1120         return PAM_SYSTEM_ERR;
1121
1122     if (strcmp (pamh->user, "log-pam") == 0)
1123         send_info (pamh, "pam_close_session");
1124
1125     return PAM_SUCCESS;
1126 }
1127
1128 int
1129 pam_acct_mgmt (pam_handle_t *pamh, int flags)
1130 {
1131     if (pamh == NULL)
1132         return PAM_SYSTEM_ERR;
1133   
1134     if (!pamh->user)
1135         return PAM_USER_UNKNOWN;
1136
1137     if (strcmp (pamh->user, "log-pam") == 0)
1138         send_info (pamh, "pam_acct_mgmt");
1139
1140     if (strcmp (pamh->user, "denied") == 0)
1141         return PAM_PERM_DENIED;
1142     if (strcmp (pamh->user, "expired") == 0)
1143         return PAM_ACCT_EXPIRED;
1144     if (strcmp (pamh->user, "new-authtok") == 0)
1145         return PAM_NEW_AUTHTOK_REQD;
1146
1147     return PAM_SUCCESS;
1148 }
1149
1150 int
1151 pam_chauthtok (pam_handle_t *pamh, int flags)
1152 {
1153     struct passwd *entry;
1154     int result;
1155     struct pam_message **msg;
1156     struct pam_response *resp = NULL;
1157
1158     if (pamh == NULL)
1159         return PAM_SYSTEM_ERR;
1160
1161     if (strcmp (pamh->user, "log-pam") == 0)
1162         send_info (pamh, "pam_chauthtok");
1163
1164     msg = malloc (sizeof (struct pam_message *) * 1);
1165     msg[0] = malloc (sizeof (struct pam_message));
1166     msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
1167     msg[0]->msg = "Enter new password:";
1168     result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1169     free (msg[0]);
1170     free (msg);
1171     if (result != PAM_SUCCESS)
1172         return result;
1173
1174     if (resp == NULL)
1175         return PAM_CONV_ERR;
1176     if (resp[0].resp == NULL)
1177     {
1178         free (resp);
1179         return PAM_CONV_ERR;
1180     }
1181
1182     /* Update password database */
1183     entry = getpwnam (pamh->user);
1184     free (entry->pw_passwd);
1185     entry->pw_passwd = resp[0].resp;
1186     free (resp);
1187
1188     return PAM_SUCCESS;
1189 }
1190
1191 int
1192 pam_setcred (pam_handle_t *pamh, int flags)
1193 {
1194     gchar *e;
1195
1196     if (pamh == NULL)
1197         return PAM_SYSTEM_ERR;
1198
1199     if (strcmp (pamh->user, "log-pam") == 0)
1200         send_info (pamh, "pam_setcred");
1201
1202     /* Put the test directories into the path */
1203     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"));
1204     pam_putenv (pamh, e);
1205     g_free (e);
1206
1207     if (strcmp (pamh->user, "cred-error") == 0)
1208         return PAM_CRED_ERR;
1209     if (strcmp (pamh->user, "cred-expired") == 0)
1210         return PAM_CRED_EXPIRED;
1211     if (strcmp (pamh->user, "cred-unavail") == 0)
1212         return PAM_CRED_UNAVAIL;
1213
1214     /* Join special groups if requested */
1215     if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
1216     {
1217         struct group *group;
1218         gid_t *groups;
1219         int groups_length;
1220
1221         group = getgrnam ("test-group");
1222         if (group)
1223         {
1224             groups_length = getgroups (0, NULL);
1225             groups = malloc (sizeof (gid_t) * (groups_length + 1));
1226             groups_length = getgroups (groups_length, groups);
1227             groups[groups_length] = group->gr_gid;
1228             groups_length++;
1229             setgroups (groups_length, groups);
1230             free (groups);
1231         }
1232
1233         /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
1234         pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
1235     }
1236
1237     return PAM_SUCCESS;
1238 }
1239
1240 int
1241 pam_end (pam_handle_t *pamh, int pam_status)
1242 {
1243     if (pamh == NULL)
1244         return PAM_SYSTEM_ERR;
1245   
1246     free (pamh->service_name);
1247     if (pamh->user)
1248         free (pamh->user);
1249     if (pamh->authtok)
1250         free (pamh->authtok);
1251     if (pamh->ruser)
1252         free (pamh->ruser);
1253     if (pamh->tty)
1254         free (pamh->tty);
1255     free (pamh);
1256
1257     return PAM_SUCCESS;
1258 }
1259
1260 const char *
1261 pam_strerror (pam_handle_t *pamh, int errnum)
1262 {
1263     if (pamh == NULL)
1264         return NULL;
1265
1266     switch (errnum)
1267     {
1268     case PAM_SUCCESS:
1269         return "Success";
1270     case PAM_ABORT:
1271         return "Critical error - immediate abort";
1272     case PAM_OPEN_ERR:
1273         return "Failed to load module";
1274     case PAM_SYMBOL_ERR:
1275         return "Symbol not found";
1276     case PAM_SERVICE_ERR:
1277         return "Error in service module";
1278     case PAM_SYSTEM_ERR:
1279         return "System error";
1280     case PAM_BUF_ERR:
1281         return "Memory buffer error";
1282     case PAM_PERM_DENIED:
1283         return "Permission denied";
1284     case PAM_AUTH_ERR:
1285         return "Authentication failure";
1286     case PAM_CRED_INSUFFICIENT:
1287         return "Insufficient credentials to access authentication data";
1288     case PAM_AUTHINFO_UNAVAIL:
1289         return "Authentication service cannot retrieve authentication info";
1290     case PAM_USER_UNKNOWN:
1291         return "User not known to the underlying authentication module";
1292     case PAM_MAXTRIES:
1293         return "Have exhausted maximum number of retries for service";
1294     case PAM_NEW_AUTHTOK_REQD:
1295         return "Authentication token is no longer valid; new one required";
1296     case PAM_ACCT_EXPIRED:
1297         return "User account has expired";
1298     case PAM_SESSION_ERR:
1299         return "Cannot make/remove an entry for the specified session";
1300     case PAM_CRED_UNAVAIL:
1301         return "Authentication service cannot retrieve user credentials";
1302     case PAM_CRED_EXPIRED:
1303         return "User credentials expired";
1304     case PAM_CRED_ERR:
1305         return "Failure setting user credentials";
1306     case PAM_NO_MODULE_DATA:
1307         return "No module specific data is present";
1308     case PAM_BAD_ITEM:
1309         return "Bad item passed to pam_*_item()";
1310     case PAM_CONV_ERR:
1311         return "Conversation error";
1312     case PAM_AUTHTOK_ERR:
1313         return "Authentication token manipulation error";
1314     case PAM_AUTHTOK_RECOVERY_ERR:
1315         return "Authentication information cannot be recovered";
1316     case PAM_AUTHTOK_LOCK_BUSY:
1317         return "Authentication token lock busy";
1318     case PAM_AUTHTOK_DISABLE_AGING:
1319         return "Authentication token aging disabled";
1320     case PAM_TRY_AGAIN:
1321         return "Failed preliminary check by password service";
1322     case PAM_IGNORE:
1323         return "The return value should be ignored by PAM dispatch";
1324     case PAM_MODULE_UNKNOWN:
1325         return "Module is unknown";
1326     case PAM_AUTHTOK_EXPIRED:
1327         return "Authentication token expired";
1328     case PAM_CONV_AGAIN:
1329         return "Conversation is waiting for event";
1330     case PAM_INCOMPLETE:
1331         return "Application needs to call libpam again";
1332     default:
1333         return "Unknown PAM error";
1334     }
1335 }
1336
1337 void
1338 setutxent (void)
1339 {
1340 }
1341   
1342 struct utmp *
1343 pututxline (struct utmp *ut)
1344 {
1345     return ut;
1346 }
1347
1348 void
1349 endutxent (void)
1350 {
1351 }
1352
1353 struct xcb_connection_t
1354 {
1355     gchar *display;
1356     int error;
1357     GSocket *socket;
1358 };
1359
1360 xcb_connection_t *
1361 xcb_connect_to_display_with_auth_info (const char *display, xcb_auth_info_t *auth, int *screen)
1362 {
1363     xcb_connection_t *c;
1364     gchar *socket_path;
1365     GSocketAddress *address;
1366     GError *error = NULL;
1367   
1368     c = malloc (sizeof (xcb_connection_t));
1369     c->display = g_strdup (display);
1370     c->error = 0;
1371
1372     if (display == NULL)
1373         display = getenv ("DISPLAY");
1374     if (display == NULL)
1375         c->error = XCB_CONN_CLOSED_PARSE_ERR;
1376
1377     if (c->error == 0)
1378     {
1379         c->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
1380         if (error)
1381             g_printerr ("%s\n", error->message);
1382         g_clear_error (&error);
1383         if (c->socket == NULL)
1384             c->error = XCB_CONN_ERROR;
1385     }
1386
1387     if (c->error == 0)
1388     {
1389         gchar *d;
1390
1391         /* Skip the hostname, we'll assume it's localhost */
1392         d = g_strdup_printf (".x%s", strchr (display, ':'));
1393
1394         socket_path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), d, NULL);
1395         g_free (d);
1396         address = g_unix_socket_address_new (socket_path);
1397         if (!g_socket_connect (c->socket, address, NULL, &error))
1398             c->error = XCB_CONN_ERROR;
1399         if (error)
1400             g_printerr ("Failed to connect to X socket %s: %s\n", socket_path, error->message);
1401         g_free (socket_path);
1402         g_clear_error (&error);
1403     }
1404
1405     // FIXME: Send auth info
1406     if (c->error == 0)
1407     {
1408     }
1409
1410     g_object_unref (address);
1411
1412     return c;
1413 }
1414
1415 xcb_connection_t *
1416 xcb_connect (const char *displayname, int *screenp)
1417 {
1418     return xcb_connect_to_display_with_auth_info(displayname, NULL, screenp);
1419 }
1420
1421 int
1422 xcb_connection_has_error (xcb_connection_t *c)
1423 {
1424     return c->error;
1425 }
1426
1427 void
1428 xcb_disconnect (xcb_connection_t *c)
1429 {
1430     free (c->display);
1431     if (c->socket)
1432         g_object_unref (c->socket);
1433     free (c);
1434 }