]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/libsystem.c
Fix session wrapper working the same as it did in 1.1.3
[sojka/lightdm.git] / tests / src / libsystem.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <sys/types.h>
4 #include <pwd.h>
5 #include <security/pam_appl.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #define __USE_GNU
9 #include <dlfcn.h>
10 #ifdef __linux__
11 #include <linux/vt.h>
12 #endif
13 #include <glib.h>
14
15 #define LOGIN_PROMPT "login:"
16
17 static int console_fd = -1;
18
19 static GList *user_entries = NULL;
20 static GList *getpwent_link = NULL;
21
22 struct pam_handle
23 {
24     char *service_name;
25     char *user;
26     char *tty;
27     char **envlist;
28     struct pam_conv conversation;
29 };
30
31 uid_t
32 getuid (void)
33 {
34     return 0;
35 }
36
37 /*uid_t
38 geteuid (void)
39 {
40     return 0;
41 }*/
42
43 int
44 initgroups (const char *user, gid_t group)
45 {
46     return 0;
47 }
48
49 int
50 setgid (gid_t gid)
51 {
52     return 0;
53 }
54
55 int
56 setuid (uid_t uid)
57 {
58     return 0;
59 }
60
61 #ifdef __linux__
62 int
63 open (const char *pathname, int flags, ...)
64 {
65     int (*_open) (const char * pathname, int flags, mode_t mode);
66     int mode = 0;
67   
68     if (flags & O_CREAT)
69     {
70         va_list ap;
71         va_start (ap, flags);
72         mode = va_arg (ap, int);
73         va_end (ap);
74     }
75
76     _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, "open");      
77     if (strcmp (pathname, "/dev/console") == 0)
78     {
79         if (console_fd < 0)
80         {
81             console_fd = _open ("/dev/null", flags, mode);
82             fcntl (console_fd, F_SETFD, FD_CLOEXEC);
83         }
84         return console_fd;
85     }
86     else
87         return _open (pathname, flags, mode);
88 }
89
90 int
91 ioctl (int d, int request, void *data)
92 {
93     int (*_ioctl) (int d, int request, void *data);
94
95     _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
96     if (d > 0 && d == console_fd)
97     {
98         struct vt_stat *console_state;
99
100         switch (request)
101         {
102         case VT_GETSTATE:
103             console_state = data;
104             console_state->v_active = 7;
105             break;          
106         case VT_ACTIVATE:
107             break;
108         }
109         return 0;
110     }
111     else
112         return _ioctl (d, request, data);
113 }
114
115 int
116 close (int fd)
117 {
118     int (*_close) (int fd);
119
120     if (fd > 0 && fd == console_fd)
121         return 0;
122
123     _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
124     return _close (fd);
125 }
126 #endif
127
128 static void
129 free_user (gpointer data)
130 {
131     struct passwd *entry = data;
132   
133     g_free (entry->pw_name);
134     g_free (entry->pw_passwd);
135     g_free (entry->pw_gecos);
136     g_free (entry->pw_dir);
137     g_free (entry->pw_shell);
138     g_free (entry);
139 }
140
141 static void
142 load_passwd_file ()
143 {
144     gchar *data = NULL, **lines;
145     gint i;
146     GError *error = NULL;
147
148     g_list_free_full (user_entries, free_user);
149     user_entries = NULL;
150     getpwent_link = NULL;
151
152     g_file_get_contents (g_getenv ("LIGHTDM_TEST_PASSWD_FILE"), &data, NULL, &error);
153     if (error)
154         g_warning ("Error loading passwd file: %s", error->message);
155     g_clear_error (&error);
156
157     if (!data)
158         return;
159
160     lines = g_strsplit (data, "\n", -1);
161     g_free (data);
162
163     for (i = 0; lines[i]; i++)
164     {
165         gchar *line, **fields;
166
167         line = g_strstrip (lines[i]);
168         fields = g_strsplit (line, ":", -1);
169         if (g_strv_length (fields) == 7)
170         {
171             struct passwd *entry = malloc (sizeof (struct passwd));
172
173             entry->pw_name = g_strdup (fields[0]);
174             entry->pw_passwd = g_strdup (fields[1]);
175             entry->pw_uid = atoi (fields[2]);
176             entry->pw_gid = atoi (fields[3]);
177             entry->pw_gecos = g_strdup (fields[4]);
178             entry->pw_dir = g_strdup (fields[5]);
179             entry->pw_shell = g_strdup (fields[6]);
180             user_entries = g_list_append (user_entries, entry);
181         }
182         g_strfreev (fields);
183     }
184     g_strfreev (lines);
185 }
186
187 struct passwd *
188 getpwent (void)
189 {
190     if (getpwent_link == NULL)
191     {
192         load_passwd_file ();
193         if (user_entries == NULL)
194             return NULL;
195         getpwent_link = user_entries;
196     }
197     else
198     {
199         if (getpwent_link->next == NULL)
200             return NULL;
201         getpwent_link = getpwent_link->next;
202     }
203
204     return getpwent_link->data;
205 }
206
207 void
208 setpwent (void)
209 {
210     getpwent_link = NULL;
211 }
212
213 void
214 endpwent (void)
215 {
216     getpwent_link = NULL;
217 }
218
219 struct passwd *
220 getpwnam (const char *name)
221 {
222     GList *link;
223   
224     if (name == NULL)
225         return NULL;
226   
227     load_passwd_file ();
228
229     for (link = user_entries; link; link = link->next)
230     {
231         struct passwd *entry = link->data;
232         if (strcmp (entry->pw_name, name) == 0)
233             break;
234     }
235     if (!link)
236         return NULL;
237
238     return link->data;
239 }
240
241 struct passwd *
242 getpwuid (uid_t uid)
243 {
244     GList *link;
245
246     load_passwd_file ();
247
248     for (link = user_entries; link; link = link->next)
249     {
250         struct passwd *entry = link->data;
251         if (entry->pw_uid == uid)
252             break;
253     }
254     if (!link)
255         return NULL;
256
257     return link->data;
258 }
259
260 int
261 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
262 {
263     pam_handle_t *handle;
264
265     if (service_name == NULL || conversation == NULL || pamh == NULL)
266         return PAM_SYSTEM_ERR;
267
268     handle = *pamh = malloc (sizeof (pam_handle_t));
269     if (handle == NULL)
270         return PAM_BUF_ERR;
271
272     handle->service_name = strdup (service_name);
273     handle->user = user ? strdup (user) : NULL;
274     handle->tty = NULL;
275     handle->conversation.conv = conversation->conv;
276     handle->conversation.appdata_ptr = conversation->appdata_ptr;
277     handle->envlist = malloc (sizeof (char *) * 1);
278     handle->envlist[0] = NULL;
279
280     return PAM_SUCCESS;
281 }
282
283 int
284 pam_authenticate (pam_handle_t *pamh, int flags)
285 {
286     struct passwd *entry;
287     gboolean password_matches = FALSE;
288
289     if (pamh == NULL)
290         return PAM_SYSTEM_ERR;
291
292     /* Prompt for username */
293     if (pamh->user == NULL)
294     {
295         int result;
296         struct pam_message **msg;
297         struct pam_response *resp = NULL;
298
299         msg = malloc (sizeof (struct pam_message *) * 1);
300         msg[0] = malloc (sizeof (struct pam_message));
301         msg[0]->msg_style = PAM_PROMPT_ECHO_ON; 
302         msg[0]->msg = LOGIN_PROMPT;
303         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
304         free (msg[0]);
305         free (msg);
306         if (result != PAM_SUCCESS)
307             return result;
308
309         if (resp == NULL)
310             return PAM_CONV_ERR;
311         if (resp[0].resp == NULL)
312         {
313             free (resp);
314             return PAM_CONV_ERR;
315         }
316       
317         pamh->user = strdup (resp[0].resp);
318         free (resp[0].resp);
319         free (resp);
320     }
321
322     /* Crash on authenticate */
323     if (strcmp (pamh->user, "crash-authenticate") == 0)
324         kill (getpid (), SIGSEGV);
325
326     /* Look up password database */
327     entry = getpwnam (pamh->user);
328
329     /* Prompt for password if required */
330     if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
331         password_matches = TRUE;
332     else
333     {
334         int result;
335         struct pam_message **msg;
336         struct pam_response *resp = NULL;
337     
338         msg = malloc (sizeof (struct pam_message *) * 1);
339         msg[0] = malloc (sizeof (struct pam_message));
340         msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
341         msg[0]->msg = "Password:";
342         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
343         free (msg[0]);
344         free (msg);
345         if (result != PAM_SUCCESS)
346             return result;
347
348         if (resp == NULL)
349             return PAM_CONV_ERR;
350         if (resp[0].resp == NULL)
351         {
352             free (resp);
353             return PAM_CONV_ERR;
354         }
355
356         if (entry)
357             password_matches = strcmp (entry->pw_passwd, resp[0].resp) == 0;
358         free (resp[0].resp);  
359         free (resp);
360     }
361
362     /* Special user has home directory created on login */
363     if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
364         g_mkdir_with_parents (entry->pw_dir, 0755);
365
366     /* Special user 'change-user1' changes user on authentication */
367     if (password_matches && strcmp (pamh->user, "change-user1") == 0)
368     {
369         g_free (pamh->user);
370         pamh->user = g_strdup ("change-user2");
371     }
372
373     /* Special user 'change-user-invalid' changes to an invalid user on authentication */
374     if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
375     {
376         g_free (pamh->user);
377         pamh->user = g_strdup ("invalid-user");
378     }
379
380     if (password_matches)
381         return PAM_SUCCESS;
382     else
383         return PAM_AUTH_ERR;
384 }
385
386 static const char *
387 get_env_value (const char *name_value, const char *name)
388 {
389     int j;
390
391     for (j = 0; name[j] && name[j] != '=' && name[j] == name_value[j]; j++);
392     if (name_value[j] == '=')
393         return &name_value[j + 1];
394
395     return NULL;
396 }
397
398 int
399 pam_putenv (pam_handle_t *pamh, const char *name_value)
400 {
401     int i;
402
403     if (pamh == NULL || name_value == NULL)
404         return PAM_SYSTEM_ERR;
405
406     for (i = 0; pamh->envlist[i]; i++)
407     {
408         if (get_env_value (pamh->envlist[i], name_value))
409             break;
410     }
411
412     if (pamh->envlist[i])
413     {
414         free (pamh->envlist[i]);
415         pamh->envlist[i] = strdup (name_value);
416     }
417     else
418     {
419         pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
420         pamh->envlist[i] = strdup (name_value);
421         pamh->envlist[i + 1] = NULL;
422     }
423
424     return PAM_SUCCESS;
425 }
426
427 const char *
428 pam_getenv (pam_handle_t *pamh, const char *name)
429 {
430     int i;
431
432     if (pamh == NULL || name == NULL)
433         return NULL;
434
435     for (i = 0; pamh->envlist[i]; i++)
436     {
437         const char *value;
438         value = get_env_value (pamh->envlist[i], name);
439         if (value)
440             return value;
441     }
442
443     return NULL;
444 }
445
446 char **
447 pam_getenvlist (pam_handle_t *pamh)
448 {
449     if (pamh == NULL)
450         return NULL;
451
452     return pamh->envlist;
453 }
454
455 int
456 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
457 {
458     if (pamh == NULL || item == NULL)
459         return PAM_SYSTEM_ERR;
460
461     switch (item_type)
462     {
463     case PAM_TTY:
464         if (pamh->tty)
465             free (pamh->tty);
466         pamh->tty = strdup ((const char *) item);
467         return PAM_SUCCESS;
468
469     default:
470         return PAM_BAD_ITEM;
471     }
472 }
473
474 int
475 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
476 {
477     if (pamh == NULL || item == NULL)
478         return PAM_SYSTEM_ERR;
479   
480     switch (item_type)
481     {
482     case PAM_SERVICE:
483         *item = pamh->service_name;
484         return PAM_SUCCESS;
485       
486     case PAM_USER:
487         *item = pamh->user;
488         return PAM_SUCCESS;
489       
490     case PAM_USER_PROMPT:
491         *item = LOGIN_PROMPT;
492         return PAM_SUCCESS;
493       
494     case PAM_TTY:
495         *item = pamh->tty;
496         return PAM_SUCCESS;
497
498     case PAM_CONV:
499         *item = &pamh->conversation;
500         return PAM_SUCCESS;
501
502     default:
503         return PAM_BAD_ITEM;
504     }
505 }
506
507 int
508 pam_open_session (pam_handle_t *pamh, int flags)
509 {
510     if (pamh == NULL)
511         return PAM_SYSTEM_ERR;
512
513     return PAM_SUCCESS;
514 }
515
516 int
517 pam_close_session (pam_handle_t *pamh, int flags)
518 {
519     if (pamh == NULL)
520         return PAM_SYSTEM_ERR;
521
522     return PAM_SUCCESS;
523 }
524
525 int
526 pam_acct_mgmt (pam_handle_t *pamh, int flags)
527 {
528     if (pamh == NULL)
529         return PAM_SYSTEM_ERR;
530   
531     if (!pamh->user)
532         return PAM_USER_UNKNOWN;
533
534     if (strcmp (pamh->user, "denied") == 0)
535         return PAM_PERM_DENIED;
536     if (strcmp (pamh->user, "expired") == 0)
537         return PAM_ACCT_EXPIRED;
538     if (strcmp (pamh->user, "new-authtok") == 0)
539         return PAM_NEW_AUTHTOK_REQD;
540
541     return PAM_SUCCESS;
542 }
543
544 int
545 pam_chauthtok (pam_handle_t *pamh, int flags)
546 {
547     struct passwd *entry;
548     int result;
549     struct pam_message **msg;
550     struct pam_response *resp = NULL;
551
552     if (pamh == NULL)
553         return PAM_SYSTEM_ERR;
554
555     msg = malloc (sizeof (struct pam_message *) * 1);
556     msg[0] = malloc (sizeof (struct pam_message));
557     msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
558     msg[0]->msg = "Enter new password:";
559     result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
560     free (msg[0]);
561     free (msg);
562     if (result != PAM_SUCCESS)
563         return result;
564
565     if (resp == NULL)
566         return PAM_CONV_ERR;
567     if (resp[0].resp == NULL)
568     {
569         free (resp);
570         return PAM_CONV_ERR;
571     }
572
573     /* Update password database */
574     entry = getpwnam (pamh->user);
575     free (entry->pw_passwd);
576     entry->pw_passwd = resp[0].resp;
577     free (resp);
578
579     return PAM_SUCCESS;
580 }
581
582 int
583 pam_setcred (pam_handle_t *pamh, int flags)
584 {
585     gchar *e;
586
587     if (pamh == NULL)
588         return PAM_SYSTEM_ERR;
589
590     /* Put the test directories into the path */
591     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"));
592     pam_putenv (pamh, e);
593     g_free (e);
594
595     return PAM_SUCCESS;
596 }
597
598 int
599 pam_end (pam_handle_t *pamh, int pam_status)
600 {
601     if (pamh == NULL)
602         return PAM_SYSTEM_ERR;
603
604     free (pamh->service_name);
605     if (pamh->user)
606         free (pamh->user);
607     if (pamh->tty)
608         free (pamh->tty);
609     free (pamh);
610
611     return PAM_SUCCESS;
612 }
613
614 const char *
615 pam_strerror (pam_handle_t *pamh, int errnum)
616 {
617     if (pamh == NULL)
618         return NULL;
619
620     switch (errnum)
621     {
622     case PAM_SUCCESS:
623         return "Success";
624     case PAM_ABORT:
625         return "Critical error - immediate abort";
626     case PAM_OPEN_ERR:
627         return "Failed to load module";
628     case PAM_SYMBOL_ERR:
629         return "Symbol not found";
630     case PAM_SERVICE_ERR:
631         return "Error in service module";
632     case PAM_SYSTEM_ERR:
633         return "System error";
634     case PAM_BUF_ERR:
635         return "Memory buffer error";
636     case PAM_PERM_DENIED:
637         return "Permission denied";
638     case PAM_AUTH_ERR:
639         return "Authentication failure";
640     case PAM_CRED_INSUFFICIENT:
641         return "Insufficient credentials to access authentication data";
642     case PAM_AUTHINFO_UNAVAIL:
643         return "Authentication service cannot retrieve authentication info";
644     case PAM_USER_UNKNOWN:
645         return "User not known to the underlying authentication module";
646     case PAM_MAXTRIES:
647         return "Have exhausted maximum number of retries for service";
648     case PAM_NEW_AUTHTOK_REQD:
649         return "Authentication token is no longer valid; new one required";
650     case PAM_ACCT_EXPIRED:
651         return "User account has expired";
652     case PAM_SESSION_ERR:
653         return "Cannot make/remove an entry for the specified session";
654     case PAM_CRED_UNAVAIL:
655         return "Authentication service cannot retrieve user credentials";
656     case PAM_CRED_EXPIRED:
657         return "User credentials expired";
658     case PAM_CRED_ERR:
659         return "Failure setting user credentials";
660     case PAM_NO_MODULE_DATA:
661         return "No module specific data is present";
662     case PAM_BAD_ITEM:
663         return "Bad item passed to pam_*_item()";
664     case PAM_CONV_ERR:
665         return "Conversation error";
666     case PAM_AUTHTOK_ERR:
667         return "Authentication token manipulation error";
668     case PAM_AUTHTOK_RECOVERY_ERR:
669         return "Authentication information cannot be recovered";
670     case PAM_AUTHTOK_LOCK_BUSY:
671         return "Authentication token lock busy";
672     case PAM_AUTHTOK_DISABLE_AGING:
673         return "Authentication token aging disabled";
674     case PAM_TRY_AGAIN:
675         return "Failed preliminary check by password service";
676     case PAM_IGNORE:
677         return "The return value should be ignored by PAM dispatch";
678     case PAM_MODULE_UNKNOWN:
679         return "Module is unknown";
680     case PAM_AUTHTOK_EXPIRED:
681         return "Authentication token expired";
682     case PAM_CONV_AGAIN:
683         return "Conversation is waiting for event";
684     case PAM_INCOMPLETE:
685         return "Application needs to call libpam again";
686     default:
687         return "Unknown PAM error";
688     }
689 }