]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/libsystem.c
c93aa202b160a8801ccb135a30a404ececca1239
[sojka/lightdm.git] / tests / src / libsystem.c
1 #define _GNU_SOURCE
2 #define __USE_GNU
3
4 #include <config.h>
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <pwd.h>
16 #include <unistd.h>
17 #include <dirent.h>
18 #include <grp.h>
19 #include <security/pam_appl.h>
20 #include <fcntl.h>
21 #include <dlfcn.h>
22 #include <utmp.h>
23 #include <utmpx.h>
24 #ifdef __linux__
25 #include <linux/vt.h>
26 #endif
27 #include <glib.h>
28 #include <xcb/xcb.h>
29 #include <gio/gunixsocketaddress.h>
30
31 #if HAVE_LIBAUDIT
32 #include <libaudit.h>
33 #endif
34
35 #include "status.h"
36
37 #define LOGIN_PROMPT "login:"
38
39 static int tty_fd = -1;
40
41 static GList *user_entries = NULL;
42 static GList *getpwent_link = NULL;
43
44 static GList *group_entries = NULL;
45
46 static int active_vt = 7;
47
48 static gboolean status_connected = FALSE;
49 static GKeyFile *config;
50
51 static void connect_status (void)
52 {
53     if (status_connected)
54         return;
55     status_connected = TRUE;
56
57     status_connect (NULL, NULL);
58
59     config = g_key_file_new ();
60     g_key_file_load_from_file (config, g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "script", NULL), G_KEY_FILE_NONE, NULL);
61 }
62
63 struct pam_handle
64 {
65     char *id;
66     char *service_name;
67     char *user;
68     char *authtok;
69     char *ruser;
70     char *tty;
71     char **envlist;
72     struct pam_conv conversation;
73 };
74
75 int
76 gethostname (char *name, size_t len)
77 {
78    snprintf (name, len, "lightdm-test");
79    return 0;
80 }
81
82 uid_t
83 getuid (void)
84 {
85     return 0;
86 }
87
88 /*uid_t
89 geteuid (void)
90 {
91     return 0;
92 }*/
93
94 int
95 initgroups (const char *user, gid_t group)
96 {
97     gid_t g[1];
98
99     g[0] = group;
100     setgroups (1, g);
101
102     return 0;
103 }
104
105 int
106 getgroups (int size, gid_t list[])
107 {
108     const gchar *group_list;
109     gchar **groups;
110     gint groups_length;
111
112     /* Get groups we are a member of */
113     group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
114     if (!group_list)
115         group_list = "";
116     groups = g_strsplit (group_list, ",", -1);
117     groups_length = g_strv_length (groups);
118
119     if (size != 0)
120     {
121         int i;
122
123         if (groups_length > size)
124         {
125             errno = EINVAL;
126             return -1;
127         }
128         for (i = 0; groups[i]; i++)
129             list[i] = atoi (groups[i]);
130     }
131     g_free (groups);
132
133     return groups_length;
134 }
135
136 int
137 setgroups (size_t size, const gid_t *list)
138 {
139     size_t i;
140     GString *group_list;
141
142     group_list = g_string_new ("");
143     for (i = 0; i < size; i++)
144     {
145         if (i != 0)
146             g_string_append (group_list, ",");
147         g_string_append_printf (group_list, "%d", list[i]);
148     }
149     g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
150     g_string_free (group_list, TRUE);
151
152     return 0;
153 }
154
155 int
156 setgid (gid_t gid)
157 {
158     return 0;
159 }
160
161 int
162 setegid (gid_t gid)
163 {
164     return 0;
165 }
166
167 int
168 setresgid (gid_t rgid, gid_t ugid, gid_t sgid)
169 {
170     return 0;
171 }
172
173 int
174 setuid (uid_t uid)
175 {
176     return 0;
177 }
178
179 int
180 seteuid (uid_t uid)
181 {
182     return 0;
183 }
184
185 int
186 setresuid (uid_t ruid, uid_t uuid, uid_t suid)
187 {
188     return 0;
189 }
190
191 static gchar *
192 redirect_path (const gchar *path)
193 {
194     // Don't redirect if inside the running directory
195     if (g_str_has_prefix (path, g_getenv ("LIGHTDM_TEST_ROOT")))
196         return g_strdup (path);
197
198     if (g_str_has_prefix (path, SYSCONFDIR))
199         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", path + strlen (SYSCONFDIR), NULL);
200
201     if (g_str_has_prefix (path, LOCALSTATEDIR))
202         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "var", path + strlen (LOCALSTATEDIR), NULL);
203
204     if (g_str_has_prefix (path, DATADIR))
205         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "usr", "share", path + strlen (DATADIR), NULL);
206
207     // Don't redirect if inside the build directory
208     if (g_str_has_prefix (path, BUILDDIR))
209         return g_strdup (path);
210
211     if (g_str_has_prefix (path, "/tmp"))
212         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", path + strlen ("/tmp"), NULL);
213
214     if (g_str_has_prefix (path, "/run"))
215         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "run", path + strlen ("/run"), NULL);
216
217     if (g_str_has_prefix (path, "/etc/xdg"))
218         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "xdg", path + strlen ("/etc/xdg"), NULL);
219
220     if (g_str_has_prefix (path, "/usr/share/lightdm"))
221         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "usr", "share", "lightdm", path + strlen ("/usr/share/lightdm"), NULL);
222
223     return g_strdup (path);
224 }
225
226 #ifdef __linux__
227 static int
228 open_wrapper (const char *func, const char *pathname, int flags, mode_t mode)
229 {
230     int (*_open) (const char *pathname, int flags, mode_t mode);
231     gchar *new_path = NULL;
232     int fd;
233
234     _open = (int (*)(const char *pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, func);
235
236     if (strcmp (pathname, "/dev/tty0") == 0)
237     {
238         if (tty_fd < 0)
239         {
240             tty_fd = _open ("/dev/null", flags, mode);
241             fcntl (tty_fd, F_SETFD, FD_CLOEXEC);
242         }
243         return tty_fd;
244     }
245
246     new_path = redirect_path (pathname);
247     fd = _open (new_path, flags, mode);
248     g_free (new_path);
249
250     return fd;
251 }
252
253 int
254 open (const char *pathname, int flags, ...)
255 {
256     int mode = 0;
257     if (flags & O_CREAT)
258     {
259         va_list ap;
260         va_start (ap, flags);
261         mode = va_arg (ap, mode_t);
262         va_end (ap);
263     }
264     return open_wrapper ("open", pathname, flags, mode);
265 }
266
267 int
268 open64 (const char *pathname, int flags, ...)
269 {
270     int mode = 0;
271     if (flags & O_CREAT)
272     {
273         va_list ap;
274         va_start (ap, flags);
275         mode = va_arg (ap, mode_t);
276         va_end (ap);
277     }
278     return open_wrapper ("open64", pathname, flags, mode);
279 }
280
281 FILE *
282 fopen (const char *path, const char *mode)
283 {
284     FILE *(*_fopen) (const char *pathname, const char *mode);
285     gchar *new_path = NULL;
286     FILE *result;
287
288     _fopen = (FILE *(*)(const char *pathname, const char *mode)) dlsym (RTLD_NEXT, "fopen");
289
290     new_path = redirect_path (path);
291     result = _fopen (new_path, mode);
292     g_free (new_path);
293
294     return result;
295 }
296
297 int
298 unlinkat (int dirfd, const char *pathname, int flags)
299 {
300     int (*_unlinkat) (int dirfd, const char *pathname, int flags);
301     gchar *new_path = NULL;
302     int result;
303
304     _unlinkat = (int (*)(int dirfd, const char *pathname, int flags)) dlsym (RTLD_NEXT, "unlinkat");
305
306     new_path = redirect_path (pathname);
307     result = _unlinkat (dirfd, new_path, flags);
308     g_free (new_path);
309
310     return result;
311 }
312
313 int
314 creat (const char *pathname, mode_t mode)
315 {
316     int (*_creat) (const char *pathname, mode_t mode);
317     gchar *new_path = NULL;
318     int result;
319
320     _creat = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "creat");
321
322     new_path = redirect_path (pathname);
323     result = _creat (new_path, mode);
324     g_free (new_path);
325
326     return result;
327 }
328
329 int
330 creat64 (const char *pathname, mode_t mode)
331 {
332     int (*_creat64) (const char *pathname, mode_t mode);
333     gchar *new_path = NULL;
334     int result;
335
336     _creat64 = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "creat64");
337
338     new_path = redirect_path (pathname);
339     result = _creat64 (new_path, mode);
340     g_free (new_path);
341
342     return result;
343 }
344
345 int
346 access (const char *pathname, int mode)
347 {
348     int (*_access) (const char *pathname, int mode);
349     gchar *new_path = NULL;
350     int ret;
351
352     _access = (int (*)(const char *pathname, int mode)) dlsym (RTLD_NEXT, "access");
353
354     if (strcmp (pathname, "/dev/tty0") == 0)
355         return F_OK;
356     if (strcmp (pathname, "/sys/class/tty/tty0/active") == 0)
357         return F_OK;
358
359     new_path = redirect_path (pathname);
360     ret = _access (new_path, mode);
361     g_free (new_path);
362
363     return ret;
364 }
365
366 int
367 stat (const char *path, struct stat *buf)
368 {
369     int (*_stat) (const char *path, struct stat *buf);
370     gchar *new_path = NULL;
371     int ret;
372
373     _stat = (int (*)(const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "stat");
374
375     new_path = redirect_path (path);
376     ret = _stat (new_path, buf);
377     g_free (new_path);
378
379     return ret;
380 }
381
382 int
383 stat64 (const char *path, struct stat64 *buf)
384 {
385     int (*_stat64) (const char *path, struct stat64 *buf);
386     gchar *new_path = NULL;
387     int ret;
388
389     _stat64 = (int (*)(const char *path, struct stat64 *buf)) dlsym (RTLD_NEXT, "stat64");
390
391     new_path = redirect_path (path);
392     ret = _stat64 (new_path, buf);
393     g_free (new_path);
394
395     return ret;
396 }
397
398 int
399 __xstat (int version, const char *path, struct stat *buf)
400 {
401     int (*___xstat) (int version, const char *path, struct stat *buf);
402     gchar *new_path = NULL;
403     int ret;
404
405     ___xstat = (int (*)(int version, const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "__xstat");
406
407     new_path = redirect_path (path);
408     ret = ___xstat (version, new_path, buf);
409     g_free (new_path);
410
411     return ret;
412 }
413
414 int
415 __xstat64 (int version, const char *path, struct stat64 *buf)
416 {
417     int (*___xstat64) (int version, const char *path, struct stat64 *buf);
418     gchar *new_path = NULL;
419     int ret;
420
421     ___xstat64 = (int (*)(int version, const char *path, struct stat64 *buf)) dlsym (RTLD_NEXT, "__xstat64");
422
423     new_path = redirect_path (path);
424     ret = ___xstat64 (version, new_path, buf);
425     g_free (new_path);
426
427     return ret;
428 }
429
430 int
431 __fxstatat(int ver, int dirfd, const char *pathname, struct stat *buf, int flags)
432 {
433     int (*___fxstatat) (int ver, int dirfd, const char *pathname, struct stat *buf, int flags);
434     gchar *new_path = NULL;
435     int ret;
436
437     ___fxstatat = (int (*)(int ver, int dirfd, const char *pathname, struct stat *buf, int flags)) dlsym (RTLD_NEXT, "__fxstatat");
438
439     new_path = redirect_path (pathname);
440     ret = ___fxstatat (ver, dirfd, new_path, buf, flags);
441     g_free (new_path);
442
443     return ret;
444 }
445
446 int
447 __fxstatat64(int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags)
448 {
449     int (*___fxstatat64) (int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags);
450     gchar *new_path = NULL;
451     int ret;
452
453     ___fxstatat64 = (int (*)(int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags)) dlsym (RTLD_NEXT, "__fxstatat64");
454
455     new_path = redirect_path (pathname);
456     ret = ___fxstatat64 (ver, dirfd, new_path, buf, flags);
457     g_free (new_path);
458
459     return ret;
460 }
461
462 DIR *
463 opendir (const char *name)
464 {
465     DIR *(*_opendir) (const char *name);
466     gchar *new_path = NULL;
467     DIR *result;
468
469     _opendir = (DIR *(*)(const char *name)) dlsym (RTLD_NEXT, "opendir");
470
471     new_path = redirect_path (name);
472     result = _opendir (new_path);
473     g_free (new_path);
474
475     return result;
476 }
477
478 int
479 mkdir (const char *pathname, mode_t mode)
480 {
481     int (*_mkdir) (const char *pathname, mode_t mode);
482     gchar *new_path = NULL;
483     int result;
484
485     _mkdir = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "mkdir");
486
487     new_path = redirect_path (pathname);
488     result = _mkdir (new_path, mode);
489     g_free (new_path);
490
491     return result;
492 }
493
494 int
495 chown (const char *pathname, uid_t owner, gid_t group)
496 {
497     /* Just fake it - we're not root */
498     return 0;
499 }
500
501 int
502 chmod (const char *path, mode_t mode)
503 {
504     int (*_chmod) (const char *path, mode_t mode);
505     gchar *new_path = NULL;
506     int result;
507
508     _chmod = (int (*)(const char *path, mode_t mode)) dlsym (RTLD_NEXT, "chmod");
509
510     new_path = redirect_path (path);
511     result = _chmod (new_path, mode);
512     g_free (new_path);
513
514     return result;
515 }
516
517 int
518 ioctl (int d, unsigned long request, ...)
519 {
520     int (*_ioctl) (int d, int request, ...);
521
522     _ioctl = (int (*)(int d, int request, ...)) dlsym (RTLD_NEXT, "ioctl");
523     if (d > 0 && d == tty_fd)
524     {
525         struct vt_stat *vt_state;
526         int vt;
527         va_list ap;
528
529         switch (request)
530         {
531         case VT_GETSTATE:
532             va_start (ap, request);
533             vt_state = va_arg (ap, struct vt_stat *);
534             va_end (ap);
535             vt_state->v_active = active_vt;
536             break;
537         case VT_ACTIVATE:
538             va_start (ap, request);
539             vt = va_arg (ap, int);
540             va_end (ap);
541             if (vt != active_vt)
542             {
543                 active_vt = vt;
544                 connect_status ();
545                 status_notify ("VT ACTIVATE VT=%d", active_vt);
546             }
547             break;
548         case VT_WAITACTIVE:
549             break;
550         }
551         return 0;
552     }
553     else
554     {
555         va_list ap;
556         void *data;
557
558         va_start (ap, request);
559         data = va_arg (ap, void *);
560         va_end (ap);
561         return _ioctl (d, request, data);
562     }
563 }
564
565 static void
566 add_port_redirect (int requested_port, int redirected_port)
567 {
568     GKeyFile *file;
569     gchar *path, *name, *data;
570
571     file = g_key_file_new ();
572     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), ".port-redirects", NULL);
573     g_key_file_load_from_file (file, path, G_KEY_FILE_NONE, NULL);
574
575     name = g_strdup_printf ("%d", requested_port);
576     g_key_file_set_integer (file, name, "redirected", redirected_port);
577     g_free (name);
578   
579     data = g_key_file_to_data (file, NULL, NULL);
580     g_file_set_contents (path, data, -1, NULL);
581     g_free (data);
582     g_free (path);
583
584     g_key_file_free (file);
585 }
586
587 static int
588 find_port_redirect (int port)
589 {
590     GKeyFile *file;
591     gchar *path, *name;
592     int redirected_port;
593
594     file = g_key_file_new ();
595     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), ".port-redirects", NULL);
596     g_key_file_load_from_file (file, path, G_KEY_FILE_NONE, NULL);
597     g_free (path);
598
599     name = g_strdup_printf ("%d", port);
600     redirected_port = g_key_file_get_integer (file, name, "redirected", NULL);
601     g_free (name);
602     g_key_file_free (file);
603
604     return redirected_port;
605 }
606
607 int
608 bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
609 {
610     int port = 0, redirected_port = 0;
611     int (*_bind) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
612     const struct sockaddr *modified_addr = addr;
613     struct sockaddr_in temp_addr;
614     struct sockaddr_in6 temp_addr6;
615     int retval;
616
617     _bind = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "bind");
618
619     switch (addr->sa_family)
620     {
621     case AF_INET:
622         port = ntohs (((const struct sockaddr_in *) addr)->sin_port);
623         redirected_port = find_port_redirect (port);
624         memcpy (&temp_addr, addr, sizeof (struct sockaddr_in));
625         modified_addr = (struct sockaddr *) &temp_addr;
626         if (redirected_port != 0)
627             temp_addr.sin_port = htons (redirected_port);
628         else
629             temp_addr.sin_port = 0;
630         break;
631     case AF_INET6:
632         port = ntohs (((const struct sockaddr_in6 *) addr)->sin6_port);
633         redirected_port = find_port_redirect (port);
634         memcpy (&temp_addr6, addr, sizeof (struct sockaddr_in6));
635         modified_addr = (struct sockaddr *) &temp_addr6;
636         if (redirected_port != 0)
637             temp_addr6.sin6_port = htons (redirected_port);
638         else
639             temp_addr6.sin6_port = 0;
640         break;
641     }
642
643     retval = _bind (sockfd, modified_addr, addrlen);
644
645     socklen_t temp_addr_len;
646     switch (addr->sa_family)
647     {
648     case AF_INET:
649         temp_addr_len = sizeof (temp_addr);
650         getsockname (sockfd, &temp_addr, &temp_addr_len);
651         if (redirected_port == 0)
652         {
653             redirected_port = ntohs (temp_addr.sin_port);
654             add_port_redirect (port, redirected_port);
655         }
656         break;
657     case AF_INET6:
658         temp_addr_len = sizeof (temp_addr6);
659         getsockname (sockfd, &temp_addr6, &temp_addr_len);
660         if (redirected_port == 0)
661         {
662             redirected_port = ntohs (temp_addr6.sin6_port);
663             add_port_redirect (port, redirected_port);
664         }
665         break;
666     }
667
668     return retval;
669 }
670
671 int
672 connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
673 {
674     int port, redirected_port;
675     const struct sockaddr *modified_addr = addr;
676     struct sockaddr_in temp_addr;
677     struct sockaddr_in6 temp_addr6;
678     int (*_connect) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
679
680     _connect = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "connect");
681
682     switch (addr->sa_family)
683     {
684     case AF_INET:
685         port = ntohs (((const struct sockaddr_in *) addr)->sin_port);
686         redirected_port = find_port_redirect (port);
687         if (redirected_port != 0) 
688         {
689             memcpy (&temp_addr, addr, sizeof (struct sockaddr_in));
690             temp_addr.sin_port = htons (redirected_port);
691             modified_addr = (struct sockaddr *) &temp_addr;
692         }
693         break;
694     case AF_INET6:
695         port = ntohs (((const struct sockaddr_in6 *) addr)->sin6_port);
696         redirected_port = find_port_redirect (port);
697         if (redirected_port != 0) 
698         {
699             memcpy (&temp_addr6, addr, sizeof (struct sockaddr_in6));
700             temp_addr6.sin6_port = htons (redirected_port);
701             modified_addr = (struct sockaddr *) &temp_addr6;
702         }
703         break;
704     }
705
706     return _connect (sockfd, modified_addr, addrlen);
707 }
708
709 ssize_t
710 sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
711 {
712     int port, redirected_port;
713     const struct sockaddr *modified_addr = dest_addr;
714     struct sockaddr_in temp_addr;
715     struct sockaddr_in6 temp_addr6;
716     ssize_t (*_sendto) (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
717
718     _sendto = (ssize_t (*)(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "sendto");
719
720     switch (dest_addr->sa_family)
721     {
722     case AF_INET:
723         port = ntohs (((const struct sockaddr_in *) dest_addr)->sin_port);
724         redirected_port = find_port_redirect (port);
725         if (redirected_port != 0) 
726         {
727             memcpy (&temp_addr, dest_addr, sizeof (struct sockaddr_in));
728             temp_addr.sin_port = htons (redirected_port);
729             modified_addr = (struct sockaddr *) &temp_addr;
730         }
731         break;
732     case AF_INET6:
733         port = ntohs (((const struct sockaddr_in6 *) dest_addr)->sin6_port);
734         redirected_port = find_port_redirect (port);
735         if (redirected_port != 0) 
736         {
737             memcpy (&temp_addr6, dest_addr, sizeof (struct sockaddr_in6));
738             temp_addr6.sin6_port = htons (redirected_port);
739             modified_addr = (struct sockaddr *) &temp_addr6;
740         }
741         break;
742     }
743
744     return _sendto (sockfd, buf, len, flags, modified_addr, addrlen);
745 }
746
747 int
748 close (int fd)
749 {
750     int (*_close) (int fd);
751
752     if (fd > 0 && fd == tty_fd)
753         return 0;
754
755     _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
756     return _close (fd);
757 }
758 #endif
759
760 static void
761 free_user (gpointer data)
762 {
763     struct passwd *entry = data;
764
765     g_free (entry->pw_name);
766     g_free (entry->pw_passwd);
767     g_free (entry->pw_gecos);
768     g_free (entry->pw_dir);
769     g_free (entry->pw_shell);
770     g_free (entry);
771 }
772
773 static void
774 load_passwd_file (void)
775 {
776     gchar *path, *data = NULL, **lines;
777     gint i;
778     GError *error = NULL;
779
780     g_list_free_full (user_entries, free_user);
781     user_entries = NULL;
782     getpwent_link = NULL;
783
784     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
785     g_file_get_contents (path, &data, NULL, &error);
786     g_free (path);
787     if (error)
788         g_warning ("Error loading passwd file: %s", error->message);
789     g_clear_error (&error);
790
791     if (!data)
792         return;
793
794     lines = g_strsplit (data, "\n", -1);
795     g_free (data);
796
797     for (i = 0; lines[i]; i++)
798     {
799         gchar *line, **fields;
800
801         line = g_strstrip (lines[i]);
802         fields = g_strsplit (line, ":", -1);
803         if (g_strv_length (fields) == 7)
804         {
805             struct passwd *entry = malloc (sizeof (struct passwd));
806
807             entry->pw_name = g_strdup (fields[0]);
808             entry->pw_passwd = g_strdup (fields[1]);
809             entry->pw_uid = atoi (fields[2]);
810             entry->pw_gid = atoi (fields[3]);
811             entry->pw_gecos = g_strdup (fields[4]);
812             entry->pw_dir = g_strdup (fields[5]);
813             entry->pw_shell = g_strdup (fields[6]);
814             user_entries = g_list_append (user_entries, entry);
815         }
816         g_strfreev (fields);
817     }
818     g_strfreev (lines);
819 }
820
821 struct passwd *
822 getpwent (void)
823 {
824     if (getpwent_link == NULL)
825     {
826         load_passwd_file ();
827         if (user_entries == NULL)
828             return NULL;
829         getpwent_link = user_entries;
830     }
831     else
832     {
833         if (getpwent_link->next == NULL)
834             return NULL;
835         getpwent_link = getpwent_link->next;
836     }
837
838     return getpwent_link->data;
839 }
840
841 void
842 setpwent (void)
843 {
844     getpwent_link = NULL;
845 }
846
847 void
848 endpwent (void)
849 {
850     getpwent_link = NULL;
851 }
852
853 struct passwd *
854 getpwnam (const char *name)
855 {
856     GList *link;
857
858     if (name == NULL)
859         return NULL;
860
861     load_passwd_file ();
862
863     for (link = user_entries; link; link = link->next)
864     {
865         struct passwd *entry = link->data;
866         if (strcmp (entry->pw_name, name) == 0)
867             break;
868     }
869     if (!link)
870         return NULL;
871
872     return link->data;
873 }
874
875 struct passwd *
876 getpwuid (uid_t uid)
877 {
878     GList *link;
879
880     load_passwd_file ();
881
882     for (link = user_entries; link; link = link->next)
883     {
884         struct passwd *entry = link->data;
885         if (entry->pw_uid == uid)
886             break;
887     }
888     if (!link)
889         return NULL;
890
891     return link->data;
892 }
893
894 static void
895 free_group (gpointer data)
896 {
897     struct group *entry = data;
898
899     g_free (entry->gr_name);
900     g_free (entry->gr_passwd);
901     g_strfreev (entry->gr_mem);
902     g_free (entry);
903 }
904
905 static void
906 load_group_file (void)
907 {
908     gchar *path, *data = NULL, **lines;
909     gint i;
910     GError *error = NULL;
911
912     g_list_free_full (group_entries, free_group);
913     group_entries = NULL;
914
915     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
916     g_file_get_contents (path, &data, NULL, &error);
917     g_free (path);
918     if (error)
919         g_warning ("Error loading group file: %s", error->message);
920     g_clear_error (&error);
921
922     if (!data)
923         return;
924
925     lines = g_strsplit (data, "\n", -1);
926     g_free (data);
927
928     for (i = 0; lines[i]; i++)
929     {
930         gchar *line, **fields;
931
932         line = g_strstrip (lines[i]);
933         fields = g_strsplit (line, ":", -1);
934         if (g_strv_length (fields) == 4)
935         {
936             struct group *entry = malloc (sizeof (struct group));
937
938             entry->gr_name = g_strdup (fields[0]);
939             entry->gr_passwd = g_strdup (fields[1]);
940             entry->gr_gid = atoi (fields[2]);
941             entry->gr_mem = g_strsplit (fields[3], ",", -1);
942             group_entries = g_list_append (group_entries, entry);
943         }
944         g_strfreev (fields);
945     }
946     g_strfreev (lines);
947 }
948
949 struct group *
950 getgrnam (const char *name)
951 {
952     GList *link;
953
954     load_group_file ();
955
956     for (link = group_entries; link; link = link->next)
957     {
958         struct group *entry = link->data;
959         if (strcmp (entry->gr_name, name) == 0)
960             break;
961     }
962     if (!link)
963         return NULL;
964
965     return link->data;
966 }
967
968 struct group *
969 getgrgid (gid_t gid)
970 {
971     GList *link;
972
973     load_group_file ();
974
975     for (link = group_entries; link; link = link->next)
976     {
977         struct group *entry = link->data;
978         if (entry->gr_gid == gid)
979             break;
980     }
981     if (!link)
982         return NULL;
983
984     return link->data;
985 }
986
987 int
988 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
989 {
990     pam_handle_t *handle;
991
992     if (service_name == NULL || conversation == NULL || pamh == NULL)
993         return PAM_SYSTEM_ERR;
994
995     handle = *pamh = malloc (sizeof (pam_handle_t));
996     if (handle == NULL)
997         return PAM_BUF_ERR;
998
999     if (user)
1000         handle->id = g_strdup_printf ("PAM-%s", user);
1001     else
1002         handle->id = g_strdup ("PAM");
1003
1004     connect_status ();
1005     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1006     {
1007         GString *status;
1008
1009         status = g_string_new ("");
1010         g_string_append_printf (status, "%s START", handle->id);
1011         g_string_append_printf (status, " SERVICE=%s", service_name);
1012         if (user)
1013             g_string_append_printf (status, " USER=%s", user);
1014         status_notify ("%s", status->str);
1015         g_string_free (status, TRUE);
1016     }
1017
1018     handle->service_name = strdup (service_name);
1019     handle->user = user ? strdup (user) : NULL;
1020     handle->authtok = NULL;
1021     handle->ruser = NULL;
1022     handle->tty = NULL;
1023     handle->conversation.conv = conversation->conv;
1024     handle->conversation.appdata_ptr = conversation->appdata_ptr;
1025     handle->envlist = malloc (sizeof (char *) * 1);
1026     handle->envlist[0] = NULL;
1027
1028     return PAM_SUCCESS;
1029 }
1030
1031 int
1032 pam_authenticate (pam_handle_t *pamh, int flags)
1033 {
1034     struct passwd *entry;
1035     gboolean password_matches = FALSE;
1036
1037     if (pamh == NULL)
1038         return PAM_SYSTEM_ERR;
1039
1040     connect_status ();
1041     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1042     {
1043         GString *status;
1044
1045         status = g_string_new ("");
1046         g_string_append_printf (status, "%s AUTHENTICATE", pamh->id);
1047         if (flags & PAM_SILENT)
1048             g_string_append (status, " SILENT");
1049         if (flags & PAM_DISALLOW_NULL_AUTHTOK)
1050             g_string_append (status, " DISALLOW_NULL_AUTHTOK");
1051
1052         status_notify ("%s", status->str);
1053         g_string_free (status, TRUE);
1054     }
1055
1056     if (strcmp (pamh->service_name, "test-remote") == 0)
1057     {
1058         int result;
1059         struct pam_message **msg;
1060         struct pam_response *resp = NULL;
1061
1062         msg = malloc (sizeof (struct pam_message *) * 1);
1063         msg[0] = malloc (sizeof (struct pam_message));
1064         msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
1065         msg[0]->msg = "remote-login:";
1066         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1067         free (msg[0]);
1068         free (msg);
1069         if (result != PAM_SUCCESS)
1070             return result;
1071
1072         if (resp == NULL)
1073             return PAM_CONV_ERR;
1074         if (resp[0].resp == NULL)
1075         {
1076             free (resp);
1077             return PAM_CONV_ERR;
1078         }
1079
1080         if (pamh->ruser)
1081             free (pamh->ruser);
1082         pamh->ruser = strdup (resp[0].resp);
1083         free (resp[0].resp);
1084         free (resp);
1085
1086         msg = malloc (sizeof (struct pam_message *) * 1);
1087         msg[0] = malloc (sizeof (struct pam_message));
1088         msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
1089         msg[0]->msg = "remote-password:";
1090         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1091         free (msg[0]);
1092         free (msg);
1093         if (result != PAM_SUCCESS)
1094             return result;
1095
1096         if (resp == NULL)
1097             return PAM_CONV_ERR;
1098         if (resp[0].resp == NULL)
1099         {
1100             free (resp);
1101             return PAM_CONV_ERR;
1102         }
1103
1104         if (pamh->authtok)
1105             free (pamh->authtok);
1106         pamh->authtok = strdup (resp[0].resp);
1107         free (resp[0].resp);
1108         free (resp);
1109
1110         password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
1111
1112         if (password_matches)
1113             return PAM_SUCCESS;
1114         else
1115             return PAM_AUTH_ERR;
1116     }
1117
1118     /* Prompt for username */
1119     if (pamh->user == NULL)
1120     {
1121         int result;
1122         struct pam_message **msg;
1123         struct pam_response *resp = NULL;
1124
1125         msg = malloc (sizeof (struct pam_message *) * 1);
1126         msg[0] = malloc (sizeof (struct pam_message));
1127         msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
1128         msg[0]->msg = LOGIN_PROMPT;
1129         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1130         free (msg[0]);
1131         free (msg);
1132         if (result != PAM_SUCCESS)
1133             return result;
1134
1135         if (resp == NULL)
1136             return PAM_CONV_ERR;
1137         if (resp[0].resp == NULL)
1138         {
1139             free (resp);
1140             return PAM_CONV_ERR;
1141         }
1142
1143         pamh->user = strdup (resp[0].resp);
1144         free (resp[0].resp);
1145         free (resp);
1146     }
1147
1148     /* Crash on authenticate */
1149     if (strcmp (pamh->user, "crash-authenticate") == 0)
1150         kill (getpid (), SIGSEGV);
1151
1152     /* Look up password database */
1153     entry = getpwnam (pamh->user);
1154
1155     /* Prompt for password if required */
1156     if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
1157         password_matches = TRUE;
1158     else
1159     {
1160         int i, n_messages = 0, password_index, result;
1161         struct pam_message **msg;
1162         struct pam_response *resp = NULL;
1163
1164         msg = malloc (sizeof (struct pam_message *) * 5);
1165         if (strcmp (pamh->user, "info-prompt") == 0)
1166         {
1167             msg[n_messages] = malloc (sizeof (struct pam_message));
1168             msg[n_messages]->msg_style = PAM_TEXT_INFO;
1169             msg[n_messages]->msg = "Welcome to LightDM";
1170             n_messages++;
1171         }
1172         if (strcmp (pamh->user, "multi-info-prompt") == 0)
1173         {
1174             msg[n_messages] = malloc (sizeof (struct pam_message));
1175             msg[n_messages]->msg_style = PAM_TEXT_INFO;
1176             msg[n_messages]->msg = "Welcome to LightDM";
1177             n_messages++;
1178             msg[n_messages] = malloc (sizeof (struct pam_message));
1179             msg[n_messages]->msg_style = PAM_ERROR_MSG;
1180             msg[n_messages]->msg = "This is an error";
1181             n_messages++;
1182             msg[n_messages] = malloc (sizeof (struct pam_message));
1183             msg[n_messages]->msg_style = PAM_TEXT_INFO;
1184             msg[n_messages]->msg = "You should have seen three messages";
1185             n_messages++;
1186         }
1187         if (strcmp (pamh->user, "multi-prompt") == 0)
1188         {
1189             msg[n_messages] = malloc (sizeof (struct pam_message));
1190             msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
1191             msg[n_messages]->msg = "Favorite Color:";
1192             n_messages++;
1193         }
1194         msg[n_messages] = malloc (sizeof (struct pam_message));
1195         msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
1196         msg[n_messages]->msg = "Password:";
1197         password_index = n_messages;
1198         n_messages++;
1199         result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1200         for (i = 0; i < n_messages; i++)
1201             free (msg[i]);
1202         free (msg);
1203         if (result != PAM_SUCCESS)
1204             return result;
1205
1206         if (resp == NULL)
1207             return PAM_CONV_ERR;
1208         if (resp[password_index].resp == NULL)
1209         {
1210             free (resp);
1211             return PAM_CONV_ERR;
1212         }
1213
1214         if (entry)
1215             password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
1216
1217         if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
1218             password_matches = strcmp ("blue", resp[0].resp) == 0;
1219
1220         for (i = 0; i < n_messages; i++)
1221         {
1222             if (resp[i].resp)
1223                 free (resp[i].resp);
1224         }
1225         free (resp);
1226
1227         /* Do two factor authentication */
1228         if (password_matches && strcmp (pamh->user, "two-factor") == 0)
1229         {
1230             msg = malloc (sizeof (struct pam_message *) * 1);
1231             msg[0] = malloc (sizeof (struct pam_message));
1232             msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
1233             msg[0]->msg = "OTP:";
1234             resp = NULL;
1235             result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1236             free (msg[0]);
1237             free (msg);
1238             if (result != PAM_SUCCESS)
1239                 return result;
1240
1241             if (resp == NULL)
1242                 return PAM_CONV_ERR;
1243             if (resp[0].resp == NULL)
1244             {
1245                 free (resp);
1246                 return PAM_CONV_ERR;
1247             }
1248             password_matches = strcmp (resp[0].resp, "otp") == 0;
1249             free (resp[0].resp);
1250             free (resp);
1251         }
1252     }
1253
1254     /* Special user has home directory created on login */
1255     if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
1256         g_mkdir_with_parents (entry->pw_dir, 0755);
1257
1258     /* Special user 'change-user1' changes user on authentication */
1259     if (password_matches && strcmp (pamh->user, "change-user1") == 0)
1260     {
1261         g_free (pamh->user);
1262         pamh->user = g_strdup ("change-user2");
1263     }
1264
1265     /* Special user 'change-user-invalid' changes to an invalid user on authentication */
1266     if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
1267     {
1268         g_free (pamh->user);
1269         pamh->user = g_strdup ("invalid-user");
1270     }
1271
1272     if (password_matches)
1273         return PAM_SUCCESS;
1274     else
1275         return PAM_AUTH_ERR;
1276 }
1277
1278 static const char *
1279 get_env_value (const char *name_value, const char *name)
1280 {
1281     int j;
1282
1283     for (j = 0; name[j] && name_value[j] && name[j] == name_value[j]; j++);
1284     if (name[j] == '\0' && name_value[j] == '=')
1285         return &name_value[j + 1];
1286
1287     return NULL;
1288 }
1289
1290 int
1291 pam_putenv (pam_handle_t *pamh, const char *name_value)
1292 {
1293     int i;
1294     gchar *name;
1295
1296     if (pamh == NULL || name_value == NULL)
1297         return PAM_SYSTEM_ERR;
1298
1299     name = strdup (name_value);
1300     for (i = 0; name[i]; i++)
1301         if (name[i] == '=')
1302             name[i] = '\0';
1303     for (i = 0; pamh->envlist[i]; i++)
1304     {
1305         if (get_env_value (pamh->envlist[i], name))
1306             break;
1307     }
1308     free (name);
1309
1310     if (pamh->envlist[i])
1311     {
1312         free (pamh->envlist[i]);
1313         pamh->envlist[i] = strdup (name_value);
1314     }
1315     else
1316     {
1317         pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
1318         pamh->envlist[i] = strdup (name_value);
1319         pamh->envlist[i + 1] = NULL;
1320     }
1321
1322     return PAM_SUCCESS;
1323 }
1324
1325 const char *
1326 pam_getenv (pam_handle_t *pamh, const char *name)
1327 {
1328     int i;
1329
1330     if (pamh == NULL || name == NULL)
1331         return NULL;
1332
1333     for (i = 0; pamh->envlist[i]; i++)
1334     {
1335         const char *value;
1336         value = get_env_value (pamh->envlist[i], name);
1337         if (value)
1338             return value;
1339     }
1340
1341     return NULL;
1342 }
1343
1344 char **
1345 pam_getenvlist (pam_handle_t *pamh)
1346 {
1347     if (pamh == NULL)
1348         return NULL;
1349
1350     return pamh->envlist;
1351 }
1352
1353 int
1354 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
1355 {
1356     if (pamh == NULL || item == NULL)
1357         return PAM_SYSTEM_ERR;
1358
1359     switch (item_type)
1360     {
1361     case PAM_TTY:
1362         if (pamh->tty)
1363             free (pamh->tty);
1364         pamh->tty = strdup ((const char *) item);
1365         return PAM_SUCCESS;
1366
1367     default:
1368         return PAM_BAD_ITEM;
1369     }
1370 }
1371
1372 int
1373 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
1374 {
1375     if (pamh == NULL || item == NULL)
1376         return PAM_SYSTEM_ERR;
1377
1378     switch (item_type)
1379     {
1380     case PAM_SERVICE:
1381         *item = pamh->service_name;
1382         return PAM_SUCCESS;
1383
1384     case PAM_USER:
1385         *item = pamh->user;
1386         return PAM_SUCCESS;
1387
1388     case PAM_AUTHTOK:
1389         *item = pamh->authtok;
1390         return PAM_SUCCESS;
1391
1392     case PAM_RUSER:
1393         *item = pamh->ruser;
1394         return PAM_SUCCESS;
1395
1396     case PAM_USER_PROMPT:
1397         *item = LOGIN_PROMPT;
1398         return PAM_SUCCESS;
1399
1400     case PAM_TTY:
1401         *item = pamh->tty;
1402         return PAM_SUCCESS;
1403
1404     case PAM_CONV:
1405         *item = &pamh->conversation;
1406         return PAM_SUCCESS;
1407
1408     default:
1409         return PAM_BAD_ITEM;
1410     }
1411 }
1412
1413 int
1414 pam_open_session (pam_handle_t *pamh, int flags)
1415 {
1416     GVariant *result;
1417     GError *error = NULL;
1418
1419     if (pamh == NULL)
1420         return PAM_SYSTEM_ERR;
1421
1422     connect_status ();
1423     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1424     {
1425         GString *status;
1426
1427         status = g_string_new ("");
1428         g_string_append_printf (status, "%s OPEN-SESSION", pamh->id);
1429         if (flags & PAM_SILENT)
1430             g_string_append (status, " SILENT");
1431
1432         status_notify ("%s", status->str);
1433         g_string_free (status, TRUE);
1434     }
1435
1436     if (strcmp (pamh->user, "session-error") == 0)
1437         return PAM_SESSION_ERR;
1438
1439     if (strcmp (pamh->user, "make-home-dir") == 0)
1440     {
1441         struct passwd *entry;
1442         entry = getpwnam (pamh->user);
1443         g_mkdir_with_parents (entry->pw_dir, 0755);
1444     }
1445
1446     /* Open logind session */
1447     result = g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
1448                                           "org.freedesktop.login1",
1449                                           "/org/freedesktop/login1",
1450                                           "org.freedesktop.login1.Manager",
1451                                           "CreateSession",
1452                                           g_variant_new ("()", ""),
1453                                           G_VARIANT_TYPE ("(so)"),
1454                                           G_DBUS_CALL_FLAGS_NONE,
1455                                           G_MAXINT,
1456                                           NULL,
1457                                           &error);
1458     if (result)
1459     {
1460         gchar *e;
1461         const gchar *id;
1462
1463         g_variant_get (result, "(&so)", &id, NULL);
1464         e = g_strdup_printf ("XDG_SESSION_ID=%s", id);
1465         pam_putenv (pamh, e);
1466         g_free (e);
1467         g_variant_unref (result);
1468     }
1469     else
1470         g_printerr ("Failed to create logind session: %s\n", error->message);
1471     g_clear_error (&error);
1472
1473     return PAM_SUCCESS;
1474 }
1475
1476 int
1477 pam_close_session (pam_handle_t *pamh, int flags)
1478 {
1479     if (pamh == NULL)
1480         return PAM_SYSTEM_ERR;
1481
1482     connect_status ();
1483     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1484     {
1485         GString *status;
1486
1487         status = g_string_new ("");
1488         g_string_append_printf (status, "%s CLOSE-SESSION", pamh->id);
1489         if (flags & PAM_SILENT)
1490             g_string_append (status, " SILENT");
1491
1492         status_notify ("%s", status->str);
1493         g_string_free (status, TRUE);
1494     }
1495
1496     return PAM_SUCCESS;
1497 }
1498
1499 int
1500 pam_acct_mgmt (pam_handle_t *pamh, int flags)
1501 {
1502     if (pamh == NULL)
1503         return PAM_SYSTEM_ERR;
1504
1505     connect_status ();
1506     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1507     {
1508         GString *status;
1509
1510         status = g_string_new ("");
1511         g_string_append_printf (status, "%s ACCT-MGMT", pamh->id);
1512         if (flags & PAM_SILENT)
1513             g_string_append (status, " SILENT");
1514         if (flags & PAM_DISALLOW_NULL_AUTHTOK)
1515             g_string_append (status, " DISALLOW_NULL_AUTHTOK");
1516
1517         status_notify ("%s", status->str);
1518         g_string_free (status, TRUE);
1519     }
1520
1521     if (!pamh->user)
1522         return PAM_USER_UNKNOWN;
1523
1524     if (strcmp (pamh->user, "denied") == 0)
1525         return PAM_PERM_DENIED;
1526     if (strcmp (pamh->user, "expired") == 0)
1527         return PAM_ACCT_EXPIRED;
1528     if (strcmp (pamh->user, "new-authtok") == 0)
1529         return PAM_NEW_AUTHTOK_REQD;
1530
1531     return PAM_SUCCESS;
1532 }
1533
1534 int
1535 pam_chauthtok (pam_handle_t *pamh, int flags)
1536 {
1537     struct passwd *entry;
1538     int result;
1539     struct pam_message **msg;
1540     struct pam_response *resp = NULL;
1541
1542     if (pamh == NULL)
1543         return PAM_SYSTEM_ERR;
1544
1545     connect_status ();
1546     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1547     {
1548         GString *status;
1549
1550         status = g_string_new ("");
1551         g_string_append_printf (status, "%s CHAUTHTOK", pamh->id);
1552         if (flags & PAM_SILENT)
1553             g_string_append (status, " SILENT");
1554         if (flags & PAM_CHANGE_EXPIRED_AUTHTOK)
1555             g_string_append (status, " CHANGE_EXPIRED_AUTHTOK");
1556
1557         status_notify ("%s", status->str);
1558         g_string_free (status, TRUE);
1559     }
1560
1561     msg = malloc (sizeof (struct pam_message *) * 1);
1562     msg[0] = malloc (sizeof (struct pam_message));
1563     msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
1564     if ((flags & PAM_CHANGE_EXPIRED_AUTHTOK) != 0)
1565         msg[0]->msg = "Enter new password (expired):";
1566     else
1567         msg[0]->msg = "Enter new password:";
1568     result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1569     free (msg[0]);
1570     free (msg);
1571     if (result != PAM_SUCCESS)
1572         return result;
1573
1574     if (resp == NULL)
1575         return PAM_CONV_ERR;
1576     if (resp[0].resp == NULL)
1577     {
1578         free (resp);
1579         return PAM_CONV_ERR;
1580     }
1581
1582     /* Update password database */
1583     entry = getpwnam (pamh->user);
1584     free (entry->pw_passwd);
1585     entry->pw_passwd = resp[0].resp;
1586     free (resp);
1587
1588     return PAM_SUCCESS;
1589 }
1590
1591 int
1592 pam_setcred (pam_handle_t *pamh, int flags)
1593 {
1594     gchar *e;
1595
1596     if (pamh == NULL)
1597         return PAM_SYSTEM_ERR;
1598
1599     connect_status ();
1600     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1601     {
1602         GString *status;
1603
1604         status = g_string_new ("");
1605         g_string_append_printf (status, "%s SETCRED", pamh->id);
1606         if (flags & PAM_SILENT)
1607             g_string_append (status, " SILENT");
1608         if (flags & PAM_ESTABLISH_CRED)
1609             g_string_append (status, " ESTABLISH_CRED");
1610         if (flags & PAM_DELETE_CRED)
1611             g_string_append (status, " DELETE_CRED");
1612         if (flags & PAM_REINITIALIZE_CRED)
1613             g_string_append (status, " REINITIALIZE_CRED");
1614         if (flags & PAM_REFRESH_CRED)
1615             g_string_append (status, " REFRESH_CRED");
1616
1617         status_notify ("%s", status->str);
1618         g_string_free (status, TRUE);
1619     }
1620
1621     /* Put the test directories into the path */
1622     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"));
1623     pam_putenv (pamh, e);
1624     g_free (e);
1625
1626     if (strcmp (pamh->user, "cred-error") == 0)
1627         return PAM_CRED_ERR;
1628     if (strcmp (pamh->user, "cred-expired") == 0)
1629         return PAM_CRED_EXPIRED;
1630     if (strcmp (pamh->user, "cred-unavail") == 0)
1631         return PAM_CRED_UNAVAIL;
1632
1633     /* Join special groups if requested */
1634     if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
1635     {
1636         struct group *group;
1637         gid_t *groups;
1638         int groups_length;
1639
1640         group = getgrnam ("test-group");
1641         if (group)
1642         {
1643             groups_length = getgroups (0, NULL);
1644             if (groups_length < 0)
1645                 return PAM_SYSTEM_ERR;
1646             groups = malloc (sizeof (gid_t) * (groups_length + 1));
1647             groups_length = getgroups (groups_length, groups);
1648             if (groups_length < 0)
1649                 return PAM_SYSTEM_ERR;
1650             groups[groups_length] = group->gr_gid;
1651             groups_length++;
1652             setgroups (groups_length, groups);
1653             free (groups);
1654         }
1655
1656         /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
1657         pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
1658     }
1659
1660     return PAM_SUCCESS;
1661 }
1662
1663 int
1664 pam_end (pam_handle_t *pamh, int pam_status)
1665 {
1666     if (pamh == NULL)
1667         return PAM_SYSTEM_ERR;
1668
1669     connect_status ();
1670     if (g_key_file_get_boolean (config, "test-pam", "log-events", NULL))
1671     {
1672         GString *status;
1673
1674         status = g_string_new ("");
1675         g_string_append_printf (status, "%s END", pamh->id);
1676         status_notify ("%s", status->str);
1677         g_string_free (status, TRUE);
1678     }
1679
1680     free (pamh->id);
1681     free (pamh->service_name);
1682     if (pamh->user)
1683         free (pamh->user);
1684     if (pamh->authtok)
1685         free (pamh->authtok);
1686     if (pamh->ruser)
1687         free (pamh->ruser);
1688     if (pamh->tty)
1689         free (pamh->tty);
1690     free (pamh);
1691
1692     return PAM_SUCCESS;
1693 }
1694
1695 const char *
1696 pam_strerror (pam_handle_t *pamh, int errnum)
1697 {
1698     if (pamh == NULL)
1699         return NULL;
1700
1701     switch (errnum)
1702     {
1703     case PAM_SUCCESS:
1704         return "Success";
1705     case PAM_ABORT:
1706         return "Critical error - immediate abort";
1707     case PAM_OPEN_ERR:
1708         return "Failed to load module";
1709     case PAM_SYMBOL_ERR:
1710         return "Symbol not found";
1711     case PAM_SERVICE_ERR:
1712         return "Error in service module";
1713     case PAM_SYSTEM_ERR:
1714         return "System error";
1715     case PAM_BUF_ERR:
1716         return "Memory buffer error";
1717     case PAM_PERM_DENIED:
1718         return "Permission denied";
1719     case PAM_AUTH_ERR:
1720         return "Authentication failure";
1721     case PAM_CRED_INSUFFICIENT:
1722         return "Insufficient credentials to access authentication data";
1723     case PAM_AUTHINFO_UNAVAIL:
1724         return "Authentication service cannot retrieve authentication info";
1725     case PAM_USER_UNKNOWN:
1726         return "User not known to the underlying authentication module";
1727     case PAM_MAXTRIES:
1728         return "Have exhausted maximum number of retries for service";
1729     case PAM_NEW_AUTHTOK_REQD:
1730         return "Authentication token is no longer valid; new one required";
1731     case PAM_ACCT_EXPIRED:
1732         return "User account has expired";
1733     case PAM_SESSION_ERR:
1734         return "Cannot make/remove an entry for the specified session";
1735     case PAM_CRED_UNAVAIL:
1736         return "Authentication service cannot retrieve user credentials";
1737     case PAM_CRED_EXPIRED:
1738         return "User credentials expired";
1739     case PAM_CRED_ERR:
1740         return "Failure setting user credentials";
1741     case PAM_NO_MODULE_DATA:
1742         return "No module specific data is present";
1743     case PAM_BAD_ITEM:
1744         return "Bad item passed to pam_*_item()";
1745     case PAM_CONV_ERR:
1746         return "Conversation error";
1747     case PAM_AUTHTOK_ERR:
1748         return "Authentication token manipulation error";
1749     case PAM_AUTHTOK_RECOVERY_ERR:
1750         return "Authentication information cannot be recovered";
1751     case PAM_AUTHTOK_LOCK_BUSY:
1752         return "Authentication token lock busy";
1753     case PAM_AUTHTOK_DISABLE_AGING:
1754         return "Authentication token aging disabled";
1755     case PAM_TRY_AGAIN:
1756         return "Failed preliminary check by password service";
1757     case PAM_IGNORE:
1758         return "The return value should be ignored by PAM dispatch";
1759     case PAM_MODULE_UNKNOWN:
1760         return "Module is unknown";
1761     case PAM_AUTHTOK_EXPIRED:
1762         return "Authentication token expired";
1763     case PAM_CONV_AGAIN:
1764         return "Conversation is waiting for event";
1765     case PAM_INCOMPLETE:
1766         return "Application needs to call libpam again";
1767     default:
1768         return "Unknown PAM error";
1769     }
1770 }
1771
1772 void
1773 setutxent (void)
1774 {
1775 }
1776
1777 struct utmpx *
1778 pututxline (const struct utmpx *ut)
1779 {
1780     connect_status ();
1781     if (g_key_file_get_boolean (config, "test-utmp-config", "check-events", NULL))
1782     {
1783         GString *status;
1784
1785         status = g_string_new ("UTMP");
1786         switch (ut->ut_type)
1787         {
1788         case INIT_PROCESS:
1789             g_string_append_printf (status, " TYPE=INIT_PROCESS");
1790             break;
1791         case LOGIN_PROCESS:
1792             g_string_append_printf (status, " TYPE=LOGIN_PROCESS");
1793             break;
1794         case USER_PROCESS:
1795             g_string_append_printf (status, " TYPE=USER_PROCESS");
1796             break;
1797         case DEAD_PROCESS:
1798             g_string_append_printf (status, " TYPE=DEAD_PROCESS");
1799             break;
1800         default:
1801             g_string_append_printf (status, " TYPE=%d", ut->ut_type);
1802         }
1803         if (ut->ut_line)
1804             g_string_append_printf (status, " LINE=%s", ut->ut_line);
1805         if (ut->ut_id)
1806             g_string_append_printf (status, " ID=%s", ut->ut_id);
1807         if (ut->ut_user)
1808             g_string_append_printf (status, " USER=%s", ut->ut_user);
1809         if (ut->ut_host)
1810             g_string_append_printf (status, " HOST=%s", ut->ut_host);
1811         status_notify ("%s", status->str);
1812         g_string_free (status, TRUE);
1813     }
1814
1815     return (struct utmpx *)ut;
1816 }
1817
1818 void
1819 endutxent (void)
1820 {
1821 }
1822
1823 void
1824 updwtmp (const char *wtmp_file, const struct utmp *ut)
1825 {
1826     connect_status ();
1827     if (g_key_file_get_boolean (config, "test-utmp-config", "check-events", NULL))
1828     {
1829         GString *status;
1830
1831         status = g_string_new ("WTMP");
1832         g_string_append_printf (status, " FILE=%s", wtmp_file);
1833         switch (ut->ut_type)
1834         {
1835         case INIT_PROCESS:
1836             g_string_append_printf (status, " TYPE=INIT_PROCESS");
1837             break;
1838         case LOGIN_PROCESS:
1839             g_string_append_printf (status, " TYPE=LOGIN_PROCESS");
1840             break;
1841         case USER_PROCESS:
1842             g_string_append_printf (status, " TYPE=USER_PROCESS");
1843             break;
1844         case DEAD_PROCESS:
1845             g_string_append_printf (status, " TYPE=DEAD_PROCESS");
1846             break;
1847         default:
1848             g_string_append_printf (status, " TYPE=%d", ut->ut_type);
1849         }
1850         if (ut->ut_line)
1851             g_string_append_printf (status, " LINE=%s", ut->ut_line);
1852         if (ut->ut_id)
1853             g_string_append_printf (status, " ID=%s", ut->ut_id);
1854         if (ut->ut_user)
1855             g_string_append_printf (status, " USER=%s", ut->ut_user);
1856         if (ut->ut_host)
1857             g_string_append_printf (status, " HOST=%s", ut->ut_host);
1858         status_notify ("%s", status->str);
1859         g_string_free (status, TRUE);
1860     }
1861 }
1862
1863 struct xcb_connection_t
1864 {
1865     gchar *display;
1866     int error;
1867     GSocket *socket;
1868 };
1869
1870 xcb_connection_t *
1871 xcb_connect_to_display_with_auth_info (const char *display, xcb_auth_info_t *auth, int *screen)
1872 {
1873     xcb_connection_t *c;
1874     gchar *socket_path;
1875     GError *error = NULL;
1876
1877     c = malloc (sizeof (xcb_connection_t));
1878     c->display = g_strdup (display);
1879     c->error = 0;
1880
1881     if (display == NULL)
1882         display = getenv ("DISPLAY");
1883     if (display == NULL)
1884         c->error = XCB_CONN_CLOSED_PARSE_ERR;
1885
1886     if (c->error == 0)
1887     {
1888         c->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
1889         if (error)
1890             g_printerr ("%s\n", error->message);
1891         g_clear_error (&error);
1892         if (c->socket == NULL)
1893             c->error = XCB_CONN_ERROR;
1894     }
1895
1896     if (c->error == 0)
1897     {
1898         gchar *d;
1899         GSocketAddress *address;
1900
1901         /* Skip the hostname, we'll assume it's localhost */
1902         d = g_strdup_printf (".x%s", strchr (display, ':'));
1903
1904         socket_path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), d, NULL);
1905         g_free (d);
1906         address = g_unix_socket_address_new (socket_path);
1907         if (!g_socket_connect (c->socket, address, NULL, &error))
1908             c->error = XCB_CONN_ERROR;
1909         g_object_unref (address);
1910         if (error)
1911             g_printerr ("Failed to connect to X socket %s: %s\n", socket_path, error->message);
1912         g_free (socket_path);
1913         g_clear_error (&error);
1914     }
1915
1916     // FIXME: Send auth info
1917     if (c->error == 0)
1918     {
1919     }
1920
1921     return c;
1922 }
1923
1924 xcb_connection_t *
1925 xcb_connect (const char *displayname, int *screenp)
1926 {
1927     return xcb_connect_to_display_with_auth_info(displayname, NULL, screenp);
1928 }
1929
1930 int
1931 xcb_connection_has_error (xcb_connection_t *c)
1932 {
1933     return c->error;
1934 }
1935
1936 void
1937 xcb_disconnect (xcb_connection_t *c)
1938 {
1939     free (c->display);
1940     if (c->socket)
1941         g_object_unref (c->socket);
1942     free (c);
1943 }
1944
1945 #if HAVE_LIBAUDIT
1946 int
1947 audit_open (void)
1948 {
1949     connect_status ();  
1950     if (g_key_file_get_boolean (config, "test-audit-config", "check-events", NULL))
1951         status_notify ("AUDIT OPEN");
1952
1953     return dup (STDOUT_FILENO);
1954 }
1955
1956 int
1957 audit_log_acct_message (int audit_fd, int type, const char *pgname,
1958                         const char *op, const char *name, unsigned int id,
1959                         const char *host, const char *addr, const char *tty, int result)
1960 {
1961     gchar *type_string;
1962
1963     connect_status ();  
1964     if (!g_key_file_get_boolean (config, "test-audit-config", "check-events", NULL))
1965         return 1;
1966
1967     switch (type)
1968     {
1969     case AUDIT_USER_LOGIN:
1970         type_string = g_strdup ("USER_LOGIN");
1971         break;      
1972     case AUDIT_USER_LOGOUT:
1973         type_string = g_strdup ("USER_LOGOUT");
1974         break;
1975     default:
1976         type_string = g_strdup_printf ("%d", type);
1977         break;
1978     }
1979   
1980     status_notify ("AUDIT LOG-ACCT TYPE=%s PGNAME=%s OP=%s NAME=%s ID=%u HOST=%s ADDR=%s TTY=%s RESULT=%d",
1981                    type_string, pgname ? pgname : "", op ? op : "", name ? name : "", id, host ? host : "", addr ? addr : "", tty ? tty : "", result);
1982
1983     g_free (type_string);
1984
1985     return 1;
1986 }
1987
1988 #endif