su.c revision 121236
124139Sjoerg/*
224139Sjoerg * Copyright (c) 1988, 1993, 1994
324139Sjoerg *	The Regents of the University of California.  All rights reserved.
424139Sjoerg * Copyright (c) 2002 Networks Associates Technologies, Inc.
524139Sjoerg * All rights reserved.
624139Sjoerg *
724139Sjoerg * Portions of this software were developed for the FreeBSD Project by
824139Sjoerg * ThinkSec AS and NAI Labs, the Security Research Division of Network
924139Sjoerg * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
1024139Sjoerg * ("CBOSS"), as part of the DARPA CHATS research program.
1124139Sjoerg *
1289757Sdwmalone * Redistribution and use in source and binary forms, with or without
1389757Sdwmalone * modification, are permitted provided that the following conditions
1489757Sdwmalone * are met:
1566641Simp * 1. Redistributions of source code must retain the above copyright
1666641Simp *    notice, this list of conditions and the following disclaimer.
1724139Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1824139Sjoerg *    notice, this list of conditions and the following disclaimer in the
1924139Sjoerg *    documentation and/or other materials provided with the distribution.
2024139Sjoerg * 3. All advertising materials mentioning features or use of this software
2124139Sjoerg *    must display the following acknowledgement:
2224139Sjoerg *	This product includes software developed by the University of
2324139Sjoerg *	California, Berkeley and its contributors.
2424139Sjoerg * 4. Neither the name of the University nor the names of its contributors
2524139Sjoerg *    may be used to endorse or promote products derived from this software
2624139Sjoerg *    without specific prior written permission.
2724139Sjoerg *
2824139Sjoerg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2924139Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3024139Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3124139Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3224139Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3324139Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3424139Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3524139Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3624139Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3786042Sdwmalone * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3824139Sjoerg * SUCH DAMAGE.
3924139Sjoerg */
4024139Sjoerg
4124139Sjoerg#ifndef lint
4224139Sjoergstatic const char copyright[] =
4324139Sjoerg"@(#) Copyright (c) 1988, 1993, 1994\n\
4424139Sjoerg	The Regents of the University of California.  All rights reserved.\n";
4524139Sjoerg#endif /* not lint */
4624139Sjoerg
4724139Sjoerg#ifndef lint
4824139Sjoerg#if 0
4924139Sjoergstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
5024139Sjoerg#endif
5124139Sjoergstatic const char rcsid[] =
5224139Sjoerg  "$FreeBSD: head/usr.bin/su/su.c 121236 2003-10-19 02:09:36Z cognet $";
5324139Sjoerg#endif /* not lint */
5424139Sjoerg
5524139Sjoerg#include <sys/param.h>
5624139Sjoerg#include <sys/time.h>
5724139Sjoerg#include <sys/resource.h>
5824139Sjoerg#include <sys/wait.h>
5924139Sjoerg
6024139Sjoerg#include <err.h>
6124139Sjoerg#include <errno.h>
6224139Sjoerg#include <grp.h>
6324139Sjoerg#include <libutil.h>
6424139Sjoerg#include <login_cap.h>
6524139Sjoerg#include <paths.h>
6624139Sjoerg#include <pwd.h>
6724139Sjoerg#include <signal.h>
68168710Sstas#include <stdio.h>
69175420Speter#include <stdlib.h>
70168710Sstas#include <string.h>
7124139Sjoerg#include <syslog.h>
7224139Sjoerg#include <unistd.h>
7324139Sjoerg
7424139Sjoerg#include <security/pam_appl.h>
7524139Sjoerg#include <security/openpam.h>
7624139Sjoerg
7724139Sjoerg#define PAM_END() do {							\
7824139Sjoerg	int local_ret;							\
7981187Skris	if (pamh != NULL) {						\
8081187Skris		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
8181187Skris		if (local_ret != PAM_SUCCESS)				\
8281187Skris			syslog(LOG_ERR, "pam_setcred: %s",		\
8324139Sjoerg				pam_strerror(pamh, local_ret));		\
8424139Sjoerg		if (asthem) {						\
8524139Sjoerg			local_ret = pam_close_session(pamh, 0);		\
8624139Sjoerg			if (local_ret != PAM_SUCCESS)			\
8724139Sjoerg				syslog(LOG_ERR, "pam_close_session: %s",\
8824139Sjoerg					pam_strerror(pamh, local_ret));	\
8924139Sjoerg		}							\
90145073Skeramida		local_ret = pam_end(pamh, local_ret);			\
9124139Sjoerg		if (local_ret != PAM_SUCCESS)				\
9224139Sjoerg			syslog(LOG_ERR, "pam_end: %s",			\
9324139Sjoerg				pam_strerror(pamh, local_ret));		\
9424139Sjoerg	}								\
9524139Sjoerg} while (0)
9624139Sjoerg
9724139Sjoerg
9824139Sjoerg#define PAM_SET_ITEM(what, item) do {					\
9924139Sjoerg	int local_ret;							\
10024139Sjoerg	local_ret = pam_set_item(pamh, what, item);			\
10124139Sjoerg	if (local_ret != PAM_SUCCESS) {					\
102133817Salfred		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
10324139Sjoerg			pam_strerror(pamh, local_ret));			\
10424139Sjoerg		errx(1, "pam_set_item(" #what "): %s",			\
105131829Skeramida			pam_strerror(pamh, local_ret));			\
10624139Sjoerg	}								\
10724139Sjoerg} while (0)
10824139Sjoerg
10924139Sjoergenum tristate { UNSET, YES, NO };
11024139Sjoerg
11124139Sjoergstatic pam_handle_t *pamh = NULL;
11224139Sjoergstatic char	**environ_pam;
11324139Sjoerg
11424139Sjoergstatic char	*ontty(void);
11524139Sjoergstatic int	chshell(char *);
11624139Sjoergstatic void	usage(void);
11724139Sjoergstatic int	export_pam_environment(void);
11824139Sjoergstatic int	ok_to_export(const char *);
11924139Sjoerg
12024139Sjoergextern char	**environ;
12124139Sjoerg
12224139Sjoergint
12324139Sjoergmain(int argc, char *argv[])
12424139Sjoerg{
12524142Sjoerg	struct passwd	*pwd;
12624142Sjoerg	struct pam_conv	conv = { openpam_ttyconv, NULL };
12724139Sjoerg	enum tristate	iscsh;
12824139Sjoerg	login_cap_t	*lc;
12924139Sjoerg	union {
13024139Sjoerg		const char	**a;
13124139Sjoerg		char		* const *b;
13224139Sjoerg	}		np;
13324139Sjoerg	uid_t		ruid;
13424139Sjoerg	pid_t		child_pid, child_pgrp, pid;
13524139Sjoerg	int		asme, ch, asthem, fastlogin, prio, i, setwhat, retcode,
13624139Sjoerg			statusp, setmaclabel;
13724139Sjoerg	char		*username, *cleanenv, *class, shellbuf[MAXPATHLEN];
13824139Sjoerg	const char	*p, *user, *shell, *mytty, **nargv;
13924142Sjoerg	struct sigaction sa, sa_int, sa_quit, sa_pipe;
14024139Sjoerg	int temp, fds[2];
14124139Sjoerg
14224139Sjoerg	shell = class = cleanenv = NULL;
14324139Sjoerg	asme = asthem = fastlogin = statusp = 0;
14424139Sjoerg	user = "root";
14524139Sjoerg	iscsh = UNSET;
14624139Sjoerg	setmaclabel = 0;
14724139Sjoerg
14824139Sjoerg	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
14924139Sjoerg		switch ((char)ch) {
15024139Sjoerg		case 'f':
15124139Sjoerg			fastlogin = 1;
15224139Sjoerg			break;
15324139Sjoerg		case '-':
15424139Sjoerg		case 'l':
15524139Sjoerg			asme = 0;
15624139Sjoerg			asthem = 1;
15724139Sjoerg			break;
15824139Sjoerg		case 'm':
15924139Sjoerg			asme = 1;
16024139Sjoerg			asthem = 0;
16124139Sjoerg			break;
16224139Sjoerg		case 's':
16324139Sjoerg			setmaclabel = 1;
16424139Sjoerg			break;
16586042Sdwmalone		case 'c':
16624139Sjoerg			class = optarg;
16724139Sjoerg			break;
16824139Sjoerg		case '?':
16924139Sjoerg		default:
17024139Sjoerg			usage();
17124139Sjoerg		}
17224139Sjoerg
17324139Sjoerg	if (optind < argc)
17424139Sjoerg		user = argv[optind++];
17524139Sjoerg
17624139Sjoerg	if (user == NULL)
17724139Sjoerg		usage();
17824139Sjoerg
17924139Sjoerg	if (strlen(user) > MAXLOGNAME - 1)
18024139Sjoerg		errx(1, "username too long");
18124139Sjoerg
18224139Sjoerg	nargv = malloc(sizeof(char *) * (argc + 4));
18324139Sjoerg	if (nargv == NULL)
18424139Sjoerg		errx(1, "malloc failure");
18524139Sjoerg
18624139Sjoerg	nargv[argc + 3] = NULL;
18724139Sjoerg	for (i = argc; i >= optind; i--)
18824139Sjoerg		nargv[i + 3] = argv[i];
18924139Sjoerg	np.a = &nargv[i + 3];
19024139Sjoerg
19124139Sjoerg	argv += optind;
19224139Sjoerg
19324139Sjoerg	errno = 0;
19489757Sdwmalone	prio = getpriority(PRIO_PROCESS, 0);
19524139Sjoerg	if (errno)
19624139Sjoerg		prio = 0;
19724139Sjoerg
19824139Sjoerg	setpriority(PRIO_PROCESS, 0, -2);
199223936Sjhb	openlog("su", LOG_CONS, LOG_AUTH);
20024139Sjoerg
201223936Sjhb	/* get current login name, real uid and shell */
20224139Sjoerg	ruid = getuid();
20324139Sjoerg	username = getlogin();
20424139Sjoerg	pwd = getpwnam(username);
20524139Sjoerg	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
20624139Sjoerg		pwd = getpwuid(ruid);
20724139Sjoerg	if (pwd == NULL)
20824139Sjoerg		errx(1, "who are you?");
20924139Sjoerg
21024139Sjoerg	username = strdup(pwd->pw_name);
21124139Sjoerg	if (username == NULL)
21224139Sjoerg		err(1, "strdup failure");
21324139Sjoerg
21424139Sjoerg	if (asme) {
21524139Sjoerg		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
21624139Sjoerg			/* must copy - pwd memory is recycled */
21724139Sjoerg			shell = strncpy(shellbuf, pwd->pw_shell,
21824139Sjoerg			    sizeof(shellbuf));
21924139Sjoerg			shellbuf[sizeof(shellbuf) - 1] = '\0';
22038090Sdes		}
221117709Sjulian		else {
222131402Salfred			shell = _PATH_BSHELL;
223132005Salfred			iscsh = NO;
224146342Skeramida		}
225168710Sstas	}
226168799Srafan
227222530Sjhb	/* Do the whole PAM startup thing */
228223936Sjhb	retcode = pam_start("su", user, &conv, &pamh);
22924139Sjoerg	if (retcode != PAM_SUCCESS) {
230223936Sjhb		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
23124139Sjoerg		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
23224139Sjoerg	}
23324139Sjoerg
23424139Sjoerg	PAM_SET_ITEM(PAM_RUSER, username);
23589757Sdwmalone
23689757Sdwmalone	mytty = ttyname(STDERR_FILENO);
23724139Sjoerg	if (!mytty)
23824139Sjoerg		mytty = "tty";
23924139Sjoerg	PAM_SET_ITEM(PAM_TTY, mytty);
24024139Sjoerg
24124139Sjoerg	retcode = pam_authenticate(pamh, 0);
24224139Sjoerg	if (retcode != PAM_SUCCESS) {
24324139Sjoerg#if 0
24424139Sjoerg		syslog(LOG_ERR, "pam_authenticate: %s",
24524139Sjoerg		    pam_strerror(pamh, retcode));
24624139Sjoerg#endif
24724139Sjoerg		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
24824139Sjoerg		    username, user, mytty);
24924139Sjoerg		errx(1, "Sorry");
25024139Sjoerg	}
25124139Sjoerg	retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
25224139Sjoerg	if (retcode == PAM_SUCCESS)
25324139Sjoerg		user = p;
25424139Sjoerg	else
25524139Sjoerg		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
25624139Sjoerg		    pam_strerror(pamh, retcode));
25738090Sdes
25824139Sjoerg	retcode = pam_acct_mgmt(pamh, 0);
25924139Sjoerg	if (retcode == PAM_NEW_AUTHTOK_REQD) {
260117709Sjulian		retcode = pam_chauthtok(pamh,
261146342Skeramida			PAM_CHANGE_EXPIRED_AUTHTOK);
262168799Srafan		if (retcode != PAM_SUCCESS) {
263222530Sjhb			syslog(LOG_ERR, "pam_chauthtok: %s",
26424139Sjoerg			    pam_strerror(pamh, retcode));
26524139Sjoerg			errx(1, "Sorry");
26624139Sjoerg		}
26724139Sjoerg	}
26824139Sjoerg	if (retcode != PAM_SUCCESS) {
26924139Sjoerg		syslog(LOG_ERR, "pam_acct_mgmt: %s",
27024139Sjoerg			pam_strerror(pamh, retcode));
27124139Sjoerg		errx(1, "Sorry");
27224139Sjoerg	}
27324139Sjoerg
27424139Sjoerg	/* get target login information, default to root */
27524139Sjoerg	pwd = getpwnam(user);
27624139Sjoerg	if (pwd == NULL)
27724139Sjoerg		errx(1, "unknown login: %s", user);
27824139Sjoerg	if (class == NULL)
27924139Sjoerg		lc = login_getpwclass(pwd);
28024139Sjoerg	else {
28124139Sjoerg		if (ruid != 0)
28224139Sjoerg			errx(1, "only root may use -c");
28324139Sjoerg		lc = login_getclass(class);
28424139Sjoerg		if (lc == NULL)
28524139Sjoerg			errx(1, "unknown class: %s", class);
28624139Sjoerg	}
28724139Sjoerg
28824139Sjoerg	/* if asme and non-standard target shell, must be root */
289222530Sjhb	if (asme) {
29024139Sjoerg		if (ruid != 0 && !chshell(pwd->pw_shell))
29124139Sjoerg			errx(1, "permission denied (shell).");
29224139Sjoerg	}
29389757Sdwmalone	else if (pwd->pw_shell && *pwd->pw_shell) {
29489757Sdwmalone		shell = pwd->pw_shell;
29589757Sdwmalone		iscsh = UNSET;
29689757Sdwmalone	}
29789757Sdwmalone	else {
29889757Sdwmalone		shell = _PATH_BSHELL;
29924139Sjoerg		iscsh = NO;
30024139Sjoerg	}
30124139Sjoerg
30224139Sjoerg	/* if we're forking a csh, we want to slightly muck the args */
30324139Sjoerg	if (iscsh == UNSET) {
30424139Sjoerg		p = strrchr(shell, '/');
30524139Sjoerg		if (p)
30624139Sjoerg			++p;
30724139Sjoerg		else
30824139Sjoerg			p = shell;
30924139Sjoerg		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
31024139Sjoerg	}
31124139Sjoerg	setpriority(PRIO_PROCESS, 0, prio);
31224139Sjoerg
31324139Sjoerg	/* Switch to home directory */
31424139Sjoerg	if (asthem) {
31524139Sjoerg		if (chdir(pwd->pw_dir) < 0)
31624139Sjoerg			errx(1, "no directory");
31724139Sjoerg	}
31824139Sjoerg
31924139Sjoerg	/*
32024139Sjoerg	 * PAM modules might add supplementary groups in pam_setcred(), so
32124139Sjoerg	 * initialize them first.
32224139Sjoerg	 */
32324139Sjoerg	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
32424139Sjoerg		err(1, "setusercontext");
32524139Sjoerg
32624139Sjoerg	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
32724139Sjoerg	if (retcode != PAM_SUCCESS) {
328168710Sstas		syslog(LOG_ERR, "pam_setcred: %s",
329168710Sstas		    pam_strerror(pamh, retcode));
330168710Sstas		errx(1, "failed to establish credentials.");
331168710Sstas	}
33224139Sjoerg	if (asthem) {
33324139Sjoerg		retcode = pam_open_session(pamh, 0);
33424139Sjoerg		if (retcode != PAM_SUCCESS) {
33524139Sjoerg			syslog(LOG_ERR, "pam_open_session: %s",
33624139Sjoerg			    pam_strerror(pamh, retcode));
33724139Sjoerg			errx(1, "failed to open session.");
33824139Sjoerg		}
33924139Sjoerg	}
34024139Sjoerg
34124139Sjoerg	/*
34224139Sjoerg	 * We must fork() before setuid() because we need to call
34324139Sjoerg	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
34424139Sjoerg	 */
34524139Sjoerg	sa.sa_flags = SA_RESTART;
34624139Sjoerg	sa.sa_handler = SIG_IGN;
34789757Sdwmalone	sigemptyset(&sa.sa_mask);
34824139Sjoerg	sigaction(SIGINT, &sa, &sa_int);
34924139Sjoerg	sigaction(SIGQUIT, &sa, &sa_quit);
35089757Sdwmalone	sigaction(SIGPIPE, &sa, &sa_pipe);
35124139Sjoerg	sa.sa_handler = SIG_DFL;
35224139Sjoerg	sigaction(SIGTSTP, &sa, NULL);
35324139Sjoerg	statusp = 1;
35424139Sjoerg	if (pipe(fds) == -1) {
35524139Sjoerg		err(1, "pipe");
35624139Sjoerg		PAM_END();
35724139Sjoerg		exit(1);
35824139Sjoerg	}
35924139Sjoerg	child_pid = fork();
36024139Sjoerg	switch (child_pid) {
36124139Sjoerg	default:
36224139Sjoerg		close(fds[0]);
36324139Sjoerg		setpgid(child_pid, child_pid);
36424139Sjoerg		tcsetpgrp(1, child_pid);
36524139Sjoerg		close(fds[1]);
36624139Sjoerg		sigaction(SIGPIPE, &sa_pipe, NULL);
36724139Sjoerg		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
36824139Sjoerg			if (WIFSTOPPED(statusp)) {
36924139Sjoerg				kill(getpid(), SIGSTOP);
37024139Sjoerg				child_pgrp = getpgid(child_pid);
37124139Sjoerg				tcsetpgrp(1, child_pgrp);
37224139Sjoerg				kill(child_pid, SIGCONT);
373131616Sdes				statusp = 1;
374131402Salfred				continue;
375131402Salfred			}
376131402Salfred			break;
377131402Salfred		}
378131402Salfred		if (pid == -1)
379131402Salfred			err(1, "waitpid");
380131402Salfred		PAM_END();
381131402Salfred		exit(statusp);
382131402Salfred	case -1:
383131402Salfred		err(1, "fork");
384131402Salfred		PAM_END();
385131402Salfred		exit(1);
386131402Salfred	case 0:
38724139Sjoerg		close(fds[1]);
38824139Sjoerg		read(fds[0], &temp, 1);
38924139Sjoerg		close(fds[0]);
39024139Sjoerg		sigaction(SIGPIPE, &sa_pipe, NULL);
39124139Sjoerg		sigaction(SIGINT, &sa_int, NULL);
39224139Sjoerg		sigaction(SIGQUIT, &sa_quit, NULL);
39324139Sjoerg
39424139Sjoerg		/*
39524139Sjoerg		 * Set all user context except for: Environmental variables
39624139Sjoerg		 * Umask Login records (wtmp, etc) Path
39724139Sjoerg		 */
39838090Sdes		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
39938090Sdes			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
40038090Sdes			   LOGIN_SETMAC);
401146342Skeramida		/*
402146342Skeramida		 * If -s is present, also set the MAC label.
403146342Skeramida		 */
404146342Skeramida		if (setmaclabel)
405146342Skeramida			setwhat |= LOGIN_SETMAC;
406117709Sjulian		/*
407117709Sjulian		 * Don't touch resource/priority settings if -m has been used
408117709Sjulian		 * or -l and -c hasn't, and we're not su'ing to root.
409146342Skeramida		 */
410168799Srafan		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
411168799Srafan			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
412168799Srafan		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
413168799Srafan			err(1, "setusercontext");
414175420Speter
415223936Sjhb		if (!asme) {
416175420Speter			if (asthem) {
417175420Speter				p = getenv("TERM");
418222530Sjhb				environ = &cleanenv;
419222530Sjhb			}
420222530Sjhb
421222530Sjhb			if (asthem || pwd->pw_uid)
42224139Sjoerg				setenv("USER", pwd->pw_name, 1);
423157842Sru			setenv("HOME", pwd->pw_dir, 1);
424157842Sru			setenv("SHELL", shell, 1);
425222530Sjhb
426157842Sru			if (asthem) {
42724139Sjoerg				/*
42824139Sjoerg				 * Add any environmental variables that the
42924139Sjoerg				 * PAM modules may have set.
43024139Sjoerg				 */
43124139Sjoerg				environ_pam = pam_getenvlist(pamh);
43224139Sjoerg				if (environ_pam)
43324139Sjoerg					export_pam_environment();
43424139Sjoerg
43524139Sjoerg				/* set the su'd user's environment & umask */
43624139Sjoerg				setusercontext(lc, pwd, pwd->pw_uid,
43724139Sjoerg					LOGIN_SETPATH | LOGIN_SETUMASK |
43824139Sjoerg					LOGIN_SETENV);
43924139Sjoerg				if (p)
44024139Sjoerg					setenv("TERM", p, 1);
44124139Sjoerg			}
44224139Sjoerg		}
44324139Sjoerg		login_close(lc);
44424139Sjoerg
44524139Sjoerg		if (iscsh == YES) {
44624139Sjoerg			if (fastlogin)
44724139Sjoerg				*np.a-- = "-f";
44824139Sjoerg			if (asme)
44924139Sjoerg				*np.a-- = "-m";
45024139Sjoerg		}
45124139Sjoerg		/* csh strips the first character... */
45224139Sjoerg		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
45324139Sjoerg
45424139Sjoerg		if (ruid != 0)
45524139Sjoerg			syslog(LOG_NOTICE, "%s to %s%s", username, user,
45624139Sjoerg			    ontty());
45724139Sjoerg
45824139Sjoerg		execv(shell, np.b);
45924139Sjoerg		err(1, "%s", shell);
46024139Sjoerg	}
46124139Sjoerg}
46224139Sjoerg
46324139Sjoergstatic int
46424139Sjoergexport_pam_environment(void)
465175195Sobrien{
46624139Sjoerg	char	**pp;
46724139Sjoerg
46824139Sjoerg	for (pp = environ_pam; *pp != NULL; pp++) {
46924139Sjoerg		if (ok_to_export(*pp))
47024139Sjoerg			putenv(*pp);
47124139Sjoerg		free(*pp);
47224139Sjoerg	}
47324139Sjoerg	return PAM_SUCCESS;
47424139Sjoerg}
47524139Sjoerg
47624139Sjoerg/*
47724139Sjoerg * Sanity checks on PAM environmental variables:
47824139Sjoerg * - Make sure there is an '=' in the string.
47924139Sjoerg * - Make sure the string doesn't run on too long.
48024139Sjoerg * - Do not export certain variables.  This list was taken from the
48124139Sjoerg *   Solaris pam_putenv(3) man page.
48224139Sjoerg * Note that if the user is chrooted, PAM may have a better idea than we
48324139Sjoerg * do of where her home directory is.
48424139Sjoerg */
48524139Sjoergstatic int
48624139Sjoergok_to_export(const char *s)
48724139Sjoerg{
48824139Sjoerg	static const char *noexport[] = {
48924139Sjoerg		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
49024139Sjoerg		"IFS", "PATH", NULL
49124139Sjoerg	};
49224139Sjoerg	const char **pp;
49324139Sjoerg	size_t n;
49424139Sjoerg
49524139Sjoerg	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
49624139Sjoerg		return 0;
49724139Sjoerg	if (strncmp(s, "LD_", 3) == 0)
49824139Sjoerg		return 0;
49924139Sjoerg	for (pp = noexport; *pp != NULL; pp++) {
50024139Sjoerg		n = strlen(*pp);
50124139Sjoerg		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
50224139Sjoerg			return 0;
50324139Sjoerg	}
50424139Sjoerg	return 1;
50524139Sjoerg}
50624139Sjoerg
50724139Sjoergstatic void
50824139Sjoergusage(void)
50924139Sjoerg{
51024139Sjoerg
51124139Sjoerg	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
51224139Sjoerg	exit(1);
51324139Sjoerg}
51424139Sjoerg
51524139Sjoergstatic int
51624139Sjoergchshell(char *sh)
51724139Sjoerg{
51824139Sjoerg	int r;
51924139Sjoerg	char *cp;
52024139Sjoerg
52124139Sjoerg	r = 0;
52224139Sjoerg	setusershell();
52324139Sjoerg	while ((cp = getusershell()) != NULL && !r)
52424139Sjoerg	    r = (strcmp(cp, sh) == 0);
52524139Sjoerg	endusershell();
52624139Sjoerg	return r;
52724139Sjoerg}
52824139Sjoerg
52924139Sjoergstatic char *
53024139Sjoergontty(void)
53124139Sjoerg{
53224139Sjoerg	char *p;
53324139Sjoerg	static char buf[MAXPATHLEN + 4];
53424139Sjoerg
53524139Sjoerg	buf[0] = 0;
53624139Sjoerg	p = ttyname(STDERR_FILENO);
53724139Sjoerg	if (p)
53824139Sjoerg		snprintf(buf, sizeof(buf), " on %s", p);
53924139Sjoerg	return buf;
54024139Sjoerg}
54124139Sjoerg