]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/greeter.c
More refactoring...
[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 <config.h>
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17
18 #include "greeter.h"
19 #include "ldm-marshal.h"
20
21 enum {
22     START_AUTHENTICATION,
23     START_SESSION,
24     LAST_SIGNAL
25 };
26 static guint signals[LAST_SIGNAL] = { 0 };
27
28 struct GreeterPrivate
29 {
30     /* Session running on */
31     Session *session;
32
33     /* Buffer for data read from greeter */
34     guint8 *read_buffer;
35     gsize n_read;
36   
37     /* Hints for the greeter */
38     GHashTable *hints;
39
40     /* Default session to use */   
41     gchar *default_session;
42
43     /* Sequence number of current PAM session */
44     guint32 authentication_sequence_number;
45
46     /* PAM session being constructed by the greeter */
47     PAMSession *authentication;
48
49     /* TRUE if can log into guest accounts */
50     gboolean allow_guest;
51
52     /* TRUE if logging into guest session */
53     gboolean guest_account_authenticated;
54
55     /* Communication channels to communicate with */
56     GIOChannel *to_greeter_channel;
57     GIOChannel *from_greeter_channel;
58 };
59
60 G_DEFINE_TYPE (Greeter, greeter, G_TYPE_OBJECT);
61
62 /* Messages from the greeter to the server */
63 typedef enum
64 {
65     GREETER_MESSAGE_CONNECT = 0,
66     GREETER_MESSAGE_AUTHENTICATE,
67     GREETER_MESSAGE_AUTHENTICATE_AS_GUEST,
68     GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
69     GREETER_MESSAGE_START_SESSION,
70     GREETER_MESSAGE_CANCEL_AUTHENTICATION
71 } GreeterMessage;
72
73 /* Messages from the server to the greeter */
74 typedef enum
75 {
76     SERVER_MESSAGE_CONNECTED = 0,
77     SERVER_MESSAGE_PROMPT_AUTHENTICATION,
78     SERVER_MESSAGE_END_AUTHENTICATION,
79     SERVER_MESSAGE_SESSION_RESULT
80 } ServerMessage;
81
82 Greeter *
83 greeter_new (Session *session)
84 {
85     Greeter *greeter = g_object_new (GREETER_TYPE, NULL);
86
87     greeter->priv->session = g_object_ref (session);
88
89     return greeter;
90 }
91
92 void
93 greeter_set_allow_guest (Greeter *greeter, gboolean allow_guest)
94 {
95     greeter->priv->allow_guest = allow_guest;
96 }
97
98 void
99 greeter_set_hint (Greeter *greeter, const gchar *name, const gchar *value)
100 {
101     g_hash_table_insert (greeter->priv->hints, g_strdup (name), g_strdup (value));
102 }
103
104 static guint32
105 int_length ()
106 {
107     return 4;
108 }
109
110 #define HEADER_SIZE (sizeof (guint32) * 2)
111 #define MAX_MESSAGE_LENGTH 1024
112
113 static void
114 write_message (Greeter *greeter, guint8 *message, gsize message_length)
115 {
116     GError *error = NULL;
117     if (g_io_channel_write_chars (greeter->priv->to_greeter_channel, (gchar *) message, message_length, NULL, &error) != G_IO_STATUS_NORMAL)
118         g_warning ("Error writing to greeter: %s", error->message);
119     else
120         g_debug ("Wrote %zi bytes to greeter", message_length);
121     g_clear_error (&error);
122     g_io_channel_flush (greeter->priv->to_greeter_channel, NULL);
123 }
124
125 static void
126 write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
127 {
128     if (*offset + 4 >= buffer_length)
129         return;
130     buffer[*offset] = value >> 24;
131     buffer[*offset+1] = (value >> 16) & 0xFF;
132     buffer[*offset+2] = (value >> 8) & 0xFF;
133     buffer[*offset+3] = value & 0xFF;
134     *offset += 4;
135 }
136
137 static void
138 write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
139 {
140     gint length;
141   
142     if (value)
143         length = strlen (value);
144     else
145         length = 0;
146     write_int (buffer, buffer_length, length, offset);
147     if (*offset + length >= buffer_length)
148         return;
149     if (length > 0)
150     {
151         memcpy (buffer + *offset, value, length);
152         *offset += length;
153     }
154 }
155
156 static void
157 write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
158 {
159     write_int (buffer, buffer_length, id, offset);
160     write_int (buffer, buffer_length, length, offset);
161 }
162
163 static guint32
164 string_length (const gchar *value)
165 {
166     if (value == NULL)
167         return int_length ();
168     else
169         return int_length () + strlen (value);
170 }
171
172 static void
173 handle_connect (Greeter *greeter, const gchar *version)
174 {
175     guint8 message[MAX_MESSAGE_LENGTH];
176     gsize offset = 0;
177     guint32 length;
178     GHashTableIter iter;
179     gpointer key, value;
180
181     g_debug ("Greeter connected version=%s", version);
182
183     length = string_length (VERSION);
184     g_hash_table_iter_init (&iter, greeter->priv->hints);
185     while (g_hash_table_iter_next (&iter, &key, &value))
186         length += string_length (key) + string_length (value);
187
188     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_CONNECTED, length, &offset);
189     write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
190     g_hash_table_iter_init (&iter, greeter->priv->hints);
191     while (g_hash_table_iter_next (&iter, &key, &value))
192     {
193         write_string (message, MAX_MESSAGE_LENGTH, key, &offset);
194         write_string (message, MAX_MESSAGE_LENGTH, value, &offset);
195     }
196     write_message (greeter, message, offset);
197 }
198
199 static void
200 pam_messages_cb (PAMSession *authentication, int num_msg, const struct pam_message **msg, Greeter *greeter)
201 {
202     int i;
203     guint32 size;
204     guint8 message[MAX_MESSAGE_LENGTH];
205     gsize offset = 0;
206
207     /* Respond to d-bus query with messages */
208     g_debug ("Prompt greeter with %d message(s)", num_msg);
209     size = int_length () + string_length (pam_session_get_username (authentication)) + int_length ();
210     for (i = 0; i < num_msg; i++)
211         size += int_length () + string_length (msg[i]->msg);
212   
213     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_PROMPT_AUTHENTICATION, size, &offset);
214     write_int (message, MAX_MESSAGE_LENGTH, greeter->priv->authentication_sequence_number, &offset);
215     write_string (message, MAX_MESSAGE_LENGTH, pam_session_get_username (authentication), &offset);
216     write_int (message, MAX_MESSAGE_LENGTH, num_msg, &offset);
217     for (i = 0; i < num_msg; i++)
218     {
219         write_int (message, MAX_MESSAGE_LENGTH, msg[i]->msg_style, &offset);
220         write_string (message, MAX_MESSAGE_LENGTH, msg[i]->msg, &offset);
221     }
222     write_message (greeter, message, offset);  
223 }
224
225 static void
226 send_end_authentication (Greeter *greeter, guint32 sequence_number, const gchar *username, int result)
227 {
228     guint8 message[MAX_MESSAGE_LENGTH];
229     gsize offset = 0;
230
231     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_END_AUTHENTICATION, int_length () + string_length (username) + int_length (), &offset);
232     write_int (message, MAX_MESSAGE_LENGTH, sequence_number, &offset);
233     write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
234     write_int (message, MAX_MESSAGE_LENGTH, result, &offset);
235     write_message (greeter, message, offset); 
236 }
237
238 static void
239 authentication_result_cb (PAMSession *authentication, int result, Greeter *greeter)
240 {
241     g_debug ("Authenticate result for user %s: %s", pam_session_get_username (authentication), pam_session_strerror (authentication, result));
242
243     if (result == PAM_SUCCESS)
244         g_debug ("User %s authorized", pam_session_get_username (authentication));
245
246     send_end_authentication (greeter, greeter->priv->authentication_sequence_number, pam_session_get_username (authentication), result);
247 }
248
249 static void
250 reset_session (Greeter *greeter)
251 {
252     if (greeter->priv->authentication)
253     {
254         g_signal_handlers_disconnect_matched (greeter->priv->authentication, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
255         pam_session_cancel (greeter->priv->authentication);
256         g_object_unref (greeter->priv->authentication);
257         greeter->priv->authentication = NULL;
258     }
259
260     greeter->priv->guest_account_authenticated = FALSE;
261 }
262
263 static void
264 handle_login (Greeter *greeter, guint32 sequence_number, const gchar *username)
265 {
266     GError *error = NULL;
267
268     if (username[0] == '\0')
269     {
270         g_debug ("Greeter start authentication");
271         username = NULL;
272     }
273     else
274         g_debug ("Greeter start authentication for %s", username);
275
276     reset_session (greeter);
277
278     greeter->priv->authentication_sequence_number = sequence_number;
279     g_signal_emit (greeter, signals[START_AUTHENTICATION], 0, username, &greeter->priv->authentication);
280     if (!greeter->priv->authentication)
281     {
282         send_end_authentication (greeter, sequence_number, "", PAM_USER_UNKNOWN);
283         return;
284     }
285
286     g_signal_connect (G_OBJECT (greeter->priv->authentication), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
287     g_signal_connect (G_OBJECT (greeter->priv->authentication), "authentication-result", G_CALLBACK (authentication_result_cb), greeter);
288     if (!pam_session_authenticate (greeter->priv->authentication, &error))
289     {
290         g_debug ("Failed to start authentication: %s", error->message);
291         send_end_authentication (greeter, sequence_number, "", PAM_SYSTEM_ERR);
292     }
293     g_clear_error (&error);
294 }
295
296 static void
297 handle_login_as_guest (Greeter *greeter, guint32 sequence_number)
298 {
299     g_debug ("Greeter start authentication for guest account");
300
301     reset_session (greeter);
302
303     if (!greeter->priv->allow_guest)
304     {
305         g_debug ("Guest account is disabled");
306         send_end_authentication (greeter, sequence_number, "", PAM_USER_UNKNOWN);
307         return;
308     }
309
310     greeter->priv->guest_account_authenticated = TRUE;  
311     send_end_authentication (greeter, sequence_number, "", PAM_SUCCESS);
312 }
313
314 static void
315 handle_continue_authentication (Greeter *greeter, gchar **secrets)
316 {
317     int num_messages;
318     const struct pam_message **messages;
319     struct pam_response *response;
320     int i, j, n_secrets = 0;
321
322     /* Not in authentication */
323     if (greeter->priv->authentication == NULL)
324         return;
325
326     num_messages = pam_session_get_num_messages (greeter->priv->authentication);
327     messages = pam_session_get_messages (greeter->priv->authentication);
328
329     /* Check correct number of responses */
330     for (i = 0; i < num_messages; i++)
331     {
332         int msg_style = messages[i]->msg_style;
333         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
334             n_secrets++;
335     }
336     if (g_strv_length (secrets) != n_secrets)
337     {
338         pam_session_cancel (greeter->priv->authentication);
339         return;
340     }
341
342     g_debug ("Continue authentication");
343
344     /* Build response */
345     response = calloc (num_messages, sizeof (struct pam_response));  
346     for (i = 0, j = 0; i < num_messages; i++)
347     {
348         int msg_style = messages[i]->msg_style;
349         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
350         {
351             response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
352             j++;
353         }
354     }
355
356     pam_session_respond (greeter->priv->authentication, response);
357 }
358
359 static void
360 handle_cancel_authentication (Greeter *greeter)
361 {
362     /* Not in authentication */
363     if (greeter->priv->authentication == NULL)
364         return;
365
366     g_debug ("Cancel authentication");
367
368     pam_session_cancel (greeter->priv->authentication);
369 }
370
371 static void
372 handle_start_session (Greeter *greeter, const gchar *session)
373 {
374     gboolean result;
375
376     if (strcmp (session, "") == 0)
377         session = NULL;
378
379     if (session)
380         g_debug ("Start session %s", session);
381     else
382         g_debug ("Start default session");
383
384     if (greeter->priv->guest_account_authenticated || pam_session_get_is_authenticated (greeter->priv->authentication))
385         g_signal_emit (greeter, signals[START_SESSION], 0, session, &result);
386     else
387     {
388         g_debug ("Ignoring start session request, user is not authorized");
389         result = FALSE;
390     }
391
392     if (!result)
393     {
394         guint8 message[MAX_MESSAGE_LENGTH];
395         gsize offset = 0;
396
397         write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_SESSION_RESULT, int_length (), &offset);
398         write_int (message, MAX_MESSAGE_LENGTH, 1, &offset);
399         write_message (greeter, message, offset);
400     }
401 }
402
403 static guint32
404 read_int (Greeter *greeter, gsize *offset)
405 {
406     guint32 value;
407     guint8 *buffer;
408     if (greeter->priv->n_read - *offset < sizeof (guint32))
409     {
410         g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), greeter->priv->n_read - *offset);
411         return 0;
412     }
413     buffer = greeter->priv->read_buffer + *offset;
414     value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
415     *offset += int_length ();
416     return value;
417 }
418
419 static gchar *
420 read_string (Greeter *greeter, gsize *offset)
421 {
422     guint32 length;
423     gchar *value;
424
425     length = read_int (greeter, offset);
426     if (greeter->priv->n_read - *offset < length)
427     {
428         g_warning ("Not enough space for string, need %u, got %zu", length, greeter->priv->n_read - *offset);
429         return g_strdup ("");
430     }
431
432     value = g_malloc (sizeof (gchar *) * (length + 1));
433     memcpy (value, greeter->priv->read_buffer + *offset, length);
434     value[length] = '\0';
435     *offset += length;
436
437     return value;
438 }
439
440 static gboolean
441 read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
442 {
443     Greeter *greeter = data;
444     gsize n_to_read, n_read, offset;
445     GIOStatus status;
446     int id, n_secrets, i;
447     guint32 sequence_number;
448     gchar *version, *username, *session_name;
449     gchar **secrets;
450     GError *error = NULL;
451
452     if (condition == G_IO_HUP)
453     {
454         g_debug ("Greeter closed communication channel");
455         return FALSE;
456     }
457   
458     n_to_read = HEADER_SIZE;
459     if (greeter->priv->n_read >= HEADER_SIZE)
460     {
461         offset = int_length ();
462         n_to_read += read_int (greeter, &offset);
463     }
464
465     status = g_io_channel_read_chars (greeter->priv->from_greeter_channel,
466                                       (gchar *) greeter->priv->read_buffer + greeter->priv->n_read,
467                                       n_to_read - greeter->priv->n_read,
468                                       &n_read,
469                                       &error);
470     if (status != G_IO_STATUS_NORMAL)
471         g_warning ("Error reading from greeter: %s", error->message);
472     g_clear_error (&error);
473     if (status != G_IO_STATUS_NORMAL)
474         return TRUE;
475
476     g_debug ("Read %zi bytes from greeter", n_read);
477     /*for (i = 0; i < n_read; i++)
478        g_print ("%02X ", greeter->priv->read_buffer[greeter->priv->n_read+i]);
479     g_print ("\n");*/
480
481     greeter->priv->n_read += n_read;
482     if (greeter->priv->n_read != n_to_read)
483         return TRUE;
484
485     /* If have header, rerun for content */
486     if (greeter->priv->n_read == HEADER_SIZE)
487     {
488         n_to_read = ((guint32 *) greeter->priv->read_buffer)[1];
489         if (n_to_read > 0)
490         {
491             greeter->priv->read_buffer = g_realloc (greeter->priv->read_buffer, HEADER_SIZE + n_to_read);
492             read_cb (source, condition, greeter);
493             return TRUE;
494         }
495     }
496   
497     offset = 0;
498     id = read_int (greeter, &offset);
499     read_int (greeter, &offset);
500     switch (id)
501     {
502     case GREETER_MESSAGE_CONNECT:
503         version = read_string (greeter, &offset);
504         handle_connect (greeter, version);
505         g_free (version);
506         break;
507     case GREETER_MESSAGE_AUTHENTICATE:
508         sequence_number = read_int (greeter, &offset);
509         username = read_string (greeter, &offset);
510         handle_login (greeter, sequence_number, username);
511         g_free (username);
512         break;
513     case GREETER_MESSAGE_AUTHENTICATE_AS_GUEST:
514         sequence_number = read_int (greeter, &offset);
515         handle_login_as_guest (greeter, sequence_number);
516         break;
517     case GREETER_MESSAGE_CONTINUE_AUTHENTICATION:
518         n_secrets = read_int (greeter, &offset);
519         secrets = g_malloc (sizeof (gchar *) * (n_secrets + 1));
520         for (i = 0; i < n_secrets; i++)
521             secrets[i] = read_string (greeter, &offset);
522         secrets[i] = NULL;
523         handle_continue_authentication (greeter, secrets);
524         g_strfreev (secrets);
525         break;
526     case GREETER_MESSAGE_CANCEL_AUTHENTICATION:
527         handle_cancel_authentication (greeter);
528         break;
529     case GREETER_MESSAGE_START_SESSION:
530         session_name = read_string (greeter, &offset);
531         handle_start_session (greeter, session_name);
532         g_free (session_name);
533         break;
534     default:
535         g_warning ("Unknown message from greeter: %d", id);
536         break;
537     }
538
539     greeter->priv->n_read = 0;
540
541     return TRUE;
542 }
543
544 gboolean
545 greeter_start (Greeter *greeter)
546 {
547     int to_greeter_pipe[2], from_greeter_pipe[2];
548     gint fd;
549     gchar *value;
550
551     if (pipe (to_greeter_pipe) != 0 || 
552         pipe (from_greeter_pipe) != 0)
553     {
554         g_warning ("Failed to create pipes: %s", strerror (errno));            
555         return FALSE;
556     }
557
558     greeter->priv->to_greeter_channel = g_io_channel_unix_new (to_greeter_pipe[1]);
559     g_io_channel_set_encoding (greeter->priv->to_greeter_channel, NULL, NULL);
560     greeter->priv->from_greeter_channel = g_io_channel_unix_new (from_greeter_pipe[0]);
561     g_io_channel_set_encoding (greeter->priv->from_greeter_channel, NULL, NULL);
562     g_io_channel_set_buffered (greeter->priv->from_greeter_channel, FALSE);
563     g_io_add_watch (greeter->priv->from_greeter_channel, G_IO_IN | G_IO_HUP, read_cb, greeter);
564
565     fd = from_greeter_pipe[1];
566     value = g_strdup_printf ("%d", fd);
567     process_set_env (PROCESS (greeter->priv->session), "LIGHTDM_TO_SERVER_FD", value);
568     g_free (value);
569
570     fd = to_greeter_pipe[0];
571     value = g_strdup_printf ("%d", fd);
572     process_set_env (PROCESS (greeter->priv->session), "LIGHTDM_FROM_SERVER_FD", value);
573     g_free (value);
574
575     return TRUE;
576 }
577
578 gboolean
579 greeter_get_guest_authenticated (Greeter *greeter)
580 {
581     g_return_val_if_fail (greeter != NULL, FALSE);
582     return greeter->priv->guest_account_authenticated;
583 }
584
585 PAMSession *
586 greeter_get_authentication (Greeter *greeter)
587 {
588     g_return_val_if_fail (greeter != NULL, NULL);
589     return greeter->priv->authentication;
590 }
591
592 void
593 greeter_quit (Greeter *greeter)
594 {
595     guint8 message[MAX_MESSAGE_LENGTH];
596     gsize offset = 0;
597
598     g_return_if_fail (greeter != NULL);
599
600     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_SESSION_RESULT, int_length (), &offset);
601     write_int (message, MAX_MESSAGE_LENGTH, 0, &offset);
602     write_message (greeter, message, offset);
603 }
604
605 static PAMSession *
606 greeter_real_start_authentication (Greeter *greeter, const gchar *username)
607 {
608     return NULL;
609 }
610
611 static gboolean
612 greeter_real_start_session (Greeter *greeter, const gchar *session, gboolean is_guest)
613 {
614     return FALSE;
615 }
616
617 static void
618 greeter_init (Greeter *greeter)
619 {
620     greeter->priv = G_TYPE_INSTANCE_GET_PRIVATE (greeter, GREETER_TYPE, GreeterPrivate);
621     greeter->priv->read_buffer = g_malloc (HEADER_SIZE);
622     greeter->priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
623 }
624
625 static void
626 greeter_finalize (GObject *object)
627 {
628     Greeter *self;
629
630     self = GREETER (object);
631
632     g_object_unref (self->priv->session);
633     g_free (self->priv->read_buffer);
634     g_hash_table_unref (self->priv->hints);
635     if (self->priv->authentication)
636     {
637         g_signal_handlers_disconnect_matched (self->priv->authentication, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
638         pam_session_cancel (self->priv->authentication);
639         g_object_unref (self->priv->authentication);
640     }
641     if (self->priv->to_greeter_channel)
642         g_io_channel_unref (self->priv->to_greeter_channel);
643     if (self->priv->from_greeter_channel)
644         g_io_channel_unref (self->priv->from_greeter_channel);
645
646     G_OBJECT_CLASS (greeter_parent_class)->finalize (object);
647 }
648
649 static void
650 greeter_class_init (GreeterClass *klass)
651 {
652     GObjectClass *object_class = G_OBJECT_CLASS (klass);
653
654     klass->start_authentication = greeter_real_start_authentication;
655     klass->start_session = greeter_real_start_session;
656     object_class->finalize = greeter_finalize;
657
658     signals[START_SESSION] =
659         g_signal_new ("start-session",
660                       G_TYPE_FROM_CLASS (klass),
661                       G_SIGNAL_RUN_LAST,
662                       G_STRUCT_OFFSET (GreeterClass, start_session),
663                       g_signal_accumulator_true_handled,
664                       NULL,
665                       ldm_marshal_BOOLEAN__STRING,
666                       G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
667
668     signals[START_AUTHENTICATION] =
669         g_signal_new ("start-authentication",
670                       G_TYPE_FROM_CLASS (klass),
671                       G_SIGNAL_RUN_LAST,
672                       G_STRUCT_OFFSET (GreeterClass, start_authentication),
673                       g_signal_accumulator_first_wins,
674                       NULL,
675                       ldm_marshal_OBJECT__STRING,
676                       PAM_SESSION_TYPE, 1, G_TYPE_STRING);
677
678     g_type_class_add_private (klass, sizeof (GreeterPrivate));
679 }