su.c revision 169177
11590Srgrimes/*
2140392Srwatson * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
397377Sdes * All rights reserved.
41590Srgrimes *
597377Sdes * Portions of this software were developed for the FreeBSD Project by
697377Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network
797377Sdes * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
897377Sdes * ("CBOSS"), as part of the DARPA CHATS research program.
997377Sdes *
101590Srgrimes * Redistribution and use in source and binary forms, with or without
111590Srgrimes * modification, are permitted provided that the following conditions
121590Srgrimes * are met:
131590Srgrimes * 1. Redistributions of source code must retain the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer.
151590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161590Srgrimes *    notice, this list of conditions and the following disclaimer in the
171590Srgrimes *    documentation and/or other materials provided with the distribution.
18140392Srwatson *
19140392Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20140392Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21140392Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22140392Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23140392Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24140392Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25140392Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26140392Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27140392Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28140392Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29140392Srwatson * SUCH DAMAGE.
30140392Srwatson */
31140392Srwatson/*-
32140392Srwatson * Copyright (c) 1988, 1993, 1994
33140392Srwatson *	The Regents of the University of California.  All rights reserved.
34140392Srwatson *
35140392Srwatson * Redistribution and use in source and binary forms, with or without
36140392Srwatson * modification, are permitted provided that the following conditions
37140392Srwatson * are met:
38140392Srwatson * 1. Redistributions of source code must retain the above copyright
39140392Srwatson *    notice, this list of conditions and the following disclaimer.
40140392Srwatson * 2. Redistributions in binary form must reproduce the above copyright
41140392Srwatson *    notice, this list of conditions and the following disclaimer in the
42140392Srwatson *    documentation and/or other materials provided with the distribution.
431590Srgrimes * 3. All advertising materials mentioning features or use of this software
441590Srgrimes *    must display the following acknowledgement:
451590Srgrimes *	This product includes software developed by the University of
461590Srgrimes *	California, Berkeley and its contributors.
471590Srgrimes * 4. Neither the name of the University nor the names of its contributors
481590Srgrimes *    may be used to endorse or promote products derived from this software
491590Srgrimes *    without specific prior written permission.
501590Srgrimes *
511590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
521590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
531590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
541590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
551590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
561590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
571590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
581590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
591590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
601590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
611590Srgrimes * SUCH DAMAGE.
621590Srgrimes */
631590Srgrimes
641590Srgrimes#ifndef lint
6514440Smarkmstatic const char copyright[] =
661590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\
671590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
681590Srgrimes#endif /* not lint */
691590Srgrimes
70127848Scharnier#if 0
711590Srgrimes#ifndef lint
721590Srgrimesstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
73127848Scharnier#endif /* not lint */
7428099Scharnier#endif
751590Srgrimes
76127848Scharnier#include <sys/cdefs.h>
77127848Scharnier__FBSDID("$FreeBSD: head/usr.bin/su/su.c 169177 2007-05-01 16:02:44Z ache $");
78127848Scharnier
791590Srgrimes#include <sys/param.h>
801590Srgrimes#include <sys/time.h>
811590Srgrimes#include <sys/resource.h>
8277220Smarkm#include <sys/wait.h>
831590Srgrimes
84161815Scsjp#ifdef USE_BSM_AUDIT
85161815Scsjp#include <bsm/libbsm.h>
86161815Scsjp#include <bsm/audit_uevents.h>
87161815Scsjp#endif
88161815Scsjp
891590Srgrimes#include <err.h>
901590Srgrimes#include <errno.h>
911590Srgrimes#include <grp.h>
9277220Smarkm#include <login_cap.h>
931590Srgrimes#include <paths.h>
941590Srgrimes#include <pwd.h>
9577220Smarkm#include <signal.h>
961590Srgrimes#include <stdio.h>
971590Srgrimes#include <stdlib.h>
981590Srgrimes#include <string.h>
991590Srgrimes#include <syslog.h>
1001590Srgrimes#include <unistd.h>
101161815Scsjp#include <stdarg.h>
10221646Sdavidn
10374874Smarkm#include <security/pam_appl.h>
10491745Sdes#include <security/openpam.h>
10574874Smarkm
106113262Sdes#define PAM_END() do {							\
107113262Sdes	int local_ret;							\
108113262Sdes	if (pamh != NULL) {						\
109113262Sdes		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
110113262Sdes		if (local_ret != PAM_SUCCESS)				\
111113262Sdes			syslog(LOG_ERR, "pam_setcred: %s",		\
112113262Sdes				pam_strerror(pamh, local_ret));		\
113113262Sdes		if (asthem) {						\
114113262Sdes			local_ret = pam_close_session(pamh, 0);		\
115113262Sdes			if (local_ret != PAM_SUCCESS)			\
116113262Sdes				syslog(LOG_ERR, "pam_close_session: %s",\
117113262Sdes					pam_strerror(pamh, local_ret));	\
118113262Sdes		}							\
119113262Sdes		local_ret = pam_end(pamh, local_ret);			\
120113262Sdes		if (local_ret != PAM_SUCCESS)				\
121113262Sdes			syslog(LOG_ERR, "pam_end: %s",			\
122113262Sdes				pam_strerror(pamh, local_ret));		\
123113262Sdes	}								\
12477220Smarkm} while (0)
12574874Smarkm
12674874Smarkm
127113262Sdes#define PAM_SET_ITEM(what, item) do {					\
128113262Sdes	int local_ret;							\
129113262Sdes	local_ret = pam_set_item(pamh, what, item);			\
130113262Sdes	if (local_ret != PAM_SUCCESS) {					\
131113262Sdes		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
132113262Sdes			pam_strerror(pamh, local_ret));			\
133113262Sdes		errx(1, "pam_set_item(" #what "): %s",			\
134113262Sdes			pam_strerror(pamh, local_ret));			\
135130409Smarkm		/* NOTREACHED */					\
136113262Sdes	}								\
13777220Smarkm} while (0)
1383702Spst
13977220Smarkmenum tristate { UNSET, YES, NO };
1401590Srgrimes
14177220Smarkmstatic pam_handle_t *pamh = NULL;
14277220Smarkmstatic char	**environ_pam;
14377220Smarkm
14477220Smarkmstatic char	*ontty(void);
145130409Smarkmstatic int	chshell(const char *);
146130409Smarkmstatic void	usage(void) __dead2;
147130409Smarkmstatic void	export_pam_environment(void);
14877220Smarkmstatic int	ok_to_export(const char *);
14977220Smarkm
15077220Smarkmextern char	**environ;
15177220Smarkm
1521590Srgrimesint
15377220Smarkmmain(int argc, char *argv[])
1541590Srgrimes{
155130409Smarkm	static char	*cleanenv;
15677220Smarkm	struct passwd	*pwd;
15791745Sdes	struct pam_conv	conv = { openpam_ttyconv, NULL };
15877220Smarkm	enum tristate	iscsh;
15977220Smarkm	login_cap_t	*lc;
16083373Smarkm	union {
16183373Smarkm		const char	**a;
16283373Smarkm		char		* const *b;
16397377Sdes	}		np;
16477220Smarkm	uid_t		ruid;
165153985Sbrian	pid_t		child_pid, child_pgrp, pid;
166130409Smarkm	int		asme, ch, asthem, fastlogin, prio, i, retcode,
167113262Sdes			statusp, setmaclabel;
168130409Smarkm	u_int		setwhat;
169130409Smarkm	char		*username, *class, shellbuf[MAXPATHLEN];
17083373Smarkm	const char	*p, *user, *shell, *mytty, **nargv;
171112695Sdavidxu	struct sigaction sa, sa_int, sa_quit, sa_pipe;
172112695Sdavidxu	int temp, fds[2];
173161815Scsjp#ifdef USE_BSM_AUDIT
174161815Scsjp	const char	*aerr;
175161815Scsjp	au_id_t		 auid;
176161815Scsjp#endif
17798837Sdillon
17877220Smarkm	shell = class = cleanenv = NULL;
17977220Smarkm	asme = asthem = fastlogin = statusp = 0;
18010586Sjoerg	user = "root";
18177220Smarkm	iscsh = UNSET;
182105758Srwatson	setmaclabel = 0;
18377220Smarkm
184105758Srwatson	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
18577220Smarkm		switch ((char)ch) {
1861590Srgrimes		case 'f':
1871590Srgrimes			fastlogin = 1;
1881590Srgrimes			break;
1891590Srgrimes		case '-':
1901590Srgrimes		case 'l':
1911590Srgrimes			asme = 0;
1921590Srgrimes			asthem = 1;
1931590Srgrimes			break;
1941590Srgrimes		case 'm':
1951590Srgrimes			asme = 1;
1961590Srgrimes			asthem = 0;
1971590Srgrimes			break;
198105758Srwatson		case 's':
199105758Srwatson			setmaclabel = 1;
200105758Srwatson			break;
20130793Sguido		case 'c':
20230793Sguido			class = optarg;
20330793Sguido			break;
2041590Srgrimes		case '?':
2051590Srgrimes		default:
20628099Scharnier			usage();
207130409Smarkm		/* NOTREACHED */
20828099Scharnier		}
20939538Sroberto
21039538Sroberto	if (optind < argc)
21110586Sjoerg		user = argv[optind++];
21210586Sjoerg
21328612Sjoerg	if (user == NULL)
21428612Sjoerg		usage();
215130409Smarkm	/* NOTREACHED */
21628612Sjoerg
217140392Srwatson	/*
218140392Srwatson	 * Try to provide more helpful debugging output if su(1) is running
219140392Srwatson	 * non-setuid, or was run from a file system not mounted setuid.
220140392Srwatson	 */
221140392Srwatson	if (geteuid() != 0)
222140392Srwatson		errx(1, "not running setuid");
223140392Srwatson
224161815Scsjp#ifdef USE_BSM_AUDIT
225161815Scsjp	if (getauid(&auid) < 0 && errno != ENOSYS) {
226161815Scsjp		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
227161815Scsjp		errx(1, "Permission denied");
228161815Scsjp	}
229161815Scsjp#endif
230161815Scsjp	if (strlen(user) > MAXLOGNAME - 1) {
231161815Scsjp#ifdef USE_BSM_AUDIT
232161815Scsjp		if (audit_submit(AUE_su, auid,
233161815Scsjp		    1, EPERM, "username too long: '%s'", user))
234161815Scsjp			errx(1, "Permission denied");
235161815Scsjp#endif
236161815Scsjp		errx(1, "username too long");
237161815Scsjp	}
238161815Scsjp
239130409Smarkm	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
24077220Smarkm	if (nargv == NULL)
24177220Smarkm		errx(1, "malloc failure");
24277220Smarkm
24310586Sjoerg	nargv[argc + 3] = NULL;
24410586Sjoerg	for (i = argc; i >= optind; i--)
24577220Smarkm		nargv[i + 3] = argv[i];
24683373Smarkm	np.a = &nargv[i + 3];
24710586Sjoerg
2481590Srgrimes	argv += optind;
2491590Srgrimes
2501590Srgrimes	errno = 0;
2511590Srgrimes	prio = getpriority(PRIO_PROCESS, 0);
2521590Srgrimes	if (errno)
2531590Srgrimes		prio = 0;
25477220Smarkm
25577220Smarkm	setpriority(PRIO_PROCESS, 0, -2);
25674874Smarkm	openlog("su", LOG_CONS, LOG_AUTH);
2571590Srgrimes
25877220Smarkm	/* get current login name, real uid and shell */
2591590Srgrimes	ruid = getuid();
2601590Srgrimes	username = getlogin();
26177220Smarkm	pwd = getpwnam(username);
26277220Smarkm	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
2631590Srgrimes		pwd = getpwuid(ruid);
264161815Scsjp	if (pwd == NULL) {
265161815Scsjp#ifdef USE_BSM_AUDIT
266161815Scsjp		if (audit_submit(AUE_su, auid, 1, EPERM,
267161815Scsjp		    "unable to determine invoking subject: '%s'", username))
268161815Scsjp			errx(1, "Permission denied");
269161815Scsjp#endif
2701590Srgrimes		errx(1, "who are you?");
271161815Scsjp	}
27277220Smarkm
2731590Srgrimes	username = strdup(pwd->pw_name);
2741590Srgrimes	if (username == NULL)
27577220Smarkm		err(1, "strdup failure");
27677220Smarkm
27721646Sdavidn	if (asme) {
27821646Sdavidn		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
27977220Smarkm			/* must copy - pwd memory is recycled */
28077220Smarkm			shell = strncpy(shellbuf, pwd->pw_shell,
28177220Smarkm			    sizeof(shellbuf));
28277220Smarkm			shellbuf[sizeof(shellbuf) - 1] = '\0';
28377220Smarkm		}
28477220Smarkm		else {
2851590Srgrimes			shell = _PATH_BSHELL;
2861590Srgrimes			iscsh = NO;
2871590Srgrimes		}
28821646Sdavidn	}
2891590Srgrimes
29077220Smarkm	/* Do the whole PAM startup thing */
29174874Smarkm	retcode = pam_start("su", user, &conv, &pamh);
29274874Smarkm	if (retcode != PAM_SUCCESS) {
29374874Smarkm		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
29474874Smarkm		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
29574874Smarkm	}
29674874Smarkm
297110456Sdes	PAM_SET_ITEM(PAM_RUSER, username);
29881529Smarkm
29974874Smarkm	mytty = ttyname(STDERR_FILENO);
30074874Smarkm	if (!mytty)
30174874Smarkm		mytty = "tty";
30277220Smarkm	PAM_SET_ITEM(PAM_TTY, mytty);
30377220Smarkm
30477220Smarkm	retcode = pam_authenticate(pamh, 0);
30574874Smarkm	if (retcode != PAM_SUCCESS) {
306161815Scsjp#ifdef USE_BSM_AUDIT
307161815Scsjp		if (audit_submit(AUE_su, auid, 1, EPERM, "bad su %s to %s on %s",
308161815Scsjp		    username, user, mytty))
309161815Scsjp			errx(1, "Permission denied");
310161815Scsjp#endif
311105386Smarkm		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
312105386Smarkm		    username, user, mytty);
31377220Smarkm		errx(1, "Sorry");
31474874Smarkm	}
315161815Scsjp#ifdef USE_BSM_AUDIT
316161815Scsjp	if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
317161815Scsjp		errx(1, "Permission denied");
318161815Scsjp#endif
31977220Smarkm	retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
32077220Smarkm	if (retcode == PAM_SUCCESS)
32177220Smarkm		user = p;
32277220Smarkm	else
32377220Smarkm		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
32477220Smarkm		    pam_strerror(pamh, retcode));
325124166Sdes	pwd = getpwnam(user);
326161815Scsjp	if (pwd == NULL) {
327161815Scsjp#ifdef USE_BSM_AUDIT
328161815Scsjp		if (audit_submit(AUE_su, auid, 1, EPERM,
329161815Scsjp		    "unknown subject: %s", user))
330161815Scsjp			errx(1, "Permission denied");
331161815Scsjp#endif
332124166Sdes		errx(1, "unknown login: %s", user);
333161815Scsjp	}
33474874Smarkm
33577220Smarkm	retcode = pam_acct_mgmt(pamh, 0);
33677220Smarkm	if (retcode == PAM_NEW_AUTHTOK_REQD) {
33777220Smarkm		retcode = pam_chauthtok(pamh,
33877220Smarkm			PAM_CHANGE_EXPIRED_AUTHTOK);
33974874Smarkm		if (retcode != PAM_SUCCESS) {
340161815Scsjp#ifdef USE_BSM_AUDIT
341161815Scsjp			aerr = pam_strerror(pamh, retcode);
342161815Scsjp			if (aerr == NULL)
343161815Scsjp				aerr = "Unknown PAM error";
344161815Scsjp			if (audit_submit(AUE_su, auid, 1, EPERM,
345161815Scsjp			    "pam_chauthtok: %s", aerr))
346161815Scsjp				errx(1, "Permission denied");
347161815Scsjp#endif
34877220Smarkm			syslog(LOG_ERR, "pam_chauthtok: %s",
34977220Smarkm			    pam_strerror(pamh, retcode));
35074874Smarkm			errx(1, "Sorry");
35174874Smarkm		}
35274874Smarkm	}
35377220Smarkm	if (retcode != PAM_SUCCESS) {
354161815Scsjp#ifdef USE_BSM_AUDIT
355161815Scsjp		if (audit_submit(AUE_su, auid, 1, EPERM, "pam_acct_mgmt: %s",
356161815Scsjp		    pam_strerror(pamh, retcode)))
357161815Scsjp			errx(1, "Permission denied");
358161815Scsjp#endif
35977220Smarkm		syslog(LOG_ERR, "pam_acct_mgmt: %s",
36077220Smarkm			pam_strerror(pamh, retcode));
36177220Smarkm		errx(1, "Sorry");
36277220Smarkm	}
36374874Smarkm
364124166Sdes	/* get target login information */
36577220Smarkm	if (class == NULL)
36630793Sguido		lc = login_getpwclass(pwd);
36777220Smarkm	else {
368161815Scsjp		if (ruid != 0) {
369161815Scsjp#ifdef USE_BSM_AUDIT
370161815Scsjp			if (audit_submit(AUE_su, auid, 1, EPERM,
371161815Scsjp			    "only root may use -c"))
372161815Scsjp				errx(1, "Permission denied");
373161815Scsjp#endif
37430793Sguido			errx(1, "only root may use -c");
375161815Scsjp		}
37630793Sguido		lc = login_getclass(class);
37730793Sguido		if (lc == NULL)
37830793Sguido			errx(1, "unknown class: %s", class);
37930793Sguido	}
3801590Srgrimes
38177220Smarkm	/* if asme and non-standard target shell, must be root */
3821590Srgrimes	if (asme) {
38377220Smarkm		if (ruid != 0 && !chshell(pwd->pw_shell))
384127848Scharnier			errx(1, "permission denied (shell)");
38577220Smarkm	}
38677220Smarkm	else if (pwd->pw_shell && *pwd->pw_shell) {
3871590Srgrimes		shell = pwd->pw_shell;
3881590Srgrimes		iscsh = UNSET;
38977220Smarkm	}
39077220Smarkm	else {
3911590Srgrimes		shell = _PATH_BSHELL;
3921590Srgrimes		iscsh = NO;
3931590Srgrimes	}
3941590Srgrimes
3951590Srgrimes	/* if we're forking a csh, we want to slightly muck the args */
3961590Srgrimes	if (iscsh == UNSET) {
39714440Smarkm		p = strrchr(shell, '/');
39814440Smarkm		if (p)
3991590Srgrimes			++p;
4001590Srgrimes		else
4011590Srgrimes			p = shell;
40277220Smarkm		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
4031590Srgrimes	}
40477220Smarkm	setpriority(PRIO_PROCESS, 0, prio);
4051590Srgrimes
40621646Sdavidn	/*
40777220Smarkm	 * PAM modules might add supplementary groups in pam_setcred(), so
40877220Smarkm	 * initialize them first.
40974874Smarkm	 */
41074874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
41174874Smarkm		err(1, "setusercontext");
41274874Smarkm
41374874Smarkm	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
414113262Sdes	if (retcode != PAM_SUCCESS) {
415113262Sdes		syslog(LOG_ERR, "pam_setcred: %s",
41677220Smarkm		    pam_strerror(pamh, retcode));
417113262Sdes		errx(1, "failed to establish credentials.");
418113262Sdes	}
419113262Sdes	if (asthem) {
420113262Sdes		retcode = pam_open_session(pamh, 0);
421113262Sdes		if (retcode != PAM_SUCCESS) {
422113262Sdes			syslog(LOG_ERR, "pam_open_session: %s",
423113262Sdes			    pam_strerror(pamh, retcode));
424113262Sdes			errx(1, "failed to open session.");
425113262Sdes		}
426113262Sdes	}
42774874Smarkm
42874874Smarkm	/*
42974874Smarkm	 * We must fork() before setuid() because we need to call
43074874Smarkm	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
43174874Smarkm	 */
43298837Sdillon	sa.sa_flags = SA_RESTART;
433105362Stjr	sa.sa_handler = SIG_IGN;
43498837Sdillon	sigemptyset(&sa.sa_mask);
43598837Sdillon	sigaction(SIGINT, &sa, &sa_int);
43698837Sdillon	sigaction(SIGQUIT, &sa, &sa_quit);
437112695Sdavidxu	sigaction(SIGPIPE, &sa, &sa_pipe);
438112085Sdavidxu	sa.sa_handler = SIG_DFL;
439112085Sdavidxu	sigaction(SIGTSTP, &sa, NULL);
44074874Smarkm	statusp = 1;
441112695Sdavidxu	if (pipe(fds) == -1) {
442130409Smarkm		PAM_END();
443112695Sdavidxu		err(1, "pipe");
444112695Sdavidxu	}
44577220Smarkm	child_pid = fork();
44677220Smarkm	switch (child_pid) {
44774874Smarkm	default:
448122013Sdavidxu		sa.sa_handler = SIG_IGN;
449122013Sdavidxu		sigaction(SIGTTOU, &sa, NULL);
450112695Sdavidxu		close(fds[0]);
451153985Sbrian		setpgid(child_pid, child_pid);
452153985Sbrian		if (tcgetpgrp(STDERR_FILENO) == getpgrp())
453153985Sbrian			tcsetpgrp(STDERR_FILENO, child_pid);
454112695Sdavidxu		close(fds[1]);
455112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
456113262Sdes		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
45777220Smarkm			if (WIFSTOPPED(statusp)) {
458153985Sbrian				child_pgrp = getpgid(child_pid);
459153985Sbrian				if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
460153985Sbrian					tcsetpgrp(STDERR_FILENO, getpgrp());
461153966Sbrian				kill(getpid(), SIGSTOP);
462153985Sbrian				if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
463153985Sbrian					child_pgrp = getpgid(child_pid);
464153985Sbrian					tcsetpgrp(STDERR_FILENO, child_pgrp);
465153985Sbrian				}
466153966Sbrian				kill(child_pid, SIGCONT);
46777220Smarkm				statusp = 1;
46877220Smarkm				continue;
46977220Smarkm			}
47077220Smarkm			break;
47174874Smarkm		}
472153985Sbrian		child_pgrp = getpgid(child_pid);
473153985Sbrian		if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
474153985Sbrian			tcsetpgrp(STDERR_FILENO, getpgrp());
475113262Sdes		if (pid == -1)
47677220Smarkm			err(1, "waitpid");
47781528Smarkm		PAM_END();
478130409Smarkm		exit(WEXITSTATUS(statusp));
47974874Smarkm	case -1:
480130409Smarkm		PAM_END();
48177220Smarkm		err(1, "fork");
48274874Smarkm	case 0:
483112695Sdavidxu		close(fds[1]);
484112695Sdavidxu		read(fds[0], &temp, 1);
485112695Sdavidxu		close(fds[0]);
486112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
48798837Sdillon		sigaction(SIGINT, &sa_int, NULL);
48898837Sdillon		sigaction(SIGQUIT, &sa_quit, NULL);
489112695Sdavidxu
49077220Smarkm		/*
49177220Smarkm		 * Set all user context except for: Environmental variables
49277220Smarkm		 * Umask Login records (wtmp, etc) Path
49377220Smarkm		 */
49477220Smarkm		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
495105758Srwatson			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
496105758Srwatson			   LOGIN_SETMAC);
49777220Smarkm		/*
498105758Srwatson		 * If -s is present, also set the MAC label.
499105758Srwatson		 */
500105758Srwatson		if (setmaclabel)
501105758Srwatson			setwhat |= LOGIN_SETMAC;
502105758Srwatson		/*
50377220Smarkm		 * Don't touch resource/priority settings if -m has been used
50477220Smarkm		 * or -l and -c hasn't, and we're not su'ing to root.
50577220Smarkm		 */
50677220Smarkm		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
50777220Smarkm			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
50877220Smarkm		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
50977220Smarkm			err(1, "setusercontext");
51074874Smarkm
51177220Smarkm		if (!asme) {
51277220Smarkm			if (asthem) {
51377220Smarkm				p = getenv("TERM");
51477220Smarkm				environ = &cleanenv;
515113262Sdes			}
51669427Srwatson
517113262Sdes			if (asthem || pwd->pw_uid)
518113262Sdes				setenv("USER", pwd->pw_name, 1);
519113262Sdes			setenv("HOME", pwd->pw_dir, 1);
520113262Sdes			setenv("SHELL", shell, 1);
521113262Sdes
522113262Sdes			if (asthem) {
52377220Smarkm				/*
52477220Smarkm				 * Add any environmental variables that the
52577220Smarkm				 * PAM modules may have set.
52677220Smarkm				 */
52777220Smarkm				environ_pam = pam_getenvlist(pamh);
52877220Smarkm				if (environ_pam)
52977220Smarkm					export_pam_environment();
5301590Srgrimes
53177220Smarkm				/* set the su'd user's environment & umask */
53277220Smarkm				setusercontext(lc, pwd, pwd->pw_uid,
53377220Smarkm					LOGIN_SETPATH | LOGIN_SETUMASK |
53477220Smarkm					LOGIN_SETENV);
53577220Smarkm				if (p)
53677220Smarkm					setenv("TERM", p, 1);
537162761Sluoqi
538162761Sluoqi				p = pam_getenv(pamh, "HOME");
539162761Sluoqi				if (chdir(p ? p : pwd->pw_dir) < 0)
540162761Sluoqi					errx(1, "no directory");
54177220Smarkm			}
54277220Smarkm		}
54377220Smarkm		login_close(lc);
54474874Smarkm
54577220Smarkm		if (iscsh == YES) {
54677220Smarkm			if (fastlogin)
54783373Smarkm				*np.a-- = "-f";
54877220Smarkm			if (asme)
54983373Smarkm				*np.a-- = "-m";
5501590Srgrimes		}
55177220Smarkm		/* csh strips the first character... */
55283373Smarkm		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
55374874Smarkm
55477220Smarkm		if (ruid != 0)
55577220Smarkm			syslog(LOG_NOTICE, "%s to %s%s", username, user,
55677220Smarkm			    ontty());
55774874Smarkm
55883373Smarkm		execv(shell, np.b);
55977220Smarkm		err(1, "%s", shell);
5601590Srgrimes	}
5611590Srgrimes}
5621590Srgrimes
563130409Smarkmstatic void
56477220Smarkmexport_pam_environment(void)
56574874Smarkm{
56674874Smarkm	char	**pp;
56774874Smarkm
56874874Smarkm	for (pp = environ_pam; *pp != NULL; pp++) {
56974874Smarkm		if (ok_to_export(*pp))
570169177Sache			putenv(*pp);
571169177Sache		free(*pp);
57274874Smarkm	}
57374874Smarkm}
57474874Smarkm
57574874Smarkm/*
57674874Smarkm * Sanity checks on PAM environmental variables:
57774874Smarkm * - Make sure there is an '=' in the string.
57874874Smarkm * - Make sure the string doesn't run on too long.
57974874Smarkm * - Do not export certain variables.  This list was taken from the
58074874Smarkm *   Solaris pam_putenv(3) man page.
581113262Sdes * Note that if the user is chrooted, PAM may have a better idea than we
582113262Sdes * do of where her home directory is.
58374874Smarkm */
58474874Smarkmstatic int
58577220Smarkmok_to_export(const char *s)
58674874Smarkm{
58774874Smarkm	static const char *noexport[] = {
588113262Sdes		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
58974874Smarkm		"IFS", "PATH", NULL
59074874Smarkm	};
59174874Smarkm	const char **pp;
59274874Smarkm	size_t n;
59374874Smarkm
59474874Smarkm	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
59574874Smarkm		return 0;
59674874Smarkm	if (strncmp(s, "LD_", 3) == 0)
59774874Smarkm		return 0;
59874874Smarkm	for (pp = noexport; *pp != NULL; pp++) {
59974874Smarkm		n = strlen(*pp);
60074874Smarkm		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
60174874Smarkm			return 0;
60274874Smarkm	}
60374874Smarkm	return 1;
60474874Smarkm}
60574874Smarkm
60628099Scharnierstatic void
60777220Smarkmusage(void)
60828099Scharnier{
60981702Sru
610105758Srwatson	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
61181702Sru	exit(1);
612130409Smarkm	/* NOTREACHED */
61328099Scharnier}
61428099Scharnier
61577220Smarkmstatic int
616130409Smarkmchshell(const char *sh)
6171590Srgrimes{
61877220Smarkm	int r;
6191590Srgrimes	char *cp;
6201590Srgrimes
62177220Smarkm	r = 0;
62221646Sdavidn	setusershell();
623121236Scognet	while ((cp = getusershell()) != NULL && !r)
624121236Scognet	    r = (strcmp(cp, sh) == 0);
62521646Sdavidn	endusershell();
62621646Sdavidn	return r;
6271590Srgrimes}
6281590Srgrimes
62977220Smarkmstatic char *
63077220Smarkmontty(void)
6311590Srgrimes{
6321590Srgrimes	char *p;
6331590Srgrimes	static char buf[MAXPATHLEN + 4];
6341590Srgrimes
6351590Srgrimes	buf[0] = 0;
63614440Smarkm	p = ttyname(STDERR_FILENO);
63714440Smarkm	if (p)
6381590Srgrimes		snprintf(buf, sizeof(buf), " on %s", p);
63977220Smarkm	return buf;
6401590Srgrimes}
641