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