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