Back to index

lightdm  1.3.2
session-child.c
Go to the documentation of this file.
00001 #include <stdlib.h>
00002 #include <stdio.h>
00003 #include <unistd.h>
00004 #include <string.h>
00005 #include <errno.h>
00006 #include <sys/types.h>
00007 #include <sys/stat.h>
00008 #include <sys/wait.h>
00009 #include <fcntl.h>
00010 #include <pwd.h>
00011 #include <grp.h>
00012 #include <glib.h>
00013 #include <security/pam_appl.h>
00014 #include <utmpx.h>
00015 
00016 #include "session-child.h"
00017 #include "session.h"
00018 #include "console-kit.h"
00019 #include "privileges.h"
00020 #include "xauthority.h"
00021 
00022 /* Child process being run */
00023 static GPid child_pid = 0;
00024 
00025 /* Pipe to communicate with daemon */
00026 static int from_daemon_output = 0;
00027 static int to_daemon_input = 0;
00028 
00029 static gboolean is_interactive;
00030 static gboolean do_authenticate;
00031 static gboolean authentication_complete = FALSE;
00032 static pam_handle_t *pam_handle;
00033 
00034 /* Maximum length of a string to pass between daemon and session */
00035 #define MAX_STRING_LENGTH 65535
00036 
00037 static void
00038 write_data (const void *buf, size_t count)
00039 {
00040     if (write (to_daemon_input, buf, count) != count)
00041         g_printerr ("Error writing to daemon: %s\n", strerror (errno));
00042 }
00043 
00044 static void
00045 write_string (const char *value)
00046 {
00047     int length;
00048 
00049     length = value ? strlen (value) : -1;
00050     write_data (&length, sizeof (length));
00051     if (value)
00052         write_data (value, sizeof (char) * length);
00053 }
00054 
00055 static ssize_t
00056 read_data (void *buf, size_t count)
00057 {
00058     ssize_t n_read;
00059 
00060     n_read = read (from_daemon_output, buf, count);
00061     if (n_read < 0)
00062         g_printerr ("Error reading from daemon: %s\n", strerror (errno));
00063   
00064     return n_read;
00065 }
00066 
00067 static gchar *
00068 read_string ()
00069 {
00070     int length;
00071     char *value;
00072 
00073     if (read_data (&length, sizeof (length)) <= 0)
00074         return NULL;
00075     if (length < 0)
00076         return NULL;
00077     if (length > MAX_STRING_LENGTH)
00078     {
00079         g_printerr ("Invalid string length %d from daemon\n", length);
00080         return NULL;
00081     }
00082   
00083     value = g_malloc (sizeof (char) * (length + 1));
00084     read_data (value, length);
00085     value[length] = '\0';      
00086 
00087     return value;
00088 }
00089 
00090 static int
00091 pam_conv_cb (int msg_length, const struct pam_message **msg, struct pam_response **resp, void *app_data)
00092 {
00093     int i, error;
00094     gboolean auth_complete = FALSE;
00095     struct pam_response *response;
00096     gchar *username = NULL;
00097 
00098     /* FIXME: We don't support communication after pam_authenticate completes */
00099     if (authentication_complete)
00100         return PAM_SUCCESS;
00101 
00102     /* Cancel authentication if requiring input */
00103     if (!is_interactive)
00104     {
00105         for (i = 0; i < msg_length; i++)
00106         {
00107             if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON || msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
00108             {
00109                 g_printerr ("Stopping PAM conversation, interaction requested but not supported\n");
00110                 return PAM_CONV_ERR;
00111             }
00112         }
00113 
00114         /* Ignore informational messages */
00115         return PAM_SUCCESS;
00116     }
00117 
00118     /* Check if we changed user */
00119     pam_get_item (pam_handle, PAM_USER, (const void **) &username);
00120 
00121     /* Notify the daemon */
00122     write_string (username);
00123     write_data (&auth_complete, sizeof (auth_complete));
00124     write_data (&msg_length, sizeof (msg_length));
00125     for (i = 0; i < msg_length; i++)
00126     {
00127         const struct pam_message *m = msg[i];
00128         write_data (&m->msg_style, sizeof (m->msg_style));
00129         write_string (m->msg);
00130     }
00131 
00132     /* Get response */
00133     read_data (&error, sizeof (error));
00134     if (error != PAM_SUCCESS)
00135         return error;
00136     response = calloc (msg_length, sizeof (struct pam_response));
00137     for (i = 0; i < msg_length; i++)
00138     {
00139         struct pam_response *r = &response[i];
00140         r->resp = read_string ();
00141         read_data (&r->resp_retcode, sizeof (r->resp_retcode));
00142     }
00143 
00144     *resp = response;
00145     return PAM_SUCCESS;
00146 }
00147 
00148 static void
00149 signal_cb (int signum)
00150 {
00151     /* Pass on signal to child, otherwise just quit */
00152     if (child_pid > 0)
00153         kill (child_pid, signum);
00154     else
00155         exit (EXIT_SUCCESS);
00156 }
00157 
00158 int
00159 session_child_run (int argc, char **argv)
00160 {
00161     struct pam_conv conversation = { pam_conv_cb, NULL };
00162     int i, version, fd, result;
00163     gboolean auth_complete = TRUE;
00164     User *user = NULL;
00165     gchar *log_filename, *log_backup_filename = NULL;
00166     gsize env_length;
00167     gsize command_argc;
00168     gchar **command_argv;
00169     GVariantBuilder ck_parameters;
00170     int return_code;
00171     int authentication_result;
00172     gchar *authentication_result_string;
00173     gchar *service;
00174     gchar *username;
00175     gchar *class;
00176     gchar *tty;
00177     gchar *remote_host_name;
00178     gchar *xdisplay;
00179     gchar *xauth_name;
00180     XAuthority *xauthority = NULL;
00181     gchar *xauth_filename;
00182     GDBusConnection *bus;
00183     gchar *console_kit_cookie;
00184     const gchar *path;
00185     GError *error = NULL;
00186 
00187     g_type_init ();
00188 
00189     /* Make input non-blocking */
00190     fd = open ("/dev/null", O_RDONLY);
00191     dup2 (fd, STDIN_FILENO);
00192     close (fd);
00193 
00194     /* Close stdout */
00195     fd = open ("/dev/null", O_WRONLY);
00196     dup2 (fd, STDOUT_FILENO);
00197     close (fd);
00198 
00199     /* Get the pipe from the daemon */
00200     if (argc != 4)
00201     {
00202         g_printerr ("Usage: lightdm --session-child INPUTFD OUTPUTFD\n");
00203         return EXIT_FAILURE;
00204     }
00205     from_daemon_output = atoi (argv[2]);
00206     to_daemon_input = atoi (argv[3]);
00207     if (from_daemon_output == 0 || to_daemon_input == 0)
00208     {
00209         g_printerr ("Invalid file descriptors %s %s\n", argv[2], argv[3]);
00210         return EXIT_FAILURE;
00211     }
00212 
00213     /* Don't let these pipes leak to the command we will run */
00214     fcntl (from_daemon_output, F_SETFD, FD_CLOEXEC);
00215     fcntl (to_daemon_input, F_SETFD, FD_CLOEXEC);
00216 
00217     /* Read a version number so we can handle upgrades (i.e. a newer version of session child is run for an old daemon */
00218     read_data (&version, sizeof (version));
00219 
00220     service = read_string ();
00221     username = read_string ();
00222     read_data (&do_authenticate, sizeof (do_authenticate));
00223     read_data (&is_interactive, sizeof (is_interactive));
00224     class = read_string ();
00225     tty = read_string ();
00226     remote_host_name = read_string ();
00227     xdisplay = read_string ();
00228     xauth_name = read_string ();
00229     if (xauth_name)
00230     {
00231         guint16 xauth_family;
00232         guint8 *xauth_address;
00233         gsize xauth_address_length;
00234         gchar *xauth_number;
00235         guint8 *xauth_data;
00236         gsize xauth_data_length;
00237 
00238         read_data (&xauth_family, sizeof (xauth_family));
00239         read_data (&xauth_address_length, sizeof (xauth_address_length));
00240         xauth_address = g_malloc (xauth_address_length);
00241         read_data (xauth_address, xauth_address_length);
00242         xauth_number = read_string ();
00243         read_data (&xauth_data_length, sizeof (xauth_data_length));
00244         xauth_data = g_malloc (xauth_data_length);
00245         read_data (xauth_data, xauth_data_length);
00246 
00247         xauthority = xauth_new (xauth_family, xauth_address, xauth_address_length, xauth_number, xauth_name, xauth_data, xauth_data_length);
00248     }
00249 
00250     /* Setup PAM */
00251     result = pam_start (service, username, &conversation, &pam_handle);
00252     if (result != PAM_SUCCESS)
00253     {
00254         g_printerr ("Failed to start PAM: %s", pam_strerror (NULL, result));
00255         return EXIT_FAILURE;
00256     }
00257     if (xdisplay)
00258     {
00259 #ifdef PAM_XDISPLAY
00260         pam_set_item (pam_handle, PAM_XDISPLAY, xdisplay);
00261 #endif
00262         pam_set_item (pam_handle, PAM_TTY, xdisplay);
00263     }
00264     else if (tty)
00265         pam_set_item (pam_handle, PAM_TTY, tty);    
00266 
00267 #ifdef PAM_XAUTHDATA
00268     if (xauthority)
00269     {
00270         struct pam_xauth_data value;
00271 
00272         value.name = (char *) xauth_get_authorization_name (xauthority);
00273         value.namelen = strlen (xauth_get_authorization_name (xauthority));
00274         value.data = (char *) xauth_get_authorization_data (xauthority);
00275         value.datalen = xauth_get_authorization_data_length (xauthority);
00276         pam_set_item (pam_handle, PAM_XAUTHDATA, &value);
00277     }
00278 #endif
00279 
00280     /* Authenticate */
00281     if (do_authenticate)
00282     {
00283         const gchar *new_username;
00284 
00285         authentication_result = pam_authenticate (pam_handle, 0);
00286 
00287         /* See what user we ended up as */
00288         if (pam_get_item (pam_handle, PAM_USER, (const void **) &new_username) != PAM_SUCCESS)
00289             return EXIT_FAILURE;
00290         g_free (username);
00291         username = g_strdup (new_username);
00292 
00293         /* Check account is valid */
00294         if (authentication_result == PAM_SUCCESS)
00295             authentication_result = pam_acct_mgmt (pam_handle, 0);
00296         if (authentication_result == PAM_NEW_AUTHTOK_REQD)
00297             authentication_result = pam_chauthtok (pam_handle, 0);
00298     }
00299     else
00300         authentication_result = PAM_SUCCESS;
00301     authentication_complete = TRUE;
00302 
00303     if (authentication_result == PAM_SUCCESS)
00304     {
00305         /* Fail authentication if user doesn't actually exist */
00306         user = accounts_get_user_by_name (username);
00307         if (!user)
00308         {
00309             g_printerr ("Failed to get information on user %s: %s\n", username, strerror (errno));
00310             authentication_result = PAM_USER_UNKNOWN;
00311         }
00312         else
00313         {
00314             /* Set POSIX variables */
00315             pam_putenv (pam_handle, "PATH=/usr/local/bin:/usr/bin:/bin");
00316             pam_putenv (pam_handle, g_strdup_printf ("USER=%s", username));
00317             pam_putenv (pam_handle, g_strdup_printf ("LOGNAME=%s", username));
00318             pam_putenv (pam_handle, g_strdup_printf ("HOME=%s", user_get_home_directory (user)));
00319             pam_putenv (pam_handle, g_strdup_printf ("SHELL=%s", user_get_shell (user)));
00320         }
00321     }
00322 
00323     authentication_result_string = g_strdup (pam_strerror (pam_handle, authentication_result));
00324 
00325     /* Report authentication result */
00326     write_string (username);
00327     write_data (&auth_complete, sizeof (auth_complete));
00328     write_data (&authentication_result, sizeof (authentication_result));
00329     write_string (authentication_result_string);
00330 
00331     /* Check we got a valid user */
00332     if (!username)
00333     {
00334         g_printerr ("No user selected during authentication\n");
00335         return EXIT_FAILURE;
00336     }
00337 
00338     /* Stop if we didn't authenticated */
00339     if (authentication_result != PAM_SUCCESS)
00340         return EXIT_FAILURE;
00341 
00342     /* Get the command to run (blocks) */
00343     log_filename = read_string ();
00344     xauth_filename = read_string ();
00345     read_data (&env_length, sizeof (env_length));
00346     for (i = 0; i < env_length; i++)
00347         pam_putenv (pam_handle, read_string ());
00348     read_data (&command_argc, sizeof (command_argc));
00349     command_argv = g_malloc (sizeof (gchar *) * (command_argc + 1));
00350     for (i = 0; i < command_argc; i++)
00351         command_argv[i] = read_string ();
00352     command_argv[i] = NULL;
00353 
00354     /* Redirect stderr to a log file */
00355     if (log_filename)
00356         log_backup_filename = g_strdup_printf ("%s.old", log_filename);
00357     if (!log_filename)
00358     {
00359         fd = open ("/dev/null", O_WRONLY);   
00360         dup2 (fd, STDERR_FILENO);
00361         close (fd);
00362     }
00363     else if (g_path_is_absolute (log_filename))
00364     {
00365         rename (log_filename, log_backup_filename);
00366         fd = open (log_filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
00367         dup2 (fd, STDERR_FILENO);
00368         close (fd);
00369     }
00370 
00371     /* Set group membership - these can be overriden in pam_setcred */
00372     if (getuid () == 0)
00373     {
00374         if (initgroups (username, user_get_gid (user)) < 0)
00375         {
00376             g_printerr ("Failed to initialize supplementary groups for %s: %s\n", username, strerror (errno));
00377             _exit (EXIT_FAILURE);
00378         }
00379     }
00380 
00381     /* Set credentials */
00382     result = pam_setcred (pam_handle, PAM_ESTABLISH_CRED);
00383     if (result != PAM_SUCCESS)
00384     {
00385         g_printerr ("Failed to establish PAM credentials: %s\n", pam_strerror (pam_handle, result));
00386         return EXIT_FAILURE;
00387     }
00388      
00389     /* Open the session */
00390     result = pam_open_session (pam_handle, 0);
00391     if (result != PAM_SUCCESS)
00392     {
00393         g_printerr ("Failed to open PAM session: %s\n", pam_strerror (pam_handle, result));
00394         return EXIT_FAILURE;
00395     }
00396 
00397     /* Open a connection to the system bus for ConsoleKit - we must keep it open or CK will close the session */
00398     bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
00399     if (error)
00400         g_printerr ("Unable to contact system bus: %s", error->message);
00401     if (!bus)
00402         return EXIT_FAILURE;
00403 
00404     /* Open a Console Kit session */
00405     g_variant_builder_init (&ck_parameters, G_VARIANT_TYPE ("(a(sv))"));
00406     g_variant_builder_open (&ck_parameters, G_VARIANT_TYPE ("a(sv)"));
00407     g_variant_builder_add (&ck_parameters, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user)));
00408     if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) == 0)
00409         g_variant_builder_add (&ck_parameters, "(sv)", "session-type", g_variant_new_string ("LoginWindow"));
00410     if (xdisplay)
00411     {
00412         g_variant_builder_add (&ck_parameters, "(sv)", "x11-display", g_variant_new_string (xdisplay));
00413         if (tty)
00414             g_variant_builder_add (&ck_parameters, "(sv)", "x11-display-device", g_variant_new_string (tty));
00415     }
00416     if (remote_host_name)
00417     {
00418         g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (FALSE));
00419         g_variant_builder_add (&ck_parameters, "(sv)", "remote-host-name", g_variant_new_string (remote_host_name));
00420     }
00421     else
00422         g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (TRUE));
00423     console_kit_cookie = ck_open_session (&ck_parameters);
00424     write_string (console_kit_cookie);
00425     if (console_kit_cookie)
00426     {
00427         gchar *value;
00428         value = g_strdup_printf ("XDG_SESSION_COOKIE=%s", console_kit_cookie);
00429         pam_putenv (pam_handle, value);
00430         g_free (value);
00431     }
00432 
00433     /* Write X authority */
00434     if (xauthority)
00435     {
00436         GFile *file;
00437         gboolean drop_privileges, result;
00438         gchar *value;
00439         GError *error = NULL;
00440 
00441         file = g_file_new_for_path (xauth_filename);
00442 
00443         drop_privileges = geteuid () == 0;
00444         if (drop_privileges)
00445             privileges_drop (user);
00446         result = xauth_write (xauthority, XAUTH_WRITE_MODE_REPLACE, file, &error);
00447         if (drop_privileges)
00448             privileges_reclaim ();
00449 
00450         g_object_unref (file);
00451         if (error)
00452             g_printerr ("Error writing X authority: %s\n", error->message);
00453         g_clear_error (&error);
00454         if (!result)
00455             return EXIT_FAILURE;
00456 
00457         value = g_strdup_printf ("XAUTHORITY=%s", xauth_filename);
00458         pam_putenv (pam_handle, value);
00459         g_free (value);
00460     }
00461 
00462     /* Put our tools directory in the path as a hack so we can use the legacy gdmflexiserver interface */
00463     path = pam_getenv (pam_handle, "PATH");
00464     if (path)
00465         pam_putenv (pam_handle, g_strdup_printf ("PATH=%s:%s", PKGLIBEXEC_DIR, path));
00466 
00467     /* Catch terminate signal and pass it to the child */
00468     signal (SIGTERM, signal_cb);
00469 
00470     /* Run the command as the authenticated user */
00471     child_pid = fork (); 
00472     if (child_pid == 0)
00473     {
00474         // FIXME: This is not thread safe (particularly the printfs)
00475 
00476         /* Make this process its own session */
00477         if (setsid () < 0)
00478             g_printerr ("Failed to make process a new session: %s\n", strerror (errno));
00479 
00480         /* Change to this user */
00481         if (getuid () == 0)
00482         {
00483             if (setgid (user_get_gid (user)) != 0)
00484             {
00485                 g_printerr ("Failed to set group ID to %d: %s\n", user_get_gid (user), strerror (errno));
00486                 _exit (EXIT_FAILURE);
00487             }
00488 
00489             if (setuid (user_get_uid (user)) != 0)
00490             {
00491                 g_printerr ("Failed to set user ID to %d: %s\n", user_get_uid (user), strerror (errno));
00492                 _exit (EXIT_FAILURE);
00493             }
00494         }
00495 
00496         /* Change working directory */
00497         /* NOTE: This must be done after the permissions are changed because NFS filesystems can
00498          * be setup so the local root user accesses the NFS files as 'nobody'.  If the home directories
00499          * are not system readable then the chdir can fail */
00500         if (chdir (user_get_home_directory (user)) != 0)
00501         {
00502             g_printerr ("Failed to change to home directory %s: %s\n", user_get_home_directory (user), strerror (errno));
00503             _exit (EXIT_FAILURE);
00504         }
00505 
00506         /* Redirect stderr to a log file */
00507         if (log_filename && !g_path_is_absolute (log_filename))
00508         {
00509             rename (log_filename, log_backup_filename);
00510             fd = open (log_filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
00511             dup2 (fd, STDERR_FILENO);
00512             close (fd);
00513         }
00514 
00515         /* Run the command */
00516         execve (command_argv[0], command_argv, pam_getenvlist (pam_handle));
00517         g_printerr ("Failed to run command: %s\n", strerror (errno));
00518         _exit (EXIT_FAILURE);
00519     }
00520 
00521     /* Bail out if failed to fork */
00522     if (child_pid < 0)
00523     {
00524         g_printerr ("Failed to fork session child process: %s\n", strerror (errno));
00525         return_code = EXIT_FAILURE;
00526     }
00527 
00528     /* Wait for the command to complete (blocks) */
00529     if (child_pid > 0)
00530     {
00531         /* Log to utmp */
00532         if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) != 0)
00533         {
00534             struct utmpx ut;
00535             struct timeval tv;
00536 
00537             memset (&ut, 0, sizeof (ut));
00538             ut.ut_type = USER_PROCESS;
00539             ut.ut_pid = child_pid;
00540             strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
00541             strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
00542             strncpy (ut.ut_user, username, sizeof (ut.ut_user));
00543             if (remote_host_name)
00544                 strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
00545             gettimeofday (&tv, NULL);
00546             ut.ut_tv.tv_sec = tv.tv_sec;
00547             ut.ut_tv.tv_usec = tv.tv_usec;
00548 
00549             setutxent ();
00550             if (!pututxline (&ut))
00551                 g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
00552             endutxent ();
00553         }
00554 
00555         waitpid (child_pid, &return_code, 0);
00556         child_pid = 0;
00557 
00558         /* Log to utmp */
00559         if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) != 0)
00560         {
00561             struct utmpx ut;
00562             struct timeval tv;
00563 
00564             memset (&ut, 0, sizeof (ut));
00565             ut.ut_type = DEAD_PROCESS;
00566             ut.ut_pid = child_pid;
00567             strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
00568             strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
00569             strncpy (ut.ut_user, username, sizeof (ut.ut_user));
00570             if (remote_host_name)
00571                 strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
00572             gettimeofday (&tv, NULL);
00573             ut.ut_tv.tv_sec = tv.tv_sec;
00574             ut.ut_tv.tv_usec = tv.tv_usec;
00575 
00576             setutxent ();
00577             if (!pututxline (&ut))
00578                 g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
00579             endutxent ();
00580         }
00581     }
00582 
00583     /* Remove X authority */
00584     if (xauthority)
00585     {
00586         GFile *file;
00587         gboolean drop_privileges, result;
00588         GError *error = NULL;
00589 
00590         file = g_file_new_for_path (xauth_filename);
00591 
00592         drop_privileges = geteuid () == 0;
00593         if (drop_privileges)
00594             privileges_drop (user);
00595         result = xauth_write (xauthority, XAUTH_WRITE_MODE_REMOVE, file, &error);
00596         if (drop_privileges)
00597             privileges_reclaim ();
00598 
00599         g_object_unref (file);
00600         if (error)
00601             g_printerr ("Error removing X authority: %s\n", error->message);
00602         g_clear_error (&error);
00603         if (!result)
00604             _exit (EXIT_FAILURE);
00605     }
00606 
00607     /* Close the Console Kit session */
00608     if (console_kit_cookie)
00609         ck_close_session (console_kit_cookie);
00610 
00611     /* Close the session */
00612     pam_close_session (pam_handle, 0);
00613 
00614     /* Remove credentials */
00615     result = pam_setcred (pam_handle, PAM_DELETE_CRED);
00616 
00617     pam_end (pam_handle, 0);
00618     pam_handle = NULL;
00619 
00620     /* Return result of session process to the daemon */
00621     return return_code;
00622 }