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