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