Back to index

lightdm  1.3.2
session.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2011 Robert Ancell.
00003  * Author: Robert Ancell <robert.ancell@canonical.com>
00004  * 
00005  * This program is free software: you can redistribute it and/or modify it under
00006  * the terms of the GNU General Public License as published by the Free Software
00007  * Foundation, either version 3 of the License, or (at your option) any later
00008  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
00009  * license.
00010  */
00011 
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <errno.h>
00015 #include <unistd.h>
00016 #include <sys/wait.h>
00017 #include <fcntl.h>
00018 #include <glib/gstdio.h>
00019 #include <grp.h>
00020 #include <pwd.h>
00021 
00022 #include "session.h"
00023 #include "configuration.h"
00024 #include "console-kit.h"
00025 
00026 enum {
00027     GOT_MESSAGES,
00028     AUTHENTICATION_COMPLETE,
00029     STOPPED,
00030     LAST_SIGNAL
00031 };
00032 static guint signals[LAST_SIGNAL] = { 0 };
00033 
00034 struct SessionPrivate
00035 {
00036     /* PID of child process */
00037     GPid pid;
00038 
00039     /* Pipes to talk to child */
00040     int to_child_input;
00041     int from_child_output;
00042     GIOChannel *from_child_channel;
00043     guint from_child_watch;
00044     guint child_watch;
00045 
00046     /* User to authenticate as */
00047     gchar *username;
00048 
00049     /* User object that matches the current username */
00050     User *user;
00051 
00052     /* Messages being requested by PAM */
00053     int messages_length;
00054     struct pam_message *messages;
00055 
00056     /* Authentication result from PAM */
00057     gboolean authentication_started;
00058     gboolean authentication_complete;
00059     int authentication_result;
00060     gchar *authentication_result_string;
00061   
00062     /* File to log to */
00063     gchar *log_filename;
00064   
00065     /* Seat class */
00066     gchar *class;
00067 
00068     /* tty this session is running on */
00069     gchar *tty;
00070   
00071     /* X display connected to */
00072     gchar *xdisplay;
00073     XAuthority *xauthority;
00074     gboolean xauth_use_system_location;
00075 
00076     /* Remote host this session is being controlled from */
00077     gchar *remote_host_name;
00078 
00079     /* Console kit cookie */
00080     gchar *console_kit_cookie;
00081 
00082     /* Environment to set in child */
00083     GList *env;
00084 };
00085 
00086 /* Maximum length of a string to pass between daemon and session */
00087 #define MAX_STRING_LENGTH 65535
00088 
00089 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
00090 
00091 void
00092 session_set_log_file (Session *session, const gchar *filename)
00093 {
00094     g_return_if_fail (session != NULL);
00095     g_free (session->priv->log_filename);
00096     session->priv->log_filename = g_strdup (filename);
00097 }
00098 
00099 void
00100 session_set_class (Session *session, const gchar *class)
00101 {
00102     g_return_if_fail (session != NULL);
00103     g_free (session->priv->class);
00104     session->priv->class = g_strdup (class);
00105 }
00106 
00107 void
00108 session_set_tty (Session *session, const gchar *tty)
00109 {
00110     g_return_if_fail (session != NULL);
00111     g_free (session->priv->tty);
00112     session->priv->tty = g_strdup (tty);
00113 }
00114 
00115 void
00116 session_set_xdisplay (Session *session, const gchar *xdisplay)
00117 {
00118     g_return_if_fail (session != NULL);
00119     g_free (session->priv->xdisplay);
00120     session->priv->xdisplay = g_strdup (xdisplay);
00121 }
00122 
00123 void
00124 session_set_xauthority (Session *session, XAuthority *authority, gboolean use_system_location)
00125 {
00126     g_return_if_fail (session != NULL);
00127     if (session->priv->xauthority)
00128         g_object_unref (session->priv->xauthority);
00129     session->priv->xauthority = g_object_ref (authority);
00130     session->priv->xauth_use_system_location = use_system_location;
00131 }
00132 
00133 void
00134 session_set_remote_host_name (Session *session, const gchar *remote_host_name)
00135 {
00136     g_return_if_fail (session != NULL);
00137     g_free (session->priv->remote_host_name);
00138     session->priv->remote_host_name = g_strdup (remote_host_name);
00139 }
00140 
00141 void
00142 session_set_env (Session *session, const gchar *name, const gchar *value)
00143 {
00144     g_return_if_fail (session != NULL);
00145     session->priv->env = g_list_append (session->priv->env, g_strdup_printf ("%s=%s", name, value));
00146 }
00147 
00148 User *
00149 session_get_user (Session *session)
00150 {
00151     g_return_val_if_fail (session != NULL, NULL);
00152 
00153     if (session->priv->username == NULL)
00154         return NULL;
00155 
00156     if (!session->priv->user)
00157         session->priv->user = accounts_get_user_by_name (session->priv->username);
00158 
00159     return session->priv->user;
00160 }
00161 
00162 static void
00163 write_data (Session *session, const void *buf, size_t count)
00164 {
00165     if (write (session->priv->to_child_input, buf, count) != count)
00166         g_warning ("Error writing to session: %s", strerror (errno));
00167 }
00168 
00169 static void
00170 write_string (Session *session, const char *value)
00171 {
00172     int length;
00173 
00174     length = value ? strlen (value) : -1;
00175     write_data (session, &length, sizeof (length));
00176     if (value)
00177         write_data (session, value, sizeof (char) * length);
00178 }
00179 
00180 static ssize_t
00181 read_from_child (Session *session, void *buf, size_t count)
00182 {
00183     ssize_t n_read;
00184     n_read = read (session->priv->from_child_output, buf, count);
00185     if (n_read < 0)
00186         g_warning ("Error reading from session: %s", strerror (errno));
00187     return n_read;
00188 }
00189 
00190 static gchar *
00191 read_string_from_child (Session *session)
00192 {
00193     int length;
00194     char *value;
00195 
00196     if (read_from_child (session, &length, sizeof (length)) <= 0)
00197         return NULL;
00198     if (length < 0)
00199         return NULL;
00200     if (length > MAX_STRING_LENGTH)
00201     {
00202         g_warning ("Invalid string length %d from child", length);
00203         return NULL;
00204     }
00205   
00206     value = g_malloc (sizeof (char) * (length + 1));
00207     read_from_child (session, value, length);
00208     value[length] = '\0';      
00209 
00210     return value;
00211 }
00212 
00213 static void
00214 session_watch_cb (GPid pid, gint status, gpointer data)
00215 {
00216     Session *session = data;
00217 
00218     session->priv->pid = 0;
00219 
00220     if (WIFEXITED (status))
00221         g_debug ("Session %d exited with return value %d", pid, WEXITSTATUS (status));
00222     else if (WIFSIGNALED (status))
00223         g_debug ("Session %d terminated with signal %d", pid, WTERMSIG (status));
00224 
00225     /* If failed during authentication then report this as an authentication failure */
00226     if (session->priv->authentication_started && !session->priv->authentication_complete)
00227     {
00228         g_debug ("Session %d failed during authentication", pid);
00229         session->priv->authentication_complete = TRUE;
00230         session->priv->authentication_result = PAM_CONV_ERR;
00231         g_free (session->priv->authentication_result_string);
00232         session->priv->authentication_result_string = g_strdup ("Authentication stopped before completion");
00233         g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_COMPLETE], 0);      
00234     } 
00235 
00236     g_signal_emit (G_OBJECT (session), signals[STOPPED], 0);
00237 }
00238 
00239 static gboolean
00240 from_child_cb (GIOChannel *source, GIOCondition condition, gpointer data)
00241 {
00242     Session *session = data;
00243     gchar *username;
00244     ssize_t n_read;
00245     gboolean auth_complete;
00246 
00247     /* Remote end gone */
00248     if (condition == G_IO_HUP)
00249     {
00250         session->priv->from_child_watch = 0;
00251         return FALSE;
00252     }
00253 
00254     /* Get the username currently being authenticated (may change during authentication) */
00255     username = read_string_from_child (session);
00256     if (g_strcmp0 (username, session->priv->username) != 0)
00257     {
00258         g_free (session->priv->username);
00259         session->priv->username = username;
00260         if (session->priv->user)
00261             g_object_unref (session->priv->user);
00262         session->priv->user = NULL;
00263     }
00264     else
00265         g_free (username);
00266 
00267     /* Check if authentication completed */
00268     n_read = read_from_child (session, &auth_complete, sizeof (auth_complete));
00269     if (n_read < 0)
00270         g_debug ("Error reading from child: %s", strerror (errno));
00271     if (n_read <= 0)
00272     {
00273         session->priv->from_child_watch = 0;
00274         return FALSE;
00275     }
00276 
00277     if (auth_complete)
00278     {
00279         session->priv->authentication_complete = TRUE;
00280         read_from_child (session, &session->priv->authentication_result, sizeof (session->priv->authentication_result));
00281         g_free (session->priv->authentication_result_string);
00282         session->priv->authentication_result_string = read_string_from_child (session);
00283 
00284         g_debug ("Session %d authentication complete with return value %d: %s", session->priv->pid, session->priv->authentication_result, session->priv->authentication_result_string);
00285 
00286         /* No longer expect any more messages */
00287         session->priv->from_child_watch = 0;
00288 
00289         g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_COMPLETE], 0);
00290 
00291         return FALSE;
00292     }
00293     else
00294     {
00295         int i;
00296 
00297         session->priv->messages_length = 0;
00298         read_from_child (session, &session->priv->messages_length, sizeof (session->priv->messages_length));
00299         session->priv->messages = calloc (session->priv->messages_length, sizeof (struct pam_message));
00300         for (i = 0; i < session->priv->messages_length; i++)
00301         {
00302             struct pam_message *m = &session->priv->messages[i];
00303             read_from_child (session, &m->msg_style, sizeof (m->msg_style));          
00304             m->msg = read_string_from_child (session);
00305         }
00306 
00307         g_debug ("Session %d got %d message(s) from PAM", session->priv->pid, session->priv->messages_length);
00308 
00309         g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0);
00310     }    
00311 
00312     return TRUE;
00313 }
00314 
00315 gboolean
00316 session_start (Session *session, const gchar *service, const gchar *username, gboolean do_authenticate, gboolean is_interactive)
00317 {
00318     int version;
00319     int to_child_pipe[2], from_child_pipe[2];
00320     int to_child_output, from_child_input;
00321 
00322     g_return_val_if_fail (session != NULL, FALSE);
00323     g_return_val_if_fail (service != NULL, FALSE);
00324     g_return_val_if_fail (session->priv->pid == 0, FALSE);
00325 
00326     /* Create pipes to talk to the child */
00327     if (pipe (to_child_pipe) < 0 || pipe (from_child_pipe) < 0)
00328     {
00329         g_warning ("Failed to create pipe to communicate with session process: %s", strerror (errno));
00330         return FALSE;
00331     }
00332     to_child_output = to_child_pipe[0];
00333     session->priv->to_child_input = to_child_pipe[1];
00334     session->priv->from_child_output = from_child_pipe[0];
00335     from_child_input = from_child_pipe[1];
00336     session->priv->from_child_channel = g_io_channel_unix_new (session->priv->from_child_output);
00337     session->priv->from_child_watch = g_io_add_watch (session->priv->from_child_channel, G_IO_IN | G_IO_HUP, from_child_cb, session);
00338 
00339     /* Don't allow the daemon end of the pipes to be accessed in child processes */
00340     fcntl (session->priv->to_child_input, F_SETFD, FD_CLOEXEC);
00341     fcntl (session->priv->from_child_output, F_SETFD, FD_CLOEXEC);
00342 
00343     /* Remember what username we started with - it will be updated by PAM during authentication */
00344     session->priv->username = g_strdup (username);
00345 
00346     /* Run the child */
00347     session->priv->pid = fork ();
00348     if (session->priv->pid < 0)
00349     {
00350         g_debug ("Failed to fork session child process: %s", strerror (errno));
00351         return FALSE;
00352     }
00353     if (session->priv->pid == 0)
00354     {
00355         /* Run us again in session child mode */
00356         execlp ("lightdm",
00357                 "lightdm",
00358                 "--session-child",
00359                 g_strdup_printf ("%d", to_child_output),
00360                 g_strdup_printf ("%d", from_child_input),
00361                 NULL);
00362         _exit (EXIT_FAILURE);
00363     }
00364 
00365     /* Listen for session termination */
00366     session->priv->authentication_started = TRUE;
00367     session->priv->child_watch = g_child_watch_add (session->priv->pid, session_watch_cb, session);
00368 
00369     /* Close the ends of the pipes we don't need */
00370     close (to_child_output);
00371     close (from_child_input);
00372   
00373     /* Indicate what version of the protocol we are using */
00374     version = 0;
00375     write_data (session, &version, sizeof (version));
00376 
00377     /* Send configuration */
00378     write_string (session, service);
00379     write_string (session, username);
00380     write_data (session, &do_authenticate, sizeof (do_authenticate));
00381     write_data (session, &is_interactive, sizeof (is_interactive));
00382     write_string (session, session->priv->class);
00383     write_string (session, session->priv->tty);
00384     write_string (session, session->priv->remote_host_name);
00385     write_string (session, session->priv->xdisplay);
00386     if (session->priv->xauthority)
00387     {
00388         guint16 family;
00389         gsize length;
00390 
00391         write_string (session, xauth_get_authorization_name (session->priv->xauthority));
00392         family = xauth_get_family (session->priv->xauthority);
00393         write_data (session, &family, sizeof (family));
00394         length = xauth_get_address_length (session->priv->xauthority);
00395         write_data (session, &length, sizeof (length));
00396         write_data (session, xauth_get_address (session->priv->xauthority), length);
00397         write_string (session, xauth_get_number (session->priv->xauthority));
00398         length = xauth_get_authorization_data_length (session->priv->xauthority);
00399         write_data (session, &length, sizeof (length));
00400         write_data (session, xauth_get_authorization_data (session->priv->xauthority), length);
00401     }
00402     else
00403         write_string (session, NULL);    
00404 
00405     g_debug ("Started session %d with service '%s', username '%s'", session->priv->pid, service, username);
00406 
00407     return TRUE;
00408 }
00409 
00410 const gchar *
00411 session_get_username (Session *session)
00412 {
00413     g_return_val_if_fail (session != NULL, NULL);
00414     return session->priv->username;
00415 }
00416 
00417 const gchar *
00418 session_get_console_kit_cookie (Session *session)
00419 {
00420     g_return_val_if_fail (session != NULL, NULL);
00421     return session->priv->console_kit_cookie;
00422 }
00423 
00424 void
00425 session_respond (Session *session, struct pam_response *response)
00426 {
00427     int error = PAM_SUCCESS;
00428     int i;
00429 
00430     g_return_if_fail (session != NULL);
00431 
00432     write_data (session, &error, sizeof (error));
00433     for (i = 0; i < session->priv->messages_length; i++)
00434     {
00435         write_string (session, response[i].resp);
00436         write_data (session, &response[i].resp_retcode, sizeof (response[i].resp_retcode));
00437     }
00438 
00439     /* Delete the old messages */
00440     for (i = 0; i < session->priv->messages_length; i++)
00441         g_free ((char *) session->priv->messages[i].msg);
00442     g_free (session->priv->messages);
00443     session->priv->messages = NULL;
00444     session->priv->messages_length = 0;
00445 }
00446 
00447 void
00448 session_respond_error (Session *session, int error)
00449 {
00450     g_return_if_fail (session != NULL);
00451     g_return_if_fail (error != PAM_SUCCESS);
00452 
00453     write_data (session, &error, sizeof (error));  
00454 }
00455 
00456 int
00457 session_get_messages_length (Session *session)
00458 {
00459     g_return_val_if_fail (session != NULL, 0);
00460     return session->priv->messages_length;
00461 }
00462 
00463 const struct pam_message *
00464 session_get_messages (Session *session)
00465 {
00466     g_return_val_if_fail (session != NULL, NULL);
00467     return session->priv->messages;
00468 }
00469 
00470 gboolean
00471 session_get_is_authenticated (Session *session)
00472 {
00473     g_return_val_if_fail (session != NULL, FALSE);
00474     return session->priv->authentication_complete && session->priv->authentication_result == PAM_SUCCESS;
00475 }
00476 
00477 int
00478 session_get_authentication_result (Session *session)
00479 {
00480     g_return_val_if_fail (session != NULL, 0);
00481     return session->priv->authentication_result;
00482 }
00483 
00484 const gchar *
00485 session_get_authentication_result_string (Session *session)
00486 {
00487     g_return_val_if_fail (session != NULL, NULL);
00488     return session->priv->authentication_result_string;
00489 }
00490 
00491 void
00492 session_run (Session *session, gchar **argv)
00493 {
00494     gsize i, argc;
00495     gchar *command, *filename;
00496     GList *link;
00497 
00498     g_return_if_fail (session != NULL);
00499     g_return_if_fail (session_get_is_authenticated (session));
00500 
00501     command = g_strjoinv (" ", argv);
00502     g_debug ("Session %d running command %s", session->priv->pid, command);
00503     g_free (command);
00504 
00505     /* Create authority location */
00506     if (session->priv->xauth_use_system_location)
00507     {
00508         gchar *run_dir, *dir;
00509 
00510         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");          
00511         dir = g_build_filename (run_dir, session->priv->username, NULL);
00512         g_free (run_dir);
00513 
00514         g_mkdir_with_parents (dir, S_IRWXU);
00515         if (getuid () == 0)
00516         {
00517             if (chown (dir, user_get_uid (session_get_user (session)), user_get_gid (session_get_user (session))) < 0)
00518                 g_warning ("Failed to set ownership of user authority dir: %s", strerror (errno));
00519         }
00520 
00521         filename = g_build_filename (dir, "xauthority", NULL);
00522         g_free (dir);
00523     }
00524     else
00525         filename = g_build_filename (user_get_home_directory (session_get_user (session)), ".Xauthority", NULL);
00526 
00527     write_string (session, session->priv->log_filename);
00528     write_string (session, filename);
00529     g_free (filename);
00530     argc = g_list_length (session->priv->env);
00531     write_data (session, &argc, sizeof (argc));
00532     for (link = session->priv->env; link; link = link->next)
00533         write_string (session, (gchar *) link->data);
00534     argc = g_strv_length (argv);
00535     write_data (session, &argc, sizeof (argc));
00536     for (i = 0; i < argc; i++)
00537         write_string (session, argv[i]);
00538 
00539     session->priv->console_kit_cookie = read_string_from_child (session);
00540 }
00541 
00542 void
00543 session_lock (Session *session)
00544 {    
00545     g_return_if_fail (session != NULL);
00546     if (getuid () == 0)
00547         ck_lock_session (session->priv->console_kit_cookie);
00548 }
00549 
00550 void
00551 session_unlock (Session *session)
00552 {    
00553     g_return_if_fail (session != NULL);
00554     if (getuid () == 0)
00555         ck_unlock_session (session->priv->console_kit_cookie);
00556 }
00557 
00558 void
00559 session_stop (Session *session)
00560 {
00561     g_return_if_fail (session != NULL);
00562   
00563     if (session->priv->pid > 0)
00564     {
00565         g_debug ("Session %d: Sending SIGTERM", session->priv->pid);
00566         kill (session->priv->pid, SIGTERM);
00567         // FIXME: Handle timeout
00568     }
00569 }
00570 
00571 gboolean
00572 session_get_is_stopped (Session *session)
00573 {
00574     g_return_val_if_fail (session != NULL, TRUE);
00575     return session->priv->pid == 0;
00576 }
00577 
00578 static void
00579 session_init (Session *session)
00580 {
00581     session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, SESSION_TYPE, SessionPrivate);
00582 }
00583 
00584 static void
00585 session_finalize (GObject *object)
00586 {
00587     Session *self = SESSION (object);
00588     int i;
00589   
00590     if (self->priv->pid)
00591         kill (self->priv->pid, SIGKILL);
00592     if (self->priv->from_child_channel)
00593         g_io_channel_unref (self->priv->from_child_channel);
00594     if (self->priv->from_child_watch)
00595         g_source_remove (self->priv->from_child_watch);
00596     if (self->priv->child_watch)
00597         g_source_remove (self->priv->child_watch);
00598     g_free (self->priv->username);
00599     if (self->priv->user)
00600         g_object_unref (self->priv->user);
00601     for (i = 0; i < self->priv->messages_length; i++)
00602         g_free ((char *) self->priv->messages[i].msg);
00603     g_free (self->priv->messages);
00604     g_free (self->priv->authentication_result_string);
00605     g_free (self->priv->log_filename);
00606     g_free (self->priv->class);
00607     g_free (self->priv->tty);
00608     g_free (self->priv->xdisplay);
00609     if (self->priv->xauthority)
00610         g_object_unref (self->priv->xauthority);
00611     g_free (self->priv->remote_host_name);
00612     g_free (self->priv->console_kit_cookie);
00613     g_list_free_full (self->priv->env, g_free);
00614 
00615     G_OBJECT_CLASS (session_parent_class)->finalize (object);
00616 }
00617 
00618 static void
00619 session_class_init (SessionClass *klass)
00620 {
00621     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00622 
00623     object_class->finalize = session_finalize;
00624 
00625     g_type_class_add_private (klass, sizeof (SessionPrivate));
00626 
00627     signals[GOT_MESSAGES] =
00628         g_signal_new ("got-messages",
00629                       G_TYPE_FROM_CLASS (klass),
00630                       G_SIGNAL_RUN_LAST,
00631                       G_STRUCT_OFFSET (SessionClass, got_messages),
00632                       NULL, NULL,
00633                       g_cclosure_marshal_VOID__VOID,
00634                       G_TYPE_NONE, 0);
00635 
00636     signals[AUTHENTICATION_COMPLETE] =
00637         g_signal_new ("authentication-complete",
00638                       G_TYPE_FROM_CLASS (klass),
00639                       G_SIGNAL_RUN_LAST,
00640                       G_STRUCT_OFFSET (SessionClass, authentication_complete),
00641                       NULL, NULL,
00642                       g_cclosure_marshal_VOID__VOID,
00643                       G_TYPE_NONE, 0);
00644 
00645     signals[STOPPED] =
00646         g_signal_new ("stopped",
00647                       G_TYPE_FROM_CLASS (klass),
00648                       G_SIGNAL_RUN_LAST,
00649                       G_STRUCT_OFFSET (SessionClass, stopped),
00650                       NULL, NULL,
00651                       g_cclosure_marshal_VOID__VOID,
00652                       G_TYPE_NONE, 0);
00653 }