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