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