]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/greeter.c
6106147efbef32ee570d5df81ccd5899134e955c
[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 #include <pwd.h>
15 #include <errno.h>
16
17 #include "greeter.h"
18 #include "ldm-marshal.h"
19 #include "greeter-protocol.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     QUIT,
27     LAST_SIGNAL
28 };
29 static guint signals[LAST_SIGNAL] = { 0 };
30
31 struct GreeterPrivate
32 {
33     gboolean connected;
34
35     /* Pipe to communicate to greeter */
36     int pipe[2];
37     gchar *read_buffer;
38     gsize n_read;
39   
40     gchar *theme;
41     gchar *layout;
42     gchar *session;
43     gchar *default_user;
44     gint autologin_timeout;
45
46     guint quit_timeout;
47   
48     PAMSession *pam_session;    
49 };
50
51 G_DEFINE_TYPE (Greeter, greeter, SESSION_TYPE);
52
53 Greeter *
54 greeter_new (void)
55 {
56     return g_object_new (GREETER_TYPE, NULL);
57 }
58
59 void
60 greeter_set_default_user (Greeter *greeter, const gchar *username, gint timeout)
61 {
62     g_free (greeter->priv->default_user);
63     greeter->priv->default_user = g_strdup (username);
64     greeter->priv->autologin_timeout = timeout;
65 }
66
67 void
68 greeter_set_theme (Greeter *greeter, const gchar *theme)
69 {
70     g_free (greeter->priv->theme);
71     greeter->priv->theme = g_strdup (theme);
72 }
73
74 void
75 greeter_set_layout (Greeter *greeter, const gchar *layout)
76 {
77     g_free (greeter->priv->layout);
78     greeter->priv->layout = g_strdup (layout);
79 }
80
81 const gchar *
82 greeter_get_layout (Greeter *greeter)
83 {
84     return greeter->priv->layout;
85 }
86
87 void
88 greeter_set_session (Greeter *greeter, const gchar *session)
89 {
90     g_free (greeter->priv->session);
91     greeter->priv->session = g_strdup (session);
92 }
93
94 const gchar *
95 greeter_get_session (Greeter *greeter)
96 {
97     return greeter->priv->session;
98 }
99
100 PAMSession *
101 greeter_get_pam_session (Greeter *greeter)
102 {
103     return greeter->priv->pam_session;
104 }
105
106 static guint32
107 int_length ()
108 {
109     return 4;
110 }
111
112 static void
113 write_int (Greeter *greeter, guint32 value)
114 {
115     gchar buffer[4];
116     buffer[0] = value >> 24;
117     buffer[1] = (value >> 16) & 0xFF;
118     buffer[2] = (value >> 8) & 0xFF;
119     buffer[3] = value & 0xFF;
120     g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), buffer, int_length (), NULL, NULL);
121 }
122
123 static void
124 write_string (Greeter *greeter, const gchar *value)
125 {
126     write_int (greeter, strlen (value));
127     g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), value, -1, NULL, NULL);  
128 }
129
130 static void
131 write_header (Greeter *greeter, guint32 id, guint32 length)
132 {
133     write_int (greeter, id);
134     write_int (greeter, length);
135 }
136
137 static guint32
138 string_length (const gchar *value)
139 {
140     return int_length () + strlen (value);
141 }
142
143 static void
144 flush (Greeter *greeter)
145 {
146     g_io_channel_flush (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), NULL);
147 }
148
149 static void
150 handle_connect (Greeter *greeter)
151 {
152     gchar *theme;
153
154     if (!greeter->priv->connected)
155     {
156         greeter->priv->connected = TRUE;
157         g_debug ("Greeter connected");
158     }
159
160     theme = g_build_filename (THEME_DIR, greeter->priv->theme, "index.theme", NULL);
161
162     write_header (greeter, GREETER_MESSAGE_CONNECTED, string_length (theme) + string_length (greeter->priv->layout) + string_length (greeter->priv->session) + string_length (greeter->priv->default_user ? greeter->priv->default_user : "") + int_length () + int_length ());
163     write_string (greeter, theme);
164     write_string (greeter, greeter->priv->layout);
165     write_string (greeter, greeter->priv->session);
166     write_string (greeter, greeter->priv->default_user ? greeter->priv->default_user : "");
167     write_int (greeter, greeter->priv->autologin_timeout);
168     write_int (greeter, FALSE);
169     flush (greeter);
170
171     g_free (theme);
172 }
173
174 static void
175 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Greeter *greeter)
176 {
177     int i;
178     guint32 size;
179
180     /* Respond to d-bus query with messages */
181     g_debug ("Prompt greeter with %d message(s)", num_msg);
182     size = int_length ();
183     for (i = 0; i < num_msg; i++)
184         size += int_length () + string_length (msg[i]->msg);
185     write_header (greeter, GREETER_MESSAGE_PROMPT_AUTHENTICATION, size);
186     write_int (greeter, num_msg);
187     for (i = 0; i < num_msg; i++)
188     {
189         write_int (greeter, msg[i]->msg_style);
190         write_string (greeter, msg[i]->msg);
191     }
192     flush (greeter);  
193 }
194
195 static void
196 authenticate_result_cb (PAMSession *session, int result, Greeter *greeter)
197 {
198     g_debug ("Authenticate result for user %s: %s", pam_session_get_username (greeter->priv->pam_session), pam_session_strerror (greeter->priv->pam_session, result));
199
200     if (result == PAM_SUCCESS)
201     {
202         //run_script ("PostLogin");
203         pam_session_authorize (session);
204     }
205
206     /* Respond to D-Bus request */
207     write_header (greeter, GREETER_MESSAGE_END_AUTHENTICATION, int_length ());
208     write_int (greeter, result);   
209     flush (greeter);
210 }
211
212 static void
213 handle_login (Greeter *greeter, const gchar *username)
214 {
215     GError *error = NULL;
216
217     // FIXME
218     //if (greeter->priv->user_session)
219     //    return;
220
221     /* Abort existing authentication */
222     if (greeter->priv->pam_session)
223     {
224         g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
225         pam_session_end (greeter->priv->pam_session);
226         g_object_unref (greeter->priv->pam_session);
227     }
228
229     g_debug ("Greeter start authentication for %s", username);
230
231     greeter->priv->pam_session = pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username);
232     g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
233     g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), greeter);
234
235     if (!pam_session_start (greeter->priv->pam_session, &error))
236         g_warning ("Failed to start authentication: %s", error->message);
237 }
238
239 static void
240 handle_login_as_guest (Greeter *greeter)
241 {
242 #if 0
243     GError *error = NULL;
244
245     // FIXME
246     //if (greeter->priv->user_session)
247     //    return;
248
249     /* Abort existing authentication */
250     if (greeter->priv->pam_session)
251     {
252         g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
253         pam_session_end (greeter->priv->pam_session);
254         g_object_unref (greeter->priv->pam_session);
255     }
256   
257     // FIXME: Create guest account
258
259     g_debug ("Greeter start authentication for guest account");
260
261     greeter->priv->pam_session = pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username);
262     g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
263     g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), greeter);
264
265     if (!pam_session_start (greeter->priv->pam_session, &error))
266         g_warning ("Failed to start authentication: %s", error->message);
267 #endif
268 }
269
270 static void
271 handle_continue_authentication (Greeter *greeter, gchar **secrets)
272 {
273     int num_messages;
274     const struct pam_message **messages;
275     struct pam_response *response;
276     int i, j, n_secrets = 0;
277
278     /* Not connected */
279     if (!greeter->priv->connected)
280         return;
281
282     /* Not in authorization */
283     if (greeter->priv->pam_session == NULL)
284         return;
285
286     num_messages = pam_session_get_num_messages (greeter->priv->pam_session);
287     messages = pam_session_get_messages (greeter->priv->pam_session);
288
289     /* Check correct number of responses */
290     for (i = 0; i < num_messages; i++)
291     {
292         int msg_style = messages[i]->msg_style;
293         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
294             n_secrets++;
295     }
296     if (g_strv_length (secrets) != n_secrets)
297     {
298         pam_session_end (greeter->priv->pam_session);
299         return;
300     }
301
302     g_debug ("Continue authentication");
303
304     /* Build response */
305     response = calloc (num_messages, sizeof (struct pam_response));  
306     for (i = 0, j = 0; i < num_messages; i++)
307     {
308         int msg_style = messages[i]->msg_style;
309         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
310         {
311             response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
312             j++;
313         }
314     }
315
316     pam_session_respond (greeter->priv->pam_session, response);
317 }
318
319 static void
320 handle_cancel_authentication (Greeter *greeter)
321 {
322     /* Not connected */
323     if (!greeter->priv->connected)
324         return;
325
326     /* Not in authorization */
327     if (greeter->priv->pam_session == NULL)
328         return;
329
330     g_debug ("Cancel authentication");
331
332     pam_session_cancel (greeter->priv->pam_session);
333 }
334
335 static gboolean
336 quit_greeter_cb (gpointer data)
337 {
338     Greeter *greeter = data;
339     g_warning ("Greeter did not quit, sending kill signal");
340     session_stop (SESSION (greeter));
341     greeter->priv->quit_timeout = 0;
342     return TRUE;
343 }
344
345 void
346 greeter_quit (Greeter *greeter)
347 {
348     write_header (greeter, GREETER_MESSAGE_QUIT, 0);
349     flush (greeter);
350
351     if (greeter->priv->quit_timeout)
352         g_source_remove (greeter->priv->quit_timeout);
353     greeter->priv->quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, greeter);
354 }
355
356 static void
357 handle_start_session (Greeter *greeter, gchar *session, gchar *language)
358 {
359     /*if (greeter->priv->user_session != NULL)
360     {
361         g_warning ("Ignoring request to log in when already logged in");
362         return;
363     }*/
364
365     g_debug ("Greeter start session %s with language", session, language);
366
367     g_signal_emit (greeter, signals[START_SESSION], 0, session, language);
368 }
369
370 static void
371 handle_get_user_defaults (Greeter *greeter, gchar *username)
372 {
373     struct passwd *user_info;
374     GKeyFile *dmrc_file;
375     gboolean have_dmrc = FALSE;
376     gchar *language, *layout, *session;
377
378     user_info = getpwnam (username);
379     if (!user_info)
380     {
381         if (errno == 0)
382             g_warning ("Unable to get information on user %s: User does not exist", username);
383         else
384             g_warning ("Unable to get information on user %s: %s", username, strerror (errno));
385     }
386       
387     dmrc_file = g_key_file_new ();
388
389     /* Load the users login settings (~/.dmrc) */  
390     if (user_info)
391     {
392         gchar *path;
393         path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
394         have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
395         g_free (path);
396     }
397
398     /* If no .dmrc, then load from the cache */
399     if (!have_dmrc)
400     {
401         gchar *path, *filename;
402
403         filename = g_strdup_printf ("%s.dmrc", username);
404         path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
405         g_free (filename);
406         have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
407         g_free (path);
408     }
409
410     language = g_key_file_get_string (dmrc_file, "Desktop", "Language", NULL);
411     if (!language)
412         language = g_strdup ("");
413     layout = g_key_file_get_string (dmrc_file, "Desktop", "Layout", NULL);
414     if (!layout)
415         layout = g_strdup ("");
416     session = g_key_file_get_string (dmrc_file, "Desktop", "Session", NULL);
417     if (!session)
418         session = g_strdup ("");
419
420     write_header (greeter, GREETER_MESSAGE_USER_DEFAULTS, string_length (language) + string_length (layout) + string_length (session));
421     write_string (greeter, language);
422     write_string (greeter, layout);
423     write_string (greeter, session);
424     flush (greeter);
425   
426     g_free (language);
427     g_free (layout);
428     g_free (session);
429
430     g_key_file_free (dmrc_file);
431 }
432
433 #define HEADER_SIZE (sizeof (guint32) * 2)
434
435 static guint32
436 read_int (Greeter *greeter, gsize *offset)
437 {
438     guint32 value;
439     gchar *buffer;
440     if (greeter->priv->n_read - *offset < sizeof (guint32))
441     {
442         g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), greeter->priv->n_read - *offset);
443         return 0;
444     }
445     buffer = greeter->priv->read_buffer + *offset;
446     value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
447     *offset += int_length ();
448     return value;
449 }
450
451 static gchar *
452 read_string (Greeter *greeter, gsize *offset)
453 {
454     guint32 length;
455     gchar *value;
456
457     length = read_int (greeter, offset);
458     if (greeter->priv->n_read - *offset < length)
459     {
460         g_warning ("Not enough space for string, need %u, got %zu", length, greeter->priv->n_read - *offset);
461         return g_strdup ("");
462     }
463
464     value = g_malloc (sizeof (gchar *) * (length + 1));
465     memcpy (value, greeter->priv->read_buffer + *offset, length);
466     value[length] = '\0';
467     *offset += length;
468
469     return value;
470 }
471
472 static void
473 got_data_cb (Greeter *greeter)
474 {
475     gsize n_to_read, n_read, offset;
476     GIOStatus status;
477     int id, n_secrets, i;
478     gchar *username, *session_name, *language;
479     gchar **secrets;
480     GError *error = NULL;
481   
482     n_to_read = HEADER_SIZE;
483     if (greeter->priv->n_read >= HEADER_SIZE)
484     {
485         offset = int_length ();
486         n_to_read += read_int (greeter, &offset);
487     }
488
489     status = g_io_channel_read_chars (child_process_get_from_child_channel (CHILD_PROCESS (greeter)),
490                                       greeter->priv->read_buffer + greeter->priv->n_read,
491                                       n_to_read - greeter->priv->n_read,
492                                       &n_read,
493                                       &error);
494     if (status != G_IO_STATUS_NORMAL)
495         g_warning ("Error reading from greeter: %s", error->message);
496     g_clear_error (&error);
497     if (status != G_IO_STATUS_NORMAL)
498         return;
499
500     g_debug ("Read %zi bytes from greeter", n_read);
501
502     greeter->priv->n_read += n_read;
503     if (greeter->priv->n_read != n_to_read)
504         return;
505
506     /* If have header, rerun for content */
507     if (greeter->priv->n_read == HEADER_SIZE)
508     {
509         n_to_read = ((guint32 *) greeter->priv->read_buffer)[1];
510         if (n_to_read > 0)
511         {
512             greeter->priv->read_buffer = g_realloc (greeter->priv->read_buffer, HEADER_SIZE + n_to_read);
513             got_data_cb (greeter);
514             return;
515         }
516     }
517   
518     offset = 0;
519     id = read_int (greeter, &offset);
520     read_int (greeter, &offset);
521     switch (id)
522     {
523     case GREETER_MESSAGE_CONNECT:
524         handle_connect (greeter);
525         break;
526     case GREETER_MESSAGE_LOGIN:
527         username = read_string (greeter, &offset);
528         handle_login (greeter, username);
529         g_free (username);
530         break;
531     case GREETER_MESSAGE_LOGIN_AS_GUEST:
532         handle_login_as_guest (greeter);
533         break;
534     case GREETER_MESSAGE_CONTINUE_AUTHENTICATION:
535         n_secrets = read_int (greeter, &offset);
536         secrets = g_malloc (sizeof (gchar *) * (n_secrets + 1));
537         for (i = 0; i < n_secrets; i++)
538             secrets[i] = read_string (greeter, &offset);
539         secrets[i] = NULL;
540         handle_continue_authentication (greeter, secrets);
541         g_strfreev (secrets);
542         break;
543     case GREETER_MESSAGE_CANCEL_AUTHENTICATION:
544         handle_cancel_authentication (greeter);
545         break;
546     case GREETER_MESSAGE_START_SESSION:
547         session_name = read_string (greeter, &offset);
548         language = read_string (greeter, &offset);
549         handle_start_session (greeter, session_name, language);
550         g_free (session_name);
551         g_free (language);
552         break;
553     case GREETER_MESSAGE_GET_USER_DEFAULTS:
554         username = read_string (greeter, &offset);
555         handle_get_user_defaults (greeter, username);
556         g_free (username);
557         break;
558     default:
559         g_warning ("Unknown message from greeter: %d", id);
560         break;
561     }
562
563     greeter->priv->n_read = 0;
564 }
565
566 static void
567 end_session (Greeter *greeter, gboolean clean_exit)
568 {  
569     if (greeter->priv->quit_timeout)
570     {
571         g_source_remove (greeter->priv->quit_timeout);
572         greeter->priv->quit_timeout = 0;
573     }
574
575     /*if (!clean_exit)
576         g_warning ("Greeter failed");
577     else if (!greeter_connected)
578         g_warning ("Greeter quit before connecting");
579     else if (!display->priv->user_session)
580         g_warning ("Greeter quit before session started");
581     else
582         return;*/
583
584     // FIXME: Issue with greeter, don't want to start a new one, report error to user
585
586     g_signal_emit (greeter, signals[QUIT], 0);
587 }
588
589 static void
590 session_exited_cb (Greeter *greeter, gint status)
591 {
592     end_session (greeter, status == 0);
593 }
594
595 static void
596 session_terminated_cb (Greeter *greeter, gint signum)
597 {
598     end_session (greeter, FALSE);
599 }
600
601 static void
602 greeter_init (Greeter *greeter)
603 {
604     greeter->priv = G_TYPE_INSTANCE_GET_PRIVATE (greeter, GREETER_TYPE, GreeterPrivate);
605     greeter->priv->read_buffer = g_malloc (HEADER_SIZE);
606     g_signal_connect (G_OBJECT (greeter), "got-data", G_CALLBACK (got_data_cb), NULL);
607     g_signal_connect (G_OBJECT (greeter), "exited", G_CALLBACK (session_exited_cb), NULL);
608     g_signal_connect (G_OBJECT (greeter), "terminated", G_CALLBACK (session_terminated_cb), NULL);
609 }
610
611 static void
612 greeter_finalize (GObject *object)
613 {
614     Greeter *self;
615
616     self = GREETER (object);
617   
618     g_free (self->priv->read_buffer);
619     g_free (self->priv->theme);
620     g_free (self->priv->layout);
621     g_free (self->priv->session);
622     g_free (self->priv->default_user);
623   
624     G_OBJECT_CLASS (greeter_parent_class)->finalize (object);
625 }
626
627 static void
628 greeter_class_init (GreeterClass *klass)
629 {
630     GObjectClass *object_class = G_OBJECT_CLASS (klass);
631
632     object_class->finalize = greeter_finalize;
633
634     signals[START_SESSION] =
635         g_signal_new ("start-session",
636                       G_TYPE_FROM_CLASS (klass),
637                       G_SIGNAL_RUN_LAST,
638                       G_STRUCT_OFFSET (GreeterClass, start_session),
639                       NULL, NULL,
640                       ldm_marshal_VOID__STRING_STRING,
641                       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
642     signals[QUIT] =
643         g_signal_new ("quit",
644                       G_TYPE_FROM_CLASS (klass),
645                       G_SIGNAL_RUN_LAST,
646                       G_STRUCT_OFFSET (GreeterClass, quit),
647                       NULL, NULL,
648                       g_cclosure_marshal_VOID__VOID,
649                       G_TYPE_NONE, 0);
650
651     g_type_class_add_private (klass, sizeof (GreeterPrivate));
652 }