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