]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/greeter.c
b001473288a8393822c362580997babeb3384c54
[sojka/lightdm.git] / src / greeter.c
1 /*
2  * Copyright (C) 2010-2011 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 <string.h>
14
15 #include "greeter.h"
16 #include "configuration.h"
17 #include "ldm-marshal.h"
18 #include "greeter-protocol.h"
19 #include "guest-account.h"
20
21 /* Length of time in milliseconds to wait for a greeter to quit */
22 #define GREETER_QUIT_TIMEOUT 1000
23
24 enum {
25     START_SESSION,
26     LAST_SIGNAL
27 };
28 static guint signals[LAST_SIGNAL] = { 0 };
29
30 struct GreeterPrivate
31 {
32     /* TRUE if the greeter has connected to the daemon pipe */
33     gboolean connected;
34
35     /* Pipe to communicate to greeter */
36     int pipe[2];
37
38     /* Buffer for data read from greeter */
39     guint8 *read_buffer;
40     gsize n_read;
41
42     /* Theme for greeter to use */
43     gchar *theme;
44
45     /* Default session to use */   
46     gchar *default_session;
47
48     /* Default user to log in as, or NULL for no default */
49     gchar *default_user;
50
51     /* Time in seconds to wait until logging in as default user */
52     gint autologin_timeout;
53
54     /* Timeout for greeter to respond to quit request */
55     guint quit_timeout;
56
57     /* Sequence number of current PAM session */
58     guint32 authentication_sequence_number;
59
60     /* PAM session being constructed by the greeter */
61     PAMSession *pam_session;
62
63     /* TRUE if logging into guest session */
64     gboolean using_guest_account;
65 };
66
67 G_DEFINE_TYPE (Greeter, greeter, SESSION_TYPE);
68
69 Greeter *
70 greeter_new (const gchar *theme)
71 {
72     Greeter *greeter = g_object_new (GREETER_TYPE, NULL);
73
74     greeter->priv->theme = g_strdup (theme);
75
76     return greeter;
77 }
78
79 void
80 greeter_set_default_user (Greeter *greeter, const gchar *username, gint timeout)
81 {
82     g_return_if_fail (greeter != NULL);
83
84     g_free (greeter->priv->default_user);
85     greeter->priv->default_user = g_strdup (username);
86     greeter->priv->autologin_timeout = timeout;
87 }
88
89 const gchar *
90 greeter_get_theme (Greeter *greeter)
91 {
92     g_return_val_if_fail (greeter != NULL, NULL);
93     return greeter->priv->theme;
94 }
95
96 void
97 greeter_set_default_session (Greeter *greeter, const gchar *session)
98 {
99     g_return_if_fail (greeter != NULL);
100
101     g_free (greeter->priv->default_session);
102     greeter->priv->default_session = g_strdup (session);
103 }
104
105 const gchar *
106 greeter_get_default_session (Greeter *greeter)
107 {
108     g_return_val_if_fail (greeter != NULL, NULL);
109     return greeter->priv->default_session;
110 }
111
112 PAMSession *
113 greeter_get_pam_session (Greeter *greeter)
114 {
115     g_return_val_if_fail (greeter != NULL, NULL);
116     return greeter->priv->pam_session;
117 }
118
119 static guint32
120 int_length ()
121 {
122     return 4;
123 }
124
125 #define HEADER_SIZE (sizeof (guint32) * 2)
126 #define MAX_MESSAGE_LENGTH 1024
127
128 static void
129 write_message (Greeter *greeter, guint8 *message, gint message_length)
130 {
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);
137 }
138
139 static void
140 write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
141 {
142     if (*offset + 4 >= buffer_length)
143         return;
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;
148     *offset += 4;
149 }
150
151 static void
152 write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
153 {
154     gint length = strlen (value);
155     write_int (buffer, buffer_length, length, offset);
156     if (*offset + length >= buffer_length)
157         return;
158     memcpy (buffer + *offset, value, length);
159     *offset += length;
160 }
161
162 static void
163 write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
164 {
165     write_int (buffer, buffer_length, id, offset);
166     write_int (buffer, buffer_length, length, offset);
167 }
168
169 static guint32
170 string_length (const gchar *value)
171 {
172     return int_length () + strlen (value);
173 }
174
175 static void
176 handle_connect (Greeter *greeter)
177 {
178     gchar *theme_dir, *theme;
179     guint8 message[MAX_MESSAGE_LENGTH];
180     gsize offset = 0;
181
182     if (!greeter->priv->connected)
183     {
184         greeter->priv->connected = TRUE;
185         g_debug ("Greeter connected");
186     }
187
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);
190     g_free (theme_dir);
191
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);
199
200     g_free (theme);
201 }
202
203 static void
204 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Greeter *greeter)
205 {
206     int i;
207     guint32 size;
208     guint8 message[MAX_MESSAGE_LENGTH];
209     gsize offset = 0;
210
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++)
220     {
221         write_int (message, MAX_MESSAGE_LENGTH, msg[i]->msg_style, &offset);
222         write_string (message, MAX_MESSAGE_LENGTH, msg[i]->msg, &offset);
223     }
224     write_message (greeter, message, offset);  
225 }
226
227 static void
228 send_end_authentication (Greeter *greeter, guint32 sequence_number, int result)
229 {
230     guint8 message[MAX_MESSAGE_LENGTH];
231     gsize offset = 0;
232
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); 
237 }
238
239 static void
240 authentication_result_cb (PAMSession *session, int result, Greeter *greeter)
241 {
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));
243
244     if (result == PAM_SUCCESS)
245     {
246         g_debug ("User %s authorized", pam_session_get_username (session));
247         //run_script ("PostLogin");
248         pam_session_authorize (session);
249     }
250
251     send_end_authentication (greeter, greeter->priv->authentication_sequence_number, result);
252 }
253
254 static void
255 reset_session (Greeter *greeter)
256 {
257     if (greeter->priv->pam_session == NULL)
258         return;
259
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);
263
264     if (greeter->priv->using_guest_account)
265         guest_account_unref ();
266     greeter->priv->using_guest_account = FALSE;
267 }
268
269 static void
270 start_authentication (Greeter *greeter, guint32 sequence_number, PAMSession *session)
271 {
272     GError *error = NULL;
273
274     // FIXME
275     //if (greeter->priv->user_session)
276     //    return;
277
278     greeter->priv->pam_session = session;
279     greeter->priv->authentication_sequence_number = sequence_number;
280
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);
283
284     if (!pam_session_start (greeter->priv->pam_session, &error))
285     {
286         g_warning ("Failed to start authentication: %s", error->message);
287         send_end_authentication (greeter, sequence_number, PAM_SYSTEM_ERR);
288     }
289 }
290
291 static void
292 handle_login (Greeter *greeter, guint32 sequence_number, const gchar *username)
293 {
294     if (username[0] == '\0')
295     {
296         g_debug ("Greeter start authentication");
297         username = NULL;
298     }
299     else
300         g_debug ("Greeter start authentication for %s", username);        
301
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));
305 }
306
307 static void
308 handle_login_as_guest (Greeter *greeter, guint32 sequence_number)
309 {
310     g_debug ("Greeter start authentication for guest account");
311
312     reset_session (greeter);
313
314     if (!guest_account_get_is_enabled ())
315     {
316         g_debug ("Guest account is disabled");
317         send_end_authentication (greeter, sequence_number, PAM_USER_UNKNOWN);
318         return;
319     }
320
321     if (!guest_account_ref ())
322     {
323         g_debug ("Unable to create guest account");
324         send_end_authentication (greeter, sequence_number, PAM_USER_UNKNOWN);
325         return;
326     }
327     greeter->priv->using_guest_account = TRUE;
328
329     start_authentication (greeter, sequence_number, pam_session_new ("lightdm-autologin"/*FIXMEgreeter->priv->pam_service*/, guest_account_get_username ()));
330 }
331
332 static void
333 handle_continue_authentication (Greeter *greeter, gchar **secrets)
334 {
335     int num_messages;
336     const struct pam_message **messages;
337     struct pam_response *response;
338     int i, j, n_secrets = 0;
339
340     /* Not connected */
341     if (!greeter->priv->connected)
342         return;
343
344     /* Not in authorization */
345     if (greeter->priv->pam_session == NULL)
346         return;
347
348     num_messages = pam_session_get_num_messages (greeter->priv->pam_session);
349     messages = pam_session_get_messages (greeter->priv->pam_session);
350
351     /* Check correct number of responses */
352     for (i = 0; i < num_messages; i++)
353     {
354         int msg_style = messages[i]->msg_style;
355         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
356             n_secrets++;
357     }
358     if (g_strv_length (secrets) != n_secrets)
359     {
360         pam_session_end (greeter->priv->pam_session);
361         return;
362     }
363
364     g_debug ("Continue authentication");
365
366     /* Build response */
367     response = calloc (num_messages, sizeof (struct pam_response));  
368     for (i = 0, j = 0; i < num_messages; i++)
369     {
370         int msg_style = messages[i]->msg_style;
371         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
372         {
373             response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
374             j++;
375         }
376     }
377
378     pam_session_respond (greeter->priv->pam_session, response);
379 }
380
381 static void
382 handle_cancel_authentication (Greeter *greeter)
383 {
384     /* Not connected */
385     if (!greeter->priv->connected)
386         return;
387
388     /* Not in authorization */
389     if (greeter->priv->pam_session == NULL)
390         return;
391
392     g_debug ("Cancel authentication");
393
394     pam_session_cancel (greeter->priv->pam_session);
395 }
396
397 void
398 greeter_select_user (Greeter *greeter, const gchar *username)
399 {
400     guint8 message[MAX_MESSAGE_LENGTH];
401     gsize offset = 0;
402
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);
406 }
407
408 void
409 greeter_select_guest (Greeter *greeter)
410 {
411     guint8 message[MAX_MESSAGE_LENGTH];
412     gsize offset = 0;
413
414     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SELECT_GUEST, 0, &offset);
415     write_message (greeter, message, offset);
416 }
417
418 static gboolean
419 quit_greeter_cb (gpointer data)
420 {
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;
425     return TRUE;
426 }
427
428 void
429 greeter_quit (Greeter *greeter)
430 {
431     guint8 message[MAX_MESSAGE_LENGTH];
432     gsize offset = 0;
433
434     g_return_if_fail (greeter != NULL);
435
436     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_QUIT, 0, &offset);
437     write_message (greeter, message, offset);
438
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);
442 }
443
444 static void
445 handle_start_session (Greeter *greeter, gchar *session)
446 {
447     /*if (greeter->priv->user_session != NULL)
448     {
449         g_warning ("Ignoring request to log in when already logged in");
450         return;
451     }*/
452
453     g_debug ("Greeter start session %s", session);
454
455     g_signal_emit (greeter, signals[START_SESSION], 0, session);
456 }
457
458 static guint32
459 read_int (Greeter *greeter, gsize *offset)
460 {
461     guint32 value;
462     guint8 *buffer;
463     if (greeter->priv->n_read - *offset < sizeof (guint32))
464     {
465         g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), greeter->priv->n_read - *offset);
466         return 0;
467     }
468     buffer = greeter->priv->read_buffer + *offset;
469     value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
470     *offset += int_length ();
471     return value;
472 }
473
474 static gchar *
475 read_string (Greeter *greeter, gsize *offset)
476 {
477     guint32 length;
478     gchar *value;
479
480     length = read_int (greeter, offset);
481     if (greeter->priv->n_read - *offset < length)
482     {
483         g_warning ("Not enough space for string, need %u, got %zu", length, greeter->priv->n_read - *offset);
484         return g_strdup ("");
485     }
486
487     value = g_malloc (sizeof (gchar *) * (length + 1));
488     memcpy (value, greeter->priv->read_buffer + *offset, length);
489     value[length] = '\0';
490     *offset += length;
491
492     return value;
493 }
494
495 static void
496 got_data_cb (Greeter *greeter)
497 {
498     gsize n_to_read, n_read, offset;
499     GIOStatus status;
500     int id, n_secrets, i;
501     guint32 sequence_number;
502     gchar *username, *session_name;
503     gchar **secrets;
504     GError *error = NULL;
505   
506     n_to_read = HEADER_SIZE;
507     if (greeter->priv->n_read >= HEADER_SIZE)
508     {
509         offset = int_length ();
510         n_to_read += read_int (greeter, &offset);
511     }
512
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,
516                                       &n_read,
517                                       &error);
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)
522         return;
523
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]);
527     g_print ("\n");*/
528
529     greeter->priv->n_read += n_read;
530     if (greeter->priv->n_read != n_to_read)
531         return;
532
533     /* If have header, rerun for content */
534     if (greeter->priv->n_read == HEADER_SIZE)
535     {
536         n_to_read = ((guint32 *) greeter->priv->read_buffer)[1];
537         if (n_to_read > 0)
538         {
539             greeter->priv->read_buffer = g_realloc (greeter->priv->read_buffer, HEADER_SIZE + n_to_read);
540             got_data_cb (greeter);
541             return;
542         }
543     }
544   
545     offset = 0;
546     id = read_int (greeter, &offset);
547     read_int (greeter, &offset);
548     switch (id)
549     {
550     case GREETER_MESSAGE_CONNECT:
551         handle_connect (greeter);
552         break;
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);
557         g_free (username);
558         break;
559     case GREETER_MESSAGE_LOGIN_AS_GUEST:
560         sequence_number = read_int (greeter, &offset);
561         handle_login_as_guest (greeter, sequence_number);
562         break;
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);
568         secrets[i] = NULL;
569         handle_continue_authentication (greeter, secrets);
570         g_strfreev (secrets);
571         break;
572     case GREETER_MESSAGE_CANCEL_AUTHENTICATION:
573         handle_cancel_authentication (greeter);
574         break;
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);
579         break;
580     default:
581         g_warning ("Unknown message from greeter: %d", id);
582         break;
583     }
584
585     greeter->priv->n_read = 0;
586 }
587
588 static void
589 end_session (Greeter *greeter, gboolean clean_exit)
590 {  
591     if (greeter->priv->quit_timeout)
592     {
593         g_source_remove (greeter->priv->quit_timeout);
594         greeter->priv->quit_timeout = 0;
595     }
596
597     /*if (!clean_exit)
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");
603     else
604         return;*/
605
606     // FIXME: Issue with greeter, don't want to start a new one, report error to user
607 }
608
609 static void
610 session_exited_cb (Greeter *greeter, gint status)
611 {
612     end_session (greeter, status == 0);
613 }
614
615 static void
616 session_terminated_cb (Greeter *greeter, gint signum)
617 {
618     end_session (greeter, FALSE);
619 }
620
621 static void
622 greeter_init (Greeter *greeter)
623 {
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);
629 }
630
631 static void
632 greeter_finalize (GObject *object)
633 {
634     Greeter *self;
635
636     self = GREETER (object);
637
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 ();
644
645     G_OBJECT_CLASS (greeter_parent_class)->finalize (object);
646 }
647
648 static void
649 greeter_class_init (GreeterClass *klass)
650 {
651     GObjectClass *object_class = G_OBJECT_CLASS (klass);
652
653     object_class->finalize = greeter_finalize;
654
655     signals[START_SESSION] =
656         g_signal_new ("start-session",
657                       G_TYPE_FROM_CLASS (klass),
658                       G_SIGNAL_RUN_LAST,
659                       G_STRUCT_OFFSET (GreeterClass, start_session),
660                       NULL, NULL,
661                       g_cclosure_marshal_VOID__STRING,
662                       G_TYPE_NONE, 1, G_TYPE_STRING);
663
664     g_type_class_add_private (klass, sizeof (GreeterPrivate));
665 }