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