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