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