summaryrefslogtreecommitdiff
path: root/modules/pam_xauth
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_xauth')
-rw-r--r--modules/pam_xauth/Makefile12
-rw-r--r--modules/pam_xauth/README41
-rw-r--r--modules/pam_xauth/pam_xauth.882
-rw-r--r--modules/pam_xauth/pam_xauth.c621
4 files changed, 756 insertions, 0 deletions
diff --git a/modules/pam_xauth/Makefile b/modules/pam_xauth/Makefile
new file mode 100644
index 00000000..385466a2
--- /dev/null
+++ b/modules/pam_xauth/Makefile
@@ -0,0 +1,12 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+include ../../Make.Rules
+
+TITLE=pam_xauth
+MAN8=pam_xauth.8
+
+include ../Simple.Rules
diff --git a/modules/pam_xauth/README b/modules/pam_xauth/README
new file mode 100644
index 00000000..dd65292f
--- /dev/null
+++ b/modules/pam_xauth/README
@@ -0,0 +1,41 @@
+pam_xauth:
+ Forward xauth cookies from user to user, normally used by su, sudo, or
+ userhelper.
+
+ Primitive access control is provided by ~/.xauth/export in the invoking
+ user's home directory and ~/.xauth/import in the target user's home
+ directory.
+
+ If a user has a ~/.xauth/import file, the user will only receive cookies
+ from users listed in the file. If there is no ~/.xauth/import file,
+ the user will accept cookies from any other user.
+
+ If a user has a .xauth/export file, the user will only forward cookies
+ to users listed in the file. If there is no ~/.xauth/export file, and
+ the invoking user is not "root", the user will forward cookies to
+ any other user. If there is no ~/.xauth/export file, and the invoking
+ user is "root", the user will NOT forward cookies to other users.
+
+ Both the import and export files support wildcards (such as "*"). Both
+ the import and export files can be empty, signifying that no users are
+ allowed.
+
+RECOGNIZED ARGUMENTS:
+ debug write debugging messages to syslog
+ xauthpath= the path to the xauth program, by default
+ /usr/X11R6/bin/xauth
+ systemuser= highest user id assigned to system users, defaults
+ to 499 (pam_xauth will refuse to forward creds to
+ target users with id equal to or below this number,
+ except for root and possibly another specified user)
+ targetuser= a target user id which is excepted from the systemuser
+ checks
+
+
+MODULE SERVICES PROVIDED:
+ session open session copies xauth cookie to new user
+ close session deletes copied xauth cookie
+
+AUTHOR:
+ Nalin Dahyabhai <nalin@redhat.com>, based on original version by
+ Michael K. Johnson <johnsonm@redhat.com>
diff --git a/modules/pam_xauth/pam_xauth.8 b/modules/pam_xauth/pam_xauth.8
new file mode 100644
index 00000000..9acb7249
--- /dev/null
+++ b/modules/pam_xauth/pam_xauth.8
@@ -0,0 +1,82 @@
+.\" Copyright 2001,2003 Red Hat, Inc.
+.\" Written by Nalin Dahyabhai <nalin@redhat.com>, based on the original
+.\" version by Michael K. Johnson
+.TH pam_xauth 8 2003/7/24 "Red Hat Linux" "System Administrator's Manual"
+.SH NAME
+pam_xauth \- forward xauth keys between users
+.SH SYNOPSIS
+.B session optional /lib/security/pam_xauth.so \fIarguments\fP
+.SH DESCRIPTION
+pam_xauth.so is designed to forward xauth keys (sometimes referred
+to as "cookies") between users.
+
+Without pam_xauth, when xauth is enabled and a user uses the \fBsu\fP command
+to assume another user's priviledges, that user is no longer able to access
+the original user's X display because the new user does not have the key
+needed to access the display. pam_xauth solves the problem by forwarding the
+key from the user running su (the source user) to the user whose
+identity the source user is assuming (the target user) when the session
+is created, and destroying the key when the session is torn down.
+
+This means, for example, that when you run \fBsu\fP from an xterm sesssion,
+you will be able to run X programs without explicitly dealing with the
+xauth command or ~/.Xauthority files.
+
+pam_xauth will only forward keys if xauth can list a key connected
+to the $DISPLAY environment variable.
+
+Primitive access control is provided by \fB~/.xauth/export\fP in the invoking
+user's home directory and \fB~/.xauth/import\fP in the target user's home
+directory.
+
+If a user has a \fB~/.xauth/import\fP file, the user will only receive cookies
+from users listed in the file. If there is no \fB~/.xauth/import\fP file,
+the user will accept cookies from any other user.
+
+If a user has a \fB.xauth/export\fP file, the user will only forward cookies
+to users listed in the file. If there is no \fB~/.xauth/export\fP file, and
+the invoking user is not \fBroot\fP, the user will forward cookies to
+any other user. If there is no \fB~/.xauth/export\fP file, and the invoking
+user is \fBroot\fP, the user will \fInot\fP forward cookies to other users.
+
+Both the import and export files support wildcards (such as \fI*\fP). Both
+the import and export files can be empty, signifying that no users are allowed.
+
+.SH ARGUMENTS
+.IP debug
+Turns on debugging messages sent to syslog.
+.IP xauthpath=\fI/usr/X11R6/bin/xauth\fP
+Specify the path the xauth program (the default is /usr/X11R6/bin/xauth).
+.IP systemuser=\fInumber\fP
+Specify the highest UID which will be assumed to belong to a "system" user.
+pam_xauth will refuse to forward credentials to users with UID less than or
+equal to this number, except for root and the "targetuser", if specified.
+.IP targetuser=\fInumber\fP
+Specify a single target UID which is exempt from the systemuser check.
+.SH "IMPLEMENTATION DETAILS"
+pam_xauth will work \fIonly\fP if it is used from a setuid application
+in which the getuid() call returns the id of the user running the
+application, and for which PAM can supply the name of the account that
+the user is attempting to assume. The typical application of this
+type is \fBsu\fP. The application must call both pam_open_session() and
+pam_close_session() with the ruid set to the uid of the calling user
+and the euid set to root, and must have provided as the PAM_USER item
+the name of the target user.
+
+pam_xauth calls \fBxauth\fP as the source user to extract the key for
+$DISPLAY, then calls xauth as the target user to merge the key
+into the a temporary database and later remove the database.
+
+pam_xauth cannot be told not to remove the keys when the session
+is closed.
+.SH "SEE ALSO"
+\fI/usr/share/doc/pam*/html/index.html\fP
+.SH FILES
+\fI~/.xauth/import\fP
+\fI~/.xauth/export\fP
+.SH BUGS
+Let's hope not, but if you find any, please report them via the "Bug Track"
+link at http://bugzilla.redhat.com/bugzilla/
+.SH AUTHOR
+Nalin Dahyabhai <nalin@redhat.com>, based on original version by
+Michael K. Johnson <johnsonm@redhat.com>
diff --git a/modules/pam_xauth/pam_xauth.c b/modules/pam_xauth/pam_xauth.c
new file mode 100644
index 00000000..58dd059a
--- /dev/null
+++ b/modules/pam_xauth/pam_xauth.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright 2001-2003 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ident "$Id$"
+
+#include "../../_pam_aconf.h"
+#include <sys/types.h>
+#include <sys/fsuid.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <grp.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/_pam_modutil.h>
+
+#define DATANAME "pam_xauth_cookie_file"
+#define XAUTHBIN "/usr/X11R6/bin/xauth"
+#define XAUTHENV "XAUTHORITY"
+#define HOMEENV "HOME"
+#define XAUTHDEF ".Xauthority"
+#define XAUTHTMP ".xauthXXXXXX"
+
+/* Run a given command (with a NULL-terminated argument list), feeding it the
+ * given input on stdin, and storing any output it generates. */
+static int
+run_coprocess(const char *input, char **output,
+ uid_t uid, gid_t gid, const char *command, ...)
+{
+ int ipipe[2], opipe[2], i;
+ char buf[LINE_MAX];
+ pid_t child;
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+ va_list ap;
+
+ *output = NULL;
+
+ /* Create stdio pipery. */
+ if (pipe(ipipe) == -1) {
+ return -1;
+ }
+ if (pipe(opipe) == -1) {
+ close(ipipe[0]);
+ close(ipipe[1]);
+ return -1;
+ }
+
+ /* Fork off a child. */
+ child = fork();
+ if (child == -1) {
+ close(ipipe[0]);
+ close(ipipe[1]);
+ close(opipe[0]);
+ close(opipe[1]);
+ return -1;
+ }
+
+ if (child == 0) {
+ /* We're the child. */
+ char *args[10];
+ const char *tmp;
+ /* Drop privileges. */
+ setgid(gid);
+ setgroups(0, NULL);
+ setuid(uid);
+ /* Initialize the argument list. */
+ memset(args, 0, sizeof(args));
+ /* Set the pipe descriptors up as stdin and stdout, and close
+ * everything else, including the original values for the
+ * descriptors. */
+ dup2(ipipe[0], STDIN_FILENO);
+ dup2(opipe[1], STDOUT_FILENO);
+ for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) {
+ if ((i != STDIN_FILENO) && (i != STDOUT_FILENO)) {
+ close(i);
+ }
+ }
+ /* Convert the varargs list into a regular array of strings. */
+ va_start(ap, command);
+ args[0] = strdup(command);
+ for (i = 1; i < ((sizeof(args) / sizeof(args[0])) - 1); i++) {
+ tmp = va_arg(ap, const char*);
+ if (tmp == NULL) {
+ break;
+ }
+ args[i] = strdup(tmp);
+ }
+ /* Run the command. */
+ execvp(command, args);
+ /* Never reached. */
+ exit(1);
+ }
+
+ /* We're the parent, so close the other ends of the pipes. */
+ close(ipipe[0]);
+ close(opipe[1]);
+ /* Send input to the process (if we have any), then send an EOF. */
+ if (input) {
+ (void)_pammodutil_write(ipipe[1], input, strlen(input));
+ }
+ close(ipipe[1]);
+
+ /* Read data output until we run out of stuff to read. */
+ i = _pammodutil_read(opipe[0], buf, sizeof(buf));
+ while ((i != 0) && (i != -1)) {
+ char *tmp;
+ /* Resize the buffer to hold the data. */
+ tmp = realloc(buffer, buffer_size + i + 1);
+ if (tmp == NULL) {
+ /* Uh-oh, bail. */
+ if (buffer != NULL) {
+ free(buffer);
+ }
+ close(opipe[0]);
+ waitpid(child, NULL, 0);
+ return -1;
+ }
+ /* Save the new buffer location, copy the newly-read data into
+ * the buffer, and make sure the result will be
+ * nul-terminated. */
+ buffer = tmp;
+ memcpy(buffer + buffer_size, buf, i);
+ buffer[buffer_size + i] = '\0';
+ buffer_size += i;
+ /* Try to read again. */
+ i = _pammodutil_read(opipe[0], buf, sizeof(buf));
+ }
+ /* No more data. Clean up and return data. */
+ close(opipe[0]);
+ *output = buffer;
+ waitpid(child, NULL, 0);
+ return 0;
+}
+
+/* Free a data item. */
+static void
+cleanup(pam_handle_t *pamh, void *data, int err)
+{
+ free(data);
+}
+
+/* Check if we want to allow export to the other user, or import from the
+ * other user. */
+static int
+check_acl(pam_handle_t *pamh,
+ const char *sense, const char *this_user, const char *other_user,
+ int noent_code, int debug)
+{
+ char path[PATH_MAX];
+ struct passwd *pwd;
+ FILE *fp;
+ int i;
+ uid_t euid;
+ /* Check this user's <sense> file. */
+ pwd = _pammodutil_getpwnam(pamh, this_user);
+ if (pwd == NULL) {
+ syslog(LOG_ERR, "pam_xauth: error determining "
+ "home directory for '%s'", this_user);
+ return PAM_SESSION_ERR;
+ }
+ /* Figure out what that file is really named. */
+ i = snprintf(path, sizeof(path), "%s/.xauth/%s", pwd->pw_dir, sense);
+ if ((i >= sizeof(path)) || (i < 0)) {
+ syslog(LOG_ERR, "pam_xauth: name of user's home directory "
+ "is too long");
+ return PAM_SESSION_ERR;
+ }
+ euid = geteuid();
+ setfsuid(pwd->pw_uid);
+ fp = fopen(path, "r");
+ setfsuid(euid);
+ if (fp != NULL) {
+ char buf[LINE_MAX], *tmp;
+ /* Scan the file for a list of specs of users to "trust". */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ tmp = memchr(buf, '\r', sizeof(buf));
+ if (tmp != NULL) {
+ *tmp = '\0';
+ }
+ tmp = memchr(buf, '\n', sizeof(buf));
+ if (tmp != NULL) {
+ *tmp = '\0';
+ }
+ if (fnmatch(buf, other_user, 0) == 0) {
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: %s %s "
+ "allowed by %s",
+ other_user, sense, path);
+ }
+ fclose(fp);
+ return PAM_SUCCESS;
+ }
+ }
+ /* If there's no match in the file, we fail. */
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: %s not listed in %s",
+ other_user, path);
+ }
+ fclose(fp);
+ return PAM_PERM_DENIED;
+ } else {
+ /* Default to okay if the file doesn't exist. */
+ switch (errno) {
+ case ENOENT:
+ if (noent_code == PAM_SUCCESS) {
+ if (debug) {
+ syslog(LOG_DEBUG, "%s does not exist, "
+ "ignoring", path);
+ }
+ } else {
+ if (debug) {
+ syslog(LOG_DEBUG, "%s does not exist, "
+ "failing", path);
+ }
+ }
+ return noent_code;
+ default:
+ if (debug) {
+ syslog(LOG_ERR, "%s opening %s",
+ strerror(errno), path);
+ }
+ return PAM_PERM_DENIED;
+ }
+ }
+}
+
+int
+pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ char xauthpath[] = XAUTHBIN;
+ char *cookiefile = NULL, *xauthority = NULL,
+ *cookie = NULL, *display = NULL, *tmp = NULL;
+ const char *user, *xauth = xauthpath;
+ struct passwd *tpwd, *rpwd;
+ int fd, i, debug = 0;
+ uid_t systemuser = 499, targetuser = 0, euid;
+
+ /* Parse arguments. We don't understand many, so no sense in breaking
+ * this into a separate function. */
+ for (i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "debug") == 0) {
+ debug = 1;
+ continue;
+ }
+ if (strncmp(argv[i], "xauthpath=", 10) == 0) {
+ xauth = argv[i] + 10;
+ continue;
+ }
+ if (strncmp(argv[i], "targetuser=", 11) == 0) {
+ long l = strtol(argv[i] + 11, &tmp, 10);
+ if ((strlen(argv[i] + 11) > 0) && (*tmp == '\0')) {
+ targetuser = l;
+ } else {
+ syslog(LOG_WARNING, "pam_xauth: invalid value "
+ "for targetuser (`%s')", argv[i] + 11);
+ }
+ continue;
+ }
+ if (strncmp(argv[i], "systemuser=", 11) == 0) {
+ long l = strtol(argv[i] + 11, &tmp, 10);
+ if ((strlen(argv[i] + 11) > 0) && (*tmp == '\0')) {
+ systemuser = l;
+ } else {
+ syslog(LOG_WARNING, "pam_xauth: invalid value "
+ "for systemuser (`%s')", argv[i] + 11);
+ }
+ continue;
+ }
+ syslog(LOG_WARNING, "pam_xauth: unrecognized option `%s'",
+ argv[i]);
+ }
+
+ /* If DISPLAY isn't set, we don't really care, now do we? */
+ if ((display = getenv("DISPLAY")) == NULL) {
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: user has no DISPLAY,"
+ " doing nothing");
+ }
+ return PAM_SUCCESS;
+ }
+
+ /* Read the target user's name. */
+ if (pam_get_item(pamh, PAM_USER, (const void**)&user) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_xauth: error determining target "
+ "user's name");
+ return PAM_SESSION_ERR;
+ }
+ rpwd = _pammodutil_getpwuid(pamh, getuid());
+ if (rpwd == NULL) {
+ syslog(LOG_ERR, "pam_xauth: error determining invoking "
+ "user's name");
+ return PAM_SESSION_ERR;
+ }
+
+ /* Get the target user's UID and primary GID, which we'll need to set
+ * on the xauthority file we create later on. */
+ tpwd = _pammodutil_getpwnam(pamh, user);
+ if (tpwd == NULL) {
+ syslog(LOG_ERR, "pam_xauth: error determining target "
+ "user's UID");
+ return PAM_SESSION_ERR;
+ }
+
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: requesting user %lu/%lu, "
+ "target user %lu/%lu",
+ (unsigned long) rpwd->pw_uid,
+ (unsigned long) rpwd->pw_gid,
+ (unsigned long) tpwd->pw_uid,
+ (unsigned long) tpwd->pw_gid);
+ }
+
+ /* If the UID is a system account (and not the superuser), forget
+ * about forwarding keys. */
+ if ((tpwd->pw_uid != 0) &&
+ (tpwd->pw_uid != targetuser) &&
+ (tpwd->pw_uid <= systemuser)) {
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: not forwarding cookies "
+ "to user ID %ld", (long) tpwd->pw_uid);
+ }
+ return PAM_SESSION_ERR;
+ }
+
+ /* Check that both users are amenable to this. By default, this
+ * boils down to this policy:
+ * export(ruser=root): only if <user> is listed in .xauth/export
+ * export(ruser=*) if <user> is listed in .xauth/export, or
+ * if .xauth/export does not exist
+ * import(user=*): if <ruser> is listed in .xauth/import, or
+ * if .xauth/import does not exist */
+ i = (getuid() != 0) ? PAM_SUCCESS : PAM_PERM_DENIED;
+ i = check_acl(pamh, "export", rpwd->pw_name, user, i, debug);
+ if (i != PAM_SUCCESS) {
+ return PAM_SESSION_ERR;
+ }
+ i = PAM_SUCCESS;
+ i = check_acl(pamh, "import", user, rpwd->pw_name, i, debug);
+ if (i != PAM_SUCCESS) {
+ return PAM_SESSION_ERR;
+ }
+
+ /* Figure out where the source user's .Xauthority file is. */
+ if (getenv(XAUTHENV) != NULL) {
+ cookiefile = strdup(getenv(XAUTHENV));
+ } else {
+ cookiefile = malloc(strlen(rpwd->pw_dir) + 1 +
+ strlen(XAUTHDEF) + 1);
+ if (cookiefile == NULL) {
+ return PAM_SESSION_ERR;
+ }
+ strcpy(cookiefile, rpwd->pw_dir);
+ strcat(cookiefile, "/");
+ strcat(cookiefile, XAUTHDEF);
+ }
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: reading keys from `%s'",
+ cookiefile);
+ }
+
+ /* Read the user's .Xauthority file. Because the current UID is
+ * the original user's UID, this will only fail if something has
+ * gone wrong, or we have no cookies. */
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: running \"%s %s %s %s %s\" as "
+ "%lu/%lu",
+ xauth,
+ "-f",
+ cookiefile,
+ "nlist",
+ display,
+ (unsigned long) getuid(),
+ (unsigned long) getgid());
+ }
+ if (run_coprocess(NULL, &cookie,
+ getuid(), getgid(),
+ xauth, "-f", cookiefile, "nlist", display,
+ NULL) == 0) {
+ /* Check that we got a cookie. If not, we get creative. */
+ if (((cookie == NULL) || (strlen(cookie) == 0)) &&
+ ((strncmp(display, "localhost:", 10) == 0) ||
+ (strncmp(display, "localhost/unix:", 15) == 0))) {
+ char *t, *screen;
+ size_t tlen, slen;
+ /* Free the useless cookie string. */
+ if (cookie != NULL) {
+ free(cookie);
+ cookie = NULL;
+ }
+ /* Allocate enough space to hold an adjusted name. */
+ tlen = strlen(display) + LINE_MAX + 1;
+ t = malloc(tlen);
+ if (t != NULL) {
+ memset(t, 0, tlen);
+ if (gethostname(t, tlen - 1) != -1) {
+ /* Append the protocol and then the
+ * screen number. */
+ if (strlen(t) < tlen - 6) {
+ strcat(t, "/unix:");
+ }
+ screen = strchr(display, ':');
+ if (screen != NULL) {
+ screen++;
+ slen = strlen(screen);
+ if (strlen(t) + slen < tlen) {
+ strcat(t, screen);
+ }
+ }
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: "
+ "no key for `%s', trying"
+ " `%s'", display, t);
+ }
+ /* Read the cookie for this display. */
+ if (debug) {
+ syslog(LOG_DEBUG,
+ "pam_xauth: running "
+ "\"%s %s %s %s %s\" as "
+ "%lu/%lu",
+ xauth,
+ "-f",
+ cookiefile,
+ "nlist",
+ t,
+ (unsigned long) getuid(),
+ (unsigned long) getgid());
+ }
+ run_coprocess(NULL, &cookie,
+ getuid(), getgid(),
+ xauth, "-f", cookiefile,
+ "nlist", t, NULL);
+ }
+ free(t);
+ t = NULL;
+ }
+ }
+
+ /* Check that we got a cookie, this time for real. */
+ if ((cookie == NULL) || (strlen(cookie) == 0)) {
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: no key");
+ }
+ return PAM_SESSION_ERR;
+ }
+
+ /* Generate the environment variable
+ * "XAUTHORITY=<homedir>/filename". */
+ xauthority = malloc(strlen(XAUTHENV) + 1 +
+ strlen(tpwd->pw_dir) + 1 +
+ strlen(XAUTHTMP) + 1);
+ if (xauthority == NULL) {
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: no free memory");
+ }
+ free(cookiefile);
+ free(cookie);
+ return PAM_SESSION_ERR;
+ }
+ strcpy(xauthority, XAUTHENV);
+ strcat(xauthority, "=");
+ strcat(xauthority, tpwd->pw_dir);
+ strcat(xauthority, "/");
+ strcat(xauthority, XAUTHTMP);
+
+ /* Generate a new file to hold the data. */
+ euid = geteuid();
+ setfsuid(tpwd->pw_uid);
+ fd = mkstemp(xauthority + strlen(XAUTHENV) + 1);
+ setfsuid(euid);
+ if (fd == -1) {
+ syslog(LOG_ERR, "pam_xauth: error creating "
+ "temporary file `%s': %s",
+ xauthority + strlen(XAUTHENV) + 1,
+ strerror(errno));
+ free(cookiefile);
+ free(cookie);
+ free(xauthority);
+ return PAM_SESSION_ERR;
+ }
+ /* Set permissions on the new file and dispose of the
+ * descriptor. */
+ fchown(fd, tpwd->pw_uid, tpwd->pw_gid);
+ close(fd);
+
+ /* Get a copy of the filename to save as a data item for
+ * removal at session-close time. */
+ free(cookiefile);
+ cookiefile = strdup(xauthority + strlen(XAUTHENV) + 1);
+
+ /* Save the filename. */
+ if (pam_set_data(pamh, DATANAME, cookiefile, cleanup) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_xauth: error saving name of "
+ "temporary file `%s'", cookiefile);
+ unlink(cookiefile);
+ free(xauthority);
+ free(cookiefile);
+ free(cookie);
+ return PAM_SESSION_ERR;
+ }
+
+ /* Unset any old XAUTHORITY variable in the environment. */
+ if (getenv(XAUTHENV)) {
+ unsetenv(XAUTHENV);
+ }
+
+ /* Set the new variable in the environment. */
+ pam_putenv(pamh, xauthority);
+ putenv(xauthority); /* The environment owns this string now. */
+
+ /* Merge the cookie we read before into the new file. */
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: writing key `%s' to "
+ "temporary file `%s'", cookie, cookiefile);
+ }
+ if (debug) {
+ syslog(LOG_DEBUG,
+ "pam_xauth: running \"%s %s %s %s %s\" as "
+ "%lu/%lu",
+ xauth,
+ "-f",
+ cookiefile,
+ "nmerge",
+ "-",
+ (unsigned long) tpwd->pw_uid,
+ (unsigned long) tpwd->pw_gid);
+ }
+ run_coprocess(cookie, &tmp,
+ tpwd->pw_uid, tpwd->pw_gid,
+ xauth, "-f", cookiefile, "nmerge", "-", NULL);
+
+ /* We don't need to keep a copy of these around any more. */
+ free(cookie);
+ cookie = NULL;
+ }
+ return PAM_SUCCESS;
+}
+
+int
+pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ void *cookiefile;
+ int i, debug = 0;
+
+ /* Parse arguments. We don't understand many, so no sense in breaking
+ * this into a separate function. */
+ for (i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "debug") == 0) {
+ debug = 1;
+ continue;
+ }
+ if (strncmp(argv[i], "xauthpath=", 10) == 0) {
+ continue;
+ }
+ if (strncmp(argv[i], "systemuser=", 11) == 0) {
+ continue;
+ }
+ if (strncmp(argv[i], "targetuser=", 11) == 0) {
+ continue;
+ }
+ syslog(LOG_WARNING, "pam_xauth: unrecognized option `%s'",
+ argv[i]);
+ }
+
+ /* Try to retrieve the name of a file we created when the session was
+ * opened. */
+ if (pam_get_data(pamh, DATANAME, (const void**) &cookiefile) == PAM_SUCCESS) {
+ /* We'll only try to remove the file once. */
+ if (strlen((char*)cookiefile) > 0) {
+ if (debug) {
+ syslog(LOG_DEBUG, "pam_xauth: removing `%s'",
+ (char*)cookiefile);
+ }
+ unlink((char*)cookiefile);
+ *((char*)cookiefile) = '\0';
+ }
+ }
+ return PAM_SUCCESS;
+}