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
30 static guint signals[LAST_SIGNAL] = { 0 };
36 /* Pipe to communicate to greeter */
45 gint autologin_timeout;
49 PAMSession *pam_session;
52 G_DEFINE_TYPE (Greeter, greeter, SESSION_TYPE);
57 return g_object_new (GREETER_TYPE, NULL);
61 greeter_set_default_user (Greeter *greeter, const gchar *username, gint timeout)
63 g_free (greeter->priv->default_user);
64 greeter->priv->default_user = g_strdup (username);
65 greeter->priv->autologin_timeout = timeout;
69 greeter_set_theme (Greeter *greeter, const gchar *theme)
71 g_free (greeter->priv->theme);
72 greeter->priv->theme = g_strdup (theme);
76 greeter_set_layout (Greeter *greeter, const gchar *layout)
78 g_free (greeter->priv->layout);
79 greeter->priv->layout = g_strdup (layout);
83 greeter_get_layout (Greeter *greeter)
85 return greeter->priv->layout;
89 greeter_set_session (Greeter *greeter, const gchar *session)
91 g_free (greeter->priv->session);
92 greeter->priv->session = g_strdup (session);
96 greeter_get_session (Greeter *greeter)
98 return greeter->priv->session;
102 greeter_get_pam_session (Greeter *greeter)
104 return greeter->priv->pam_session;
114 write_int (Greeter *greeter, guint32 value)
117 buffer[0] = value >> 24;
118 buffer[1] = (value >> 16) & 0xFF;
119 buffer[2] = (value >> 8) & 0xFF;
120 buffer[3] = value & 0xFF;
121 g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), buffer, int_length (), NULL, NULL);
125 write_string (Greeter *greeter, const gchar *value)
127 write_int (greeter, strlen (value));
128 g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), value, -1, NULL, NULL);
132 write_header (Greeter *greeter, guint32 id, guint32 length)
134 write_int (greeter, id);
135 write_int (greeter, length);
139 string_length (const gchar *value)
141 return int_length () + strlen (value);
145 flush (Greeter *greeter)
147 g_io_channel_flush (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), NULL);
151 handle_connect (Greeter *greeter)
155 if (!greeter->priv->connected)
157 greeter->priv->connected = TRUE;
158 g_debug ("Greeter connected");
161 theme = g_build_filename (THEME_DIR, greeter->priv->theme, "index.theme", NULL);
163 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 ());
164 write_string (greeter, theme);
165 write_string (greeter, greeter->priv->layout);
166 write_string (greeter, greeter->priv->session);
167 write_string (greeter, greeter->priv->default_user ? greeter->priv->default_user : "");
168 write_int (greeter, greeter->priv->autologin_timeout);
169 write_int (greeter, FALSE);
176 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Greeter *greeter)
181 /* Respond to d-bus query with messages */
182 g_debug ("Prompt greeter with %d message(s)", num_msg);
183 size = int_length ();
184 for (i = 0; i < num_msg; i++)
185 size += int_length () + string_length (msg[i]->msg);
186 write_header (greeter, GREETER_MESSAGE_PROMPT_AUTHENTICATION, size);
187 write_int (greeter, num_msg);
188 for (i = 0; i < num_msg; i++)
190 write_int (greeter, msg[i]->msg_style);
191 write_string (greeter, msg[i]->msg);
197 authenticate_result_cb (PAMSession *session, int result, Greeter *greeter)
199 g_debug ("Authenticate result for user %s: %s", pam_session_get_username (greeter->priv->pam_session), pam_session_strerror (greeter->priv->pam_session, result));
201 if (result == PAM_SUCCESS)
203 //run_script ("PostLogin");
204 pam_session_authorize (session);
207 /* Respond to D-Bus request */
208 write_header (greeter, GREETER_MESSAGE_END_AUTHENTICATION, int_length ());
209 write_int (greeter, result);
214 handle_start_authentication (Greeter *greeter, const gchar *username)
216 GError *error = NULL;
219 //if (greeter->priv->user_session)
222 /* Abort existing authentication */
223 if (greeter->priv->pam_session)
225 g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
226 pam_session_end (greeter->priv->pam_session);
227 g_object_unref (greeter->priv->pam_session);
230 g_debug ("Greeter start authentication for %s", username);
232 greeter->priv->pam_session = pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username);
233 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
234 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), greeter);
236 if (!pam_session_start (greeter->priv->pam_session, &error))
237 g_warning ("Failed to start authentication: %s", error->message);
241 handle_continue_authentication (Greeter *greeter, gchar **secrets)
244 const struct pam_message **messages;
245 struct pam_response *response;
246 int i, j, n_secrets = 0;
249 if (!greeter->priv->connected)
252 /* Not in authorization */
253 if (greeter->priv->pam_session == NULL)
256 num_messages = pam_session_get_num_messages (greeter->priv->pam_session);
257 messages = pam_session_get_messages (greeter->priv->pam_session);
259 /* Check correct number of responses */
260 for (i = 0; i < num_messages; i++)
262 int msg_style = messages[i]->msg_style;
263 if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
266 if (g_strv_length (secrets) != n_secrets)
268 pam_session_end (greeter->priv->pam_session);
272 g_debug ("Continue authentication");
275 response = calloc (num_messages, sizeof (struct pam_response));
276 for (i = 0, j = 0; i < num_messages; i++)
278 int msg_style = messages[i]->msg_style;
279 if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
281 response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
286 pam_session_respond (greeter->priv->pam_session, response);
290 handle_cancel_authentication (Greeter *greeter)
293 if (!greeter->priv->connected)
296 /* Not in authorization */
297 if (greeter->priv->pam_session == NULL)
300 g_debug ("Cancel authentication");
302 pam_session_cancel (greeter->priv->pam_session);
306 quit_greeter_cb (gpointer data)
308 Greeter *greeter = data;
309 g_warning ("Greeter did not quit, sending kill signal");
310 session_stop (SESSION (greeter));
311 greeter->priv->quit_timeout = 0;
316 greeter_quit (Greeter *greeter)
318 write_header (greeter, GREETER_MESSAGE_QUIT, 0);
321 if (greeter->priv->quit_timeout)
322 g_source_remove (greeter->priv->quit_timeout);
323 greeter->priv->quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, greeter);
327 handle_login (Greeter *greeter, gchar *username, gchar *session, gchar *language)
329 /*if (greeter->priv->user_session != NULL)
331 g_warning ("Ignoring request to log in when already logged in");
335 g_debug ("Greeter login for user %s on session %s", username, session);
337 g_signal_emit (greeter, signals[LOGIN], 0, username, session, language);
341 handle_guest_login (Greeter *greeter, gchar *session, gchar *language)
343 /*if (greeter->priv->user_session != NULL)
345 g_warning ("Ignoring request to log in when already logged in");
349 g_debug ("Greeter login to guest account on session %s", session);
351 g_signal_emit (greeter, signals[LOGIN_AS_GUEST], 0, session, language);
355 handle_get_user_defaults (Greeter *greeter, gchar *username)
357 struct passwd *user_info;
359 gboolean have_dmrc = FALSE;
360 gchar *language, *layout, *session;
362 user_info = getpwnam (username);
366 g_warning ("Unable to get information on user %s: User does not exist", username);
368 g_warning ("Unable to get information on user %s: %s", username, strerror (errno));
371 dmrc_file = g_key_file_new ();
373 /* Load the users login settings (~/.dmrc) */
377 path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
378 have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
382 /* If no .dmrc, then load from the cache */
385 gchar *path, *filename;
387 filename = g_strdup_printf ("%s.dmrc", username);
388 path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
390 have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
394 language = g_key_file_get_string (dmrc_file, "Desktop", "Language", NULL);
396 language = g_strdup ("");
397 layout = g_key_file_get_string (dmrc_file, "Desktop", "Layout", NULL);
399 layout = g_strdup ("");
400 session = g_key_file_get_string (dmrc_file, "Desktop", "Session", NULL);
402 session = g_strdup ("");
404 write_header (greeter, GREETER_MESSAGE_USER_DEFAULTS, string_length (language) + string_length (layout) + string_length (session));
405 write_string (greeter, language);
406 write_string (greeter, layout);
407 write_string (greeter, session);
414 g_key_file_free (dmrc_file);
417 #define HEADER_SIZE (sizeof (guint32) * 2)
420 read_int (Greeter *greeter, gsize *offset)
424 if (greeter->priv->n_read - *offset < sizeof (guint32))
426 g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), greeter->priv->n_read - *offset);
429 buffer = greeter->priv->read_buffer + *offset;
430 value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
431 *offset += int_length ();
436 read_string (Greeter *greeter, gsize *offset)
441 length = read_int (greeter, offset);
442 if (greeter->priv->n_read - *offset < length)
444 g_warning ("Not enough space for string, need %u, got %zu", length, greeter->priv->n_read - *offset);
445 return g_strdup ("");
448 value = g_malloc (sizeof (gchar *) * (length + 1));
449 memcpy (value, greeter->priv->read_buffer + *offset, length);
450 value[length] = '\0';
457 got_data_cb (Greeter *greeter)
459 gsize n_to_read, n_read, offset;
461 int id, n_secrets, i;
462 gchar *username, *session_name, *language;
464 GError *error = NULL;
466 n_to_read = HEADER_SIZE;
467 if (greeter->priv->n_read >= HEADER_SIZE)
469 offset = int_length ();
470 n_to_read += read_int (greeter, &offset);
473 status = g_io_channel_read_chars (child_process_get_from_child_channel (CHILD_PROCESS (greeter)),
474 greeter->priv->read_buffer + greeter->priv->n_read,
475 n_to_read - greeter->priv->n_read,
478 if (status != G_IO_STATUS_NORMAL)
479 g_warning ("Error reading from greeter: %s", error->message);
480 g_clear_error (&error);
481 if (status != G_IO_STATUS_NORMAL)
484 g_debug ("Read %zi bytes from greeter", n_read);
486 greeter->priv->n_read += n_read;
487 if (greeter->priv->n_read != n_to_read)
490 /* If have header, rerun for content */
491 if (greeter->priv->n_read == HEADER_SIZE)
493 n_to_read = ((guint32 *) greeter->priv->read_buffer)[1];
496 greeter->priv->read_buffer = g_realloc (greeter->priv->read_buffer, HEADER_SIZE + n_to_read);
497 got_data_cb (greeter);
503 id = read_int (greeter, &offset);
504 read_int (greeter, &offset);
507 case GREETER_MESSAGE_CONNECT:
508 handle_connect (greeter);
510 case GREETER_MESSAGE_START_AUTHENTICATION:
511 username = read_string (greeter, &offset);
512 handle_start_authentication (greeter, username);
515 case GREETER_MESSAGE_CONTINUE_AUTHENTICATION:
516 n_secrets = read_int (greeter, &offset);
517 secrets = g_malloc (sizeof (gchar *) * (n_secrets + 1));
518 for (i = 0; i < n_secrets; i++)
519 secrets[i] = read_string (greeter, &offset);
521 handle_continue_authentication (greeter, secrets);
522 g_strfreev (secrets);
524 case GREETER_MESSAGE_CANCEL_AUTHENTICATION:
525 handle_cancel_authentication (greeter);
527 case GREETER_MESSAGE_LOGIN:
528 username = read_string (greeter, &offset);
529 session_name = read_string (greeter, &offset);
530 language = read_string (greeter, &offset);
531 handle_login (greeter, username, session_name, language);
533 g_free (session_name);
536 case GREETER_MESSAGE_LOGIN_AS_GUEST:
537 session_name = read_string (greeter, &offset);
538 language = read_string (greeter, &offset);
539 handle_guest_login (greeter, session_name, language);
540 g_free (session_name);
542 case GREETER_MESSAGE_GET_USER_DEFAULTS:
543 username = read_string (greeter, &offset);
544 handle_get_user_defaults (greeter, username);
548 g_warning ("Unknown message from greeter: %d", id);
552 greeter->priv->n_read = 0;
556 end_session (Greeter *greeter, gboolean clean_exit)
558 if (greeter->priv->quit_timeout)
560 g_source_remove (greeter->priv->quit_timeout);
561 greeter->priv->quit_timeout = 0;
565 g_warning ("Greeter failed");
566 else if (!greeter_connected)
567 g_warning ("Greeter quit before connecting");
568 else if (!display->priv->user_session)
569 g_warning ("Greeter quit before session started");
573 // FIXME: Issue with greeter, don't want to start a new one, report error to user
575 g_signal_emit (greeter, signals[QUIT], 0);
579 session_exited_cb (Greeter *greeter, gint status)
581 end_session (greeter, status == 0);
585 session_terminated_cb (Greeter *greeter, gint signum)
587 end_session (greeter, FALSE);
591 greeter_init (Greeter *greeter)
593 greeter->priv = G_TYPE_INSTANCE_GET_PRIVATE (greeter, GREETER_TYPE, GreeterPrivate);
594 greeter->priv->read_buffer = g_malloc (HEADER_SIZE);
595 g_signal_connect (G_OBJECT (greeter), "got-data", G_CALLBACK (got_data_cb), NULL);
596 g_signal_connect (G_OBJECT (greeter), "exited", G_CALLBACK (session_exited_cb), NULL);
597 g_signal_connect (G_OBJECT (greeter), "terminated", G_CALLBACK (session_terminated_cb), NULL);
601 greeter_finalize (GObject *object)
605 self = GREETER (object);
607 g_free (self->priv->read_buffer);
608 g_free (self->priv->theme);
609 g_free (self->priv->layout);
610 g_free (self->priv->session);
611 g_free (self->priv->default_user);
613 G_OBJECT_CLASS (greeter_parent_class)->finalize (object);
617 greeter_class_init (GreeterClass *klass)
619 GObjectClass *object_class = G_OBJECT_CLASS (klass);
621 object_class->finalize = greeter_finalize;
624 g_signal_new ("login",
625 G_TYPE_FROM_CLASS (klass),
627 G_STRUCT_OFFSET (GreeterClass, login),
629 ldm_marshal_VOID__STRING_STRING_STRING,
630 G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
631 signals[LOGIN_AS_GUEST] =
632 g_signal_new ("login-as-guest",
633 G_TYPE_FROM_CLASS (klass),
635 G_STRUCT_OFFSET (GreeterClass, login_as_guest),
637 ldm_marshal_VOID__STRING_STRING,
638 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
640 g_signal_new ("quit",
641 G_TYPE_FROM_CLASS (klass),
643 G_STRUCT_OFFSET (GreeterClass, quit),
645 g_cclosure_marshal_VOID__VOID,
648 g_type_class_add_private (klass, sizeof (GreeterPrivate));