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