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