]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/session-child.c
e393ce4c7c41ae48ab88efa69efff5851434a879
[sojka/lightdm.git] / src / session-child.c
1 #include <config.h>
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/wait.h>
11 #include <fcntl.h>
12 #include <pwd.h>
13 #include <grp.h>
14 #include <glib.h>
15 #include <security/pam_appl.h>
16 #include <utmp.h>
17 #include <utmpx.h>
18 #include <sys/mman.h>
19
20 #if HAVE_LIBAUDIT
21 #include <libaudit.h>
22 #endif
23
24 #include "configuration.h"
25 #include "session-child.h"
26 #include "session.h"
27 #include "console-kit.h"
28 #include "login1.h"
29 #include "log-file.h"
30 #include "privileges.h"
31 #include "x-authority.h"
32 #include "configuration.h"
33
34 /* Child process being run */
35 static GPid child_pid = 0;
36
37 /* Pipe to communicate with daemon */
38 static int from_daemon_output = 0;
39 static int to_daemon_input = 0;
40
41 static gboolean is_interactive;
42 static gboolean do_authenticate;
43 static gboolean authentication_complete = FALSE;
44 static pam_handle_t *pam_handle;
45
46 /* Maximum length of a string to pass between daemon and session */
47 #define MAX_STRING_LENGTH 65535
48
49 static void
50 write_data (const void *buf, size_t count)
51 {
52     if (write (to_daemon_input, buf, count) != count)
53         g_printerr ("Error writing to daemon: %s\n", strerror (errno));
54 }
55
56 static void
57 write_string (const char *value)
58 {
59     int length;
60
61     length = value ? strlen (value) : -1;
62     write_data (&length, sizeof (length));
63     if (value)
64         write_data (value, sizeof (char) * length);
65 }
66
67 static ssize_t
68 read_data (void *buf, size_t count)
69 {
70     ssize_t n_read;
71
72     n_read = read (from_daemon_output, buf, count);
73     if (n_read < 0)
74         g_printerr ("Error reading from daemon: %s\n", strerror (errno));
75
76     return n_read;
77 }
78
79 static gchar *
80 read_string_full (void* (*alloc_fn)(size_t n))
81 {
82     int length;
83     char *value;
84
85     if (read_data (&length, sizeof (length)) <= 0)
86         return NULL;
87     if (length < 0)
88         return NULL;
89     if (length > MAX_STRING_LENGTH)
90     {
91         g_printerr ("Invalid string length %d from daemon\n", length);
92         return NULL;
93     }
94
95     value = (*alloc_fn) (sizeof (char) * (length + 1));
96     read_data (value, length);
97     value[length] = '\0';
98
99     return value;
100 }
101
102 static gchar *
103 read_string (void)
104 {
105     return read_string_full (g_malloc);
106 }
107
108 static int
109 pam_conv_cb (int msg_length, const struct pam_message **msg, struct pam_response **resp, void *app_data)
110 {
111     int i, error;
112     gboolean auth_complete = FALSE;
113     struct pam_response *response;
114     gchar *username = NULL;
115
116     /* FIXME: We don't support communication after pam_authenticate completes */
117     if (authentication_complete)
118         return PAM_SUCCESS;
119
120     /* Cancel authentication if requiring input */
121     if (!is_interactive)
122     {
123         for (i = 0; i < msg_length; i++)
124         {
125             if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON || msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
126             {
127                 g_printerr ("Stopping PAM conversation, interaction requested but not supported\n");
128                 return PAM_CONV_ERR;
129             }
130         }
131
132         /* Ignore informational messages */
133         return PAM_SUCCESS;
134     }
135
136     /* Check if we changed user */
137     pam_get_item (pam_handle, PAM_USER, (const void **) &username);
138
139     /* Notify the daemon */
140     write_string (username);
141     write_data (&auth_complete, sizeof (auth_complete));
142     write_data (&msg_length, sizeof (msg_length));
143     for (i = 0; i < msg_length; i++)
144     {
145         const struct pam_message *m = msg[i];
146         write_data (&m->msg_style, sizeof (m->msg_style));
147         write_string (m->msg);
148     }
149
150     /* Get response */
151     read_data (&error, sizeof (error));
152     if (error != PAM_SUCCESS)
153         return error;
154     response = calloc (msg_length, sizeof (struct pam_response));
155     for (i = 0; i < msg_length; i++)
156     {
157         struct pam_response *r = &response[i];
158         // callers of this function inside pam will expect to be able to call
159         // free() on the strings we give back.  So alloc with malloc.
160         r->resp = read_string_full (malloc);
161         read_data (&r->resp_retcode, sizeof (r->resp_retcode));
162     }
163
164     *resp = response;
165     return PAM_SUCCESS;
166 }
167
168 static void
169 signal_cb (int signum)
170 {
171     /* Pass on signal to child, otherwise just quit */
172     if (child_pid > 0)
173         kill (child_pid, signum);
174     else
175         exit (EXIT_SUCCESS);
176 }
177
178 static XAuthority *
179 read_xauth (void)
180 {
181     gchar *x_authority_name;
182     guint16 x_authority_family;
183     guint8 *x_authority_address;
184     gsize x_authority_address_length;
185     gchar *x_authority_number;
186     guint8 *x_authority_data;
187     gsize x_authority_data_length;
188
189     x_authority_name = read_string ();
190     if (!x_authority_name)
191         return NULL;
192
193     read_data (&x_authority_family, sizeof (x_authority_family));
194     read_data (&x_authority_address_length, sizeof (x_authority_address_length));
195     x_authority_address = g_malloc (x_authority_address_length);
196     read_data (x_authority_address, x_authority_address_length);
197     x_authority_number = read_string ();
198     read_data (&x_authority_data_length, sizeof (x_authority_data_length));
199     x_authority_data = g_malloc (x_authority_data_length);
200     read_data (x_authority_data, x_authority_data_length);
201
202     return x_authority_new (x_authority_family, x_authority_address, x_authority_address_length, x_authority_number, x_authority_name, x_authority_data, x_authority_data_length);
203 }
204
205 /* GNU provides this but we can't rely on that so let's make our own version */
206 static void
207 updwtmpx (const gchar *wtmp_file, struct utmpx *ut)
208 {
209     struct utmp u;
210
211     memset (&u, 0, sizeof (u));
212     u.ut_type = ut->ut_type;
213     u.ut_pid = ut->ut_pid;
214     if (ut->ut_line)
215         strncpy (u.ut_line, ut->ut_line, sizeof (u.ut_line));
216     if (ut->ut_id)
217         strncpy (u.ut_id, ut->ut_id, sizeof (u.ut_id));
218     if (ut->ut_user)
219         strncpy (u.ut_user, ut->ut_user, sizeof (u.ut_user));
220     if (ut->ut_host)
221         strncpy (u.ut_host, ut->ut_host, sizeof (u.ut_host));
222     u.ut_tv.tv_sec = ut->ut_tv.tv_sec;
223     u.ut_tv.tv_usec = ut->ut_tv.tv_usec;
224
225     updwtmp (wtmp_file, &u);
226 }
227
228 #if HAVE_LIBAUDIT
229 static void
230 audit_event (int type, const gchar *username, uid_t uid, const gchar *remote_host_name, const gchar *tty, gboolean success)
231 {
232     int auditfd, result;
233     const char *op = NULL;
234
235     auditfd = audit_open ();
236     if (auditfd < 0) {
237         g_printerr ("Error opening audit socket: %s\n", strerror (errno));
238         return;
239     }
240
241     if (type == AUDIT_USER_LOGIN)
242         op = "login";
243     else if (type == AUDIT_USER_LOGOUT)
244         op = "logout";
245     result = success == TRUE ? 1 : 0;
246
247     if (audit_log_acct_message (auditfd, type, NULL, op, username, uid, remote_host_name, NULL, tty, result) <= 0)
248         g_printerr ("Error writing audit message: %s\n", strerror (errno));
249
250     close (auditfd);
251 }
252 #endif
253
254 int
255 session_child_run (int argc, char **argv)
256 {
257     struct pam_conv conversation = { pam_conv_cb, NULL };
258     int i, version, fd, result;
259     gboolean auth_complete = TRUE;
260     User *user = NULL;
261     gchar *log_filename;
262     LogMode log_mode = LOG_MODE_BACKUP_AND_TRUNCATE;
263     gsize env_length;
264     gsize command_argc;
265     gchar **command_argv;
266     GVariantBuilder ck_parameters;
267     int return_code;
268     int authentication_result;
269     gchar *authentication_result_string;
270     gchar *service;
271     gchar *username;
272     gchar *tty;
273     gchar *remote_host_name;
274     gchar *xdisplay;
275     XAuthority *x_authority = NULL;
276     gchar *x_authority_filename;
277     GDBusConnection *bus;
278     const gchar *login1_session_id = NULL;
279     gchar *console_kit_cookie = NULL;
280     const gchar *locale_value;
281     gchar *locale_var;
282     static const gchar * const locale_var_names[] = {
283         "LC_PAPER",
284         "LC_NAME",
285         "LC_ADDRESS",
286         "LC_TELEPHONE",
287         "LC_MEASUREMENT",
288         "LC_IDENTIFICATION",
289         "LC_COLLATE",
290         "LC_CTYPE",
291         "LC_MONETARY",
292         "LC_NUMERIC",
293         "LC_TIME",
294         "LC_MESSAGES",
295         "LC_ALL",
296         "LANG",
297         NULL
298     };
299     gid_t gid;
300     uid_t uid;
301     const gchar *home_directory;
302     GError *error = NULL;
303
304 #if !defined(GLIB_VERSION_2_36)
305     g_type_init ();
306 #endif
307
308     if (config_get_boolean (config_get_instance (), "LightDM", "lock-memory"))
309     {
310         /* Protect memory from being paged to disk, as we deal with passwords */
311         mlockall (MCL_CURRENT | MCL_FUTURE);
312     }
313
314     /* Make input non-blocking */
315     fd = open ("/dev/null", O_RDONLY);
316     dup2 (fd, STDIN_FILENO);
317     close (fd);
318
319     /* Close stdout */
320     fd = open ("/dev/null", O_WRONLY);
321     dup2 (fd, STDOUT_FILENO);
322     close (fd);
323
324     /* Get the pipe from the daemon */
325     if (argc != 4)
326     {
327         g_printerr ("Usage: lightdm --session-child INPUTFD OUTPUTFD\n");
328         return EXIT_FAILURE;
329     }
330     from_daemon_output = atoi (argv[2]);
331     to_daemon_input = atoi (argv[3]);
332     if (from_daemon_output == 0 || to_daemon_input == 0)
333     {
334         g_printerr ("Invalid file descriptors %s %s\n", argv[2], argv[3]);
335         return EXIT_FAILURE;
336     }
337
338     /* Don't let these pipes leak to the command we will run */
339     fcntl (from_daemon_output, F_SETFD, FD_CLOEXEC);
340     fcntl (to_daemon_input, F_SETFD, FD_CLOEXEC);
341
342     /* Read a version number so we can handle upgrades (i.e. a newer version of session child is run for an old daemon */
343     read_data (&version, sizeof (version));
344
345     service = read_string ();
346     username = read_string ();
347     read_data (&do_authenticate, sizeof (do_authenticate));
348     read_data (&is_interactive, sizeof (is_interactive));
349     read_string (); /* Used to be class, now we just use the environment variable */
350     tty = read_string ();
351     remote_host_name = read_string ();
352     xdisplay = read_string ();
353     x_authority = read_xauth ();
354
355     /* Setup PAM */
356     result = pam_start (service, username, &conversation, &pam_handle);
357     if (result != PAM_SUCCESS)
358     {
359         g_printerr ("Failed to start PAM: %s", pam_strerror (NULL, result));
360         return EXIT_FAILURE;
361     }
362     if (xdisplay)
363     {
364 #ifdef PAM_XDISPLAY
365         pam_set_item (pam_handle, PAM_XDISPLAY, xdisplay);
366 #endif
367         pam_set_item (pam_handle, PAM_TTY, xdisplay);
368     }
369     else if (tty)
370         pam_set_item (pam_handle, PAM_TTY, tty);
371
372 #ifdef PAM_XAUTHDATA
373     if (x_authority)
374     {
375         struct pam_xauth_data value;
376
377         value.name = (char *) x_authority_get_authorization_name (x_authority);
378         value.namelen = strlen (x_authority_get_authorization_name (x_authority));
379         value.data = (char *) x_authority_get_authorization_data (x_authority);
380         value.datalen = x_authority_get_authorization_data_length (x_authority);
381         pam_set_item (pam_handle, PAM_XAUTHDATA, &value);
382     }
383 #endif
384
385     /* Authenticate */
386     if (do_authenticate)
387     {
388         const gchar *new_username;
389
390         authentication_result = pam_authenticate (pam_handle, 0);
391
392         /* See what user we ended up as */
393         if (pam_get_item (pam_handle, PAM_USER, (const void **) &new_username) != PAM_SUCCESS)
394         {
395             pam_end (pam_handle, 0);
396             return EXIT_FAILURE;
397         }
398         g_free (username);
399         username = g_strdup (new_username);
400
401         /* Write record to btmp database */
402         if (authentication_result == PAM_AUTH_ERR)
403         {
404             struct utmpx ut;
405             struct timeval tv;
406
407             memset (&ut, 0, sizeof (ut));
408             ut.ut_type = USER_PROCESS;
409             ut.ut_pid = getpid ();
410             if (xdisplay)
411                 strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
412             if (tty && g_str_has_prefix (tty, "/dev/"))
413                 strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
414             strncpy (ut.ut_user, username, sizeof (ut.ut_user));
415             if (xdisplay)
416                 strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
417             else if (remote_host_name)
418                 strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
419             gettimeofday (&tv, NULL);
420             ut.ut_tv.tv_sec = tv.tv_sec;
421             ut.ut_tv.tv_usec = tv.tv_usec;
422
423             updwtmpx ("/var/log/btmp", &ut);
424
425 #if HAVE_LIBAUDIT
426             audit_event (AUDIT_USER_LOGIN, username, -1, remote_host_name, tty, FALSE);
427 #endif
428         }
429
430         /* Check account is valid */
431         if (authentication_result == PAM_SUCCESS)
432             authentication_result = pam_acct_mgmt (pam_handle, 0);
433         if (authentication_result == PAM_NEW_AUTHTOK_REQD)
434             authentication_result = pam_chauthtok (pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
435     }
436     else
437         authentication_result = PAM_SUCCESS;
438     authentication_complete = TRUE;
439
440     if (authentication_result == PAM_SUCCESS)
441     {
442         /* Fail authentication if user doesn't actually exist */
443         user = accounts_get_user_by_name (username);
444         if (!user)
445         {
446             g_printerr ("Failed to get information on user %s: %s\n", username, strerror (errno));
447             authentication_result = PAM_USER_UNKNOWN;
448         }
449         else
450         {
451             /* Set POSIX variables */
452             pam_putenv (pam_handle, "PATH=/usr/local/bin:/usr/bin:/bin");
453             pam_putenv (pam_handle, g_strdup_printf ("USER=%s", username));
454             pam_putenv (pam_handle, g_strdup_printf ("LOGNAME=%s", username));
455             pam_putenv (pam_handle, g_strdup_printf ("HOME=%s", user_get_home_directory (user)));
456             pam_putenv (pam_handle, g_strdup_printf ("SHELL=%s", user_get_shell (user)));
457
458             /* Let the greeter and user session inherit the system default locale */
459             for (i = 0; locale_var_names[i] != NULL; i++)
460             {
461                 if ((locale_value = g_getenv (locale_var_names[i])) != NULL)
462                 {
463                     locale_var = g_strdup_printf ("%s=%s", locale_var_names[i], locale_value);
464                     pam_putenv (pam_handle, locale_var);
465                     g_free (locale_var);
466                 }
467             }
468         }
469     }
470
471     authentication_result_string = g_strdup (pam_strerror (pam_handle, authentication_result));
472
473     /* Report authentication result */
474     write_string (username);
475     write_data (&auth_complete, sizeof (auth_complete));
476     write_data (&authentication_result, sizeof (authentication_result));
477     write_string (authentication_result_string);
478
479     /* Check we got a valid user */
480     if (!username)
481     {
482         g_printerr ("No user selected during authentication\n");
483         pam_end (pam_handle, 0);
484         return EXIT_FAILURE;
485     }
486
487     /* Stop if we didn't authenticated */
488     if (authentication_result != PAM_SUCCESS)
489     {
490         pam_end (pam_handle, 0);
491         return EXIT_FAILURE;
492     }
493
494     /* Get the command to run (blocks) */
495     log_filename = read_string ();
496     if (version >= 3)
497         read_data (&log_mode, sizeof (log_mode));
498     if (version >= 1)
499     {
500         g_free (tty);
501         tty = read_string ();
502     }
503     x_authority_filename = read_string ();
504     if (version >= 1)
505     {
506         g_free (xdisplay);
507         xdisplay = read_string ();
508         if (x_authority)
509             g_object_unref (x_authority);
510         x_authority = read_xauth ();
511     }
512     read_data (&env_length, sizeof (env_length));
513     for (i = 0; i < env_length; i++)
514         pam_putenv (pam_handle, read_string ());
515     read_data (&command_argc, sizeof (command_argc));
516     command_argv = g_malloc (sizeof (gchar *) * (command_argc + 1));
517     for (i = 0; i < command_argc; i++)
518         command_argv[i] = read_string ();
519     command_argv[i] = NULL;
520
521     /* If nothing to run just refresh credentials because we successfully authenticated */
522     if (command_argc == 0)
523     {
524         pam_setcred (pam_handle, PAM_REINITIALIZE_CRED);
525         pam_end (pam_handle, 0);
526         return EXIT_SUCCESS;
527     }
528
529     /* Redirect stderr to a log file */
530     if (log_filename)
531     {
532         if (g_path_is_absolute (log_filename))
533         {
534             fd = log_file_open (log_filename, log_mode);
535             dup2 (fd, STDERR_FILENO);
536             close (fd);
537             g_free (log_filename);
538             log_filename = NULL;
539         }
540     }
541     else
542     {
543         fd = open ("/dev/null", O_WRONLY);
544         dup2 (fd, STDERR_FILENO);
545         close (fd);
546     }
547
548     /* Set group membership - these can be overriden in pam_setcred */
549     if (getuid () == 0)
550     {
551         if (initgroups (username, user_get_gid (user)) < 0)
552         {
553             g_printerr ("Failed to initialize supplementary groups for %s: %s\n", username, strerror (errno));
554             _exit (EXIT_FAILURE);
555         }
556     }
557
558     /* Set credentials */
559     result = pam_setcred (pam_handle, PAM_ESTABLISH_CRED);
560     if (result != PAM_SUCCESS)
561     {
562         g_printerr ("Failed to establish PAM credentials: %s\n", pam_strerror (pam_handle, result));
563         pam_end (pam_handle, 0);
564         return EXIT_FAILURE;
565     }
566
567     /* Open the session */
568     result = pam_open_session (pam_handle, 0);
569     if (result != PAM_SUCCESS)
570     {
571         g_printerr ("Failed to open PAM session: %s\n", pam_strerror (pam_handle, result));
572         pam_end (pam_handle, 0);
573         return EXIT_FAILURE;
574     }
575
576     /* Open a connection to the system bus for ConsoleKit - we must keep it open or CK will close the session */
577     bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
578     if (error)
579         g_printerr ("Unable to contact system bus: %s", error->message);
580     if (!bus)
581     {
582         pam_end (pam_handle, 0);
583         return EXIT_FAILURE;
584     }
585
586     /* Check what logind session we are, or fallback to ConsoleKit */
587     login1_session_id = pam_getenv (pam_handle, "XDG_SESSION_ID");
588     if (login1_session_id)
589     {
590         write_string (login1_session_id);
591         if (version >= 2)
592             write_string (NULL);
593     }
594     else
595     {
596         g_variant_builder_init (&ck_parameters, G_VARIANT_TYPE ("(a(sv))"));
597         g_variant_builder_open (&ck_parameters, G_VARIANT_TYPE ("a(sv)"));
598         g_variant_builder_add (&ck_parameters, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user)));
599         if (g_strcmp0 (pam_getenv (pam_handle, "XDG_SESSION_CLASS"), "greeter") == 0)
600             g_variant_builder_add (&ck_parameters, "(sv)", "session-type", g_variant_new_string ("LoginWindow"));
601         if (xdisplay)
602         {
603             g_variant_builder_add (&ck_parameters, "(sv)", "x11-display", g_variant_new_string (xdisplay));
604             if (tty)
605                 g_variant_builder_add (&ck_parameters, "(sv)", "x11-display-device", g_variant_new_string (tty));
606         }
607         if (remote_host_name)
608         {
609             g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (FALSE));
610             g_variant_builder_add (&ck_parameters, "(sv)", "remote-host-name", g_variant_new_string (remote_host_name));
611         }
612         else
613             g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (TRUE));
614         console_kit_cookie = ck_open_session (&ck_parameters);
615         if (version >= 2)
616             write_string (NULL);
617         write_string (console_kit_cookie);
618         if (console_kit_cookie)
619         {
620             gchar *value;
621             gchar *runtime_dir;
622             value = g_strdup_printf ("XDG_SESSION_COOKIE=%s", console_kit_cookie);
623             pam_putenv (pam_handle, value);
624             g_free (value);
625
626             runtime_dir = ck_get_xdg_runtime_dir (console_kit_cookie);
627             if (runtime_dir)
628             {
629                 gchar *value;
630                 value = g_strdup_printf ("XDG_RUNTIME_DIR=%s", runtime_dir);
631                 pam_putenv (pam_handle, value);
632                 g_free (value);
633                 g_free (runtime_dir);
634             }
635         }
636     }
637
638     /* Write X authority */
639     if (x_authority)
640     {
641         gboolean drop_privileges, result;
642         gchar *value;
643         GError *error = NULL;
644
645         drop_privileges = geteuid () == 0;
646         if (drop_privileges)
647             privileges_drop (user_get_uid (user), user_get_gid (user));
648         result = x_authority_write (x_authority, XAUTH_WRITE_MODE_REPLACE, x_authority_filename, &error);
649         if (drop_privileges)
650             privileges_reclaim ();
651
652         if (error)
653             g_printerr ("Error writing X authority: %s\n", error->message);
654         g_clear_error (&error);
655         if (!result)
656         {
657             pam_end (pam_handle, 0);
658             return EXIT_FAILURE;
659         }
660
661         value = g_strdup_printf ("XAUTHORITY=%s", x_authority_filename);
662         pam_putenv (pam_handle, value);
663         g_free (value);
664     }
665
666     /* Catch terminate signal and pass it to the child */
667     signal (SIGTERM, signal_cb);
668
669     /* Run the command as the authenticated user */
670     uid = user_get_uid (user);
671     gid = user_get_gid (user);
672     home_directory = user_get_home_directory (user);
673     child_pid = fork ();
674     if (child_pid == 0)
675     {
676         /* Make this process its own session */
677         if (setsid () < 0)
678             _exit (errno);
679
680         /* Change to this user */
681         if (getuid () == 0)
682         {
683             if (setgid (gid) != 0)
684                 _exit (errno);
685
686             if (setuid (uid) != 0)
687                 _exit (errno);
688         }
689
690         /* Change working directory */
691         /* NOTE: This must be done after the permissions are changed because NFS filesystems can
692          * be setup so the local root user accesses the NFS files as 'nobody'.  If the home directories
693          * are not system readable then the chdir can fail */
694         if (chdir (home_directory) != 0)
695             _exit (errno);
696
697         if (log_filename)
698         {
699             fd = log_file_open (log_filename, log_mode);
700             if (fd >= 0)
701             {
702                 dup2 (fd, STDERR_FILENO);
703                 close (fd);
704             }
705         }
706
707         /* Run the command */
708         execve (command_argv[0], command_argv, pam_getenvlist (pam_handle));
709         _exit (EXIT_FAILURE);
710     }
711
712     /* Bail out if failed to fork */
713     if (child_pid < 0)
714     {
715         g_printerr ("Failed to fork session child process: %s\n", strerror (errno));
716         return_code = EXIT_FAILURE;
717     }
718
719     /* Wait for the command to complete (blocks) */
720     if (child_pid > 0)
721     {
722         /* Log to utmp */
723         if (g_strcmp0 (pam_getenv (pam_handle, "XDG_SESSION_CLASS"), "greeter") != 0)
724         {
725             struct utmpx ut;
726             struct timeval tv;
727
728             memset (&ut, 0, sizeof (ut));
729             ut.ut_type = USER_PROCESS;
730             ut.ut_pid = child_pid;
731             if (xdisplay)
732                 strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
733             if (tty && g_str_has_prefix (tty, "/dev/"))
734                 strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
735             strncpy (ut.ut_user, username, sizeof (ut.ut_user));
736             if (xdisplay)
737                 strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
738             else if (remote_host_name)
739                 strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
740             gettimeofday (&tv, NULL);
741             ut.ut_tv.tv_sec = tv.tv_sec;
742             ut.ut_tv.tv_usec = tv.tv_usec;
743
744             /* Write records to utmp/wtmp databases */
745             setutxent ();
746             if (!pututxline (&ut))
747                 g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
748             endutxent ();
749             updwtmpx ("/var/log/wtmp", &ut);
750
751 #if HAVE_LIBAUDIT          
752             audit_event (AUDIT_USER_LOGIN, username, uid, remote_host_name, tty, TRUE);
753 #endif
754         }
755
756         waitpid (child_pid, &return_code, 0);
757         child_pid = 0;
758
759         /* Log to utmp */
760         if (g_strcmp0 (pam_getenv (pam_handle, "XDG_SESSION_CLASS"), "greeter") != 0)
761         {
762             struct utmpx ut;
763             struct timeval tv;
764
765             memset (&ut, 0, sizeof (ut));
766             ut.ut_type = DEAD_PROCESS;
767             ut.ut_pid = child_pid;
768             if (xdisplay)
769                 strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
770             if (tty && g_str_has_prefix (tty, "/dev/"))
771                 strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
772             strncpy (ut.ut_user, username, sizeof (ut.ut_user));
773             if (xdisplay)
774                 strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
775             else if (remote_host_name)
776                 strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
777             gettimeofday (&tv, NULL);
778             ut.ut_tv.tv_sec = tv.tv_sec;
779             ut.ut_tv.tv_usec = tv.tv_usec;
780
781             /* Write records to utmp/wtmp databases */
782             setutxent ();
783             if (!pututxline (&ut))
784                 g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
785             endutxent ();
786             updwtmpx ("/var/log/wtmp", &ut);
787
788 #if HAVE_LIBAUDIT
789             audit_event (AUDIT_USER_LOGOUT, username, uid, remote_host_name, tty, TRUE);
790 #endif
791         }
792     }
793
794     /* Remove X authority */
795     if (x_authority)
796     {
797         gboolean drop_privileges, result;
798         GError *error = NULL;
799
800         drop_privileges = geteuid () == 0;
801         if (drop_privileges)
802             privileges_drop (user_get_uid (user), user_get_gid (user));
803         result = x_authority_write (x_authority, XAUTH_WRITE_MODE_REMOVE, x_authority_filename, &error);
804         if (drop_privileges)
805             privileges_reclaim ();
806
807         if (error)
808             g_printerr ("Error removing X authority: %s\n", error->message);
809         g_clear_error (&error);
810         if (!result)
811             _exit (EXIT_FAILURE);
812     }
813
814     /* Close the Console Kit session */
815     if (console_kit_cookie)
816         ck_close_session (console_kit_cookie);
817
818     /* Close the session */
819     pam_close_session (pam_handle, 0);
820
821     /* Remove credentials */
822     pam_setcred (pam_handle, PAM_DELETE_CRED);
823
824     pam_end (pam_handle, 0);
825     pam_handle = NULL;
826
827     /* Return result of session process to the daemon */
828     return return_code;
829 }