su.c revision 130409
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
47127848Scharnier#if 0
481590Srgrimes#ifndef lint
491590Srgrimesstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
50127848Scharnier#endif /* not lint */
5128099Scharnier#endif
521590Srgrimes
53127848Scharnier#include <sys/cdefs.h>
54127848Scharnier__FBSDID("$FreeBSD: head/usr.bin/su/su.c 130409 2004-06-13 11:21:06Z markm $");
55127848Scharnier
561590Srgrimes#include <sys/param.h>
571590Srgrimes#include <sys/time.h>
581590Srgrimes#include <sys/resource.h>
5977220Smarkm#include <sys/wait.h>
601590Srgrimes
611590Srgrimes#include <err.h>
621590Srgrimes#include <errno.h>
631590Srgrimes#include <grp.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));			\
106130409Smarkm		/* NOTREACHED */					\
107113262Sdes	}								\
10877220Smarkm} while (0)
1093702Spst
11077220Smarkmenum tristate { UNSET, YES, NO };
1111590Srgrimes
11277220Smarkmstatic pam_handle_t *pamh = NULL;
11377220Smarkmstatic char	**environ_pam;
11477220Smarkm
11577220Smarkmstatic char	*ontty(void);
116130409Smarkmstatic int	chshell(const char *);
117130409Smarkmstatic void	usage(void) __dead2;
118130409Smarkmstatic void	export_pam_environment(void);
11977220Smarkmstatic int	ok_to_export(const char *);
12077220Smarkm
12177220Smarkmextern char	**environ;
12277220Smarkm
1231590Srgrimesint
12477220Smarkmmain(int argc, char *argv[])
1251590Srgrimes{
126130409Smarkm	static char	*cleanenv;
12777220Smarkm	struct passwd	*pwd;
12891745Sdes	struct pam_conv	conv = { openpam_ttyconv, NULL };
12977220Smarkm	enum tristate	iscsh;
13077220Smarkm	login_cap_t	*lc;
13183373Smarkm	union {
13283373Smarkm		const char	**a;
13383373Smarkm		char		* const *b;
13497377Sdes	}		np;
13577220Smarkm	uid_t		ruid;
136113262Sdes	pid_t		child_pid, child_pgrp, pid;
137130409Smarkm	int		asme, ch, asthem, fastlogin, prio, i, retcode,
138113262Sdes			statusp, setmaclabel;
139130409Smarkm	u_int		setwhat;
140130409Smarkm	char		*username, *class, shellbuf[MAXPATHLEN];
14183373Smarkm	const char	*p, *user, *shell, *mytty, **nargv;
142112695Sdavidxu	struct sigaction sa, sa_int, sa_quit, sa_pipe;
143112695Sdavidxu	int temp, fds[2];
14498837Sdillon
14577220Smarkm	shell = class = cleanenv = NULL;
14677220Smarkm	asme = asthem = fastlogin = statusp = 0;
14710586Sjoerg	user = "root";
14877220Smarkm	iscsh = UNSET;
149105758Srwatson	setmaclabel = 0;
15077220Smarkm
151105758Srwatson	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
15277220Smarkm		switch ((char)ch) {
1531590Srgrimes		case 'f':
1541590Srgrimes			fastlogin = 1;
1551590Srgrimes			break;
1561590Srgrimes		case '-':
1571590Srgrimes		case 'l':
1581590Srgrimes			asme = 0;
1591590Srgrimes			asthem = 1;
1601590Srgrimes			break;
1611590Srgrimes		case 'm':
1621590Srgrimes			asme = 1;
1631590Srgrimes			asthem = 0;
1641590Srgrimes			break;
165105758Srwatson		case 's':
166105758Srwatson			setmaclabel = 1;
167105758Srwatson			break;
16830793Sguido		case 'c':
16930793Sguido			class = optarg;
17030793Sguido			break;
1711590Srgrimes		case '?':
1721590Srgrimes		default:
17328099Scharnier			usage();
174130409Smarkm		/* NOTREACHED */
17528099Scharnier		}
17639538Sroberto
17739538Sroberto	if (optind < argc)
17810586Sjoerg		user = argv[optind++];
17910586Sjoerg
18028612Sjoerg	if (user == NULL)
18128612Sjoerg		usage();
182130409Smarkm	/* NOTREACHED */
18328612Sjoerg
18477220Smarkm	if (strlen(user) > MAXLOGNAME - 1)
18577220Smarkm		errx(1, "username too long");
18610586Sjoerg
187130409Smarkm	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
18877220Smarkm	if (nargv == NULL)
18977220Smarkm		errx(1, "malloc failure");
19077220Smarkm
19110586Sjoerg	nargv[argc + 3] = NULL;
19210586Sjoerg	for (i = argc; i >= optind; i--)
19377220Smarkm		nargv[i + 3] = argv[i];
19483373Smarkm	np.a = &nargv[i + 3];
19510586Sjoerg
1961590Srgrimes	argv += optind;
1971590Srgrimes
1981590Srgrimes	errno = 0;
1991590Srgrimes	prio = getpriority(PRIO_PROCESS, 0);
2001590Srgrimes	if (errno)
2011590Srgrimes		prio = 0;
20277220Smarkm
20377220Smarkm	setpriority(PRIO_PROCESS, 0, -2);
20474874Smarkm	openlog("su", LOG_CONS, LOG_AUTH);
2051590Srgrimes
20677220Smarkm	/* get current login name, real uid and shell */
2071590Srgrimes	ruid = getuid();
2081590Srgrimes	username = getlogin();
20977220Smarkm	pwd = getpwnam(username);
21077220Smarkm	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
2111590Srgrimes		pwd = getpwuid(ruid);
2121590Srgrimes	if (pwd == NULL)
2131590Srgrimes		errx(1, "who are you?");
21477220Smarkm
2151590Srgrimes	username = strdup(pwd->pw_name);
2161590Srgrimes	if (username == NULL)
21777220Smarkm		err(1, "strdup failure");
21877220Smarkm
21921646Sdavidn	if (asme) {
22021646Sdavidn		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
22177220Smarkm			/* must copy - pwd memory is recycled */
22277220Smarkm			shell = strncpy(shellbuf, pwd->pw_shell,
22377220Smarkm			    sizeof(shellbuf));
22477220Smarkm			shellbuf[sizeof(shellbuf) - 1] = '\0';
22577220Smarkm		}
22677220Smarkm		else {
2271590Srgrimes			shell = _PATH_BSHELL;
2281590Srgrimes			iscsh = NO;
2291590Srgrimes		}
23021646Sdavidn	}
2311590Srgrimes
23277220Smarkm	/* Do the whole PAM startup thing */
23374874Smarkm	retcode = pam_start("su", user, &conv, &pamh);
23474874Smarkm	if (retcode != PAM_SUCCESS) {
23574874Smarkm		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
23674874Smarkm		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
23774874Smarkm	}
23874874Smarkm
239110456Sdes	PAM_SET_ITEM(PAM_RUSER, username);
24081529Smarkm
24174874Smarkm	mytty = ttyname(STDERR_FILENO);
24274874Smarkm	if (!mytty)
24374874Smarkm		mytty = "tty";
24477220Smarkm	PAM_SET_ITEM(PAM_TTY, mytty);
24577220Smarkm
24677220Smarkm	retcode = pam_authenticate(pamh, 0);
24774874Smarkm	if (retcode != PAM_SUCCESS) {
248105386Smarkm		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
249105386Smarkm		    username, user, mytty);
25077220Smarkm		errx(1, "Sorry");
25174874Smarkm	}
25277220Smarkm	retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
25377220Smarkm	if (retcode == PAM_SUCCESS)
25477220Smarkm		user = p;
25577220Smarkm	else
25677220Smarkm		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
25777220Smarkm		    pam_strerror(pamh, retcode));
258124166Sdes	pwd = getpwnam(user);
259124166Sdes	if (pwd == NULL)
260124166Sdes		errx(1, "unknown login: %s", user);
26174874Smarkm
26277220Smarkm	retcode = pam_acct_mgmt(pamh, 0);
26377220Smarkm	if (retcode == PAM_NEW_AUTHTOK_REQD) {
26477220Smarkm		retcode = pam_chauthtok(pamh,
26577220Smarkm			PAM_CHANGE_EXPIRED_AUTHTOK);
26674874Smarkm		if (retcode != PAM_SUCCESS) {
26777220Smarkm			syslog(LOG_ERR, "pam_chauthtok: %s",
26877220Smarkm			    pam_strerror(pamh, retcode));
26974874Smarkm			errx(1, "Sorry");
27074874Smarkm		}
27174874Smarkm	}
27277220Smarkm	if (retcode != PAM_SUCCESS) {
27377220Smarkm		syslog(LOG_ERR, "pam_acct_mgmt: %s",
27477220Smarkm			pam_strerror(pamh, retcode));
27577220Smarkm		errx(1, "Sorry");
27677220Smarkm	}
27774874Smarkm
278124166Sdes	/* get target login information */
27977220Smarkm	if (class == NULL)
28030793Sguido		lc = login_getpwclass(pwd);
28177220Smarkm	else {
28277220Smarkm		if (ruid != 0)
28330793Sguido			errx(1, "only root may use -c");
28430793Sguido		lc = login_getclass(class);
28530793Sguido		if (lc == NULL)
28630793Sguido			errx(1, "unknown class: %s", class);
28730793Sguido	}
2881590Srgrimes
28977220Smarkm	/* if asme and non-standard target shell, must be root */
2901590Srgrimes	if (asme) {
29177220Smarkm		if (ruid != 0 && !chshell(pwd->pw_shell))
292127848Scharnier			errx(1, "permission denied (shell)");
293130409Smarkm		shell = _PATH_BSHELL;
294130409Smarkm		iscsh = NO;
29577220Smarkm	}
29677220Smarkm	else if (pwd->pw_shell && *pwd->pw_shell) {
2971590Srgrimes		shell = pwd->pw_shell;
2981590Srgrimes		iscsh = UNSET;
29977220Smarkm	}
30077220Smarkm	else {
3011590Srgrimes		shell = _PATH_BSHELL;
3021590Srgrimes		iscsh = NO;
3031590Srgrimes	}
3041590Srgrimes
3051590Srgrimes	/* if we're forking a csh, we want to slightly muck the args */
3061590Srgrimes	if (iscsh == UNSET) {
30714440Smarkm		p = strrchr(shell, '/');
30814440Smarkm		if (p)
3091590Srgrimes			++p;
3101590Srgrimes		else
3111590Srgrimes			p = shell;
31277220Smarkm		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
3131590Srgrimes	}
31477220Smarkm	setpriority(PRIO_PROCESS, 0, prio);
3151590Srgrimes
316113262Sdes	/* Switch to home directory */
317113262Sdes	if (asthem) {
318113262Sdes		if (chdir(pwd->pw_dir) < 0)
319113262Sdes			errx(1, "no directory");
320113262Sdes	}
321113262Sdes
32221646Sdavidn	/*
32377220Smarkm	 * PAM modules might add supplementary groups in pam_setcred(), so
32477220Smarkm	 * initialize them first.
32574874Smarkm	 */
32674874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
32774874Smarkm		err(1, "setusercontext");
32874874Smarkm
32974874Smarkm	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
330113262Sdes	if (retcode != PAM_SUCCESS) {
331113262Sdes		syslog(LOG_ERR, "pam_setcred: %s",
33277220Smarkm		    pam_strerror(pamh, retcode));
333113262Sdes		errx(1, "failed to establish credentials.");
334113262Sdes	}
335113262Sdes	if (asthem) {
336113262Sdes		retcode = pam_open_session(pamh, 0);
337113262Sdes		if (retcode != PAM_SUCCESS) {
338113262Sdes			syslog(LOG_ERR, "pam_open_session: %s",
339113262Sdes			    pam_strerror(pamh, retcode));
340113262Sdes			errx(1, "failed to open session.");
341113262Sdes		}
342113262Sdes	}
34374874Smarkm
34474874Smarkm	/*
34574874Smarkm	 * We must fork() before setuid() because we need to call
34674874Smarkm	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
34774874Smarkm	 */
34898837Sdillon	sa.sa_flags = SA_RESTART;
349105362Stjr	sa.sa_handler = SIG_IGN;
35098837Sdillon	sigemptyset(&sa.sa_mask);
35198837Sdillon	sigaction(SIGINT, &sa, &sa_int);
35298837Sdillon	sigaction(SIGQUIT, &sa, &sa_quit);
353112695Sdavidxu	sigaction(SIGPIPE, &sa, &sa_pipe);
354112085Sdavidxu	sa.sa_handler = SIG_DFL;
355112085Sdavidxu	sigaction(SIGTSTP, &sa, NULL);
35674874Smarkm	statusp = 1;
357112695Sdavidxu	if (pipe(fds) == -1) {
358130409Smarkm		PAM_END();
359112695Sdavidxu		err(1, "pipe");
360112695Sdavidxu	}
36177220Smarkm	child_pid = fork();
36277220Smarkm	switch (child_pid) {
36374874Smarkm	default:
364122013Sdavidxu		sa.sa_handler = SIG_IGN;
365122013Sdavidxu		sigaction(SIGTTOU, &sa, NULL);
366112695Sdavidxu		close(fds[0]);
367112695Sdavidxu		setpgid(child_pid, child_pid);
368122061Sdavidxu		tcsetpgrp(STDERR_FILENO, child_pid);
369112695Sdavidxu		close(fds[1]);
370112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
371113262Sdes		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
37277220Smarkm			if (WIFSTOPPED(statusp)) {
37377220Smarkm				kill(getpid(), SIGSTOP);
374101722Sache				child_pgrp = getpgid(child_pid);
375112695Sdavidxu				tcsetpgrp(1, child_pgrp);
376112695Sdavidxu				kill(child_pid, SIGCONT);
37777220Smarkm				statusp = 1;
37877220Smarkm				continue;
37977220Smarkm			}
38077220Smarkm			break;
38174874Smarkm		}
382122061Sdavidxu		tcsetpgrp(STDERR_FILENO, getpgrp());
383113262Sdes		if (pid == -1)
38477220Smarkm			err(1, "waitpid");
38581528Smarkm		PAM_END();
386130409Smarkm		exit(WEXITSTATUS(statusp));
38774874Smarkm	case -1:
388130409Smarkm		PAM_END();
38977220Smarkm		err(1, "fork");
39074874Smarkm	case 0:
391112695Sdavidxu		close(fds[1]);
392112695Sdavidxu		read(fds[0], &temp, 1);
393112695Sdavidxu		close(fds[0]);
394112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
39598837Sdillon		sigaction(SIGINT, &sa_int, NULL);
39698837Sdillon		sigaction(SIGQUIT, &sa_quit, NULL);
397112695Sdavidxu
39877220Smarkm		/*
39977220Smarkm		 * Set all user context except for: Environmental variables
40077220Smarkm		 * Umask Login records (wtmp, etc) Path
40177220Smarkm		 */
40277220Smarkm		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
403105758Srwatson			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
404105758Srwatson			   LOGIN_SETMAC);
40577220Smarkm		/*
406105758Srwatson		 * If -s is present, also set the MAC label.
407105758Srwatson		 */
408105758Srwatson		if (setmaclabel)
409105758Srwatson			setwhat |= LOGIN_SETMAC;
410105758Srwatson		/*
41177220Smarkm		 * Don't touch resource/priority settings if -m has been used
41277220Smarkm		 * or -l and -c hasn't, and we're not su'ing to root.
41377220Smarkm		 */
41477220Smarkm		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
41577220Smarkm			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
41677220Smarkm		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
41777220Smarkm			err(1, "setusercontext");
41874874Smarkm
41977220Smarkm		if (!asme) {
42077220Smarkm			if (asthem) {
42177220Smarkm				p = getenv("TERM");
42277220Smarkm				environ = &cleanenv;
423113262Sdes			}
42469427Srwatson
425113262Sdes			if (asthem || pwd->pw_uid)
426113262Sdes				setenv("USER", pwd->pw_name, 1);
427113262Sdes			setenv("HOME", pwd->pw_dir, 1);
428113262Sdes			setenv("SHELL", shell, 1);
429113262Sdes
430113262Sdes			if (asthem) {
43177220Smarkm				/*
43277220Smarkm				 * Add any environmental variables that the
43377220Smarkm				 * PAM modules may have set.
43477220Smarkm				 */
43577220Smarkm				environ_pam = pam_getenvlist(pamh);
43677220Smarkm				if (environ_pam)
43777220Smarkm					export_pam_environment();
4381590Srgrimes
43977220Smarkm				/* set the su'd user's environment & umask */
44077220Smarkm				setusercontext(lc, pwd, pwd->pw_uid,
44177220Smarkm					LOGIN_SETPATH | LOGIN_SETUMASK |
44277220Smarkm					LOGIN_SETENV);
44377220Smarkm				if (p)
44477220Smarkm					setenv("TERM", p, 1);
44577220Smarkm			}
44677220Smarkm		}
44777220Smarkm		login_close(lc);
44874874Smarkm
44977220Smarkm		if (iscsh == YES) {
45077220Smarkm			if (fastlogin)
45183373Smarkm				*np.a-- = "-f";
45277220Smarkm			if (asme)
45383373Smarkm				*np.a-- = "-m";
4541590Srgrimes		}
45577220Smarkm		/* csh strips the first character... */
45683373Smarkm		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
45774874Smarkm
45877220Smarkm		if (ruid != 0)
45977220Smarkm			syslog(LOG_NOTICE, "%s to %s%s", username, user,
46077220Smarkm			    ontty());
46174874Smarkm
46283373Smarkm		execv(shell, np.b);
46377220Smarkm		err(1, "%s", shell);
4641590Srgrimes	}
4651590Srgrimes}
4661590Srgrimes
467130409Smarkmstatic void
46877220Smarkmexport_pam_environment(void)
46974874Smarkm{
47074874Smarkm	char	**pp;
47174874Smarkm
47274874Smarkm	for (pp = environ_pam; *pp != NULL; pp++) {
47374874Smarkm		if (ok_to_export(*pp))
47477220Smarkm			putenv(*pp);
47574874Smarkm		free(*pp);
47674874Smarkm	}
47774874Smarkm}
47874874Smarkm
47974874Smarkm/*
48074874Smarkm * Sanity checks on PAM environmental variables:
48174874Smarkm * - Make sure there is an '=' in the string.
48274874Smarkm * - Make sure the string doesn't run on too long.
48374874Smarkm * - Do not export certain variables.  This list was taken from the
48474874Smarkm *   Solaris pam_putenv(3) man page.
485113262Sdes * Note that if the user is chrooted, PAM may have a better idea than we
486113262Sdes * do of where her home directory is.
48774874Smarkm */
48874874Smarkmstatic int
48977220Smarkmok_to_export(const char *s)
49074874Smarkm{
49174874Smarkm	static const char *noexport[] = {
492113262Sdes		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
49374874Smarkm		"IFS", "PATH", NULL
49474874Smarkm	};
49574874Smarkm	const char **pp;
49674874Smarkm	size_t n;
49774874Smarkm
49874874Smarkm	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
49974874Smarkm		return 0;
50074874Smarkm	if (strncmp(s, "LD_", 3) == 0)
50174874Smarkm		return 0;
50274874Smarkm	for (pp = noexport; *pp != NULL; pp++) {
50374874Smarkm		n = strlen(*pp);
50474874Smarkm		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
50574874Smarkm			return 0;
50674874Smarkm	}
50774874Smarkm	return 1;
50874874Smarkm}
50974874Smarkm
51028099Scharnierstatic void
51177220Smarkmusage(void)
51228099Scharnier{
51381702Sru
514105758Srwatson	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
51581702Sru	exit(1);
516130409Smarkm	/* NOTREACHED */
51728099Scharnier}
51828099Scharnier
51977220Smarkmstatic int
520130409Smarkmchshell(const char *sh)
5211590Srgrimes{
52277220Smarkm	int r;
5231590Srgrimes	char *cp;
5241590Srgrimes
52577220Smarkm	r = 0;
52621646Sdavidn	setusershell();
527121236Scognet	while ((cp = getusershell()) != NULL && !r)
528121236Scognet	    r = (strcmp(cp, sh) == 0);
52921646Sdavidn	endusershell();
53021646Sdavidn	return r;
5311590Srgrimes}
5321590Srgrimes
53377220Smarkmstatic char *
53477220Smarkmontty(void)
5351590Srgrimes{
5361590Srgrimes	char *p;
5371590Srgrimes	static char buf[MAXPATHLEN + 4];
5381590Srgrimes
5391590Srgrimes	buf[0] = 0;
54014440Smarkm	p = ttyname(STDERR_FILENO);
54114440Smarkm	if (p)
5421590Srgrimes		snprintf(buf, sizeof(buf), " on %s", p);
54377220Smarkm	return buf;
5441590Srgrimes}
545