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