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