su.c revision 112695
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 112695 2003-03-27 01:32:51Z davidxu $";
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
7781528Smarkm#define PAM_END() do {						\
7877220Smarkm	int local_ret;						\
7977220Smarkm	if (pamh != NULL && creds_set) {			\
8077220Smarkm		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);	\
8177220Smarkm		if (local_ret != PAM_SUCCESS)			\
8277220Smarkm			syslog(LOG_ERR, "pam_setcred: %s",	\
8377220Smarkm				pam_strerror(pamh, local_ret));	\
8477220Smarkm		local_ret = pam_end(pamh, local_ret);		\
8577220Smarkm		if (local_ret != PAM_SUCCESS)			\
8677220Smarkm			syslog(LOG_ERR, "pam_end: %s",		\
8777220Smarkm				pam_strerror(pamh, local_ret));	\
8877220Smarkm	}							\
8977220Smarkm} while (0)
9074874Smarkm
9174874Smarkm
9277220Smarkm#define PAM_SET_ITEM(what, item) do {				\
9377220Smarkm	int local_ret;						\
9477220Smarkm	local_ret = pam_set_item(pamh, what, item);		\
9577220Smarkm	if (local_ret != PAM_SUCCESS) {				\
9677220Smarkm		syslog(LOG_ERR, "pam_set_item(" #what "): %s",	\
9777220Smarkm			pam_strerror(pamh, local_ret));		\
9877220Smarkm		errx(1, "pam_set_item(" #what "): %s",		\
9977220Smarkm			pam_strerror(pamh, local_ret));		\
10077220Smarkm	}							\
10177220Smarkm} while (0)
1023702Spst
10377220Smarkmenum tristate { UNSET, YES, NO };
1041590Srgrimes
10577220Smarkmstatic pam_handle_t *pamh = NULL;
10677220Smarkmstatic int	creds_set = 0;
10777220Smarkmstatic char	**environ_pam;
10877220Smarkm
10977220Smarkmstatic char	*ontty(void);
11077220Smarkmstatic int	chshell(char *);
11177220Smarkmstatic void	usage(void);
11277220Smarkmstatic int	export_pam_environment(void);
11377220Smarkmstatic int	ok_to_export(const char *);
11477220Smarkm
11577220Smarkmextern char	**environ;
11677220Smarkm
1171590Srgrimesint
11877220Smarkmmain(int argc, char *argv[])
1191590Srgrimes{
12077220Smarkm	struct passwd	*pwd;
12191745Sdes	struct pam_conv	conv = { openpam_ttyconv, NULL };
12277220Smarkm	enum tristate	iscsh;
12377220Smarkm	login_cap_t	*lc;
12483373Smarkm	union {
12583373Smarkm		const char	**a;
12683373Smarkm		char		* const *b;
12797377Sdes	}		np;
12877220Smarkm	uid_t		ruid;
12977220Smarkm	int		asme, ch, asthem, fastlogin, prio, i, setwhat, retcode,
130105758Srwatson			statusp, child_pid, child_pgrp, ret_pid, setmaclabel;
13189746Sdes	char		*username, *cleanenv, *class, shellbuf[MAXPATHLEN];
13283373Smarkm	const char	*p, *user, *shell, *mytty, **nargv;
133112695Sdavidxu	struct sigaction sa, sa_int, sa_quit, sa_pipe;
134112695Sdavidxu	int temp, fds[2];
13598837Sdillon
13677220Smarkm	shell = class = cleanenv = NULL;
13777220Smarkm	asme = asthem = fastlogin = statusp = 0;
13810586Sjoerg	user = "root";
13977220Smarkm	iscsh = UNSET;
140105758Srwatson	setmaclabel = 0;
14177220Smarkm
142105758Srwatson	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
14377220Smarkm		switch ((char)ch) {
1441590Srgrimes		case 'f':
1451590Srgrimes			fastlogin = 1;
1461590Srgrimes			break;
1471590Srgrimes		case '-':
1481590Srgrimes		case 'l':
1491590Srgrimes			asme = 0;
1501590Srgrimes			asthem = 1;
1511590Srgrimes			break;
1521590Srgrimes		case 'm':
1531590Srgrimes			asme = 1;
1541590Srgrimes			asthem = 0;
1551590Srgrimes			break;
156105758Srwatson		case 's':
157105758Srwatson			setmaclabel = 1;
158105758Srwatson			break;
15930793Sguido		case 'c':
16030793Sguido			class = optarg;
16130793Sguido			break;
1621590Srgrimes		case '?':
1631590Srgrimes		default:
16428099Scharnier			usage();
16528099Scharnier		}
16639538Sroberto
16739538Sroberto	if (optind < argc)
16810586Sjoerg		user = argv[optind++];
16910586Sjoerg
17028612Sjoerg	if (user == NULL)
17128612Sjoerg		usage();
17228612Sjoerg
17377220Smarkm	if (strlen(user) > MAXLOGNAME - 1)
17477220Smarkm		errx(1, "username too long");
17510586Sjoerg
17677220Smarkm	nargv = malloc(sizeof(char *) * (argc + 4));
17777220Smarkm	if (nargv == NULL)
17877220Smarkm		errx(1, "malloc failure");
17977220Smarkm
18010586Sjoerg	nargv[argc + 3] = NULL;
18110586Sjoerg	for (i = argc; i >= optind; i--)
18277220Smarkm		nargv[i + 3] = argv[i];
18383373Smarkm	np.a = &nargv[i + 3];
18410586Sjoerg
1851590Srgrimes	argv += optind;
1861590Srgrimes
1871590Srgrimes	errno = 0;
1881590Srgrimes	prio = getpriority(PRIO_PROCESS, 0);
1891590Srgrimes	if (errno)
1901590Srgrimes		prio = 0;
19177220Smarkm
19277220Smarkm	setpriority(PRIO_PROCESS, 0, -2);
19374874Smarkm	openlog("su", LOG_CONS, LOG_AUTH);
1941590Srgrimes
19577220Smarkm	/* get current login name, real uid and shell */
1961590Srgrimes	ruid = getuid();
1971590Srgrimes	username = getlogin();
19877220Smarkm	pwd = getpwnam(username);
19977220Smarkm	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
2001590Srgrimes		pwd = getpwuid(ruid);
2011590Srgrimes	if (pwd == NULL)
2021590Srgrimes		errx(1, "who are you?");
20377220Smarkm
2041590Srgrimes	username = strdup(pwd->pw_name);
2051590Srgrimes	if (username == NULL)
20677220Smarkm		err(1, "strdup failure");
20777220Smarkm
20821646Sdavidn	if (asme) {
20921646Sdavidn		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
21077220Smarkm			/* must copy - pwd memory is recycled */
21177220Smarkm			shell = strncpy(shellbuf, pwd->pw_shell,
21277220Smarkm			    sizeof(shellbuf));
21377220Smarkm			shellbuf[sizeof(shellbuf) - 1] = '\0';
21477220Smarkm		}
21577220Smarkm		else {
2161590Srgrimes			shell = _PATH_BSHELL;
2171590Srgrimes			iscsh = NO;
2181590Srgrimes		}
21921646Sdavidn	}
2201590Srgrimes
22177220Smarkm	/* Do the whole PAM startup thing */
22274874Smarkm	retcode = pam_start("su", user, &conv, &pamh);
22374874Smarkm	if (retcode != PAM_SUCCESS) {
22474874Smarkm		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
22574874Smarkm		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
22674874Smarkm	}
22774874Smarkm
228110456Sdes	PAM_SET_ITEM(PAM_RUSER, username);
22981529Smarkm
23074874Smarkm	mytty = ttyname(STDERR_FILENO);
23174874Smarkm	if (!mytty)
23274874Smarkm		mytty = "tty";
23377220Smarkm	PAM_SET_ITEM(PAM_TTY, mytty);
23477220Smarkm
23577220Smarkm	retcode = pam_authenticate(pamh, 0);
23674874Smarkm	if (retcode != PAM_SUCCESS) {
237105386Smarkm#if 0
23877220Smarkm		syslog(LOG_ERR, "pam_authenticate: %s",
23977220Smarkm		    pam_strerror(pamh, retcode));
240105386Smarkm#endif
241105386Smarkm		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
242105386Smarkm		    username, user, mytty);
24377220Smarkm		errx(1, "Sorry");
24474874Smarkm	}
24577220Smarkm	retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
24677220Smarkm	if (retcode == PAM_SUCCESS)
24777220Smarkm		user = p;
24877220Smarkm	else
24977220Smarkm		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
25077220Smarkm		    pam_strerror(pamh, retcode));
25174874Smarkm
25277220Smarkm	retcode = pam_acct_mgmt(pamh, 0);
25377220Smarkm	if (retcode == PAM_NEW_AUTHTOK_REQD) {
25477220Smarkm		retcode = pam_chauthtok(pamh,
25577220Smarkm			PAM_CHANGE_EXPIRED_AUTHTOK);
25674874Smarkm		if (retcode != PAM_SUCCESS) {
25777220Smarkm			syslog(LOG_ERR, "pam_chauthtok: %s",
25877220Smarkm			    pam_strerror(pamh, retcode));
25974874Smarkm			errx(1, "Sorry");
26074874Smarkm		}
26174874Smarkm	}
26277220Smarkm	if (retcode != PAM_SUCCESS) {
26377220Smarkm		syslog(LOG_ERR, "pam_acct_mgmt: %s",
26477220Smarkm			pam_strerror(pamh, retcode));
26577220Smarkm		errx(1, "Sorry");
26677220Smarkm	}
26774874Smarkm
2681590Srgrimes	/* get target login information, default to root */
26977220Smarkm	pwd = getpwnam(user);
27077220Smarkm	if (pwd == NULL)
2719502Swollman		errx(1, "unknown login: %s", user);
27277220Smarkm	if (class == NULL)
27330793Sguido		lc = login_getpwclass(pwd);
27477220Smarkm	else {
27577220Smarkm		if (ruid != 0)
27630793Sguido			errx(1, "only root may use -c");
27730793Sguido		lc = login_getclass(class);
27830793Sguido		if (lc == NULL)
27930793Sguido			errx(1, "unknown class: %s", class);
28030793Sguido	}
2811590Srgrimes
28277220Smarkm	/* if asme and non-standard target shell, must be root */
2831590Srgrimes	if (asme) {
28477220Smarkm		if (ruid != 0 && !chshell(pwd->pw_shell))
2851590Srgrimes			errx(1, "permission denied (shell).");
28677220Smarkm	}
28777220Smarkm	else if (pwd->pw_shell && *pwd->pw_shell) {
2881590Srgrimes		shell = pwd->pw_shell;
2891590Srgrimes		iscsh = UNSET;
29077220Smarkm	}
29177220Smarkm	else {
2921590Srgrimes		shell = _PATH_BSHELL;
2931590Srgrimes		iscsh = NO;
2941590Srgrimes	}
2951590Srgrimes
2961590Srgrimes	/* if we're forking a csh, we want to slightly muck the args */
2971590Srgrimes	if (iscsh == UNSET) {
29814440Smarkm		p = strrchr(shell, '/');
29914440Smarkm		if (p)
3001590Srgrimes			++p;
3011590Srgrimes		else
3021590Srgrimes			p = shell;
30377220Smarkm		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
3041590Srgrimes	}
30577220Smarkm	setpriority(PRIO_PROCESS, 0, prio);
3061590Srgrimes
30721646Sdavidn	/*
30877220Smarkm	 * PAM modules might add supplementary groups in pam_setcred(), so
30977220Smarkm	 * initialize them first.
31074874Smarkm	 */
31174874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
31274874Smarkm		err(1, "setusercontext");
31374874Smarkm
31474874Smarkm	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
31577220Smarkm	if (retcode != PAM_SUCCESS)
31677220Smarkm		syslog(LOG_ERR, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s",
31777220Smarkm		    pam_strerror(pamh, retcode));
31877220Smarkm	else
31977220Smarkm		creds_set = 1;
32074874Smarkm
32174874Smarkm	/*
32274874Smarkm	 * We must fork() before setuid() because we need to call
32374874Smarkm	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
32474874Smarkm	 */
32598837Sdillon	sa.sa_flags = SA_RESTART;
326105362Stjr	sa.sa_handler = SIG_IGN;
32798837Sdillon	sigemptyset(&sa.sa_mask);
32898837Sdillon	sigaction(SIGINT, &sa, &sa_int);
32998837Sdillon	sigaction(SIGQUIT, &sa, &sa_quit);
330112695Sdavidxu	sigaction(SIGPIPE, &sa, &sa_pipe);
331112085Sdavidxu	sa.sa_handler = SIG_DFL;
332112085Sdavidxu	sigaction(SIGTSTP, &sa, NULL);
33374874Smarkm	statusp = 1;
334112695Sdavidxu	if (pipe(fds) == -1) {
335112695Sdavidxu		err(1, "pipe");
336112695Sdavidxu		PAM_END();
337112695Sdavidxu		exit(1);
338112695Sdavidxu	}
33977220Smarkm	child_pid = fork();
34077220Smarkm	switch (child_pid) {
34174874Smarkm	default:
342112695Sdavidxu		close(fds[0]);
343112695Sdavidxu		setpgid(child_pid, child_pid);
344112695Sdavidxu		tcsetpgrp(1, child_pid);
345112695Sdavidxu		close(fds[1]);
346112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
347112695Sdavidxu		while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) !=
348112695Sdavidxu		        -1) {
34977220Smarkm			if (WIFSTOPPED(statusp)) {
35077220Smarkm				kill(getpid(), SIGSTOP);
351101722Sache				child_pgrp = getpgid(child_pid);
352112695Sdavidxu				tcsetpgrp(1, child_pgrp);
353112695Sdavidxu				kill(child_pid, SIGCONT);
35477220Smarkm				statusp = 1;
35577220Smarkm				continue;
35677220Smarkm			}
35777220Smarkm			break;
35874874Smarkm		}
35977220Smarkm		if (ret_pid == -1)
36077220Smarkm			err(1, "waitpid");
36181528Smarkm		PAM_END();
36277220Smarkm		exit(statusp);
36374874Smarkm	case -1:
36477220Smarkm		err(1, "fork");
36581528Smarkm		PAM_END();
36677220Smarkm		exit(1);
36774874Smarkm	case 0:
368112695Sdavidxu		close(fds[1]);
369112695Sdavidxu		read(fds[0], &temp, 1);
370112695Sdavidxu		close(fds[0]);
371112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
37298837Sdillon		sigaction(SIGINT, &sa_int, NULL);
37398837Sdillon		sigaction(SIGQUIT, &sa_quit, NULL);
374112695Sdavidxu
37577220Smarkm		/*
37677220Smarkm		 * Set all user context except for: Environmental variables
37777220Smarkm		 * Umask Login records (wtmp, etc) Path
37877220Smarkm		 */
37977220Smarkm		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
380105758Srwatson			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
381105758Srwatson			   LOGIN_SETMAC);
38277220Smarkm		/*
383105758Srwatson		 * If -s is present, also set the MAC label.
384105758Srwatson		 */
385105758Srwatson		if (setmaclabel)
386105758Srwatson			setwhat |= LOGIN_SETMAC;
387105758Srwatson		/*
38877220Smarkm		 * Don't touch resource/priority settings if -m has been used
38977220Smarkm		 * or -l and -c hasn't, and we're not su'ing to root.
39077220Smarkm		 */
39177220Smarkm		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
39277220Smarkm			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
39377220Smarkm		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
39477220Smarkm			err(1, "setusercontext");
39574874Smarkm
39677220Smarkm		if (!asme) {
39777220Smarkm			if (asthem) {
39877220Smarkm				p = getenv("TERM");
39977220Smarkm				environ = &cleanenv;
40069427Srwatson
40177220Smarkm				/*
40277220Smarkm				 * Add any environmental variables that the
40377220Smarkm				 * PAM modules may have set.
40477220Smarkm				 */
40577220Smarkm				environ_pam = pam_getenvlist(pamh);
40677220Smarkm				if (environ_pam)
40777220Smarkm					export_pam_environment();
4081590Srgrimes
40977220Smarkm				/* set the su'd user's environment & umask */
41077220Smarkm				setusercontext(lc, pwd, pwd->pw_uid,
41177220Smarkm					LOGIN_SETPATH | LOGIN_SETUMASK |
41277220Smarkm					LOGIN_SETENV);
41377220Smarkm				if (p)
41477220Smarkm					setenv("TERM", p, 1);
41577220Smarkm				if (chdir(pwd->pw_dir) < 0)
41677220Smarkm					errx(1, "no directory");
41777220Smarkm			}
41877220Smarkm			if (asthem || pwd->pw_uid)
41977220Smarkm				setenv("USER", pwd->pw_name, 1);
42077220Smarkm			setenv("HOME", pwd->pw_dir, 1);
42177220Smarkm			setenv("SHELL", shell, 1);
42277220Smarkm		}
42377220Smarkm		login_close(lc);
42474874Smarkm
42577220Smarkm		if (iscsh == YES) {
42677220Smarkm			if (fastlogin)
42783373Smarkm				*np.a-- = "-f";
42877220Smarkm			if (asme)
42983373Smarkm				*np.a-- = "-m";
4301590Srgrimes		}
43177220Smarkm		/* csh strips the first character... */
43283373Smarkm		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
43374874Smarkm
43477220Smarkm		if (ruid != 0)
43577220Smarkm			syslog(LOG_NOTICE, "%s to %s%s", username, user,
43677220Smarkm			    ontty());
43774874Smarkm
43883373Smarkm		execv(shell, np.b);
43977220Smarkm		err(1, "%s", shell);
4401590Srgrimes	}
4411590Srgrimes}
4421590Srgrimes
44374874Smarkmstatic int
44477220Smarkmexport_pam_environment(void)
44574874Smarkm{
44674874Smarkm	char	**pp;
44774874Smarkm
44874874Smarkm	for (pp = environ_pam; *pp != NULL; pp++) {
44974874Smarkm		if (ok_to_export(*pp))
45077220Smarkm			putenv(*pp);
45174874Smarkm		free(*pp);
45274874Smarkm	}
45374874Smarkm	return PAM_SUCCESS;
45474874Smarkm}
45574874Smarkm
45674874Smarkm/*
45774874Smarkm * Sanity checks on PAM environmental variables:
45874874Smarkm * - Make sure there is an '=' in the string.
45974874Smarkm * - Make sure the string doesn't run on too long.
46074874Smarkm * - Do not export certain variables.  This list was taken from the
46174874Smarkm *   Solaris pam_putenv(3) man page.
46274874Smarkm */
46374874Smarkmstatic int
46477220Smarkmok_to_export(const char *s)
46574874Smarkm{
46674874Smarkm	static const char *noexport[] = {
46774874Smarkm		"SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
46874874Smarkm		"IFS", "PATH", NULL
46974874Smarkm	};
47074874Smarkm	const char **pp;
47174874Smarkm	size_t n;
47274874Smarkm
47374874Smarkm	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
47474874Smarkm		return 0;
47574874Smarkm	if (strncmp(s, "LD_", 3) == 0)
47674874Smarkm		return 0;
47774874Smarkm	for (pp = noexport; *pp != NULL; pp++) {
47874874Smarkm		n = strlen(*pp);
47974874Smarkm		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
48074874Smarkm			return 0;
48174874Smarkm	}
48274874Smarkm	return 1;
48374874Smarkm}
48474874Smarkm
48528099Scharnierstatic void
48677220Smarkmusage(void)
48728099Scharnier{
48881702Sru
489105758Srwatson	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
49081702Sru	exit(1);
49128099Scharnier}
49228099Scharnier
49377220Smarkmstatic int
49477220Smarkmchshell(char *sh)
4951590Srgrimes{
49677220Smarkm	int r;
4971590Srgrimes	char *cp;
4981590Srgrimes
49977220Smarkm	r = 0;
50021646Sdavidn	setusershell();
50177220Smarkm	do {
50277220Smarkm		cp = getusershell();
50377220Smarkm		r = strcmp(cp, sh);
50477220Smarkm	} while (!r && cp != NULL);
50521646Sdavidn	endusershell();
50621646Sdavidn	return r;
5071590Srgrimes}
5081590Srgrimes
50977220Smarkmstatic char *
51077220Smarkmontty(void)
5111590Srgrimes{
5121590Srgrimes	char *p;
5131590Srgrimes	static char buf[MAXPATHLEN + 4];
5141590Srgrimes
5151590Srgrimes	buf[0] = 0;
51614440Smarkm	p = ttyname(STDERR_FILENO);
51714440Smarkm	if (p)
5181590Srgrimes		snprintf(buf, sizeof(buf), " on %s", p);
51977220Smarkm	return buf;
5201590Srgrimes}
521