]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/libsystem.c
Make start-local-sessions a property of seats, not display servers.
[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     _access = (int (*)(const char *pathname, int mode)) dlsym (RTLD_NEXT, "access");
294
295     new_path = redirect_path (pathname);
296     ret = _access (new_path, mode);
297     g_free (new_path);
298
299     return ret;
300 }
301
302 int
303 stat (const char *path, struct stat *buf)
304 {
305     int (*_stat) (const char *path, struct stat *buf);
306     gchar *new_path = NULL;
307     int ret;
308   
309     _stat = (int (*)(const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "stat");
310
311     new_path = redirect_path (path);
312     ret = _stat (new_path, buf);
313     g_free (new_path);
314
315     return ret;
316 }
317
318 int
319 stat64 (const char *path, struct stat *buf)
320 {
321     int (*_stat64) (const char *path, struct stat *buf);
322     gchar *new_path = NULL;
323     int ret;
324
325     _stat64 = (int (*)(const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "stat64");
326
327     new_path = redirect_path (path);
328     ret = _stat (new_path, buf);
329     g_free (new_path);
330
331     return ret;
332 }
333
334 int
335 __xstat (int version, const char *path, struct stat *buf)
336 {
337     int (*___xstat) (int version, const char *path, struct stat *buf);
338     gchar *new_path = NULL;
339     int ret;
340   
341     ___xstat = (int (*)(int version, const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "__xstat");
342
343     new_path = redirect_path (path);
344     ret = ___xstat (version, new_path, buf);
345     g_free (new_path);
346
347     return ret;
348 }
349
350 int
351 __xstat64 (int version, const char *path, struct stat *buf)
352 {
353     int (*___xstat64) (int version, const char *path, struct stat *buf);
354     gchar *new_path = NULL;
355     int ret;
356   
357     ___xstat64 = (int (*)(int version, const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "__xstat64");
358
359     new_path = redirect_path (path);
360     ret = ___xstat64 (version, new_path, buf);
361     g_free (new_path);
362
363     return ret;
364 }
365
366 DIR *
367 opendir (const char *name)
368 {
369     DIR *(*_opendir) (const char *name);
370     gchar *new_path = NULL;
371     DIR *result;
372
373     _opendir = (DIR *(*)(const char *name)) dlsym (RTLD_NEXT, "opendir");
374
375     new_path = redirect_path (name);
376     result = _opendir (new_path);
377     g_free (new_path);
378
379     return result; 
380 }
381
382 int
383 mkdir (const char *pathname, mode_t mode)
384 {
385     int (*_mkdir) (const char *pathname, mode_t mode);
386     gchar *new_path = NULL;
387     int result;
388
389     _mkdir = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "mkdir");
390
391     new_path = redirect_path (pathname);
392     result = _mkdir (new_path, mode);
393     g_free (new_path);
394
395     return result;
396 }
397
398 int
399 chown (const char *pathname, uid_t owner, gid_t group)
400 {
401     int (*_chown) (const char *pathname, uid_t owner, gid_t group);
402     gchar *new_path = NULL;
403     int result;
404
405     _chown = (int (*)(const char *pathname, uid_t owner, gid_t group)) dlsym (RTLD_NEXT, "chown");
406
407     new_path = redirect_path (pathname);
408     result = _chown (new_path, owner, group);
409     g_free (new_path);
410
411     return result;
412 }
413
414 int
415 ioctl (int d, int request, void *data)
416 {
417     int (*_ioctl) (int d, int request, void *data);
418
419     _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
420     if (d > 0 && d == console_fd)
421     {
422         struct vt_stat *console_state;
423         int *n;
424         int vt;
425
426         switch (request)
427         {
428         case VT_GETSTATE:
429             console_state = data;
430             console_state->v_active = active_vt;
431             break;
432         case VT_ACTIVATE:
433             vt = GPOINTER_TO_INT (data);
434             if (vt != active_vt)
435             {
436                 active_vt = vt;
437                 if (!status_connected)
438                     status_connected = status_connect (NULL);
439                 status_notify ("VT ACTIVATE VT=%d", active_vt);
440             }
441             break;
442         case VT_WAITACTIVE:
443             break;
444         }
445         return 0;
446     }
447     else
448         return _ioctl (d, request, data);
449 }
450
451 int
452 close (int fd)
453 {
454     int (*_close) (int fd);
455
456     if (fd > 0 && fd == console_fd)
457         return 0;
458
459     _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
460     return _close (fd);
461 }
462 #endif
463
464 static void
465 free_user (gpointer data)
466 {
467     struct passwd *entry = data;
468   
469     g_free (entry->pw_name);
470     g_free (entry->pw_passwd);
471     g_free (entry->pw_gecos);
472     g_free (entry->pw_dir);
473     g_free (entry->pw_shell);
474     g_free (entry);
475 }
476
477 static void
478 load_passwd_file ()
479 {
480     gchar *path, *data = NULL, **lines;
481     gint i;
482     GError *error = NULL;
483
484     g_list_free_full (user_entries, free_user);
485     user_entries = NULL;
486     getpwent_link = NULL;
487
488     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
489     g_file_get_contents (path, &data, NULL, &error);
490     g_free (path);
491     if (error)
492         g_warning ("Error loading passwd file: %s", error->message);
493     g_clear_error (&error);
494
495     if (!data)
496         return;
497
498     lines = g_strsplit (data, "\n", -1);
499     g_free (data);
500
501     for (i = 0; lines[i]; i++)
502     {
503         gchar *line, **fields;
504
505         line = g_strstrip (lines[i]);
506         fields = g_strsplit (line, ":", -1);
507         if (g_strv_length (fields) == 7)
508         {
509             struct passwd *entry = malloc (sizeof (struct passwd));
510
511             entry->pw_name = g_strdup (fields[0]);
512             entry->pw_passwd = g_strdup (fields[1]);
513             entry->pw_uid = atoi (fields[2]);
514             entry->pw_gid = atoi (fields[3]);
515             entry->pw_gecos = g_strdup (fields[4]);
516             entry->pw_dir = g_strdup (fields[5]);
517             entry->pw_shell = g_strdup (fields[6]);
518             user_entries = g_list_append (user_entries, entry);
519         }
520         g_strfreev (fields);
521     }
522     g_strfreev (lines);
523 }
524
525 struct passwd *
526 getpwent (void)
527 {
528     if (getpwent_link == NULL)
529     {
530         load_passwd_file ();
531         if (user_entries == NULL)
532             return NULL;
533         getpwent_link = user_entries;
534     }
535     else
536     {
537         if (getpwent_link->next == NULL)
538             return NULL;
539         getpwent_link = getpwent_link->next;
540     }
541
542     return getpwent_link->data;
543 }
544
545 void
546 setpwent (void)
547 {
548     getpwent_link = NULL;
549 }
550
551 void
552 endpwent (void)
553 {
554     getpwent_link = NULL;
555 }
556
557 struct passwd *
558 getpwnam (const char *name)
559 {
560     GList *link;
561   
562     if (name == NULL)
563         return NULL;
564   
565     load_passwd_file ();
566
567     for (link = user_entries; link; link = link->next)
568     {
569         struct passwd *entry = link->data;
570         if (strcmp (entry->pw_name, name) == 0)
571             break;
572     }
573     if (!link)
574         return NULL;
575
576     return link->data;
577 }
578
579 struct passwd *
580 getpwuid (uid_t uid)
581 {
582     GList *link;
583
584     load_passwd_file ();
585
586     for (link = user_entries; link; link = link->next)
587     {
588         struct passwd *entry = link->data;
589         if (entry->pw_uid == uid)
590             break;
591     }
592     if (!link)
593         return NULL;
594
595     return link->data;
596 }
597
598 static void
599 free_group (gpointer data)
600 {
601     struct group *entry = data;
602   
603     g_free (entry->gr_name);
604     g_free (entry->gr_passwd);
605     g_strfreev (entry->gr_mem);
606     g_free (entry);
607 }
608
609 static void
610 load_group_file ()
611 {
612     gchar *path, *data = NULL, **lines;
613     gint i;
614     GError *error = NULL;
615
616     g_list_free_full (group_entries, free_group);
617     group_entries = NULL;
618
619     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
620     g_file_get_contents (path, &data, NULL, &error);
621     g_free (path);
622     if (error)
623         g_warning ("Error loading group file: %s", error->message);
624     g_clear_error (&error);
625
626     if (!data)
627         return;
628
629     lines = g_strsplit (data, "\n", -1);
630     g_free (data);
631
632     for (i = 0; lines[i]; i++)
633     {
634         gchar *line, **fields;
635
636         line = g_strstrip (lines[i]);
637         fields = g_strsplit (line, ":", -1);
638         if (g_strv_length (fields) == 4)
639         {
640             struct group *entry = malloc (sizeof (struct group));
641
642             entry->gr_name = g_strdup (fields[0]);
643             entry->gr_passwd = g_strdup (fields[1]);
644             entry->gr_gid = atoi (fields[2]);
645             entry->gr_mem = g_strsplit (fields[3], ",", -1);
646             group_entries = g_list_append (group_entries, entry);
647         }
648         g_strfreev (fields);
649     }
650     g_strfreev (lines);
651 }
652
653 struct group *
654 getgrnam (const char *name)
655 {
656     GList *link;
657
658     load_group_file ();
659
660     for (link = group_entries; link; link = link->next)
661     {
662         struct group *entry = link->data;
663         if (strcmp (entry->gr_name, name) == 0)
664             break;
665     }
666     if (!link)
667         return NULL;
668
669     return link->data;
670 }
671
672 struct group *
673 getgrgid (gid_t gid)
674 {
675     GList *link;
676
677     load_group_file ();
678
679     for (link = group_entries; link; link = link->next)
680     {
681         struct group *entry = link->data;
682         if (entry->gr_gid == gid)
683             break;
684     }
685     if (!link)
686         return NULL;
687
688     return link->data;
689 }
690
691 int
692 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
693 {
694     pam_handle_t *handle;
695
696     if (service_name == NULL || conversation == NULL || pamh == NULL)
697         return PAM_SYSTEM_ERR;
698
699     handle = *pamh = malloc (sizeof (pam_handle_t));
700     if (handle == NULL)
701         return PAM_BUF_ERR;
702
703     handle->service_name = strdup (service_name);
704     handle->user = user ? strdup (user) : NULL;
705     handle->authtok = NULL;
706     handle->ruser = NULL;
707     handle->tty = NULL;
708     handle->conversation.conv = conversation->conv;
709     handle->conversation.appdata_ptr = conversation->appdata_ptr;
710     handle->envlist = malloc (sizeof (char *) * 1);
711     handle->envlist[0] = NULL;
712
713     return PAM_SUCCESS;
714 }
715
716 static void
717 send_info (pam_handle_t *pamh, const char *message)
718 {
719     struct pam_message **msg;
720     struct pam_response *resp = NULL;
721
722     msg = calloc (1, sizeof (struct pam_message *));
723     msg[0] = malloc (sizeof (struct pam_message));
724     msg[0]->msg_style = PAM_TEXT_INFO;
725     msg[0]->msg = message;
726     pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
727     free (msg[0]);
728     free (msg);
729     if (resp)
730     {
731         if (resp[0].resp)
732             free (resp[0].resp);
733         free (resp);
734     }
735 }
736
737 int
738 pam_authenticate (pam_handle_t *pamh, int flags)
739 {
740     struct passwd *entry;
741     gboolean password_matches = FALSE;
742
743     if (pamh == NULL)
744         return PAM_SYSTEM_ERR;
745   
746     if (strcmp (pamh->service_name, "test-remote") == 0)
747     {
748         int result;
749         struct pam_message **msg;
750         struct pam_response *resp = NULL;
751
752         msg = malloc (sizeof (struct pam_message *) * 1);
753         msg[0] = malloc (sizeof (struct pam_message));
754         msg[0]->msg_style = PAM_PROMPT_ECHO_ON; 
755         msg[0]->msg = "remote-login:";
756         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
757         free (msg[0]);
758         free (msg);
759         if (result != PAM_SUCCESS)
760             return result;
761
762         if (resp == NULL)
763             return PAM_CONV_ERR;
764         if (resp[0].resp == NULL)
765         {
766             free (resp);
767             return PAM_CONV_ERR;
768         }
769
770         if (pamh->ruser)
771             free (pamh->ruser);
772         pamh->ruser = strdup (resp[0].resp);
773         free (resp[0].resp);
774         free (resp);
775
776         msg = malloc (sizeof (struct pam_message *) * 1);
777         msg[0] = malloc (sizeof (struct pam_message));
778         msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
779         msg[0]->msg = "remote-password:";
780         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
781         free (msg[0]);
782         free (msg);
783         if (result != PAM_SUCCESS)
784             return result;
785
786         if (resp == NULL)
787             return PAM_CONV_ERR;
788         if (resp[0].resp == NULL)
789         {
790             free (resp);
791             return PAM_CONV_ERR;
792         }
793
794         if (pamh->authtok)
795             free (pamh->authtok);
796         pamh->authtok = strdup (resp[0].resp);
797         free (resp[0].resp);
798         free (resp);
799
800         password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
801
802         if (password_matches)
803             return PAM_SUCCESS;
804         else
805             return PAM_AUTH_ERR;
806     }
807
808     /* Prompt for username */
809     if (pamh->user == NULL)
810     {
811         int result;
812         struct pam_message **msg;
813         struct pam_response *resp = NULL;
814
815         msg = malloc (sizeof (struct pam_message *) * 1);
816         msg[0] = malloc (sizeof (struct pam_message));
817         msg[0]->msg_style = PAM_PROMPT_ECHO_ON; 
818         msg[0]->msg = LOGIN_PROMPT;
819         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
820         free (msg[0]);
821         free (msg);
822         if (result != PAM_SUCCESS)
823             return result;
824
825         if (resp == NULL)
826             return PAM_CONV_ERR;
827         if (resp[0].resp == NULL)
828         {
829             free (resp);
830             return PAM_CONV_ERR;
831         }
832       
833         pamh->user = strdup (resp[0].resp);
834         free (resp[0].resp);
835         free (resp);
836     }
837
838     if (strcmp (pamh->user, "log-pam") == 0)
839         send_info (pamh, "pam_authenticate");
840
841     /* Crash on authenticate */
842     if (strcmp (pamh->user, "crash-authenticate") == 0)
843         kill (getpid (), SIGSEGV);
844
845     /* Look up password database */
846     entry = getpwnam (pamh->user);
847
848     /* Prompt for password if required */
849     if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
850         password_matches = TRUE;
851     else
852     {
853         int i, n_messages = 0, password_index, result;
854         struct pam_message **msg;
855         struct pam_response *resp = NULL;
856
857         msg = malloc (sizeof (struct pam_message *) * 5);
858         if (strcmp (pamh->user, "info-prompt") == 0)
859         {
860             msg[n_messages] = malloc (sizeof (struct pam_message));
861             msg[n_messages]->msg_style = PAM_TEXT_INFO;
862             msg[n_messages]->msg = "Welcome to LightDM";
863             n_messages++;
864         }
865         if (strcmp (pamh->user, "multi-info-prompt") == 0)
866         {
867             msg[n_messages] = malloc (sizeof (struct pam_message));
868             msg[n_messages]->msg_style = PAM_TEXT_INFO;
869             msg[n_messages]->msg = "Welcome to LightDM";
870             n_messages++;
871             msg[n_messages] = malloc (sizeof (struct pam_message));
872             msg[n_messages]->msg_style = PAM_ERROR_MSG;
873             msg[n_messages]->msg = "This is an error";
874             n_messages++;
875             msg[n_messages] = malloc (sizeof (struct pam_message));
876             msg[n_messages]->msg_style = PAM_TEXT_INFO;
877             msg[n_messages]->msg = "You should have seen three messages";
878             n_messages++;
879         }
880         if (strcmp (pamh->user, "multi-prompt") == 0)
881         {
882             msg[n_messages] = malloc (sizeof (struct pam_message));
883             msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
884             msg[n_messages]->msg = "Favorite Color:";
885             n_messages++;
886         }
887         msg[n_messages] = malloc (sizeof (struct pam_message));
888         msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
889         msg[n_messages]->msg = "Password:";
890         password_index = n_messages;
891         n_messages++;
892         result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
893         for (i = 0; i < n_messages; i++)
894             free (msg[i]);
895         free (msg);
896         if (result != PAM_SUCCESS)
897             return result;
898
899         if (resp == NULL)
900             return PAM_CONV_ERR;
901         if (resp[password_index].resp == NULL)
902         {
903             free (resp);
904             return PAM_CONV_ERR;
905         }
906
907         if (entry)
908             password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
909
910         if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
911             password_matches = strcmp ("blue", resp[0].resp) == 0;
912
913         for (i = 0; i < n_messages; i++)
914         {
915             if (resp[i].resp)
916                 free (resp[i].resp);
917         }
918         free (resp);
919
920         /* Do two factor authentication */
921         if (password_matches && strcmp (pamh->user, "two-factor") == 0)
922         {
923             msg = malloc (sizeof (struct pam_message *) * 1);
924             msg[0] = malloc (sizeof (struct pam_message));
925             msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
926             msg[0]->msg = "OTP:";
927             resp = NULL;
928             result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
929             free (msg[0]);
930             free (msg);
931
932             if (resp == NULL)
933                 return PAM_CONV_ERR;
934             if (resp[0].resp == NULL)
935             {
936                 free (resp);
937                 return PAM_CONV_ERR;
938             }
939             password_matches = strcmp (resp[0].resp, "otp") == 0;
940             free (resp[0].resp);
941             free (resp);
942         }
943     }
944
945     /* Special user has home directory created on login */
946     if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
947         g_mkdir_with_parents (entry->pw_dir, 0755);
948
949     /* Special user 'change-user1' changes user on authentication */
950     if (password_matches && strcmp (pamh->user, "change-user1") == 0)
951     {
952         g_free (pamh->user);
953         pamh->user = g_strdup ("change-user2");
954     }
955
956     /* Special user 'change-user-invalid' changes to an invalid user on authentication */
957     if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
958     {
959         g_free (pamh->user);
960         pamh->user = g_strdup ("invalid-user");
961     }
962
963     if (password_matches)
964         return PAM_SUCCESS;
965     else
966         return PAM_AUTH_ERR;
967 }
968
969 static const char *
970 get_env_value (const char *name_value, const char *name)
971 {
972     int j;
973   
974     for (j = 0; name[j] && name_value[j] && name[j] == name_value[j]; j++);
975     if (name[j] == '\0' && name_value[j] == '=')
976         return &name_value[j + 1];
977
978     return NULL;
979 }
980
981 int
982 pam_putenv (pam_handle_t *pamh, const char *name_value)
983 {
984     int i;
985     gchar *name;
986
987     if (pamh == NULL || name_value == NULL)
988         return PAM_SYSTEM_ERR;
989
990     name = strdup (name_value);
991     for (i = 0; name[i]; i++)
992         if (name[i] == '=')
993             name[i] = '\0';
994     for (i = 0; pamh->envlist[i]; i++)
995     {
996         if (get_env_value (pamh->envlist[i], name))
997             break;
998     }
999     free (name);
1000
1001     if (pamh->envlist[i])
1002     {
1003         free (pamh->envlist[i]);
1004         pamh->envlist[i] = strdup (name_value);
1005     }
1006     else
1007     {
1008         pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
1009         pamh->envlist[i] = strdup (name_value);
1010         pamh->envlist[i + 1] = NULL;
1011     }
1012
1013     return PAM_SUCCESS;
1014 }
1015
1016 const char *
1017 pam_getenv (pam_handle_t *pamh, const char *name)
1018 {
1019     int i;
1020
1021     if (pamh == NULL || name == NULL)
1022         return NULL;
1023
1024     for (i = 0; pamh->envlist[i]; i++)
1025     {
1026         const char *value;
1027         value = get_env_value (pamh->envlist[i], name);
1028         if (value)
1029             return value;
1030     }
1031
1032     return NULL;
1033 }
1034
1035 char **
1036 pam_getenvlist (pam_handle_t *pamh)
1037 {
1038     if (pamh == NULL)
1039         return NULL;
1040
1041     return pamh->envlist;
1042 }
1043
1044 int
1045 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
1046 {
1047     if (pamh == NULL || item == NULL)
1048         return PAM_SYSTEM_ERR;
1049
1050     switch (item_type)
1051     {
1052     case PAM_TTY:
1053         if (pamh->tty)
1054             free (pamh->tty);
1055         pamh->tty = strdup ((const char *) item);
1056         return PAM_SUCCESS;
1057
1058     default:
1059         return PAM_BAD_ITEM;
1060     }
1061 }
1062
1063 int
1064 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
1065 {
1066     if (pamh == NULL || item == NULL)
1067         return PAM_SYSTEM_ERR;
1068   
1069     switch (item_type)
1070     {
1071     case PAM_SERVICE:
1072         *item = pamh->service_name;
1073         return PAM_SUCCESS;
1074       
1075     case PAM_USER:
1076         *item = pamh->user;
1077         return PAM_SUCCESS;
1078
1079     case PAM_AUTHTOK:
1080         *item = pamh->authtok;
1081         return PAM_SUCCESS;
1082
1083     case PAM_RUSER:
1084         *item = pamh->ruser;
1085         return PAM_SUCCESS;
1086      
1087     case PAM_USER_PROMPT:
1088         *item = LOGIN_PROMPT;
1089         return PAM_SUCCESS;
1090       
1091     case PAM_TTY:
1092         *item = pamh->tty;
1093         return PAM_SUCCESS;
1094
1095     case PAM_CONV:
1096         *item = &pamh->conversation;
1097         return PAM_SUCCESS;
1098
1099     default:
1100         return PAM_BAD_ITEM;
1101     }
1102 }
1103
1104 int
1105 pam_open_session (pam_handle_t *pamh, int flags)
1106 {
1107     if (pamh == NULL)
1108         return PAM_SYSTEM_ERR;
1109
1110     if (strcmp (pamh->user, "session-error") == 0)
1111         return PAM_SESSION_ERR;
1112
1113     if (strcmp (pamh->user, "log-pam") == 0)
1114         send_info (pamh, "pam_open_session");
1115
1116     if (strcmp (pamh->user, "make-home-dir") == 0)
1117     {
1118         struct passwd *entry;
1119         entry = getpwnam (pamh->user);
1120         g_mkdir_with_parents (entry->pw_dir, 0755);
1121     }
1122
1123     return PAM_SUCCESS;
1124 }
1125
1126 int
1127 pam_close_session (pam_handle_t *pamh, int flags)
1128 {
1129     if (pamh == NULL)
1130         return PAM_SYSTEM_ERR;
1131
1132     if (strcmp (pamh->user, "log-pam") == 0)
1133         send_info (pamh, "pam_close_session");
1134
1135     return PAM_SUCCESS;
1136 }
1137
1138 int
1139 pam_acct_mgmt (pam_handle_t *pamh, int flags)
1140 {
1141     if (pamh == NULL)
1142         return PAM_SYSTEM_ERR;
1143   
1144     if (!pamh->user)
1145         return PAM_USER_UNKNOWN;
1146
1147     if (strcmp (pamh->user, "log-pam") == 0)
1148         send_info (pamh, "pam_acct_mgmt");
1149
1150     if (strcmp (pamh->user, "denied") == 0)
1151         return PAM_PERM_DENIED;
1152     if (strcmp (pamh->user, "expired") == 0)
1153         return PAM_ACCT_EXPIRED;
1154     if (strcmp (pamh->user, "new-authtok") == 0)
1155         return PAM_NEW_AUTHTOK_REQD;
1156
1157     return PAM_SUCCESS;
1158 }
1159
1160 int
1161 pam_chauthtok (pam_handle_t *pamh, int flags)
1162 {
1163     struct passwd *entry;
1164     int result;
1165     struct pam_message **msg;
1166     struct pam_response *resp = NULL;
1167
1168     if (pamh == NULL)
1169         return PAM_SYSTEM_ERR;
1170
1171     if (strcmp (pamh->user, "log-pam") == 0)
1172         send_info (pamh, "pam_chauthtok");
1173
1174     msg = malloc (sizeof (struct pam_message *) * 1);
1175     msg[0] = malloc (sizeof (struct pam_message));
1176     msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
1177     msg[0]->msg = "Enter new password:";
1178     result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1179     free (msg[0]);
1180     free (msg);
1181     if (result != PAM_SUCCESS)
1182         return result;
1183
1184     if (resp == NULL)
1185         return PAM_CONV_ERR;
1186     if (resp[0].resp == NULL)
1187     {
1188         free (resp);
1189         return PAM_CONV_ERR;
1190     }
1191
1192     /* Update password database */
1193     entry = getpwnam (pamh->user);
1194     free (entry->pw_passwd);
1195     entry->pw_passwd = resp[0].resp;
1196     free (resp);
1197
1198     return PAM_SUCCESS;
1199 }
1200
1201 int
1202 pam_setcred (pam_handle_t *pamh, int flags)
1203 {
1204     gchar *e;
1205
1206     if (pamh == NULL)
1207         return PAM_SYSTEM_ERR;
1208
1209     if (strcmp (pamh->user, "log-pam") == 0)
1210         send_info (pamh, "pam_setcred");
1211
1212     /* Put the test directories into the path */
1213     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"));
1214     pam_putenv (pamh, e);
1215     g_free (e);
1216
1217     if (strcmp (pamh->user, "cred-error") == 0)
1218         return PAM_CRED_ERR;
1219     if (strcmp (pamh->user, "cred-expired") == 0)
1220         return PAM_CRED_EXPIRED;
1221     if (strcmp (pamh->user, "cred-unavail") == 0)
1222         return PAM_CRED_UNAVAIL;
1223
1224     /* Join special groups if requested */
1225     if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
1226     {
1227         struct group *group;
1228         gid_t *groups;
1229         int groups_length;
1230
1231         group = getgrnam ("test-group");
1232         if (group)
1233         {
1234             groups_length = getgroups (0, NULL);
1235             groups = malloc (sizeof (gid_t) * (groups_length + 1));
1236             groups_length = getgroups (groups_length, groups);
1237             groups[groups_length] = group->gr_gid;
1238             groups_length++;
1239             setgroups (groups_length, groups);
1240             free (groups);
1241         }
1242
1243         /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
1244         pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
1245     }
1246
1247     return PAM_SUCCESS;
1248 }
1249
1250 int
1251 pam_end (pam_handle_t *pamh, int pam_status)
1252 {
1253     if (pamh == NULL)
1254         return PAM_SYSTEM_ERR;
1255   
1256     free (pamh->service_name);
1257     if (pamh->user)
1258         free (pamh->user);
1259     if (pamh->authtok)
1260         free (pamh->authtok);
1261     if (pamh->ruser)
1262         free (pamh->ruser);
1263     if (pamh->tty)
1264         free (pamh->tty);
1265     free (pamh);
1266
1267     return PAM_SUCCESS;
1268 }
1269
1270 const char *
1271 pam_strerror (pam_handle_t *pamh, int errnum)
1272 {
1273     if (pamh == NULL)
1274         return NULL;
1275
1276     switch (errnum)
1277     {
1278     case PAM_SUCCESS:
1279         return "Success";
1280     case PAM_ABORT:
1281         return "Critical error - immediate abort";
1282     case PAM_OPEN_ERR:
1283         return "Failed to load module";
1284     case PAM_SYMBOL_ERR:
1285         return "Symbol not found";
1286     case PAM_SERVICE_ERR:
1287         return "Error in service module";
1288     case PAM_SYSTEM_ERR:
1289         return "System error";
1290     case PAM_BUF_ERR:
1291         return "Memory buffer error";
1292     case PAM_PERM_DENIED:
1293         return "Permission denied";
1294     case PAM_AUTH_ERR:
1295         return "Authentication failure";
1296     case PAM_CRED_INSUFFICIENT:
1297         return "Insufficient credentials to access authentication data";
1298     case PAM_AUTHINFO_UNAVAIL:
1299         return "Authentication service cannot retrieve authentication info";
1300     case PAM_USER_UNKNOWN:
1301         return "User not known to the underlying authentication module";
1302     case PAM_MAXTRIES:
1303         return "Have exhausted maximum number of retries for service";
1304     case PAM_NEW_AUTHTOK_REQD:
1305         return "Authentication token is no longer valid; new one required";
1306     case PAM_ACCT_EXPIRED:
1307         return "User account has expired";
1308     case PAM_SESSION_ERR:
1309         return "Cannot make/remove an entry for the specified session";
1310     case PAM_CRED_UNAVAIL:
1311         return "Authentication service cannot retrieve user credentials";
1312     case PAM_CRED_EXPIRED:
1313         return "User credentials expired";
1314     case PAM_CRED_ERR:
1315         return "Failure setting user credentials";
1316     case PAM_NO_MODULE_DATA:
1317         return "No module specific data is present";
1318     case PAM_BAD_ITEM:
1319         return "Bad item passed to pam_*_item()";
1320     case PAM_CONV_ERR:
1321         return "Conversation error";
1322     case PAM_AUTHTOK_ERR:
1323         return "Authentication token manipulation error";
1324     case PAM_AUTHTOK_RECOVERY_ERR:
1325         return "Authentication information cannot be recovered";
1326     case PAM_AUTHTOK_LOCK_BUSY:
1327         return "Authentication token lock busy";
1328     case PAM_AUTHTOK_DISABLE_AGING:
1329         return "Authentication token aging disabled";
1330     case PAM_TRY_AGAIN:
1331         return "Failed preliminary check by password service";
1332     case PAM_IGNORE:
1333         return "The return value should be ignored by PAM dispatch";
1334     case PAM_MODULE_UNKNOWN:
1335         return "Module is unknown";
1336     case PAM_AUTHTOK_EXPIRED:
1337         return "Authentication token expired";
1338     case PAM_CONV_AGAIN:
1339         return "Conversation is waiting for event";
1340     case PAM_INCOMPLETE:
1341         return "Application needs to call libpam again";
1342     default:
1343         return "Unknown PAM error";
1344     }
1345 }
1346
1347 void
1348 setutxent (void)
1349 {
1350 }
1351   
1352 struct utmp *
1353 pututxline (struct utmp *ut)
1354 {
1355     return ut;
1356 }
1357
1358 void
1359 endutxent (void)
1360 {
1361 }
1362
1363 struct xcb_connection_t
1364 {
1365     gchar *display;
1366     int error;
1367     GSocket *socket;
1368 };
1369
1370 xcb_connection_t *
1371 xcb_connect_to_display_with_auth_info (const char *display, xcb_auth_info_t *auth, int *screen)
1372 {
1373     xcb_connection_t *c;
1374     gchar *socket_path;
1375     GSocketAddress *address;
1376     GError *error = NULL;
1377   
1378     c = malloc (sizeof (xcb_connection_t));
1379     c->display = g_strdup (display);
1380     c->error = 0;
1381
1382     if (display == NULL)
1383         display = getenv ("DISPLAY");
1384     if (display == NULL)
1385         c->error = XCB_CONN_CLOSED_PARSE_ERR;
1386
1387     if (c->error == 0)
1388     {
1389         c->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
1390         if (error)
1391             g_printerr ("%s\n", error->message);
1392         g_clear_error (&error);
1393         if (c->socket == NULL)
1394             c->error = XCB_CONN_ERROR;
1395     }
1396
1397     if (c->error == 0)
1398     {
1399         gchar *d;
1400
1401         /* Skip the hostname, we'll assume it's localhost */
1402         d = g_strdup_printf (".x%s", strchr (display, ':'));
1403
1404         socket_path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), d, NULL);
1405         g_free (d);
1406         address = g_unix_socket_address_new (socket_path);
1407         if (!g_socket_connect (c->socket, address, NULL, &error))
1408             c->error = XCB_CONN_ERROR;
1409         if (error)
1410             g_printerr ("Failed to connect to X socket %s: %s\n", socket_path, error->message);
1411         g_free (socket_path);
1412         g_clear_error (&error);
1413     }
1414
1415     // FIXME: Send auth info
1416     if (c->error == 0)
1417     {
1418     }
1419
1420     g_object_unref (address);
1421
1422     return c;
1423 }
1424
1425 xcb_connection_t *
1426 xcb_connect (const char *displayname, int *screenp)
1427 {
1428     return xcb_connect_to_display_with_auth_info(displayname, NULL, screenp);
1429 }
1430
1431 int
1432 xcb_connection_has_error (xcb_connection_t *c)
1433 {
1434     return c->error;
1435 }
1436
1437 void
1438 xcb_disconnect (xcb_connection_t *c)
1439 {
1440     free (c->display);
1441     if (c->socket)
1442         g_object_unref (c->socket);
1443     free (c);
1444 }