]> granicus.if.org Git - linux-pam/blob - modules/pam_xauth/pam_xauth.c
Fix gratuitous use of strdup and x_strdup
[linux-pam] / modules / pam_xauth / pam_xauth.c
1 /*
2  * Copyright 2001-2003 Red Hat, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, and the entire permission notice in its entirety,
9  *    including the disclaimer of warranties.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote
14  *    products derived from this software without specific prior
15  *    written permission.
16  *
17  * ALTERNATIVELY, this product may be distributed under the terms of
18  * the GNU Public License, in which case the provisions of the GPL are
19  * required INSTEAD OF the above restrictions.  (This clause is
20  * necessary due to a potential bad interaction between the GPL and
21  * the restrictions contained in a BSD-style copyright.)
22  *
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include "config.h"
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <fnmatch.h>
44 #include <grp.h>
45 #include <limits.h>
46 #include <netdb.h>
47 #include <pwd.h>
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <syslog.h>
53 #include <unistd.h>
54
55 #define PAM_SM_SESSION
56
57 #include <security/pam_modules.h>
58 #include <security/_pam_macros.h>
59 #include <security/pam_modutil.h>
60 #include <security/pam_ext.h>
61
62 #ifdef WITH_SELINUX
63 #include <selinux/selinux.h>
64 #include <selinux/label.h>
65 #include <sys/stat.h>
66 #endif
67
68 #define DATANAME "pam_xauth_cookie_file"
69 #define XAUTHENV "XAUTHORITY"
70 #define HOMEENV  "HOME"
71 #define XAUTHDEF ".Xauthority"
72 #define XAUTHTMP ".xauthXXXXXX"
73
74 /* Hurd compatibility */
75 #ifndef PATH_MAX
76 #define PATH_MAX 4096
77 #endif
78
79 /* Possible paths to xauth executable */
80 static const char * const xauthpaths[] = {
81 #ifdef PAM_PATH_XAUTH
82         PAM_PATH_XAUTH,
83 #endif
84         "/usr/X11R6/bin/xauth",
85         "/usr/bin/xauth",
86         "/usr/bin/X11/xauth"
87 };
88
89 /* Run a given command (with a NULL-terminated argument list), feeding it the
90  * given input on stdin, and storing any output it generates. */
91 static int
92 run_coprocess(pam_handle_t *pamh, const char *input, char **output,
93               uid_t uid, gid_t gid, const char *command, ...)
94 {
95         int ipipe[2], opipe[2], i;
96         char buf[LINE_MAX];
97         pid_t child;
98         char *buffer = NULL;
99         size_t buffer_size = 0;
100         va_list ap;
101
102         *output = NULL;
103
104         /* Create stdio pipery. */
105         if (pipe(ipipe) == -1) {
106                 pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
107                 return -1;
108         }
109         if (pipe(opipe) == -1) {
110                 pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
111                 close(ipipe[0]);
112                 close(ipipe[1]);
113                 return -1;
114         }
115
116         /* Fork off a child. */
117         child = fork();
118         if (child == -1) {
119                 pam_syslog(pamh, LOG_ERR, "Could not fork: %m");
120                 close(ipipe[0]);
121                 close(ipipe[1]);
122                 close(opipe[0]);
123                 close(opipe[1]);
124                 return -1;
125         }
126
127         if (child == 0) {
128                 /* We're the child. */
129                 size_t j;
130                 const char *args[10];
131                 int maxopened;
132                 /* Drop privileges. */
133                 if (setgid(gid) == -1)
134                   {
135                     int err = errno;
136                     pam_syslog (pamh, LOG_ERR, "setgid(%lu) failed: %m",
137                                 (unsigned long) getegid ());
138                     _exit (err);
139                   }
140                 if (setgroups(0, NULL) == -1)
141                   {
142                     int err = errno;
143                     pam_syslog (pamh, LOG_ERR, "setgroups() failed: %m");
144                     _exit (err);
145                   }
146                 if (setuid(uid) == -1)
147                   {
148                     int err = errno;
149                     pam_syslog (pamh, LOG_ERR, "setuid(%lu) failed: %m",
150                                 (unsigned long) geteuid ());
151                     _exit (err);
152                   }
153                 /* Initialize the argument list. */
154                 memset(args, 0, sizeof(args));
155                 /* Set the pipe descriptors up as stdin and stdout, and close
156                  * everything else, including the original values for the
157                  * descriptors. */
158                 dup2(ipipe[0], STDIN_FILENO);
159                 dup2(opipe[1], STDOUT_FILENO);
160                 maxopened = (int)sysconf(_SC_OPEN_MAX);
161                 for (i = 0; i < maxopened; i++) {
162                         if ((i != STDIN_FILENO) && (i != STDOUT_FILENO)) {
163                                 close(i);
164                         }
165                 }
166                 /* Convert the varargs list into a regular array of strings. */
167                 va_start(ap, command);
168                 args[0] = command;
169                 for (j = 1; j < ((sizeof(args) / sizeof(args[0])) - 1); j++) {
170                         args[j] = va_arg(ap, const char*);
171                         if (args[j] == NULL) {
172                                 break;
173                         }
174                 }
175                 /* Run the command. */
176                 execv(command, (char *const *) args);
177                 /* Never reached. */
178                 _exit(1);
179         }
180
181         /* We're the parent, so close the other ends of the pipes. */
182         close(ipipe[0]);
183         close(opipe[1]);
184         /* Send input to the process (if we have any), then send an EOF. */
185         if (input) {
186                 (void)pam_modutil_write(ipipe[1], input, strlen(input));
187         }
188         close(ipipe[1]);
189
190         /* Read data output until we run out of stuff to read. */
191         i = pam_modutil_read(opipe[0], buf, sizeof(buf));
192         while ((i != 0) && (i != -1)) {
193                 char *tmp;
194                 /* Resize the buffer to hold the data. */
195                 tmp = realloc(buffer, buffer_size + i + 1);
196                 if (tmp == NULL) {
197                         /* Uh-oh, bail. */
198                         if (buffer != NULL) {
199                                 free(buffer);
200                         }
201                         close(opipe[0]);
202                         waitpid(child, NULL, 0);
203                         return -1;
204                 }
205                 /* Save the new buffer location, copy the newly-read data into
206                  * the buffer, and make sure the result will be
207                  * nul-terminated. */
208                 buffer = tmp;
209                 memcpy(buffer + buffer_size, buf, i);
210                 buffer[buffer_size + i] = '\0';
211                 buffer_size += i;
212                 /* Try to read again. */
213                 i = pam_modutil_read(opipe[0], buf, sizeof(buf));
214         }
215         /* No more data.  Clean up and return data. */
216         close(opipe[0]);
217         *output = buffer;
218         waitpid(child, NULL, 0);
219         return 0;
220 }
221
222 /* Free a data item. */
223 static void
224 cleanup (pam_handle_t *pamh UNUSED, void *data, int err UNUSED)
225 {
226         free (data);
227 }
228
229 /* Check if we want to allow export to the other user, or import from the
230  * other user. */
231 static int
232 check_acl(pam_handle_t *pamh,
233           const char *sense, const char *this_user, const char *other_user,
234           int noent_code, int debug)
235 {
236         char path[PATH_MAX];
237         struct passwd *pwd;
238         FILE *fp = NULL;
239         int i, fd = -1, save_errno;
240         struct stat st;
241         PAM_MODUTIL_DEF_PRIVS(privs);
242
243         /* Check this user's <sense> file. */
244         pwd = pam_modutil_getpwnam(pamh, this_user);
245         if (pwd == NULL) {
246                 pam_syslog(pamh, LOG_ERR,
247                            "error determining home directory for '%s'",
248                            this_user);
249                 return PAM_SESSION_ERR;
250         }
251         /* Figure out what that file is really named. */
252         i = snprintf(path, sizeof(path), "%s/.xauth/%s", pwd->pw_dir, sense);
253         if ((i >= (int)sizeof(path)) || (i < 0)) {
254                 pam_syslog(pamh, LOG_ERR,
255                            "name of user's home directory is too long");
256                 return PAM_SESSION_ERR;
257         }
258         if (pam_modutil_drop_priv(pamh, &privs, pwd))
259                 return PAM_SESSION_ERR;
260         if (!stat(path, &st)) {
261                 if (!S_ISREG(st.st_mode))
262                         errno = EINVAL;
263                 else
264                         fd = open(path, O_RDONLY | O_NOCTTY);
265         }
266         save_errno = errno;
267         if (pam_modutil_regain_priv(pamh, &privs)) {
268                 if (fd >= 0)
269                         close(fd);
270                 return PAM_SESSION_ERR;
271         }
272         if (fd >= 0) {
273                 if (!fstat(fd, &st)) {
274                         if (!S_ISREG(st.st_mode))
275                                 errno = EINVAL;
276                         else
277                                 fp = fdopen(fd, "r");
278                 }
279                 if (!fp) {
280                         save_errno = errno;
281                         close(fd);
282                 }
283         }
284         if (fp) {
285                 char buf[LINE_MAX], *tmp;
286                 /* Scan the file for a list of specs of users to "trust". */
287                 while (fgets(buf, sizeof(buf), fp) != NULL) {
288                         tmp = memchr(buf, '\r', sizeof(buf));
289                         if (tmp != NULL) {
290                                 *tmp = '\0';
291                         }
292                         tmp = memchr(buf, '\n', sizeof(buf));
293                         if (tmp != NULL) {
294                                 *tmp = '\0';
295                         }
296                         if (fnmatch(buf, other_user, 0) == 0) {
297                                 if (debug) {
298                                         pam_syslog(pamh, LOG_DEBUG,
299                                                    "%s %s allowed by %s",
300                                                    other_user, sense, path);
301                                 }
302                                 fclose(fp);
303                                 return PAM_SUCCESS;
304                         }
305                 }
306                 /* If there's no match in the file, we fail. */
307                 if (debug) {
308                         pam_syslog(pamh, LOG_DEBUG, "%s not listed in %s",
309                                    other_user, path);
310                 }
311                 fclose(fp);
312                 return PAM_PERM_DENIED;
313         } else {
314                 /* Default to okay if the file doesn't exist. */
315                 errno = save_errno;
316                 switch (errno) {
317                 case ENOENT:
318                         if (noent_code == PAM_SUCCESS) {
319                                 if (debug) {
320                                         pam_syslog(pamh, LOG_DEBUG,
321                                                    "%s does not exist, ignoring",
322                                                    path);
323                                 }
324                         } else {
325                                 if (debug) {
326                                         pam_syslog(pamh, LOG_DEBUG,
327                                                    "%s does not exist, failing",
328                                                    path);
329                                 }
330                         }
331                         return noent_code;
332                 default:
333                         if (debug) {
334                                 pam_syslog(pamh, LOG_DEBUG,
335                                            "error opening %s: %m", path);
336                         }
337                         return PAM_PERM_DENIED;
338                 }
339         }
340 }
341
342 int
343 pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
344                      int argc, const char **argv)
345 {
346         char *cookiefile = NULL, *xauthority = NULL,
347              *cookie = NULL, *display = NULL, *tmp = NULL,
348              *xauthlocalhostname = NULL;
349         const char *user, *xauth = NULL;
350         struct passwd *tpwd, *rpwd;
351         int fd, i, debug = 0;
352         int retval = PAM_SUCCESS;
353         uid_t systemuser = 499, targetuser = 0;
354
355         /* Parse arguments.  We don't understand many, so no sense in breaking
356          * this into a separate function. */
357         for (i = 0; i < argc; i++) {
358                 if (strcmp(argv[i], "debug") == 0) {
359                         debug = 1;
360                         continue;
361                 }
362                 if (strncmp(argv[i], "xauthpath=", 10) == 0) {
363                         xauth = argv[i] + 10;
364                         continue;
365                 }
366                 if (strncmp(argv[i], "targetuser=", 11) == 0) {
367                         long l = strtol(argv[i] + 11, &tmp, 10);
368                         if ((strlen(argv[i] + 11) > 0) && (*tmp == '\0')) {
369                                 targetuser = l;
370                         } else {
371                                 pam_syslog(pamh, LOG_WARNING,
372                                            "invalid value for targetuser (`%s')",
373                                            argv[i] + 11);
374                         }
375                         continue;
376                 }
377                 if (strncmp(argv[i], "systemuser=", 11) == 0) {
378                         long l = strtol(argv[i] + 11, &tmp, 10);
379                         if ((strlen(argv[i] + 11) > 0) && (*tmp == '\0')) {
380                                 systemuser = l;
381                         } else {
382                                 pam_syslog(pamh, LOG_WARNING,
383                                            "invalid value for systemuser (`%s')",
384                                            argv[i] + 11);
385                         }
386                         continue;
387                 }
388                 pam_syslog(pamh, LOG_WARNING, "unrecognized option `%s'",
389                            argv[i]);
390         }
391
392         if (xauth == NULL) {
393                 size_t j;
394                 for (j = 0; j < sizeof(xauthpaths)/sizeof(xauthpaths[0]); j++) {
395                         if (access(xauthpaths[j], X_OK) == 0) {
396                                 xauth = xauthpaths[j];
397                                 break;
398                         }
399                 }
400                 if (xauth == NULL) {
401                         /* xauth executable not found - nothing to do */
402                         return PAM_SUCCESS;
403                 }
404         }
405
406         /* If DISPLAY isn't set, we don't really care, now do we? */
407         if ((display = getenv("DISPLAY")) == NULL) {
408                 if (debug) {
409                         pam_syslog(pamh, LOG_DEBUG,
410                                    "user has no DISPLAY, doing nothing");
411                 }
412                 return PAM_SUCCESS;
413         }
414
415         /* Read the target user's name. */
416         if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
417                 pam_syslog(pamh, LOG_ERR,
418                            "error determining target user's name");
419                 retval = PAM_SESSION_ERR;
420                 goto cleanup;
421         }
422         rpwd = pam_modutil_getpwuid(pamh, getuid());
423         if (rpwd == NULL) {
424                 pam_syslog(pamh, LOG_ERR,
425                            "error determining invoking user's name");
426                 retval = PAM_SESSION_ERR;
427                 goto cleanup;
428         }
429
430         /* Get the target user's UID and primary GID, which we'll need to set
431          * on the xauthority file we create later on. */
432         tpwd = pam_modutil_getpwnam(pamh, user);
433         if (tpwd == NULL) {
434                 pam_syslog(pamh, LOG_ERR,
435                            "error determining target user's UID");
436                 retval = PAM_SESSION_ERR;
437                 goto cleanup;
438         }
439
440         if (debug) {
441                 pam_syslog(pamh, LOG_DEBUG,
442                            "requesting user %lu/%lu, target user %lu/%lu",
443                            (unsigned long) rpwd->pw_uid,
444                            (unsigned long) rpwd->pw_gid,
445                            (unsigned long) tpwd->pw_uid,
446                            (unsigned long) tpwd->pw_gid);
447         }
448
449         /* If the UID is a system account (and not the superuser), forget
450          * about forwarding keys. */
451         if ((tpwd->pw_uid != 0) &&
452             (tpwd->pw_uid != targetuser) &&
453             (tpwd->pw_uid <= systemuser)) {
454                 if (debug) {
455                         pam_syslog(pamh, LOG_DEBUG,
456                                    "not forwarding cookies to user ID %lu",
457                                    (unsigned long) tpwd->pw_uid);
458                 }
459                 retval = PAM_SESSION_ERR;
460                 goto cleanup;
461         }
462
463
464         /* If current user and the target user are the same, don't
465            check the ACL list, but forward X11 */
466         if (strcmp (rpwd->pw_name, tpwd->pw_name) != 0) {
467
468           /* Check that both users are amenable to this.  By default, this
469            * boils down to this policy:
470            * export(ruser=root): only if <user> is listed in .xauth/export
471            * export(ruser=*) if <user> is listed in .xauth/export, or
472            *                 if .xauth/export does not exist
473            * import(user=*): if <ruser> is listed in .xauth/import, or
474            *                 if .xauth/import does not exist */
475           i = (getuid() != 0 || tpwd->pw_uid == 0) ? PAM_SUCCESS : PAM_PERM_DENIED;
476           i = check_acl(pamh, "export", rpwd->pw_name, user, i, debug);
477           if (i != PAM_SUCCESS) {
478             retval = PAM_SESSION_ERR;
479             goto cleanup;
480           }
481           i = PAM_SUCCESS;
482           i = check_acl(pamh, "import", user, rpwd->pw_name, i, debug);
483           if (i != PAM_SUCCESS) {
484             retval = PAM_SESSION_ERR;
485             goto cleanup;
486           }
487         }  else {
488           if (debug)
489             pam_syslog (pamh, LOG_DEBUG, "current and target user are the same, forward X11");
490         }
491
492         /* Figure out where the source user's .Xauthority file is. */
493         if (getenv(XAUTHENV) != NULL) {
494                 cookiefile = strdup(getenv(XAUTHENV));
495         } else {
496                 cookiefile = malloc(strlen(rpwd->pw_dir) + 1 +
497                                     strlen(XAUTHDEF) + 1);
498                 if (cookiefile == NULL) {
499                         retval = PAM_SESSION_ERR;
500                         goto cleanup;
501                 }
502                 strcpy(cookiefile, rpwd->pw_dir);
503                 strcat(cookiefile, "/");
504                 strcat(cookiefile, XAUTHDEF);
505         }
506         if (debug) {
507                 pam_syslog(pamh, LOG_DEBUG, "reading keys from `%s'",
508                            cookiefile);
509         }
510
511         /* Read the user's .Xauthority file.  Because the current UID is
512          * the original user's UID, this will only fail if something has
513          * gone wrong, or we have no cookies. */
514         if (debug) {
515                 pam_syslog(pamh, LOG_DEBUG,
516                            "running \"%s %s %s %s %s\" as %lu/%lu",
517                            xauth, "-f", cookiefile, "nlist", display,
518                            (unsigned long) getuid(), (unsigned long) getgid());
519         }
520         if (run_coprocess(pamh, NULL, &cookie,
521                           getuid(), getgid(),
522                           xauth, "-f", cookiefile, "nlist", display,
523                           NULL) == 0) {
524 #ifdef WITH_SELINUX
525                 security_context_t context = NULL;
526 #endif
527                 PAM_MODUTIL_DEF_PRIVS(privs);
528
529                 /* Check that we got a cookie.  If not, we get creative. */
530                 if (((cookie == NULL) || (strlen(cookie) == 0)) &&
531                     ((strncmp(display, "localhost:", 10) == 0) ||
532                      (strncmp(display, "localhost/unix:", 15) == 0))) {
533                         char *t, *screen;
534                         size_t tlen, slen;
535                         /* Free the useless cookie string. */
536                         if (cookie != NULL) {
537                                 free(cookie);
538                                 cookie = NULL;
539                         }
540                         /* Allocate enough space to hold an adjusted name. */
541                         tlen = strlen(display) + LINE_MAX + 1;
542                         t = malloc(tlen);
543                         if (t != NULL) {
544                                 memset(t, 0, tlen);
545                                 if (gethostname(t, tlen - 1) != -1) {
546                                         /* Append the protocol and then the
547                                          * screen number. */
548                                         if (strlen(t) < tlen - 6) {
549                                                 strcat(t, "/unix:");
550                                         }
551                                         screen = strchr(display, ':');
552                                         if (screen != NULL) {
553                                                 screen++;
554                                                 slen = strlen(screen);
555                                                 if (strlen(t) + slen < tlen) {
556                                                         strcat(t, screen);
557                                                 }
558                                         }
559                                         if (debug) {
560                                                 pam_syslog(pamh, LOG_DEBUG,
561                                                            "no key for `%s', "
562                                                            "trying `%s'",
563                                                            display, t);
564                                         }
565                                         /* Read the cookie for this display. */
566                                         if (debug) {
567                                                 pam_syslog(pamh, LOG_DEBUG,
568                                                        "running "
569                                                        "\"%s %s %s %s %s\" as "
570                                                        "%lu/%lu",
571                                                        xauth,
572                                                        "-f",
573                                                        cookiefile,
574                                                        "nlist",
575                                                        t,
576                                                        (unsigned long) getuid(),
577                                                        (unsigned long) getgid());
578                                         }
579                                         run_coprocess(pamh, NULL, &cookie,
580                                                       getuid(), getgid(),
581                                                       xauth, "-f", cookiefile,
582                                                       "nlist", t, NULL);
583                                 }
584                                 free(t);
585                                 t = NULL;
586                         }
587                 }
588
589                 /* Check that we got a cookie, this time for real. */
590                 if ((cookie == NULL) || (strlen(cookie) == 0)) {
591                         if (debug) {
592                                 pam_syslog(pamh, LOG_DEBUG, "no key");
593                         }
594                         retval = PAM_SESSION_ERR;
595                         goto cleanup;
596                 }
597
598                 /* Generate the environment variable
599                  * "XAUTHORITY=<homedir>/filename". */
600                 if (asprintf(&xauthority, "%s=%s/%s",
601                              XAUTHENV, tpwd->pw_dir, XAUTHTMP) < 0) {
602                         xauthority = NULL;
603                         if (debug) {
604                                 pam_syslog(pamh, LOG_DEBUG, "out of memory");
605                         }
606                         retval = PAM_SESSION_ERR;
607                         goto cleanup;
608                 }
609
610                 /* Generate a new file to hold the data. */
611                 if (pam_modutil_drop_priv(pamh, &privs, tpwd)) {
612                         retval = PAM_SESSION_ERR;
613                         goto cleanup;
614                 }
615 #ifdef WITH_SELINUX
616                 if (is_selinux_enabled() > 0) {
617                         struct selabel_handle *ctx = selabel_open(SELABEL_CTX_FILE, NULL, 0);
618                         if (ctx != NULL) {
619                                 if (selabel_lookup(ctx, &context,
620                                                    xauthority + sizeof(XAUTHENV), S_IFREG) != 0) {
621                                         pam_syslog(pamh, LOG_WARNING,
622                                                    "could not get SELinux label for '%s'",
623                                                    xauthority + sizeof(XAUTHENV));
624                                 }
625                                 selabel_close(ctx);
626                                 if (setfscreatecon(context)) {
627                                         pam_syslog(pamh, LOG_WARNING,
628                                                    "setfscreatecon(%s) failed: %m", context);
629                                 }
630                         }
631                 }
632 #endif /* WITH_SELINUX */
633                 fd = mkstemp(xauthority + sizeof(XAUTHENV));
634                 if (fd < 0)
635                         pam_syslog(pamh, LOG_ERR,
636                                    "error creating temporary file `%s': %m",
637                                    xauthority + sizeof(XAUTHENV));
638 #ifdef WITH_SELINUX
639                 if (context != NULL) {
640                         free(context);
641                         setfscreatecon(NULL);
642                 }
643 #endif /* WITH_SELINUX */
644                 if (fd >= 0)
645                         close(fd);
646                 if (pam_modutil_regain_priv(pamh, &privs) || fd < 0) {
647                         retval = PAM_SESSION_ERR;
648                         goto cleanup;
649                 }
650
651                 /* Get a copy of the filename to save as a data item for
652                  * removal at session-close time. */
653                 free(cookiefile);
654                 cookiefile = strdup(xauthority + sizeof(XAUTHENV));
655
656                 /* Save the filename. */
657                 if (pam_set_data(pamh, DATANAME, cookiefile, cleanup) != PAM_SUCCESS) {
658                         pam_syslog(pamh, LOG_ERR,
659                                    "error saving name of temporary file `%s'",
660                                    cookiefile);
661                         unlink(cookiefile);
662                         retval = PAM_SESSION_ERR;
663                         goto cleanup;
664                 }
665
666                 /* Set the new variable in the environment. */
667                 if (pam_putenv (pamh, xauthority) != PAM_SUCCESS)
668                         pam_syslog(pamh, LOG_ERR,
669                                    "can't set environment variable '%s'",
670                                    xauthority);
671                 putenv (xauthority); /* The environment owns this string now. */
672                 xauthority = NULL; /* Don't free environment variables. */
673
674                 /* set $DISPLAY in pam handle to make su - work */
675                 {
676                   char *d;
677
678                   if (asprintf(&d, "DISPLAY=%s", display) < 0)
679                     {
680                       pam_syslog(pamh, LOG_ERR, "out of memory");
681                       cookiefile = NULL;
682                       retval = PAM_SESSION_ERR;
683                       goto cleanup;
684                     }
685
686                   if (pam_putenv (pamh, d) != PAM_SUCCESS)
687                     pam_syslog (pamh, LOG_ERR,
688                                 "can't set environment variable '%s'", d);
689                   free (d);
690                 }
691
692                 /* set XAUTHLOCALHOSTNAME to make sure that su - work under gnome */
693                 if ((xauthlocalhostname = getenv("XAUTHLOCALHOSTNAME")) != NULL) {
694                   char *d;
695
696                   if (asprintf(&d, "XAUTHLOCALHOSTNAME=%s", xauthlocalhostname) < 0) {
697                     pam_syslog(pamh, LOG_ERR, "out of memory");
698                     retval = PAM_SESSION_ERR;
699                     goto cleanup;
700                   }
701
702                   if (pam_putenv (pamh, d) != PAM_SUCCESS)
703                     pam_syslog (pamh, LOG_ERR,
704                                 "can't set environment variable '%s'", d);
705                   free (d);
706                 }
707
708                 /* Merge the cookie we read before into the new file. */
709                 if (debug) {
710                         pam_syslog(pamh, LOG_DEBUG,
711                                    "writing key `%s' to temporary file `%s'",
712                                    cookie, cookiefile);
713                 }
714                 if (debug) {
715                         pam_syslog(pamh, LOG_DEBUG,
716                                   "running \"%s %s %s %s %s\" as %lu/%lu",
717                                   xauth, "-f", cookiefile, "nmerge", "-",
718                                   (unsigned long) tpwd->pw_uid,
719                                   (unsigned long) tpwd->pw_gid);
720                 }
721                 run_coprocess(pamh, cookie, &tmp,
722                               tpwd->pw_uid, tpwd->pw_gid,
723                               xauth, "-f", cookiefile, "nmerge", "-", NULL);
724
725                 /* We don't need to keep a copy of these around any more. */
726                 cookiefile = NULL;
727                 free(tmp);
728         }
729 cleanup:
730         /* Unset any old XAUTHORITY variable in the environment. */
731         if (retval != PAM_SUCCESS && getenv (XAUTHENV))
732                 unsetenv (XAUTHENV);
733         free(cookiefile);
734         free(cookie);
735         free(xauthority);
736         return retval;
737 }
738
739 int
740 pam_sm_close_session (pam_handle_t *pamh, int flags UNUSED,
741                       int argc, const char **argv)
742 {
743         int i, debug = 0;
744         const char *user;
745         const void *data;
746         const char *cookiefile;
747         struct passwd *tpwd;
748         PAM_MODUTIL_DEF_PRIVS(privs);
749
750         /* Try to retrieve the name of a file we created when
751          * the session was opened. */
752         if (pam_get_data(pamh, DATANAME, &data) != PAM_SUCCESS)
753                 return PAM_SUCCESS;
754         cookiefile = data;
755
756         /* Parse arguments.  We don't understand many, so
757          * no sense in breaking this into a separate function. */
758         for (i = 0; i < argc; i++) {
759                 if (strcmp(argv[i], "debug") == 0) {
760                         debug = 1;
761                         continue;
762                 }
763                 if (strncmp(argv[i], "xauthpath=", 10) == 0)
764                         continue;
765                 if (strncmp(argv[i], "systemuser=", 11) == 0)
766                         continue;
767                 if (strncmp(argv[i], "targetuser=", 11) == 0)
768                         continue;
769                 pam_syslog(pamh, LOG_WARNING, "unrecognized option `%s'",
770                        argv[i]);
771         }
772
773         if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
774                 pam_syslog(pamh, LOG_ERR,
775                            "error determining target user's name");
776                 return PAM_SESSION_ERR;
777         }
778         if (!(tpwd = pam_modutil_getpwnam(pamh, user))) {
779                 pam_syslog(pamh, LOG_ERR,
780                            "error determining target user's UID");
781                 return PAM_SESSION_ERR;
782         }
783
784         if (debug)
785                 pam_syslog(pamh, LOG_DEBUG, "removing `%s'", cookiefile);
786         if (pam_modutil_drop_priv(pamh, &privs, tpwd))
787                 return PAM_SESSION_ERR;
788         if (unlink(cookiefile) == -1 && errno != ENOENT)
789           pam_syslog(pamh, LOG_WARNING, "Couldn't remove `%s': %m", cookiefile);
790         if (pam_modutil_regain_priv(pamh, &privs))
791                 return PAM_SESSION_ERR;
792
793         return PAM_SUCCESS;
794 }
795
796 /* static module data */
797 #ifdef PAM_STATIC
798 struct pam_module _pam_xauth_modstruct = {
799   "pam_xauth",
800   NULL,
801   NULL,
802   NULL,
803   pam_sm_open_session,
804   pam_sm_close_session,
805   NULL
806 };
807 #endif