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