]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/session-child.c
Merge with trunk
[sojka/lightdm.git] / src / session-child.c
1 #include <config.h>
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/wait.h>
11 #include <fcntl.h>
12 #include <pwd.h>
13 #include <grp.h>
14 #include <glib.h>
15 #include <security/pam_appl.h>
16 #include <utmpx.h>
17 #include <sys/mman.h>
18
19 #include "configuration.h"
20 #include "session-child.h"
21 #include "session.h"
22 #include "console-kit.h"
23 #include "login1.h"
24 #include "privileges.h"
25 #include "xauthority.h"
26 #include "configuration.h"
27
28 /* Child process being run */
29 static GPid child_pid = 0;
30
31 /* Pipe to communicate with daemon */
32 static int from_daemon_output = 0;
33 static int to_daemon_input = 0;
34
35 static gboolean is_interactive;
36 static gboolean do_authenticate;
37 static gboolean authentication_complete = FALSE;
38 static pam_handle_t *pam_handle;
39
40 /* Maximum length of a string to pass between daemon and session */
41 #define MAX_STRING_LENGTH 65535
42
43 static void
44 write_data (const void *buf, size_t count)
45 {
46     if (write (to_daemon_input, buf, count) != count)
47         g_printerr ("Error writing to daemon: %s\n", strerror (errno));
48 }
49
50 static void
51 write_string (const char *value)
52 {
53     int length;
54
55     length = value ? strlen (value) : -1;
56     write_data (&length, sizeof (length));
57     if (value)
58         write_data (value, sizeof (char) * length);
59 }
60
61 static ssize_t
62 read_data (void *buf, size_t count)
63 {
64     ssize_t n_read;
65
66     n_read = read (from_daemon_output, buf, count);
67     if (n_read < 0)
68         g_printerr ("Error reading from daemon: %s\n", strerror (errno));
69   
70     return n_read;
71 }
72
73 static gchar *
74 read_string_full (void* (*alloc_fn)(size_t n))
75 {
76     int length;
77     char *value;
78
79     if (read_data (&length, sizeof (length)) <= 0)
80         return NULL;
81     if (length < 0)
82         return NULL;
83     if (length > MAX_STRING_LENGTH)
84     {
85         g_printerr ("Invalid string length %d from daemon\n", length);
86         return NULL;
87     }
88   
89     value = (*alloc_fn) (sizeof (char) * (length + 1));
90     read_data (value, length);
91     value[length] = '\0';      
92
93     return value;
94 }
95
96 static gchar *
97 read_string (void)
98 {
99     return read_string_full (g_malloc);
100 }
101
102 static int
103 pam_conv_cb (int msg_length, const struct pam_message **msg, struct pam_response **resp, void *app_data)
104 {
105     int i, error;
106     gboolean auth_complete = FALSE;
107     struct pam_response *response;
108     gchar *username = NULL;
109
110     /* FIXME: We don't support communication after pam_authenticate completes */
111     if (authentication_complete)
112         return PAM_SUCCESS;
113
114     /* Cancel authentication if requiring input */
115     if (!is_interactive)
116     {
117         for (i = 0; i < msg_length; i++)
118         {
119             if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON || msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
120             {
121                 g_printerr ("Stopping PAM conversation, interaction requested but not supported\n");
122                 return PAM_CONV_ERR;
123             }
124         }
125
126         /* Ignore informational messages */
127         return PAM_SUCCESS;
128     }
129
130     /* Check if we changed user */
131     pam_get_item (pam_handle, PAM_USER, (const void **) &username);
132
133     /* Notify the daemon */
134     write_string (username);
135     write_data (&auth_complete, sizeof (auth_complete));
136     write_data (&msg_length, sizeof (msg_length));
137     for (i = 0; i < msg_length; i++)
138     {
139         const struct pam_message *m = msg[i];
140         write_data (&m->msg_style, sizeof (m->msg_style));
141         write_string (m->msg);
142     }
143
144     /* Get response */
145     read_data (&error, sizeof (error));
146     if (error != PAM_SUCCESS)
147         return error;
148     response = calloc (msg_length, sizeof (struct pam_response));
149     for (i = 0; i < msg_length; i++)
150     {
151         struct pam_response *r = &response[i];
152         // callers of this function inside pam will expect to be able to call
153         // free() on the strings we give back.  So alloc with malloc.
154         r->resp = read_string_full (malloc);
155         read_data (&r->resp_retcode, sizeof (r->resp_retcode));
156     }
157
158     *resp = response;
159     return PAM_SUCCESS;
160 }
161
162 static void
163 signal_cb (int signum)
164 {
165     /* Pass on signal to child, otherwise just quit */
166     if (child_pid > 0)
167         kill (child_pid, signum);
168     else
169         exit (EXIT_SUCCESS);
170 }
171
172 int
173 session_child_run (int argc, char **argv)
174 {
175     struct pam_conv conversation = { pam_conv_cb, NULL };
176     int i, version, fd, result;
177     gboolean auth_complete = TRUE;
178     User *user = NULL;
179     gchar *log_filename, *log_backup_filename = NULL;
180     gsize env_length;
181     gsize command_argc;
182     gchar **command_argv;
183     GVariantBuilder ck_parameters;
184     int return_code;
185     int authentication_result;
186     gchar *authentication_result_string;
187     gchar *service;
188     gchar *username;
189     gchar *class;
190     gchar *tty;
191     gchar *remote_host_name;
192     gchar *xdisplay;
193     gchar *xauth_name;
194     XAuthority *xauthority = NULL;
195     gchar *xauth_filename;
196     GDBusConnection *bus;
197     gchar *console_kit_cookie = NULL;
198     gchar *login1_session = NULL;
199
200     const gchar *path;
201     GError *error = NULL;
202     const gchar *locale_value;
203     gchar *locale_var;
204     static const gchar * const locale_var_names[] = {
205         "LC_COLLATE",
206         "LC_CTYPE",
207         "LC_MONETARY",
208         "LC_NUMERIC",
209         "LC_TIME",
210         "LC_MESSAGES",
211         "LC_ALL",
212         "LANG",
213         NULL
214     };
215
216 #if !defined(GLIB_VERSION_2_36)
217     g_type_init ();
218 #endif
219
220     if (config_get_boolean (config_get_instance (), "LightDM", "lock-memory"))
221     {
222         /* Protect memory from being paged to disk, as we deal with passwords */
223         mlockall (MCL_CURRENT | MCL_FUTURE);
224     }
225
226     /* Make input non-blocking */
227     fd = open ("/dev/null", O_RDONLY);
228     dup2 (fd, STDIN_FILENO);
229     close (fd);
230
231     /* Close stdout */
232     fd = open ("/dev/null", O_WRONLY);
233     dup2 (fd, STDOUT_FILENO);
234     close (fd);
235
236     /* Get the pipe from the daemon */
237     if (argc != 4)
238     {
239         g_printerr ("Usage: lightdm --session-child INPUTFD OUTPUTFD\n");
240         return EXIT_FAILURE;
241     }
242     from_daemon_output = atoi (argv[2]);
243     to_daemon_input = atoi (argv[3]);
244     if (from_daemon_output == 0 || to_daemon_input == 0)
245     {
246         g_printerr ("Invalid file descriptors %s %s\n", argv[2], argv[3]);
247         return EXIT_FAILURE;
248     }
249
250     /* Don't let these pipes leak to the command we will run */
251     fcntl (from_daemon_output, F_SETFD, FD_CLOEXEC);
252     fcntl (to_daemon_input, F_SETFD, FD_CLOEXEC);
253
254     /* Read a version number so we can handle upgrades (i.e. a newer version of session child is run for an old daemon */
255     read_data (&version, sizeof (version));
256
257     service = read_string ();
258     username = read_string ();
259     read_data (&do_authenticate, sizeof (do_authenticate));
260     read_data (&is_interactive, sizeof (is_interactive));
261     class = read_string ();
262     tty = read_string ();
263     remote_host_name = read_string ();
264     xdisplay = read_string ();
265     xauth_name = read_string ();
266     if (xauth_name)
267     {
268         guint16 xauth_family;
269         guint8 *xauth_address;
270         gsize xauth_address_length;
271         gchar *xauth_number;
272         guint8 *xauth_data;
273         gsize xauth_data_length;
274
275         read_data (&xauth_family, sizeof (xauth_family));
276         read_data (&xauth_address_length, sizeof (xauth_address_length));
277         xauth_address = g_malloc (xauth_address_length);
278         read_data (xauth_address, xauth_address_length);
279         xauth_number = read_string ();
280         read_data (&xauth_data_length, sizeof (xauth_data_length));
281         xauth_data = g_malloc (xauth_data_length);
282         read_data (xauth_data, xauth_data_length);
283
284         xauthority = xauth_new (xauth_family, xauth_address, xauth_address_length, xauth_number, xauth_name, xauth_data, xauth_data_length);
285     }
286
287     /* Setup PAM */
288     result = pam_start (service, username, &conversation, &pam_handle);
289     if (result != PAM_SUCCESS)
290     {
291         g_printerr ("Failed to start PAM: %s", pam_strerror (NULL, result));
292         return EXIT_FAILURE;
293     }
294     if (xdisplay)
295     {
296 #ifdef PAM_XDISPLAY
297         pam_set_item (pam_handle, PAM_XDISPLAY, xdisplay);
298 #endif
299         pam_set_item (pam_handle, PAM_TTY, xdisplay);
300     }
301     else if (tty)
302         pam_set_item (pam_handle, PAM_TTY, tty);    
303
304 #ifdef PAM_XAUTHDATA
305     if (xauthority)
306     {
307         struct pam_xauth_data value;
308
309         value.name = (char *) xauth_get_authorization_name (xauthority);
310         value.namelen = strlen (xauth_get_authorization_name (xauthority));
311         value.data = (char *) xauth_get_authorization_data (xauthority);
312         value.datalen = xauth_get_authorization_data_length (xauthority);
313         pam_set_item (pam_handle, PAM_XAUTHDATA, &value);
314     }
315 #endif
316
317     /* Authenticate */
318     if (do_authenticate)
319     {
320         const gchar *new_username;
321
322         authentication_result = pam_authenticate (pam_handle, 0);
323
324         /* See what user we ended up as */
325         if (pam_get_item (pam_handle, PAM_USER, (const void **) &new_username) != PAM_SUCCESS)
326             return EXIT_FAILURE;
327         g_free (username);
328         username = g_strdup (new_username);
329
330         /* Check account is valid */
331         if (authentication_result == PAM_SUCCESS)
332             authentication_result = pam_acct_mgmt (pam_handle, 0);
333         if (authentication_result == PAM_NEW_AUTHTOK_REQD)
334             authentication_result = pam_chauthtok (pam_handle, 0);
335     }
336     else
337         authentication_result = PAM_SUCCESS;
338     authentication_complete = TRUE;
339
340     if (authentication_result == PAM_SUCCESS)
341     {
342         /* Fail authentication if user doesn't actually exist */
343         user = accounts_get_user_by_name (username);
344         if (!user)
345         {
346             g_printerr ("Failed to get information on user %s: %s\n", username, strerror (errno));
347             authentication_result = PAM_USER_UNKNOWN;
348         }
349         else
350         {
351             /* Set POSIX variables */
352             pam_putenv (pam_handle, "PATH=/usr/local/bin:/usr/bin:/bin");
353             pam_putenv (pam_handle, g_strdup_printf ("USER=%s", username));
354             pam_putenv (pam_handle, g_strdup_printf ("LOGNAME=%s", username));
355             pam_putenv (pam_handle, g_strdup_printf ("HOME=%s", user_get_home_directory (user)));
356             pam_putenv (pam_handle, g_strdup_printf ("SHELL=%s", user_get_shell (user)));
357
358             /* Let the greeter and user session inherit the system default locale */
359             for (i = 0; locale_var_names[i] != NULL; i++)
360             {
361                 if ((locale_value = g_getenv (locale_var_names[i])) != NULL)
362                 {
363                     locale_var = g_strdup_printf ("%s=%s", locale_var_names[i], locale_value);
364                     pam_putenv (pam_handle, locale_var);
365                     g_free (locale_var);
366                 }
367             }
368         }
369     }
370
371     authentication_result_string = g_strdup (pam_strerror (pam_handle, authentication_result));
372
373     /* Report authentication result */
374     write_string (username);
375     write_data (&auth_complete, sizeof (auth_complete));
376     write_data (&authentication_result, sizeof (authentication_result));
377     write_string (authentication_result_string);
378
379     /* Check we got a valid user */
380     if (!username)
381     {
382         g_printerr ("No user selected during authentication\n");
383         return EXIT_FAILURE;
384     }
385
386     /* Stop if we didn't authenticated */
387     if (authentication_result != PAM_SUCCESS)
388         return EXIT_FAILURE;
389
390     /* Get the command to run (blocks) */
391     log_filename = read_string ();
392     xauth_filename = read_string ();
393     read_data (&env_length, sizeof (env_length));
394     for (i = 0; i < env_length; i++)
395         pam_putenv (pam_handle, read_string ());
396     read_data (&command_argc, sizeof (command_argc));
397     command_argv = g_malloc (sizeof (gchar *) * (command_argc + 1));
398     for (i = 0; i < command_argc; i++)
399         command_argv[i] = read_string ();
400     command_argv[i] = NULL;
401
402     /* Redirect stderr to a log file */
403     if (log_filename)
404         log_backup_filename = g_strdup_printf ("%s.old", log_filename);
405     if (!log_filename)
406     {
407         fd = open ("/dev/null", O_WRONLY);   
408         dup2 (fd, STDERR_FILENO);
409         close (fd);
410     }
411     else if (g_path_is_absolute (log_filename))
412     {
413         rename (log_filename, log_backup_filename);
414         fd = open (log_filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
415         dup2 (fd, STDERR_FILENO);
416         close (fd);
417     }
418
419     /* Set group membership - these can be overriden in pam_setcred */
420     if (getuid () == 0)
421     {
422         if (initgroups (username, user_get_gid (user)) < 0)
423         {
424             g_printerr ("Failed to initialize supplementary groups for %s: %s\n", username, strerror (errno));
425             _exit (EXIT_FAILURE);
426         }
427     }
428
429     /* Set credentials */
430     result = pam_setcred (pam_handle, PAM_ESTABLISH_CRED);
431     if (result != PAM_SUCCESS)
432     {
433         g_printerr ("Failed to establish PAM credentials: %s\n", pam_strerror (pam_handle, result));
434         return EXIT_FAILURE;
435     }
436      
437     /* Open the session */
438     result = pam_open_session (pam_handle, 0);
439     if (result != PAM_SUCCESS)
440     {
441         g_printerr ("Failed to open PAM session: %s\n", pam_strerror (pam_handle, result));
442         return EXIT_FAILURE;
443     }
444
445     /* Open a connection to the system bus for ConsoleKit - we must keep it open or CK will close the session */
446     bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
447     if (error)
448         g_printerr ("Unable to contact system bus: %s", error->message);
449     if (!bus)
450         return EXIT_FAILURE;
451
452     if (login1_is_running ())
453     {
454         login1_session = login1_get_session_id ();
455         write_string (login1_session);
456     }
457
458     if (!login1_session)
459     {
460         /* Open a Console Kit session */
461         g_variant_builder_init (&ck_parameters, G_VARIANT_TYPE ("(a(sv))"));
462         g_variant_builder_open (&ck_parameters, G_VARIANT_TYPE ("a(sv)"));
463         g_variant_builder_add (&ck_parameters, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user)));
464         if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) == 0)
465             g_variant_builder_add (&ck_parameters, "(sv)", "session-type", g_variant_new_string ("LoginWindow"));
466         if (xdisplay)
467         {
468             g_variant_builder_add (&ck_parameters, "(sv)", "x11-display", g_variant_new_string (xdisplay));
469             if (tty)
470                 g_variant_builder_add (&ck_parameters, "(sv)", "x11-display-device", g_variant_new_string (tty));
471         }
472         if (remote_host_name)
473         {
474             g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (FALSE));
475             g_variant_builder_add (&ck_parameters, "(sv)", "remote-host-name", g_variant_new_string (remote_host_name));
476         }
477         else
478             g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (TRUE));
479         console_kit_cookie = ck_open_session (&ck_parameters);
480         write_string (console_kit_cookie);
481         if (console_kit_cookie)
482         {
483             gchar *value;
484             value = g_strdup_printf ("XDG_SESSION_COOKIE=%s", console_kit_cookie);
485             pam_putenv (pam_handle, value);
486             g_free (value);
487         }
488     }
489
490     /* Write X authority */
491     if (xauthority)
492     {
493         gboolean drop_privileges, result;
494         gchar *value;
495         GError *error = NULL;
496
497         drop_privileges = geteuid () == 0;
498         if (drop_privileges)
499             privileges_drop (user);
500         result = xauth_write (xauthority, XAUTH_WRITE_MODE_REPLACE, xauth_filename, &error);
501         if (drop_privileges)
502             privileges_reclaim ();
503
504         if (error)
505             g_printerr ("Error writing X authority: %s\n", error->message);
506         g_clear_error (&error);
507         if (!result)
508             return EXIT_FAILURE;
509
510         value = g_strdup_printf ("XAUTHORITY=%s", xauth_filename);
511         pam_putenv (pam_handle, value);
512         g_free (value);
513     }
514
515     /* Put our tools directory in the path as a hack so we can use the legacy gdmflexiserver interface */
516     path = pam_getenv (pam_handle, "PATH");
517     if (path)
518         pam_putenv (pam_handle, g_strdup_printf ("PATH=%s:%s", PKGLIBEXEC_DIR, path));
519
520     /* Catch terminate signal and pass it to the child */
521     signal (SIGTERM, signal_cb);
522
523     /* Run the command as the authenticated user */
524     child_pid = fork (); 
525     if (child_pid == 0)
526     {
527         // FIXME: This is not thread safe (particularly the printfs)
528
529         /* Make this process its own session */
530         if (setsid () < 0)
531             g_printerr ("Failed to make process a new session: %s\n", strerror (errno));
532
533         /* Change to this user */
534         if (getuid () == 0)
535         {
536             if (setgid (user_get_gid (user)) != 0)
537             {
538                 g_printerr ("Failed to set group ID to %d: %s\n", user_get_gid (user), strerror (errno));
539                 _exit (EXIT_FAILURE);
540             }
541
542             if (setuid (user_get_uid (user)) != 0)
543             {
544                 g_printerr ("Failed to set user ID to %d: %s\n", user_get_uid (user), strerror (errno));
545                 _exit (EXIT_FAILURE);
546             }
547         }
548
549         /* Change working directory */
550         /* NOTE: This must be done after the permissions are changed because NFS filesystems can
551          * be setup so the local root user accesses the NFS files as 'nobody'.  If the home directories
552          * are not system readable then the chdir can fail */
553         if (chdir (user_get_home_directory (user)) != 0)
554         {
555             g_printerr ("Failed to change to home directory %s: %s\n", user_get_home_directory (user), strerror (errno));
556             _exit (EXIT_FAILURE);
557         }
558
559         /* Redirect stderr to a log file */
560         if (log_filename && !g_path_is_absolute (log_filename))
561         {
562             rename (log_filename, log_backup_filename);
563             fd = open (log_filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
564             dup2 (fd, STDERR_FILENO);
565             close (fd);
566         }
567
568         /* Run the command */
569         execve (command_argv[0], command_argv, pam_getenvlist (pam_handle));
570         g_printerr ("Failed to run command: %s\n", strerror (errno));
571         _exit (EXIT_FAILURE);
572     }
573
574     /* Bail out if failed to fork */
575     if (child_pid < 0)
576     {
577         g_printerr ("Failed to fork session child process: %s\n", strerror (errno));
578         return_code = EXIT_FAILURE;
579     }
580
581     /* Wait for the command to complete (blocks) */
582     if (child_pid > 0)
583     {
584         /* Log to utmp */
585         if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) != 0)
586         {
587             struct utmpx ut;
588             struct timeval tv;
589
590             memset (&ut, 0, sizeof (ut));
591             ut.ut_type = USER_PROCESS;
592             ut.ut_pid = child_pid;
593             strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
594             strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
595             strncpy (ut.ut_user, username, sizeof (ut.ut_user));
596             if (xdisplay)
597                 strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
598             else if (remote_host_name)
599                 strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
600             gettimeofday (&tv, NULL);
601             ut.ut_tv.tv_sec = tv.tv_sec;
602             ut.ut_tv.tv_usec = tv.tv_usec;
603
604             setutxent ();
605             if (!pututxline (&ut))
606                 g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
607             endutxent ();
608         }
609
610         waitpid (child_pid, &return_code, 0);
611         child_pid = 0;
612
613         /* Log to utmp */
614         if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) != 0)
615         {
616             struct utmpx ut;
617             struct timeval tv;
618
619             memset (&ut, 0, sizeof (ut));
620             ut.ut_type = DEAD_PROCESS;
621             ut.ut_pid = child_pid;
622             strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
623             strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
624             strncpy (ut.ut_user, username, sizeof (ut.ut_user));
625             if (xdisplay)
626                 strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
627             else if (remote_host_name)
628                 strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
629             gettimeofday (&tv, NULL);
630             ut.ut_tv.tv_sec = tv.tv_sec;
631             ut.ut_tv.tv_usec = tv.tv_usec;
632
633             setutxent ();
634             if (!pututxline (&ut))
635                 g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
636             endutxent ();
637         }
638     }
639
640     /* Remove X authority */
641     if (xauthority)
642     {
643         gboolean drop_privileges, result;
644         GError *error = NULL;
645
646         drop_privileges = geteuid () == 0;
647         if (drop_privileges)
648             privileges_drop (user);
649         result = xauth_write (xauthority, XAUTH_WRITE_MODE_REMOVE, xauth_filename, &error);
650         if (drop_privileges)
651             privileges_reclaim ();
652
653         if (error)
654             g_printerr ("Error removing X authority: %s\n", error->message);
655         g_clear_error (&error);
656         if (!result)
657             _exit (EXIT_FAILURE);
658     }
659
660     /* Close the Console Kit session */
661     if (console_kit_cookie)
662         ck_close_session (console_kit_cookie);
663
664     /* Close the session */
665     pam_close_session (pam_handle, 0);
666
667     /* Remove credentials */
668     result = pam_setcred (pam_handle, PAM_DELETE_CRED);
669
670     pam_end (pam_handle, 0);
671     pam_handle = NULL;
672
673     /* Return result of session process to the daemon */
674     return return_code;
675 }