]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/session.c
Set correct ownership on xauth files
[sojka/lightdm.git] / src / session.c
1 /*
2  * Copyright (C) 2010 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  * 
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 3 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/wait.h>
15 #include <pwd.h>
16 #include <errno.h>
17 #include <string.h>
18
19 #include "session.h"
20
21 enum {
22     EXITED,
23     KILLED,
24     LAST_SIGNAL
25 };
26 static guint signals[LAST_SIGNAL] = { 0 };
27
28 struct SessionPrivate
29 {
30     /* User running this session */
31     gchar *username;
32
33     /* Environment variables */
34     GHashTable *env;
35
36     /* Command to run for this session */
37     gchar *command;
38
39     /* Session process */
40     GPid pid;
41
42     /* X authorization */
43     XAuthorization *authorization;
44     gchar *authorization_path;
45     GFile *authorization_file;
46 };
47
48 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
49
50 Session *
51 session_new (const char *username, const char *command)
52 {
53     Session *self = g_object_new (SESSION_TYPE, NULL);
54
55     self->priv->username = g_strdup (username);
56     self->priv->command = g_strdup (command);
57
58     return self;
59 }
60
61 const gchar *
62 session_get_username (Session *session)
63 {
64     return session->priv->username;
65 }
66
67 const gchar *
68 session_get_command (Session *session)
69 {
70     return session->priv->command;
71 }
72
73 void
74 session_set_env (Session *session, const gchar *name, const gchar *value)
75 {
76     g_hash_table_insert (session->priv->env, g_strdup (name), g_strdup (value));
77 }
78
79 void
80 session_set_authorization (Session *session, XAuthorization *authorization, const gchar *path)
81 {
82     session->priv->authorization = g_object_ref (authorization);
83     session->priv->authorization_path = g_strdup (path);
84 }
85
86 XAuthorization *session_get_authorization (Session *session)
87 {
88     return session->priv->authorization;
89 }
90
91 static void
92 session_watch_cb (GPid pid, gint status, gpointer data)
93 {
94     Session *session = data;
95
96     session->priv->pid = 0;
97
98     if (WIFEXITED (status))
99     {
100         g_debug ("Session exited with return value %d", WEXITSTATUS (status));
101         g_signal_emit (session, signals[EXITED], 0, WEXITSTATUS (status));
102     }
103     else if (WIFSIGNALED (status))
104     {
105         g_debug ("Session terminated with signal %d", WTERMSIG (status));
106         g_signal_emit (session, signals[KILLED], 0, WTERMSIG (status));
107     }
108 }
109
110 static void
111 session_fork_cb (gpointer data)
112 {
113     struct passwd *user_info = data;
114   
115     if (!user_info)
116         return;
117   
118     if (setgid (user_info->pw_gid) != 0)
119     {
120         g_warning ("Failed to set group ID: %s", strerror (errno));
121         _exit(1);
122     }
123     // FIXME: Is there a risk of connecting to the process for a user in the given group and accessing memory?
124     if (setuid (user_info->pw_uid) != 0)
125     {
126         g_warning ("Failed to set user ID: %s", strerror (errno));
127         _exit(1);
128     }
129     if (chdir (user_info->pw_dir) != 0)
130         g_warning ("Failed to change directory: %s", strerror (errno));
131 }
132
133 static gchar **session_get_env (Session *session)
134 {
135     gchar **env;
136     gpointer key, value;
137     GHashTableIter iter;
138     gint i = 0;
139
140     env = g_malloc (sizeof (gchar *) * (g_hash_table_size (session->priv->env) + 1));
141     g_hash_table_iter_init (&iter, session->priv->env);
142     while (g_hash_table_iter_next (&iter, &key, &value))
143     {
144         // FIXME: Do these need to be freed?
145         env[i] = g_strdup_printf("%s=%s", (gchar *)key, (gchar *)value);
146         i++;
147     }
148     env[i] = NULL;
149
150     return env;
151 }
152
153 gboolean
154 session_start (Session *session)
155 {
156     struct passwd *user_info = NULL;
157     //gint session_stdin, session_stdout, session_stderr;
158     gboolean result;
159     gint argc;
160     gchar *username;
161     const gchar *working_dir = NULL;
162     gchar **argv, **env;
163     gchar *env_string;
164     GError *error = NULL;
165   
166     g_return_val_if_fail (session->priv->pid == 0, FALSE);
167
168     if (session->priv->username)
169     {
170         errno = 0;
171         user_info = getpwnam (session->priv->username);
172         if (!user_info)
173         {
174             if (errno == 0)
175                 g_warning ("Unable to get information on user %s: User does not exist", session->priv->username);
176             else
177                 g_warning ("Unable to get information on user %s: %s", session->priv->username, strerror (errno));
178             return FALSE;
179         }
180
181         username = session->priv->username;
182         working_dir = user_info->pw_dir;
183         session_set_env (session, "USER", user_info->pw_name);
184         session_set_env (session, "HOME", user_info->pw_dir);
185         session_set_env (session, "SHELL", user_info->pw_shell);
186     }
187     else
188     {
189         username = getenv ("USER");
190         session_set_env (session, "USER", getenv ("USER"));
191         session_set_env (session, "HOME", getenv ("HOME"));
192         session_set_env (session, "SHELL", getenv ("SHELL"));
193     }
194
195     if (session->priv->authorization)
196     {
197         session->priv->authorization_file = xauth_write (session->priv->authorization, username, session->priv->authorization_path, &error);
198         if (session->priv->authorization_file)
199             session_set_env (session, "XAUTHORITY", session->priv->authorization_path);
200         else
201             g_warning ("Failed to write authorization: %s", error->message);
202         g_clear_error (&error);
203     }
204
205     env = session_get_env (session);
206
207     result = g_shell_parse_argv (session->priv->command, &argc, &argv, &error);
208     if (!result)
209         g_warning ("Failed to parse session command line: %s", error->message);
210     g_clear_error (&error);
211     if (!result)
212         return FALSE;
213
214     env_string = g_strjoinv (" ", env);
215     g_debug ("Launching session: %s %s", env_string, session->priv->command);
216     g_free (env_string);
217
218     result = g_spawn_async/*_with_pipes*/ (working_dir,
219                                        argv,
220                                        env,
221                                        G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
222                                        session_fork_cb, user_info,
223                                        &session->priv->pid,
224                                        //&session_stdin, &session_stdout, &session_stderr,
225                                        &error);
226
227     if (!result)
228         g_warning ("Failed to spawn session: %s", error->message);
229     else
230         g_child_watch_add (session->priv->pid, session_watch_cb, session);
231     g_clear_error (&error);
232
233     return session->priv->pid != 0;
234 }
235
236 static void
237 session_init (Session *session)
238 {
239     session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, SESSION_TYPE, SessionPrivate);
240     session->priv->env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
241 }
242
243 static void
244 session_finalize (GObject *object)
245 {
246     Session *self;
247
248     self = SESSION (object);
249     if (self->priv->pid)
250         kill (self->priv->pid, SIGTERM);    
251     g_free (self->priv->username);
252     g_hash_table_unref (self->priv->env);
253     g_free (self->priv->command);
254     if (self->priv->authorization)
255         g_object_unref (self->priv->authorization);
256     g_free (self->priv->authorization_path);
257     if (self->priv->authorization_file)
258     {
259         g_file_delete (self->priv->authorization_file, NULL, NULL);
260         g_object_unref (self->priv->authorization_file);
261     }
262 }
263
264 static void
265 session_class_init (SessionClass *klass)
266 {
267     GObjectClass *object_class = G_OBJECT_CLASS (klass);
268
269     object_class->finalize = session_finalize;
270
271     g_type_class_add_private (klass, sizeof (SessionPrivate));
272
273     signals[EXITED] =
274         g_signal_new ("exited",
275                       G_TYPE_FROM_CLASS (klass),
276                       G_SIGNAL_RUN_LAST,
277                       G_STRUCT_OFFSET (SessionClass, exited),
278                       NULL, NULL,
279                       g_cclosure_marshal_VOID__INT,
280                       G_TYPE_NONE, 1, G_TYPE_INT);
281     signals[KILLED] =
282         g_signal_new ("killed",
283                       G_TYPE_FROM_CLASS (klass),
284                       G_SIGNAL_RUN_LAST,
285                       G_STRUCT_OFFSET (SessionClass, killed),
286                       NULL, NULL,
287                       g_cclosure_marshal_VOID__INT,
288                       G_TYPE_NONE, 1, G_TYPE_INT);
289 }