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