(root)/
Linux-PAM-1.5.3/
modules/
pam_xauth/
pam_xauth.c
       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  }