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