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
16 #include "configuration.h"
17 #include "ldm-marshal.h"
18 #include "greeter-protocol.h"
19 #include "guest-account.h"
21 /* Length of time in milliseconds to wait for a greeter to quit */
22 #define GREETER_QUIT_TIMEOUT 1000
28 static guint signals[LAST_SIGNAL] = { 0 };
32 /* TRUE if the greeter has connected to the daemon pipe */
35 /* Pipe to communicate to greeter */
38 /* Buffer for data read from greeter */
42 /* Theme for greeter to use */
45 /* Default session to use */
46 gchar *default_session;
48 /* Default user to log in as, or NULL for no default */
51 /* Time in seconds to wait until logging in as default user */
52 gint autologin_timeout;
54 /* Timeout for greeter to respond to quit request */
57 /* Sequence number of current PAM session */
58 guint32 authentication_sequence_number;
60 /* PAM session being constructed by the greeter */
61 PAMSession *pam_session;
63 /* TRUE if logging into guest session */
64 gboolean using_guest_account;
67 G_DEFINE_TYPE (Greeter, greeter, SESSION_TYPE);
70 greeter_new (const gchar *theme)
72 Greeter *greeter = g_object_new (GREETER_TYPE, NULL);
74 greeter->priv->theme = g_strdup (theme);
80 greeter_set_default_user (Greeter *greeter, const gchar *username, gint timeout)
82 g_return_if_fail (greeter != NULL);
84 g_free (greeter->priv->default_user);
85 greeter->priv->default_user = g_strdup (username);
86 greeter->priv->autologin_timeout = timeout;
90 greeter_get_theme (Greeter *greeter)
92 g_return_val_if_fail (greeter != NULL, NULL);
93 return greeter->priv->theme;
97 greeter_set_default_session (Greeter *greeter, const gchar *session)
99 g_return_if_fail (greeter != NULL);
101 g_free (greeter->priv->default_session);
102 greeter->priv->default_session = g_strdup (session);
106 greeter_get_default_session (Greeter *greeter)
108 g_return_val_if_fail (greeter != NULL, NULL);
109 return greeter->priv->default_session;
113 greeter_get_pam_session (Greeter *greeter)
115 g_return_val_if_fail (greeter != NULL, NULL);
116 return greeter->priv->pam_session;
125 #define HEADER_SIZE (sizeof (guint32) * 2)
126 #define MAX_MESSAGE_LENGTH 1024
129 write_message (Greeter *greeter, guint8 *message, gint message_length)
131 GError *error = NULL;
132 g_debug ("Wrote %d bytes to greeter", message_length);
133 if (g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), (gchar *) message, message_length, NULL, &error) != G_IO_STATUS_NORMAL)
134 g_warning ("Error writing to greeter: %s", error->message);
135 g_clear_error (&error);
136 g_io_channel_flush (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), NULL);
140 write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
142 if (*offset + 4 >= buffer_length)
144 buffer[*offset] = value >> 24;
145 buffer[*offset+1] = (value >> 16) & 0xFF;
146 buffer[*offset+2] = (value >> 8) & 0xFF;
147 buffer[*offset+3] = value & 0xFF;
152 write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
154 gint length = strlen (value);
155 write_int (buffer, buffer_length, length, offset);
156 if (*offset + length >= buffer_length)
158 memcpy (buffer + *offset, value, length);
163 write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
165 write_int (buffer, buffer_length, id, offset);
166 write_int (buffer, buffer_length, length, offset);
170 string_length (const gchar *value)
172 return int_length () + strlen (value);
176 handle_connect (Greeter *greeter)
178 gchar *theme_dir, *theme;
179 guint8 message[MAX_MESSAGE_LENGTH];
182 if (!greeter->priv->connected)
184 greeter->priv->connected = TRUE;
185 g_debug ("Greeter connected");
188 theme_dir = config_get_string (config_get_instance (), "LightDM", "theme-directory");
189 theme = g_build_filename (theme_dir, greeter->priv->theme, "index.theme", NULL);
192 write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECTED, string_length (theme) + string_length (greeter->priv->default_session) + string_length (greeter->priv->default_user ? greeter->priv->default_user : "") + int_length () + int_length (), &offset);
193 write_string (message, MAX_MESSAGE_LENGTH, theme, &offset);
194 write_string (message, MAX_MESSAGE_LENGTH, greeter->priv->default_session, &offset);
195 write_string (message, MAX_MESSAGE_LENGTH, greeter->priv->default_user ? greeter->priv->default_user : "", &offset);
196 write_int (message, MAX_MESSAGE_LENGTH, greeter->priv->autologin_timeout, &offset);
197 write_int (message, MAX_MESSAGE_LENGTH, guest_account_get_is_enabled (), &offset);
198 write_message (greeter, message, offset);
204 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Greeter *greeter)
208 guint8 message[MAX_MESSAGE_LENGTH];
211 /* Respond to d-bus query with messages */
212 g_debug ("Prompt greeter with %d message(s)", num_msg);
213 size = int_length () + int_length ();
214 for (i = 0; i < num_msg; i++)
215 size += int_length () + string_length (msg[i]->msg);
216 write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_PROMPT_AUTHENTICATION, size, &offset);
217 write_int (message, MAX_MESSAGE_LENGTH, greeter->priv->authentication_sequence_number, &offset);
218 write_int (message, MAX_MESSAGE_LENGTH, num_msg, &offset);
219 for (i = 0; i < num_msg; i++)
221 write_int (message, MAX_MESSAGE_LENGTH, msg[i]->msg_style, &offset);
222 write_string (message, MAX_MESSAGE_LENGTH, msg[i]->msg, &offset);
224 write_message (greeter, message, offset);
228 send_end_authentication (Greeter *greeter, guint32 sequence_number, int result)
230 guint8 message[MAX_MESSAGE_LENGTH];
233 write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_END_AUTHENTICATION, int_length () + int_length (), &offset);
234 write_int (message, MAX_MESSAGE_LENGTH, sequence_number, &offset);
235 write_int (message, MAX_MESSAGE_LENGTH, result, &offset);
236 write_message (greeter, message, offset);
240 authentication_result_cb (PAMSession *session, int result, Greeter *greeter)
242 g_debug ("Authenticate result for user %s: %s", pam_session_get_username (greeter->priv->pam_session), pam_session_strerror (greeter->priv->pam_session, result));
244 if (result == PAM_SUCCESS)
246 g_debug ("User %s authorized", pam_session_get_username (session));
247 //run_script ("PostLogin");
248 pam_session_authorize (session);
251 send_end_authentication (greeter, greeter->priv->authentication_sequence_number, result);
255 reset_session (Greeter *greeter)
257 if (greeter->priv->pam_session == NULL)
260 g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
261 pam_session_end (greeter->priv->pam_session);
262 g_object_unref (greeter->priv->pam_session);
264 if (greeter->priv->using_guest_account)
265 guest_account_unref ();
266 greeter->priv->using_guest_account = FALSE;
270 start_authentication (Greeter *greeter, guint32 sequence_number, PAMSession *session)
272 GError *error = NULL;
275 //if (greeter->priv->user_session)
278 greeter->priv->pam_session = session;
279 greeter->priv->authentication_sequence_number = sequence_number;
281 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
282 g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authentication_result_cb), greeter);
284 if (!pam_session_start (greeter->priv->pam_session, &error))
286 g_warning ("Failed to start authentication: %s", error->message);
287 send_end_authentication (greeter, sequence_number, PAM_SYSTEM_ERR);
292 handle_login (Greeter *greeter, guint32 sequence_number, const gchar *username)
294 if (username[0] == '\0')
296 g_debug ("Greeter start authentication");
300 g_debug ("Greeter start authentication for %s", username);
302 reset_session (greeter);
303 greeter->priv->using_guest_account = FALSE;
304 start_authentication (greeter, sequence_number, pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username));
308 handle_login_as_guest (Greeter *greeter, guint32 sequence_number)
310 g_debug ("Greeter start authentication for guest account");
312 reset_session (greeter);
314 if (!guest_account_get_is_enabled ())
316 g_debug ("Guest account is disabled");
317 send_end_authentication (greeter, sequence_number, PAM_USER_UNKNOWN);
321 if (!guest_account_ref ())
323 g_debug ("Unable to create guest account");
324 send_end_authentication (greeter, sequence_number, PAM_USER_UNKNOWN);
327 greeter->priv->using_guest_account = TRUE;
329 start_authentication (greeter, sequence_number, pam_session_new ("lightdm-autologin"/*FIXMEgreeter->priv->pam_service*/, guest_account_get_username ()));
333 handle_continue_authentication (Greeter *greeter, gchar **secrets)
336 const struct pam_message **messages;
337 struct pam_response *response;
338 int i, j, n_secrets = 0;
341 if (!greeter->priv->connected)
344 /* Not in authorization */
345 if (greeter->priv->pam_session == NULL)
348 num_messages = pam_session_get_num_messages (greeter->priv->pam_session);
349 messages = pam_session_get_messages (greeter->priv->pam_session);
351 /* Check correct number of responses */
352 for (i = 0; i < num_messages; i++)
354 int msg_style = messages[i]->msg_style;
355 if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
358 if (g_strv_length (secrets) != n_secrets)
360 pam_session_end (greeter->priv->pam_session);
364 g_debug ("Continue authentication");
367 response = calloc (num_messages, sizeof (struct pam_response));
368 for (i = 0, j = 0; i < num_messages; i++)
370 int msg_style = messages[i]->msg_style;
371 if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
373 response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
378 pam_session_respond (greeter->priv->pam_session, response);
382 handle_cancel_authentication (Greeter *greeter)
385 if (!greeter->priv->connected)
388 /* Not in authorization */
389 if (greeter->priv->pam_session == NULL)
392 g_debug ("Cancel authentication");
394 pam_session_cancel (greeter->priv->pam_session);
398 greeter_select_user (Greeter *greeter, const gchar *username)
400 guint8 message[MAX_MESSAGE_LENGTH];
403 write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SELECT_USER, string_length (username), &offset);
404 write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
405 write_message (greeter, message, offset);
409 greeter_select_guest (Greeter *greeter)
411 guint8 message[MAX_MESSAGE_LENGTH];
414 write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SELECT_GUEST, 0, &offset);
415 write_message (greeter, message, offset);
419 quit_greeter_cb (gpointer data)
421 Greeter *greeter = data;
422 g_warning ("Greeter did not quit, sending kill signal");
423 session_stop (SESSION (greeter));
424 greeter->priv->quit_timeout = 0;
429 greeter_quit (Greeter *greeter)
431 guint8 message[MAX_MESSAGE_LENGTH];
434 g_return_if_fail (greeter != NULL);
436 write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_QUIT, 0, &offset);
437 write_message (greeter, message, offset);
439 if (greeter->priv->quit_timeout)
440 g_source_remove (greeter->priv->quit_timeout);
441 greeter->priv->quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, greeter);
445 handle_start_session (Greeter *greeter, gchar *session)
447 /*if (greeter->priv->user_session != NULL)
449 g_warning ("Ignoring request to log in when already logged in");
453 g_debug ("Greeter start session %s", session);
455 g_signal_emit (greeter, signals[START_SESSION], 0, session);
459 read_int (Greeter *greeter, gsize *offset)
463 if (greeter->priv->n_read - *offset < sizeof (guint32))
465 g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), greeter->priv->n_read - *offset);
468 buffer = greeter->priv->read_buffer + *offset;
469 value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
470 *offset += int_length ();
475 read_string (Greeter *greeter, gsize *offset)
480 length = read_int (greeter, offset);
481 if (greeter->priv->n_read - *offset < length)
483 g_warning ("Not enough space for string, need %u, got %zu", length, greeter->priv->n_read - *offset);
484 return g_strdup ("");
487 value = g_malloc (sizeof (gchar *) * (length + 1));
488 memcpy (value, greeter->priv->read_buffer + *offset, length);
489 value[length] = '\0';
496 got_data_cb (Greeter *greeter)
498 gsize n_to_read, n_read, offset;
500 int id, n_secrets, i;
501 guint32 sequence_number;
502 gchar *username, *session_name;
504 GError *error = NULL;
506 n_to_read = HEADER_SIZE;
507 if (greeter->priv->n_read >= HEADER_SIZE)
509 offset = int_length ();
510 n_to_read += read_int (greeter, &offset);
513 status = g_io_channel_read_chars (child_process_get_from_child_channel (CHILD_PROCESS (greeter)),
514 (gchar *) greeter->priv->read_buffer + greeter->priv->n_read,
515 n_to_read - greeter->priv->n_read,
518 if (status != G_IO_STATUS_NORMAL)
519 g_warning ("Error reading from greeter: %s", error->message);
520 g_clear_error (&error);
521 if (status != G_IO_STATUS_NORMAL)
524 g_debug ("Read %zi bytes from greeter", n_read);
525 /*for (i = 0; i < n_read; i++)
526 g_print ("%02X ", greeter->priv->read_buffer[greeter->priv->n_read+i]);
529 greeter->priv->n_read += n_read;
530 if (greeter->priv->n_read != n_to_read)
533 /* If have header, rerun for content */
534 if (greeter->priv->n_read == HEADER_SIZE)
536 n_to_read = ((guint32 *) greeter->priv->read_buffer)[1];
539 greeter->priv->read_buffer = g_realloc (greeter->priv->read_buffer, HEADER_SIZE + n_to_read);
540 got_data_cb (greeter);
546 id = read_int (greeter, &offset);
547 read_int (greeter, &offset);
550 case GREETER_MESSAGE_CONNECT:
551 handle_connect (greeter);
553 case GREETER_MESSAGE_LOGIN:
554 sequence_number = read_int (greeter, &offset);
555 username = read_string (greeter, &offset);
556 handle_login (greeter, sequence_number, username);
559 case GREETER_MESSAGE_LOGIN_AS_GUEST:
560 sequence_number = read_int (greeter, &offset);
561 handle_login_as_guest (greeter, sequence_number);
563 case GREETER_MESSAGE_CONTINUE_AUTHENTICATION:
564 n_secrets = read_int (greeter, &offset);
565 secrets = g_malloc (sizeof (gchar *) * (n_secrets + 1));
566 for (i = 0; i < n_secrets; i++)
567 secrets[i] = read_string (greeter, &offset);
569 handle_continue_authentication (greeter, secrets);
570 g_strfreev (secrets);
572 case GREETER_MESSAGE_CANCEL_AUTHENTICATION:
573 handle_cancel_authentication (greeter);
575 case GREETER_MESSAGE_START_SESSION:
576 session_name = read_string (greeter, &offset);
577 handle_start_session (greeter, session_name);
578 g_free (session_name);
581 g_warning ("Unknown message from greeter: %d", id);
585 greeter->priv->n_read = 0;
589 end_session (Greeter *greeter, gboolean clean_exit)
591 if (greeter->priv->quit_timeout)
593 g_source_remove (greeter->priv->quit_timeout);
594 greeter->priv->quit_timeout = 0;
598 g_warning ("Greeter failed");
599 else if (!greeter_connected)
600 g_warning ("Greeter quit before connecting");
601 else if (!display->priv->user_session)
602 g_warning ("Greeter quit before session started");
606 // FIXME: Issue with greeter, don't want to start a new one, report error to user
610 session_exited_cb (Greeter *greeter, gint status)
612 end_session (greeter, status == 0);
616 session_terminated_cb (Greeter *greeter, gint signum)
618 end_session (greeter, FALSE);
622 greeter_init (Greeter *greeter)
624 greeter->priv = G_TYPE_INSTANCE_GET_PRIVATE (greeter, GREETER_TYPE, GreeterPrivate);
625 greeter->priv->read_buffer = g_malloc (HEADER_SIZE);
626 g_signal_connect (G_OBJECT (greeter), "got-data", G_CALLBACK (got_data_cb), NULL);
627 g_signal_connect (G_OBJECT (greeter), "exited", G_CALLBACK (session_exited_cb), NULL);
628 g_signal_connect (G_OBJECT (greeter), "terminated", G_CALLBACK (session_terminated_cb), NULL);
632 greeter_finalize (GObject *object)
636 self = GREETER (object);
638 g_free (self->priv->read_buffer);
639 g_free (self->priv->theme);
640 g_free (self->priv->default_session);
641 g_free (self->priv->default_user);
642 if (self->priv->using_guest_account)
643 guest_account_unref ();
645 G_OBJECT_CLASS (greeter_parent_class)->finalize (object);
649 greeter_class_init (GreeterClass *klass)
651 GObjectClass *object_class = G_OBJECT_CLASS (klass);
653 object_class->finalize = greeter_finalize;
655 signals[START_SESSION] =
656 g_signal_new ("start-session",
657 G_TYPE_FROM_CLASS (klass),
659 G_STRUCT_OFFSET (GreeterClass, start_session),
661 g_cclosure_marshal_VOID__STRING,
662 G_TYPE_NONE, 1, G_TYPE_STRING);
664 g_type_class_add_private (klass, sizeof (GreeterPrivate));