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