su.c revision 113262
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1988, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
497377Sdes * Copyright (c) 2002 Networks Associates Technologies, Inc.
597377Sdes * All rights reserved.
61590Srgrimes *
797377Sdes * Portions of this software were developed for the FreeBSD Project by
897377Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network
997377Sdes * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
1097377Sdes * ("CBOSS"), as part of the DARPA CHATS research program.
1197377Sdes *
121590Srgrimes * Redistribution and use in source and binary forms, with or without
131590Srgrimes * modification, are permitted provided that the following conditions
141590Srgrimes * are met:
151590Srgrimes * 1. Redistributions of source code must retain the above copyright
161590Srgrimes *    notice, this list of conditions and the following disclaimer.
171590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
181590Srgrimes *    notice, this list of conditions and the following disclaimer in the
191590Srgrimes *    documentation and/or other materials provided with the distribution.
201590Srgrimes * 3. All advertising materials mentioning features or use of this software
211590Srgrimes *    must display the following acknowledgement:
221590Srgrimes *	This product includes software developed by the University of
231590Srgrimes *	California, Berkeley and its contributors.
241590Srgrimes * 4. Neither the name of the University nor the names of its contributors
251590Srgrimes *    may be used to endorse or promote products derived from this software
261590Srgrimes *    without specific prior written permission.
271590Srgrimes *
281590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
291590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
301590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
311590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
321590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
331590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
341590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
351590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
361590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
371590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
381590Srgrimes * SUCH DAMAGE.
391590Srgrimes */
401590Srgrimes
411590Srgrimes#ifndef lint
4214440Smarkmstatic const char copyright[] =
431590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\
441590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
451590Srgrimes#endif /* not lint */
461590Srgrimes
471590Srgrimes#ifndef lint
4828099Scharnier#if 0
491590Srgrimesstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
5028099Scharnier#endif
5114440Smarkmstatic const char rcsid[] =
5250477Speter  "$FreeBSD: head/usr.bin/su/su.c 113262 2003-04-08 16:59:53Z des $";
531590Srgrimes#endif /* not lint */
541590Srgrimes
551590Srgrimes#include <sys/param.h>
561590Srgrimes#include <sys/time.h>
571590Srgrimes#include <sys/resource.h>
5877220Smarkm#include <sys/wait.h>
591590Srgrimes
601590Srgrimes#include <err.h>
611590Srgrimes#include <errno.h>
621590Srgrimes#include <grp.h>
6377220Smarkm#include <libutil.h>
6477220Smarkm#include <login_cap.h>
651590Srgrimes#include <paths.h>
661590Srgrimes#include <pwd.h>
6777220Smarkm#include <signal.h>
681590Srgrimes#include <stdio.h>
691590Srgrimes#include <stdlib.h>
701590Srgrimes#include <string.h>
711590Srgrimes#include <syslog.h>
721590Srgrimes#include <unistd.h>
7321646Sdavidn
7474874Smarkm#include <security/pam_appl.h>
7591745Sdes#include <security/openpam.h>
7674874Smarkm
77113262Sdes#define PAM_END() do {							\
78113262Sdes	int local_ret;							\
79113262Sdes	if (pamh != NULL) {						\
80113262Sdes		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
81113262Sdes		if (local_ret != PAM_SUCCESS)				\
82113262Sdes			syslog(LOG_ERR, "pam_setcred: %s",		\
83113262Sdes				pam_strerror(pamh, local_ret));		\
84113262Sdes		if (asthem) {						\
85113262Sdes			local_ret = pam_close_session(pamh, 0);		\
86113262Sdes			if (local_ret != PAM_SUCCESS)			\
87113262Sdes				syslog(LOG_ERR, "pam_close_session: %s",\
88113262Sdes					pam_strerror(pamh, local_ret));	\
89113262Sdes		}							\
90113262Sdes		local_ret = pam_end(pamh, local_ret);			\
91113262Sdes		if (local_ret != PAM_SUCCESS)				\
92113262Sdes			syslog(LOG_ERR, "pam_end: %s",			\
93113262Sdes				pam_strerror(pamh, local_ret));		\
94113262Sdes	}								\
9577220Smarkm} while (0)
9674874Smarkm
9774874Smarkm
98113262Sdes#define PAM_SET_ITEM(what, item) do {					\
99113262Sdes	int local_ret;							\
100113262Sdes	local_ret = pam_set_item(pamh, what, item);			\
101113262Sdes	if (local_ret != PAM_SUCCESS) {					\
102113262Sdes		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
103113262Sdes			pam_strerror(pamh, local_ret));			\
104113262Sdes		errx(1, "pam_set_item(" #what "): %s",			\
105113262Sdes			pam_strerror(pamh, local_ret));			\
106113262Sdes	}								\
10777220Smarkm} while (0)
1083702Spst
10977220Smarkmenum tristate { UNSET, YES, NO };
1101590Srgrimes
11177220Smarkmstatic pam_handle_t *pamh = NULL;
11277220Smarkmstatic char	**environ_pam;
11377220Smarkm
11477220Smarkmstatic char	*ontty(void);
11577220Smarkmstatic int	chshell(char *);
11677220Smarkmstatic void	usage(void);
11777220Smarkmstatic int	export_pam_environment(void);
11877220Smarkmstatic int	ok_to_export(const char *);
11977220Smarkm
12077220Smarkmextern char	**environ;
12177220Smarkm
1221590Srgrimesint
12377220Smarkmmain(int argc, char *argv[])
1241590Srgrimes{
12577220Smarkm	struct passwd	*pwd;
12691745Sdes	struct pam_conv	conv = { openpam_ttyconv, NULL };
12777220Smarkm	enum tristate	iscsh;
12877220Smarkm	login_cap_t	*lc;
12983373Smarkm	union {
13083373Smarkm		const char	**a;
13183373Smarkm		char		* const *b;
13297377Sdes	}		np;
13377220Smarkm	uid_t		ruid;
134113262Sdes	pid_t		child_pid, child_pgrp, pid;
13577220Smarkm	int		asme, ch, asthem, fastlogin, prio, i, setwhat, retcode,
136113262Sdes			statusp, setmaclabel;
13789746Sdes	char		*username, *cleanenv, *class, shellbuf[MAXPATHLEN];
13883373Smarkm	const char	*p, *user, *shell, *mytty, **nargv;
139112695Sdavidxu	struct sigaction sa, sa_int, sa_quit, sa_pipe;
140112695Sdavidxu	int temp, fds[2];
14198837Sdillon
14277220Smarkm	shell = class = cleanenv = NULL;
14377220Smarkm	asme = asthem = fastlogin = statusp = 0;
14410586Sjoerg	user = "root";
14577220Smarkm	iscsh = UNSET;
146105758Srwatson	setmaclabel = 0;
14777220Smarkm
148105758Srwatson	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
14977220Smarkm		switch ((char)ch) {
1501590Srgrimes		case 'f':
1511590Srgrimes			fastlogin = 1;
1521590Srgrimes			break;
1531590Srgrimes		case '-':
1541590Srgrimes		case 'l':
1551590Srgrimes			asme = 0;
1561590Srgrimes			asthem = 1;
1571590Srgrimes			break;
1581590Srgrimes		case 'm':
1591590Srgrimes			asme = 1;
1601590Srgrimes			asthem = 0;
1611590Srgrimes			break;
162105758Srwatson		case 's':
163105758Srwatson			setmaclabel = 1;
164105758Srwatson			break;
16530793Sguido		case 'c':
16630793Sguido			class = optarg;
16730793Sguido			break;
1681590Srgrimes		case '?':
1691590Srgrimes		default:
17028099Scharnier			usage();
17128099Scharnier		}
17239538Sroberto
17339538Sroberto	if (optind < argc)
17410586Sjoerg		user = argv[optind++];
17510586Sjoerg
17628612Sjoerg	if (user == NULL)
17728612Sjoerg		usage();
17828612Sjoerg
17977220Smarkm	if (strlen(user) > MAXLOGNAME - 1)
18077220Smarkm		errx(1, "username too long");
18110586Sjoerg
18277220Smarkm	nargv = malloc(sizeof(char *) * (argc + 4));
18377220Smarkm	if (nargv == NULL)
18477220Smarkm		errx(1, "malloc failure");
18577220Smarkm
18610586Sjoerg	nargv[argc + 3] = NULL;
18710586Sjoerg	for (i = argc; i >= optind; i--)
18877220Smarkm		nargv[i + 3] = argv[i];
18983373Smarkm	np.a = &nargv[i + 3];
19010586Sjoerg
1911590Srgrimes	argv += optind;
1921590Srgrimes
1931590Srgrimes	errno = 0;
1941590Srgrimes	prio = getpriority(PRIO_PROCESS, 0);
1951590Srgrimes	if (errno)
1961590Srgrimes		prio = 0;
19777220Smarkm
19877220Smarkm	setpriority(PRIO_PROCESS, 0, -2);
19974874Smarkm	openlog("su", LOG_CONS, LOG_AUTH);
2001590Srgrimes
20177220Smarkm	/* get current login name, real uid and shell */
2021590Srgrimes	ruid = getuid();
2031590Srgrimes	username = getlogin();
20477220Smarkm	pwd = getpwnam(username);
20577220Smarkm	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
2061590Srgrimes		pwd = getpwuid(ruid);
2071590Srgrimes	if (pwd == NULL)
2081590Srgrimes		errx(1, "who are you?");
20977220Smarkm
2101590Srgrimes	username = strdup(pwd->pw_name);
2111590Srgrimes	if (username == NULL)
21277220Smarkm		err(1, "strdup failure");
21377220Smarkm
21421646Sdavidn	if (asme) {
21521646Sdavidn		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
21677220Smarkm			/* must copy - pwd memory is recycled */
21777220Smarkm			shell = strncpy(shellbuf, pwd->pw_shell,
21877220Smarkm			    sizeof(shellbuf));
21977220Smarkm			shellbuf[sizeof(shellbuf) - 1] = '\0';
22077220Smarkm		}
22177220Smarkm		else {
2221590Srgrimes			shell = _PATH_BSHELL;
2231590Srgrimes			iscsh = NO;
2241590Srgrimes		}
22521646Sdavidn	}
2261590Srgrimes
22777220Smarkm	/* Do the whole PAM startup thing */
22874874Smarkm	retcode = pam_start("su", user, &conv, &pamh);
22974874Smarkm	if (retcode != PAM_SUCCESS) {
23074874Smarkm		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
23174874Smarkm		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
23274874Smarkm	}
23374874Smarkm
234110456Sdes	PAM_SET_ITEM(PAM_RUSER, username);
23581529Smarkm
23674874Smarkm	mytty = ttyname(STDERR_FILENO);
23774874Smarkm	if (!mytty)
23874874Smarkm		mytty = "tty";
23977220Smarkm	PAM_SET_ITEM(PAM_TTY, mytty);
24077220Smarkm
24177220Smarkm	retcode = pam_authenticate(pamh, 0);
24274874Smarkm	if (retcode != PAM_SUCCESS) {
243105386Smarkm#if 0
24477220Smarkm		syslog(LOG_ERR, "pam_authenticate: %s",
24577220Smarkm		    pam_strerror(pamh, retcode));
246105386Smarkm#endif
247105386Smarkm		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
248105386Smarkm		    username, user, mytty);
24977220Smarkm		errx(1, "Sorry");
25074874Smarkm	}
25177220Smarkm	retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
25277220Smarkm	if (retcode == PAM_SUCCESS)
25377220Smarkm		user = p;
25477220Smarkm	else
25577220Smarkm		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
25677220Smarkm		    pam_strerror(pamh, retcode));
25774874Smarkm
25877220Smarkm	retcode = pam_acct_mgmt(pamh, 0);
25977220Smarkm	if (retcode == PAM_NEW_AUTHTOK_REQD) {
26077220Smarkm		retcode = pam_chauthtok(pamh,
26177220Smarkm			PAM_CHANGE_EXPIRED_AUTHTOK);
26274874Smarkm		if (retcode != PAM_SUCCESS) {
26377220Smarkm			syslog(LOG_ERR, "pam_chauthtok: %s",
26477220Smarkm			    pam_strerror(pamh, retcode));
26574874Smarkm			errx(1, "Sorry");
26674874Smarkm		}
26774874Smarkm	}
26877220Smarkm	if (retcode != PAM_SUCCESS) {
26977220Smarkm		syslog(LOG_ERR, "pam_acct_mgmt: %s",
27077220Smarkm			pam_strerror(pamh, retcode));
27177220Smarkm		errx(1, "Sorry");
27277220Smarkm	}
27374874Smarkm
2741590Srgrimes	/* get target login information, default to root */
27577220Smarkm	pwd = getpwnam(user);
27677220Smarkm	if (pwd == NULL)
2779502Swollman		errx(1, "unknown login: %s", user);
27877220Smarkm	if (class == NULL)
27930793Sguido		lc = login_getpwclass(pwd);
28077220Smarkm	else {
28177220Smarkm		if (ruid != 0)
28230793Sguido			errx(1, "only root may use -c");
28330793Sguido		lc = login_getclass(class);
28430793Sguido		if (lc == NULL)
28530793Sguido			errx(1, "unknown class: %s", class);
28630793Sguido	}
2871590Srgrimes
28877220Smarkm	/* if asme and non-standard target shell, must be root */
2891590Srgrimes	if (asme) {
29077220Smarkm		if (ruid != 0 && !chshell(pwd->pw_shell))
2911590Srgrimes			errx(1, "permission denied (shell).");
29277220Smarkm	}
29377220Smarkm	else if (pwd->pw_shell && *pwd->pw_shell) {
2941590Srgrimes		shell = pwd->pw_shell;
2951590Srgrimes		iscsh = UNSET;
29677220Smarkm	}
29777220Smarkm	else {
2981590Srgrimes		shell = _PATH_BSHELL;
2991590Srgrimes		iscsh = NO;
3001590Srgrimes	}
3011590Srgrimes
3021590Srgrimes	/* if we're forking a csh, we want to slightly muck the args */
3031590Srgrimes	if (iscsh == UNSET) {
30414440Smarkm		p = strrchr(shell, '/');
30514440Smarkm		if (p)
3061590Srgrimes			++p;
3071590Srgrimes		else
3081590Srgrimes			p = shell;
30977220Smarkm		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
3101590Srgrimes	}
31177220Smarkm	setpriority(PRIO_PROCESS, 0, prio);
3121590Srgrimes
313113262Sdes	/* Switch to home directory */
314113262Sdes	if (asthem) {
315113262Sdes		if (chdir(pwd->pw_dir) < 0)
316113262Sdes			errx(1, "no directory");
317113262Sdes	}
318113262Sdes
31921646Sdavidn	/*
32077220Smarkm	 * PAM modules might add supplementary groups in pam_setcred(), so
32177220Smarkm	 * initialize them first.
32274874Smarkm	 */
32374874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
32474874Smarkm		err(1, "setusercontext");
32574874Smarkm
32674874Smarkm	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
327113262Sdes	if (retcode != PAM_SUCCESS) {
328113262Sdes		syslog(LOG_ERR, "pam_setcred: %s",
32977220Smarkm		    pam_strerror(pamh, retcode));
330113262Sdes		errx(1, "failed to establish credentials.");
331113262Sdes	}
332113262Sdes	if (asthem) {
333113262Sdes		retcode = pam_open_session(pamh, 0);
334113262Sdes		if (retcode != PAM_SUCCESS) {
335113262Sdes			syslog(LOG_ERR, "pam_open_session: %s",
336113262Sdes			    pam_strerror(pamh, retcode));
337113262Sdes			errx(1, "failed to open session.");
338113262Sdes		}
339113262Sdes	}
34074874Smarkm
34174874Smarkm	/*
34274874Smarkm	 * We must fork() before setuid() because we need to call
34374874Smarkm	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
34474874Smarkm	 */
34598837Sdillon	sa.sa_flags = SA_RESTART;
346105362Stjr	sa.sa_handler = SIG_IGN;
34798837Sdillon	sigemptyset(&sa.sa_mask);
34898837Sdillon	sigaction(SIGINT, &sa, &sa_int);
34998837Sdillon	sigaction(SIGQUIT, &sa, &sa_quit);
350112695Sdavidxu	sigaction(SIGPIPE, &sa, &sa_pipe);
351112085Sdavidxu	sa.sa_handler = SIG_DFL;
352112085Sdavidxu	sigaction(SIGTSTP, &sa, NULL);
35374874Smarkm	statusp = 1;
354112695Sdavidxu	if (pipe(fds) == -1) {
355112695Sdavidxu		err(1, "pipe");
356112695Sdavidxu		PAM_END();
357112695Sdavidxu		exit(1);
358112695Sdavidxu	}
35977220Smarkm	child_pid = fork();
36077220Smarkm	switch (child_pid) {
36174874Smarkm	default:
362112695Sdavidxu		close(fds[0]);
363112695Sdavidxu		setpgid(child_pid, child_pid);
364112695Sdavidxu		tcsetpgrp(1, child_pid);
365112695Sdavidxu		close(fds[1]);
366112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
367113262Sdes		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
36877220Smarkm			if (WIFSTOPPED(statusp)) {
36977220Smarkm				kill(getpid(), SIGSTOP);
370101722Sache				child_pgrp = getpgid(child_pid);
371112695Sdavidxu				tcsetpgrp(1, child_pgrp);
372112695Sdavidxu				kill(child_pid, SIGCONT);
37377220Smarkm				statusp = 1;
37477220Smarkm				continue;
37577220Smarkm			}
37677220Smarkm			break;
37774874Smarkm		}
378113262Sdes		if (pid == -1)
37977220Smarkm			err(1, "waitpid");
38081528Smarkm		PAM_END();
38177220Smarkm		exit(statusp);
38274874Smarkm	case -1:
38377220Smarkm		err(1, "fork");
38481528Smarkm		PAM_END();
38577220Smarkm		exit(1);
38674874Smarkm	case 0:
387112695Sdavidxu		close(fds[1]);
388112695Sdavidxu		read(fds[0], &temp, 1);
389112695Sdavidxu		close(fds[0]);
390112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
39198837Sdillon		sigaction(SIGINT, &sa_int, NULL);
39298837Sdillon		sigaction(SIGQUIT, &sa_quit, NULL);
393112695Sdavidxu
39477220Smarkm		/*
39577220Smarkm		 * Set all user context except for: Environmental variables
39677220Smarkm		 * Umask Login records (wtmp, etc) Path
39777220Smarkm		 */
39877220Smarkm		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
399105758Srwatson			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
400105758Srwatson			   LOGIN_SETMAC);
40177220Smarkm		/*
402105758Srwatson		 * If -s is present, also set the MAC label.
403105758Srwatson		 */
404105758Srwatson		if (setmaclabel)
405105758Srwatson			setwhat |= LOGIN_SETMAC;
406105758Srwatson		/*
40777220Smarkm		 * Don't touch resource/priority settings if -m has been used
40877220Smarkm		 * or -l and -c hasn't, and we're not su'ing to root.
40977220Smarkm		 */
41077220Smarkm		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
41177220Smarkm			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
41277220Smarkm		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
41377220Smarkm			err(1, "setusercontext");
41474874Smarkm
41577220Smarkm		if (!asme) {
41677220Smarkm			if (asthem) {
41777220Smarkm				p = getenv("TERM");
41877220Smarkm				environ = &cleanenv;
419113262Sdes			}
42069427Srwatson
421113262Sdes			if (asthem || pwd->pw_uid)
422113262Sdes				setenv("USER", pwd->pw_name, 1);
423113262Sdes			setenv("HOME", pwd->pw_dir, 1);
424113262Sdes			setenv("SHELL", shell, 1);
425113262Sdes
426113262Sdes			if (asthem) {
42777220Smarkm				/*
42877220Smarkm				 * Add any environmental variables that the
42977220Smarkm				 * PAM modules may have set.
43077220Smarkm				 */
43177220Smarkm				environ_pam = pam_getenvlist(pamh);
43277220Smarkm				if (environ_pam)
43377220Smarkm					export_pam_environment();
4341590Srgrimes
43577220Smarkm				/* set the su'd user's environment & umask */
43677220Smarkm				setusercontext(lc, pwd, pwd->pw_uid,
43777220Smarkm					LOGIN_SETPATH | LOGIN_SETUMASK |
43877220Smarkm					LOGIN_SETENV);
43977220Smarkm				if (p)
44077220Smarkm					setenv("TERM", p, 1);
44177220Smarkm			}
44277220Smarkm		}
44377220Smarkm		login_close(lc);
44474874Smarkm
44577220Smarkm		if (iscsh == YES) {
44677220Smarkm			if (fastlogin)
44783373Smarkm				*np.a-- = "-f";
44877220Smarkm			if (asme)
44983373Smarkm				*np.a-- = "-m";
4501590Srgrimes		}
45177220Smarkm		/* csh strips the first character... */
45283373Smarkm		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
45374874Smarkm
45477220Smarkm		if (ruid != 0)
45577220Smarkm			syslog(LOG_NOTICE, "%s to %s%s", username, user,
45677220Smarkm			    ontty());
45774874Smarkm
45883373Smarkm		execv(shell, np.b);
45977220Smarkm		err(1, "%s", shell);
4601590Srgrimes	}
4611590Srgrimes}
4621590Srgrimes
46374874Smarkmstatic int
46477220Smarkmexport_pam_environment(void)
46574874Smarkm{
46674874Smarkm	char	**pp;
46774874Smarkm
46874874Smarkm	for (pp = environ_pam; *pp != NULL; pp++) {
46974874Smarkm		if (ok_to_export(*pp))
47077220Smarkm			putenv(*pp);
47174874Smarkm		free(*pp);
47274874Smarkm	}
47374874Smarkm	return PAM_SUCCESS;
47474874Smarkm}
47574874Smarkm
47674874Smarkm/*
47774874Smarkm * Sanity checks on PAM environmental variables:
47874874Smarkm * - Make sure there is an '=' in the string.
47974874Smarkm * - Make sure the string doesn't run on too long.
48074874Smarkm * - Do not export certain variables.  This list was taken from the
48174874Smarkm *   Solaris pam_putenv(3) man page.
482113262Sdes * Note that if the user is chrooted, PAM may have a better idea than we
483113262Sdes * do of where her home directory is.
48474874Smarkm */
48574874Smarkmstatic int
48677220Smarkmok_to_export(const char *s)
48774874Smarkm{
48874874Smarkm	static const char *noexport[] = {
489113262Sdes		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
49074874Smarkm		"IFS", "PATH", NULL
49174874Smarkm	};
49274874Smarkm	const char **pp;
49374874Smarkm	size_t n;
49474874Smarkm
49574874Smarkm	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
49674874Smarkm		return 0;
49774874Smarkm	if (strncmp(s, "LD_", 3) == 0)
49874874Smarkm		return 0;
49974874Smarkm	for (pp = noexport; *pp != NULL; pp++) {
50074874Smarkm		n = strlen(*pp);
50174874Smarkm		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
50274874Smarkm			return 0;
50374874Smarkm	}
50474874Smarkm	return 1;
50574874Smarkm}
50674874Smarkm
50728099Scharnierstatic void
50877220Smarkmusage(void)
50928099Scharnier{
51081702Sru
511105758Srwatson	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
51281702Sru	exit(1);
51328099Scharnier}
51428099Scharnier
51577220Smarkmstatic int
51677220Smarkmchshell(char *sh)
5171590Srgrimes{
51877220Smarkm	int r;
5191590Srgrimes	char *cp;
5201590Srgrimes
52177220Smarkm	r = 0;
52221646Sdavidn	setusershell();
52377220Smarkm	do {
52477220Smarkm		cp = getusershell();
52577220Smarkm		r = strcmp(cp, sh);
52677220Smarkm	} while (!r && cp != NULL);
52721646Sdavidn	endusershell();
52821646Sdavidn	return r;
5291590Srgrimes}
5301590Srgrimes
53177220Smarkmstatic char *
53277220Smarkmontty(void)
5331590Srgrimes{
5341590Srgrimes	char *p;
5351590Srgrimes	static char buf[MAXPATHLEN + 4];
5361590Srgrimes
5371590Srgrimes	buf[0] = 0;
53814440Smarkm	p = ttyname(STDERR_FILENO);
53914440Smarkm	if (p)
5401590Srgrimes		snprintf(buf, sizeof(buf), " on %s", p);
54177220Smarkm	return buf;
5421590Srgrimes}
543