]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/greeter.c
connect_to_server -> connect_sync, start_session -> start_session_sync
[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_PROMPT_AUTHENTICATION,
75     SERVER_MESSAGE_END_AUTHENTICATION,
76     SERVER_MESSAGE_SESSION_RESULT
77 } ServerMessage;
78
79 Greeter *
80 greeter_new (Session *session)
81 {
82     Greeter *greeter = g_object_new (GREETER_TYPE, NULL);
83
84     greeter->priv->session = g_object_ref (session);
85
86     return greeter;
87 }
88
89 void
90 greeter_set_hint (Greeter *greeter, const gchar *name, const gchar *value)
91 {
92     g_hash_table_insert (greeter->priv->hints, g_strdup (name), g_strdup (value));
93 }
94
95 static guint32
96 int_length ()
97 {
98     return 4;
99 }
100
101 #define HEADER_SIZE (sizeof (guint32) * 2)
102 #define MAX_MESSAGE_LENGTH 1024
103
104 static void
105 write_message (Greeter *greeter, guint8 *message, gsize message_length)
106 {
107     GError *error = NULL;
108     if (g_io_channel_write_chars (greeter->priv->to_greeter_channel, (gchar *) message, message_length, NULL, &error) != G_IO_STATUS_NORMAL)
109         g_warning ("Error writing to greeter: %s", error->message);
110     else
111         g_debug ("Wrote %zi bytes to greeter", message_length);
112     g_clear_error (&error);
113     g_io_channel_flush (greeter->priv->to_greeter_channel, NULL);
114 }
115
116 static void
117 write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
118 {
119     if (*offset + 4 >= buffer_length)
120         return;
121     buffer[*offset] = value >> 24;
122     buffer[*offset+1] = (value >> 16) & 0xFF;
123     buffer[*offset+2] = (value >> 8) & 0xFF;
124     buffer[*offset+3] = value & 0xFF;
125     *offset += 4;
126 }
127
128 static void
129 write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
130 {
131     gint length;
132   
133     if (value)
134         length = strlen (value);
135     else
136         length = 0;
137     write_int (buffer, buffer_length, length, offset);
138     if (*offset + length >= buffer_length)
139         return;
140     if (length > 0)
141     {
142         memcpy (buffer + *offset, value, length);
143         *offset += length;
144     }
145 }
146
147 static void
148 write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
149 {
150     write_int (buffer, buffer_length, id, offset);
151     write_int (buffer, buffer_length, length, offset);
152 }
153
154 static guint32
155 string_length (const gchar *value)
156 {
157     if (value == NULL)
158         return int_length ();
159     else
160         return int_length () + strlen (value);
161 }
162
163 static void
164 handle_connect (Greeter *greeter, const gchar *version)
165 {
166     guint8 message[MAX_MESSAGE_LENGTH];
167     gsize offset = 0;
168     guint32 length;
169     GHashTableIter iter;
170     gpointer key, value;
171
172     g_debug ("Greeter connected version=%s", version);
173
174     length = string_length (VERSION);
175     g_hash_table_iter_init (&iter, greeter->priv->hints);
176     while (g_hash_table_iter_next (&iter, &key, &value))
177         length += string_length (key) + string_length (value);
178
179     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_CONNECTED, length, &offset);
180     write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
181     g_hash_table_iter_init (&iter, greeter->priv->hints);
182     while (g_hash_table_iter_next (&iter, &key, &value))
183     {
184         write_string (message, MAX_MESSAGE_LENGTH, key, &offset);
185         write_string (message, MAX_MESSAGE_LENGTH, value, &offset);
186     }
187     write_message (greeter, message, offset);
188 }
189
190 static void
191 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Greeter *greeter)
192 {
193     int i;
194     guint32 size;
195     guint8 message[MAX_MESSAGE_LENGTH];
196     gsize offset = 0;
197
198     /* Respond to d-bus query with messages */
199     g_debug ("Prompt greeter with %d message(s)", num_msg);
200     size = int_length () + int_length ();
201     for (i = 0; i < num_msg; i++)
202     {
203       g_debug ("%s", msg[i]->msg);
204         size += int_length () + string_length (msg[i]->msg);
205     }
206   
207     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_PROMPT_AUTHENTICATION, size, &offset);
208     write_int (message, MAX_MESSAGE_LENGTH, greeter->priv->authentication_sequence_number, &offset);
209     write_int (message, MAX_MESSAGE_LENGTH, num_msg, &offset);
210     for (i = 0; i < num_msg; i++)
211     {
212         write_int (message, MAX_MESSAGE_LENGTH, msg[i]->msg_style, &offset);
213         write_string (message, MAX_MESSAGE_LENGTH, msg[i]->msg, &offset);
214     }
215     write_message (greeter, message, offset);  
216 }
217
218 static void
219 send_end_authentication (Greeter *greeter, guint32 sequence_number, int result)
220 {
221     guint8 message[MAX_MESSAGE_LENGTH];
222     gsize offset = 0;
223
224     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_END_AUTHENTICATION, int_length () + int_length (), &offset);
225     write_int (message, MAX_MESSAGE_LENGTH, sequence_number, &offset);
226     write_int (message, MAX_MESSAGE_LENGTH, result, &offset);
227     write_message (greeter, message, offset); 
228 }
229
230 static void
231 authentication_result_cb (PAMSession *session, int result, Greeter *greeter)
232 {
233     g_debug ("Authenticate result for user %s: %s", pam_session_get_username (greeter->priv->pam_session), pam_session_strerror (greeter->priv->pam_session, result));
234
235     if (result == PAM_SUCCESS)
236     {
237         g_debug ("User %s authorized", pam_session_get_username (session));
238         pam_session_authorize (session);
239     }
240
241     send_end_authentication (greeter, greeter->priv->authentication_sequence_number, result);
242 }
243
244 static void
245 reset_session (Greeter *greeter)
246 {
247     if (greeter->priv->pam_session == NULL)
248         return;
249
250     g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
251     pam_session_end (greeter->priv->pam_session);
252     g_object_unref (greeter->priv->pam_session);
253
254     greeter->priv->using_guest_account = FALSE;
255 }
256
257 static void
258 handle_login (Greeter *greeter, guint32 sequence_number, const gchar *username)
259 {
260     GError *error = NULL;
261
262     if (username[0] == '\0')
263     {
264         g_debug ("Greeter start authentication");
265         username = NULL;
266     }
267     else
268         g_debug ("Greeter start authentication for %s", username);        
269
270     reset_session (greeter);
271
272     greeter->priv->pam_session = pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username);
273     greeter->priv->authentication_sequence_number = sequence_number;
274
275     g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
276     g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authentication_result_cb), greeter);
277
278     if (!pam_session_start (greeter->priv->pam_session, &error))
279     {
280         g_warning ("Failed to start authentication: %s", error->message);
281         send_end_authentication (greeter, sequence_number, PAM_SYSTEM_ERR);
282     }
283     g_clear_error (&error);
284 }
285
286 static void
287 handle_login_as_guest (Greeter *greeter, guint32 sequence_number)
288 {
289     g_debug ("Greeter start authentication for guest account");
290
291     reset_session (greeter);
292
293     if (!guest_account_get_is_enabled ())
294     {
295         g_debug ("Guest account is disabled");
296         send_end_authentication (greeter, sequence_number, PAM_USER_UNKNOWN);
297         return;
298     }
299
300     greeter->priv->using_guest_account = TRUE;
301     greeter->priv->pam_session = pam_session_new ("lightdm-autologin" /*FIXME*/, guest_account_get_username ());
302     pam_session_authorize (greeter->priv->pam_session);
303     send_end_authentication (greeter, sequence_number, PAM_SUCCESS);
304 }
305
306 static void
307 handle_continue_authentication (Greeter *greeter, gchar **secrets)
308 {
309     int num_messages;
310     const struct pam_message **messages;
311     struct pam_response *response;
312     int i, j, n_secrets = 0;
313
314     /* Not in authorization */
315     if (greeter->priv->pam_session == NULL)
316         return;
317
318     num_messages = pam_session_get_num_messages (greeter->priv->pam_session);
319     messages = pam_session_get_messages (greeter->priv->pam_session);
320
321     /* Check correct number of responses */
322     for (i = 0; i < num_messages; i++)
323     {
324         int msg_style = messages[i]->msg_style;
325         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
326             n_secrets++;
327     }
328     if (g_strv_length (secrets) != n_secrets)
329     {
330         pam_session_end (greeter->priv->pam_session);
331         return;
332     }
333
334     g_debug ("Continue authentication");
335
336     /* Build response */
337     response = calloc (num_messages, sizeof (struct pam_response));  
338     for (i = 0, j = 0; i < num_messages; i++)
339     {
340         int msg_style = messages[i]->msg_style;
341         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
342         {
343             response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
344             j++;
345         }
346     }
347
348     pam_session_respond (greeter->priv->pam_session, response);
349 }
350
351 static void
352 handle_cancel_authentication (Greeter *greeter)
353 {
354     /* Not in authorization */
355     if (greeter->priv->pam_session == NULL)
356         return;
357
358     g_debug ("Cancel authentication");
359
360     pam_session_cancel (greeter->priv->pam_session);
361 }
362
363 static void
364 handle_start_session (Greeter *greeter, gchar *session)
365 {
366     gboolean result;
367
368     if (strcmp (session, "") == 0)
369         session = NULL;
370
371     g_signal_emit (greeter, signals[START_SESSION], 0, session, greeter->priv->using_guest_account, &result);
372
373     if (!result)
374     {
375         guint8 message[MAX_MESSAGE_LENGTH];
376         gsize offset = 0;
377
378         write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_SESSION_RESULT, int_length (), &offset);
379         write_int (message, MAX_MESSAGE_LENGTH, 1, &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_SESSION_RESULT, int_length (), &offset);
575     write_int (message, MAX_MESSAGE_LENGTH, 0, &offset);
576     write_message (greeter, message, offset);
577 }
578
579 static void
580 greeter_init (Greeter *greeter)
581 {
582     greeter->priv = G_TYPE_INSTANCE_GET_PRIVATE (greeter, GREETER_TYPE, GreeterPrivate);
583     greeter->priv->read_buffer = g_malloc (HEADER_SIZE);
584     greeter->priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
585 }
586
587 static void
588 greeter_finalize (GObject *object)
589 {
590     Greeter *self;
591
592     self = GREETER (object);
593
594     g_object_unref (self->priv->session);
595     g_hash_table_unref (self->priv->hints);
596     g_free (self->priv->read_buffer);
597     if (self->priv->to_greeter_channel)
598         g_io_channel_unref (self->priv->to_greeter_channel);
599     if (self->priv->from_greeter_channel)
600         g_io_channel_unref (self->priv->from_greeter_channel);
601
602     G_OBJECT_CLASS (greeter_parent_class)->finalize (object);
603 }
604
605 static void
606 greeter_class_init (GreeterClass *klass)
607 {
608     GObjectClass *object_class = G_OBJECT_CLASS (klass);
609
610     object_class->finalize = greeter_finalize;
611
612     signals[START_SESSION] =
613         g_signal_new ("start-session",
614                       G_TYPE_FROM_CLASS (klass),
615                       G_SIGNAL_RUN_LAST,
616                       G_STRUCT_OFFSET (GreeterClass, start_session),
617                       NULL, NULL,
618                       ldm_marshal_BOOLEAN__STRING_BOOLEAN,
619                       G_TYPE_BOOLEAN, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
620
621     g_type_class_add_private (klass, sizeof (GreeterPrivate));
622 }