2
* Copyright (C) 2010 Canonical Ltd.
3
* Author: Robert Ancell <robert.ancell@canonical.com>
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
18
#include <security/pam_appl.h>
26
static guint signals[LAST_SIGNAL] = { 0 };
33
/* Session process (either greeter or user session) */
36
/* Authentication thread */
37
GThread *authentication_thread;
39
/* Queue to feed responses to the authentication thread */
40
GAsyncQueue *authentication_response_queue;
42
/* Current D-Bus call context */
43
DBusGMethodInvocation *dbus_context;
45
/* Authentication handle */
46
pam_handle_t *pam_handle;
48
/* User logged in as */
51
/* TRUE if user has been authenticated */
52
gboolean authenticated;
54
/* TRUE if in user session */
55
gboolean in_user_session;
57
// FIXME: Token for secure access to this server
60
G_DEFINE_TYPE (Display, display, G_TYPE_OBJECT);
62
static void start_greeter (Display *display);
63
static void start_user_session (Display *display);
68
return g_object_new (DISPLAY_TYPE, NULL);
72
session_watch_cb (GPid pid, gint status, gpointer data)
74
Display *display = data;
76
if (WIFEXITED (status))
77
g_debug ("Display exited with return value %d", WEXITSTATUS (status));
78
else if (WIFSIGNALED (status))
79
g_debug ("Display terminated with signal %d", WTERMSIG (status));
81
display->priv->session_pid = 0;
83
// FIXME: Check for respawn loops
84
if (display->priv->authenticated && !display->priv->in_user_session)
85
start_user_session (display);
87
start_greeter (display);
91
start_session (Display *display, const char *username, char * const argv[])
94
struct passwd *user_info;
96
g_return_if_fail (display->priv->session_pid == 0);
99
user_info = getpwnam (username);
102
g_warning ("Unable to get information on user %s: %s", username, strerror (errno));
109
g_warning ("Failed to fork session: %s", strerror (errno));
114
g_debug ("Child process started with PID %d", pid);
115
display->priv->session_pid = pid;
116
g_child_watch_add (display->priv->session_pid, session_watch_cb, display);
120
if (setgid (user_info->pw_gid) != 0)
122
g_warning ("Failed to set group ID: %s", strerror (errno));
125
// FIXME: Is there a risk of connecting to the process for a user in the given group and accessing memory?
126
if (setuid (user_info->pw_uid) != 0)
128
g_warning ("Failed to set user ID: %s", strerror (errno));
131
if (chdir (user_info->pw_dir) != 0)
132
g_warning ("Failed to change directory: %s", strerror (errno));
134
/* Reset environment */
135
// FIXME: Check these work
137
setenv ("USER", user_info->pw_name, 1);
138
setenv ("HOME", user_info->pw_dir, 1);
139
setenv ("SHELL", user_info->pw_shell, 1);
140
setenv ("HOME", user_info->pw_dir, 1);
141
setenv ("DISPLAY", ":0", 1);
143
execv (argv[0], argv);
147
start_user_session (Display *display)
149
char *argv[] = { "/usr/bin/gnome-terminal", NULL };
150
display->priv->in_user_session = TRUE;
151
start_session (display, display->priv->username, argv);
155
start_greeter (Display *display)
157
char *argv[] = { "/usr/bin/lightdm-greeter", NULL };
158
display->priv->in_user_session = FALSE;
159
g_free (display->priv->username);
160
display->priv->username = NULL;
161
start_session (display, "gdm", argv);
165
pam_conv_cb (int num_msg, const struct pam_message **msg,
166
struct pam_response **resp, void *app_data)
168
Display *display = app_data;
171
/* Respond to d-bus query with messages */
172
dbus_g_method_return (display->priv->dbus_context, 666); // ACTIONS
173
display->priv->dbus_context = NULL;
175
/* Wait for responses */
176
response = g_async_queue_pop (display->priv->authentication_response_queue);
185
authenticate_cb (gpointer data)
187
Display *display = data;
188
struct pam_conv conversation = { pam_conv_cb, display };
191
pam_start ("check_pass", display->priv->username, &conversation, &display->priv->pam_handle);
192
result = pam_authenticate (display->priv->pam_handle, 0);
194
/* Thread complete */
195
g_thread_join (display->priv->authentication_thread);
196
display->priv->authentication_thread = NULL;
197
g_async_queue_unref (display->priv->authentication_response_queue);
198
display->priv->authentication_response_queue = NULL;
200
/* Respond to D-Bus request */
201
dbus_g_method_return (display->priv->dbus_context, 888); // FINISHED
202
display->priv->dbus_context = NULL;
208
display_start_authentication (Display *display, const char *username, DBusGMethodInvocation *context)
210
GError *error = NULL;
212
// FIXME: Only allow calls from the greeter
214
g_return_val_if_fail (display->priv->authentication_thread != NULL, FALSE);
216
/* Store authentication request and D-Bus request to respond to */
217
g_free (display->priv->username);
218
display->priv->username = g_strdup (username);
219
display->priv->dbus_context = context;
222
display->priv->authentication_response_queue = g_async_queue_new ();
223
display->priv->authentication_thread = g_thread_create (authenticate_cb, display, TRUE, &error);
224
if (!display->priv->authentication_thread)
226
g_warning ("Failed to start authentication thread: %s", error->message);
227
display->priv->dbus_context = NULL;
230
g_clear_error (&error);
236
display_continue_authentication (Display *display, int data, DBusGMethodInvocation *context)
238
g_return_val_if_fail (display->priv->authentication_thread == NULL, FALSE);
239
g_return_val_if_fail (display->priv->dbus_context != NULL, FALSE);
241
// FIXME: Only allow calls from the greeter
243
/* Push onto queue and store request to respond to */
244
display->priv->dbus_context = context;
245
g_async_queue_push (display->priv->authentication_response_queue, GINT_TO_POINTER (data));
251
xserver_watch_cb (GPid pid, gint status, gpointer data)
253
Display *display = data;
255
if (WIFEXITED (status))
256
g_debug ("Display exited with return value %d", WEXITSTATUS (status));
257
else if (WIFSIGNALED (status))
258
g_debug ("Display terminated with signal %d", WTERMSIG (status));
260
display->priv->xserver_pid = 0;
262
g_signal_emit (display, signals[EXITED], 0);
266
display_init (Display *display)
268
GError *error = NULL;
269
char *argv[] = { "/usr/bin/X", ":0", NULL };
270
char *env[] = { NULL };
273
display->priv = G_TYPE_INSTANCE_GET_PRIVATE (display, DISPLAY_TYPE, DisplayPrivate);
275
result = g_spawn_async (NULL, /* Working directory */
278
G_SPAWN_DO_NOT_REAP_CHILD,
280
&display->priv->xserver_pid,
283
g_warning ("Unable to create display: %s", error->message);
285
g_child_watch_add (display->priv->xserver_pid, xserver_watch_cb, display);
286
g_clear_error (&error);
288
/* TODO: Do autologin if this is requested */
289
start_greeter (display);
293
display_class_init (DisplayClass *klass)
295
g_type_class_add_private (klass, sizeof (DisplayPrivate));
298
g_signal_new ("exited",
299
G_TYPE_FROM_CLASS (klass),
301
G_STRUCT_OFFSET (DisplayClass, exited),
303
g_cclosure_marshal_VOID__VOID,