2 * Copyright (C) 2010-2011 Robert Ancell.
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 "ldm-marshal.h"
19 #include "greeter-protocol.h"
21 /* Length of time in milliseconds to wait for a greeter to quit */
22 #define GREETER_QUIT_TIMEOUT 1000
29 static guint signals[LAST_SIGNAL] = { 0 };
35 /* Pipe to communicate to greeter */
44 gint autologin_timeout;
48 PAMSession *pam_session;
51 G_DEFINE_TYPE (Greeter, greeter, SESSION_TYPE);
56 return g_object_new (GREETER_TYPE, NULL);
60 greeter_set_default_user (Greeter *greeter, const gchar *username, gint timeout)
62 g_free (greeter->priv->default_user);
63 greeter->priv->default_user = g_strdup (username);
64 greeter->priv->autologin_timeout = timeout;
68 greeter_set_theme (Greeter *greeter, const gchar *theme)
70 g_free (greeter->priv->theme);
71 greeter->priv->theme = g_strdup (theme);
75 greeter_set_layout (Greeter *greeter, const gchar *layout)
77 g_free (greeter->priv->layout);
78 greeter->priv->layout = g_strdup (layout);
82 greeter_get_layout (Greeter *greeter)
84 return greeter->priv->layout;
88 greeter_set_session (Greeter *greeter, const gchar *session)
90 g_free (greeter->priv->session);
91 greeter->priv->session = g_strdup (session);
95 greeter_get_session (Greeter *greeter)
97 return greeter->priv->session;
101 greeter_get_pam_session (Greeter *greeter)
103 return greeter->priv->pam_session;
113 write_int (Greeter *greeter, guint32 value)
116 buffer[0] = value >> 24;
117 buffer[1] = (value >> 16) & 0xFF;
118 buffer[2] = (value >> 8) & 0xFF;
119 buffer[3] = value & 0xFF;
120 g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), buffer, int_length (), NULL, NULL);
124 write_string (Greeter *greeter, const gchar *value)
126 write_int (greeter, strlen (value));
127 g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), value, -1, NULL, NULL);
131 write_header (Greeter *greeter, guint32 id, guint32 length)
133 write_int (greeter, id);
134 write_int (greeter, length);
138 string_length (const gchar *value)
140 return int_length () + strlen (value);
144 flush (Greeter *greeter)
146 g_io_channel_flush (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), NULL);
150 handle_connect (Greeter *greeter)
154 if (!greeter->priv->connected)
156 greeter->priv->connected = TRUE;
157 g_debug ("Greeter connected");
160 theme = g_build_filename (THEME_DIR, greeter->priv->theme, "index.theme", NULL);
162 write_header (greeter, GREETER_MESSAGE_CONNECTED, string_length (theme) + string_length (greeter->priv->layout) + string_length (greeter->priv->session) + string_length (greeter->priv->default_user ? greeter->priv->default_user : "") + int_length () + int_length ());
163 write_string (greeter, theme);
164 write_string (greeter, greeter->priv->layout);
165 write_string (greeter, greeter->priv->session);
166 write_string (greeter, greeter->priv->default_user ? greeter->priv->default_user : "");
167 write_int (greeter, greeter->priv->autologin_timeout);
168 write_int (greeter, FALSE);
175 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Greeter *greeter)
180 /* Respond to d-bus query with messages */
181 g_debug ("Prompt greeter with %d message(s)", num_msg);
182 size = int_length ();
183 for (i = 0; i < num_msg; i++)
184 size += int_length () + string_length (msg[i]->msg);
185 write_header (greeter, GREETER_MESSAGE_PROMPT_AUTHENTICATION, size);
186 write_int (greeter, num_msg);
187 for (i = 0; i < num_msg; i++)
189 write_int (greeter, msg[i]->msg_style);
190 write_string (greeter, msg[i]->msg);
196 authenticate_result_cb (PAMSession *session, int result, Greeter *greeter)
198 g_debug ("Authenticate result for user %s: %s", pam_session_get_username (greeter->priv->pam_session), pam_session_strerror (greeter->priv->pam_session, result));
200 if (result == PAM_SUCCESS)
202 //run_script ("PostLogin");
203 pam_session_authorize (session);
206 /* Respond to D-Bus request */
207 write_header (greeter, GREETER_MESSAGE_END_AUTHENTICATION, int_length ());
208 write_int (greeter, result);
213 handle_login (Greeter *greeter, const gchar *username)
215 GError *error = NULL;
218 //if (greeter->priv->user_session)
221 /* Abort existing authentication */
222 if (greeter->priv->pam_session)
224 g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
225 pam_session_end (greeter->priv->pam_session);
226 g_object_unref (greeter->priv->pam_session);
229 g_debug ("Greeter start authentication for %s", username);
231 greeter->priv->pam_session = pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username);
232 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
233 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), greeter);
235 if (!pam_session_start (greeter->priv->pam_session, &error))
236 g_warning ("Failed to start authentication: %s", error->message);
240 handle_login_as_guest (Greeter *greeter)
243 GError *error = NULL;
246 //if (greeter->priv->user_session)
249 /* Abort existing authentication */
250 if (greeter->priv->pam_session)
252 g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
253 pam_session_end (greeter->priv->pam_session);
254 g_object_unref (greeter->priv->pam_session);
257 // FIXME: Create guest account
259 g_debug ("Greeter start authentication for guest account");
261 greeter->priv->pam_session = pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username);
262 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
263 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), greeter);
265 if (!pam_session_start (greeter->priv->pam_session, &error))
266 g_warning ("Failed to start authentication: %s", error->message);
271 handle_continue_authentication (Greeter *greeter, gchar **secrets)
274 const struct pam_message **messages;
275 struct pam_response *response;
276 int i, j, n_secrets = 0;
279 if (!greeter->priv->connected)
282 /* Not in authorization */
283 if (greeter->priv->pam_session == NULL)
286 num_messages = pam_session_get_num_messages (greeter->priv->pam_session);
287 messages = pam_session_get_messages (greeter->priv->pam_session);
289 /* Check correct number of responses */
290 for (i = 0; i < num_messages; i++)
292 int msg_style = messages[i]->msg_style;
293 if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
296 if (g_strv_length (secrets) != n_secrets)
298 pam_session_end (greeter->priv->pam_session);
302 g_debug ("Continue authentication");
305 response = calloc (num_messages, sizeof (struct pam_response));
306 for (i = 0, j = 0; i < num_messages; i++)
308 int msg_style = messages[i]->msg_style;
309 if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
311 response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
316 pam_session_respond (greeter->priv->pam_session, response);
320 handle_cancel_authentication (Greeter *greeter)
323 if (!greeter->priv->connected)
326 /* Not in authorization */
327 if (greeter->priv->pam_session == NULL)
330 g_debug ("Cancel authentication");
332 pam_session_cancel (greeter->priv->pam_session);
336 quit_greeter_cb (gpointer data)
338 Greeter *greeter = data;
339 g_warning ("Greeter did not quit, sending kill signal");
340 session_stop (SESSION (greeter));
341 greeter->priv->quit_timeout = 0;
346 greeter_quit (Greeter *greeter)
348 write_header (greeter, GREETER_MESSAGE_QUIT, 0);
351 if (greeter->priv->quit_timeout)
352 g_source_remove (greeter->priv->quit_timeout);
353 greeter->priv->quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, greeter);
357 handle_start_session (Greeter *greeter, gchar *session, gchar *language)
359 /*if (greeter->priv->user_session != NULL)
361 g_warning ("Ignoring request to log in when already logged in");
365 g_debug ("Greeter start session %s with language", session, language);
367 g_signal_emit (greeter, signals[START_SESSION], 0, session, language);
371 handle_get_user_defaults (Greeter *greeter, gchar *username)
373 struct passwd *user_info;
375 gboolean have_dmrc = FALSE;
376 gchar *language, *layout, *session;
378 user_info = getpwnam (username);
382 g_warning ("Unable to get information on user %s: User does not exist", username);
384 g_warning ("Unable to get information on user %s: %s", username, strerror (errno));
387 dmrc_file = g_key_file_new ();
389 /* Load the users login settings (~/.dmrc) */
393 path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
394 have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
398 /* If no .dmrc, then load from the cache */
401 gchar *path, *filename;
403 filename = g_strdup_printf ("%s.dmrc", username);
404 path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
406 have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
410 language = g_key_file_get_string (dmrc_file, "Desktop", "Language", NULL);
412 language = g_strdup ("");
413 layout = g_key_file_get_string (dmrc_file, "Desktop", "Layout", NULL);
415 layout = g_strdup ("");
416 session = g_key_file_get_string (dmrc_file, "Desktop", "Session", NULL);
418 session = g_strdup ("");
420 write_header (greeter, GREETER_MESSAGE_USER_DEFAULTS, string_length (language) + string_length (layout) + string_length (session));
421 write_string (greeter, language);
422 write_string (greeter, layout);
423 write_string (greeter, session);
430 g_key_file_free (dmrc_file);
433 #define HEADER_SIZE (sizeof (guint32) * 2)
436 read_int (Greeter *greeter, gsize *offset)
440 if (greeter->priv->n_read - *offset < sizeof (guint32))
442 g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), greeter->priv->n_read - *offset);
445 buffer = greeter->priv->read_buffer + *offset;
446 value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
447 *offset += int_length ();
452 read_string (Greeter *greeter, gsize *offset)
457 length = read_int (greeter, offset);
458 if (greeter->priv->n_read - *offset < length)
460 g_warning ("Not enough space for string, need %u, got %zu", length, greeter->priv->n_read - *offset);
461 return g_strdup ("");
464 value = g_malloc (sizeof (gchar *) * (length + 1));
465 memcpy (value, greeter->priv->read_buffer + *offset, length);
466 value[length] = '\0';
473 got_data_cb (Greeter *greeter)
475 gsize n_to_read, n_read, offset;
477 int id, n_secrets, i;
478 gchar *username, *session_name, *language;
480 GError *error = NULL;
482 n_to_read = HEADER_SIZE;
483 if (greeter->priv->n_read >= HEADER_SIZE)
485 offset = int_length ();
486 n_to_read += read_int (greeter, &offset);
489 status = g_io_channel_read_chars (child_process_get_from_child_channel (CHILD_PROCESS (greeter)),
490 greeter->priv->read_buffer + greeter->priv->n_read,
491 n_to_read - greeter->priv->n_read,
494 if (status != G_IO_STATUS_NORMAL)
495 g_warning ("Error reading from greeter: %s", error->message);
496 g_clear_error (&error);
497 if (status != G_IO_STATUS_NORMAL)
500 g_debug ("Read %zi bytes from greeter", n_read);
502 greeter->priv->n_read += n_read;
503 if (greeter->priv->n_read != n_to_read)
506 /* If have header, rerun for content */
507 if (greeter->priv->n_read == HEADER_SIZE)
509 n_to_read = ((guint32 *) greeter->priv->read_buffer)[1];
512 greeter->priv->read_buffer = g_realloc (greeter->priv->read_buffer, HEADER_SIZE + n_to_read);
513 got_data_cb (greeter);
519 id = read_int (greeter, &offset);
520 read_int (greeter, &offset);
523 case GREETER_MESSAGE_CONNECT:
524 handle_connect (greeter);
526 case GREETER_MESSAGE_LOGIN:
527 username = read_string (greeter, &offset);
528 handle_login (greeter, username);
531 case GREETER_MESSAGE_LOGIN_AS_GUEST:
532 handle_login_as_guest (greeter);
534 case GREETER_MESSAGE_CONTINUE_AUTHENTICATION:
535 n_secrets = read_int (greeter, &offset);
536 secrets = g_malloc (sizeof (gchar *) * (n_secrets + 1));
537 for (i = 0; i < n_secrets; i++)
538 secrets[i] = read_string (greeter, &offset);
540 handle_continue_authentication (greeter, secrets);
541 g_strfreev (secrets);
543 case GREETER_MESSAGE_CANCEL_AUTHENTICATION:
544 handle_cancel_authentication (greeter);
546 case GREETER_MESSAGE_START_SESSION:
547 session_name = read_string (greeter, &offset);
548 language = read_string (greeter, &offset);
549 handle_start_session (greeter, session_name, language);
550 g_free (session_name);
553 case GREETER_MESSAGE_GET_USER_DEFAULTS:
554 username = read_string (greeter, &offset);
555 handle_get_user_defaults (greeter, username);
559 g_warning ("Unknown message from greeter: %d", id);
563 greeter->priv->n_read = 0;
567 end_session (Greeter *greeter, gboolean clean_exit)
569 if (greeter->priv->quit_timeout)
571 g_source_remove (greeter->priv->quit_timeout);
572 greeter->priv->quit_timeout = 0;
576 g_warning ("Greeter failed");
577 else if (!greeter_connected)
578 g_warning ("Greeter quit before connecting");
579 else if (!display->priv->user_session)
580 g_warning ("Greeter quit before session started");
584 // FIXME: Issue with greeter, don't want to start a new one, report error to user
586 g_signal_emit (greeter, signals[QUIT], 0);
590 session_exited_cb (Greeter *greeter, gint status)
592 end_session (greeter, status == 0);
596 session_terminated_cb (Greeter *greeter, gint signum)
598 end_session (greeter, FALSE);
602 greeter_init (Greeter *greeter)
604 greeter->priv = G_TYPE_INSTANCE_GET_PRIVATE (greeter, GREETER_TYPE, GreeterPrivate);
605 greeter->priv->read_buffer = g_malloc (HEADER_SIZE);
606 g_signal_connect (G_OBJECT (greeter), "got-data", G_CALLBACK (got_data_cb), NULL);
607 g_signal_connect (G_OBJECT (greeter), "exited", G_CALLBACK (session_exited_cb), NULL);
608 g_signal_connect (G_OBJECT (greeter), "terminated", G_CALLBACK (session_terminated_cb), NULL);
612 greeter_finalize (GObject *object)
616 self = GREETER (object);
618 g_free (self->priv->read_buffer);
619 g_free (self->priv->theme);
620 g_free (self->priv->layout);
621 g_free (self->priv->session);
622 g_free (self->priv->default_user);
624 G_OBJECT_CLASS (greeter_parent_class)->finalize (object);
628 greeter_class_init (GreeterClass *klass)
630 GObjectClass *object_class = G_OBJECT_CLASS (klass);
632 object_class->finalize = greeter_finalize;
634 signals[START_SESSION] =
635 g_signal_new ("start-session",
636 G_TYPE_FROM_CLASS (klass),
638 G_STRUCT_OFFSET (GreeterClass, start_session),
640 ldm_marshal_VOID__STRING_STRING,
641 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
643 g_signal_new ("quit",
644 G_TYPE_FROM_CLASS (klass),
646 G_STRUCT_OFFSET (GreeterClass, quit),
648 g_cclosure_marshal_VOID__VOID,
651 g_type_class_add_private (klass, sizeof (GreeterPrivate));