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