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 * 4. Neither the name of the University nor the names of its contributors
441590Srgrimes *    may be used to endorse or promote products derived from this software
451590Srgrimes *    without specific prior written permission.
461590Srgrimes *
471590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
481590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
491590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
501590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
511590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
521590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
531590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
541590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
551590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
561590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
571590Srgrimes * SUCH DAMAGE.
581590Srgrimes */
591590Srgrimes
601590Srgrimes#ifndef lint
6114440Smarkmstatic const char copyright[] =
621590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\
631590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
641590Srgrimes#endif /* not lint */
651590Srgrimes
66127848Scharnier#if 0
671590Srgrimes#ifndef lint
681590Srgrimesstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
69127848Scharnier#endif /* not lint */
7028099Scharnier#endif
711590Srgrimes
72127848Scharnier#include <sys/cdefs.h>
73127848Scharnier__FBSDID("$FreeBSD: stable/10/usr.bin/su/su.c 324692 2017-10-17 15:53:19Z ngie $");
74127848Scharnier
751590Srgrimes#include <sys/param.h>
761590Srgrimes#include <sys/time.h>
771590Srgrimes#include <sys/resource.h>
7877220Smarkm#include <sys/wait.h>
791590Srgrimes
80161815Scsjp#ifdef USE_BSM_AUDIT
81161815Scsjp#include <bsm/libbsm.h>
82161815Scsjp#include <bsm/audit_uevents.h>
83161815Scsjp#endif
84161815Scsjp
851590Srgrimes#include <err.h>
861590Srgrimes#include <errno.h>
87200462Sdelphij#include <grp.h>
8877220Smarkm#include <login_cap.h>
891590Srgrimes#include <paths.h>
901590Srgrimes#include <pwd.h>
9177220Smarkm#include <signal.h>
921590Srgrimes#include <stdio.h>
931590Srgrimes#include <stdlib.h>
941590Srgrimes#include <string.h>
951590Srgrimes#include <syslog.h>
961590Srgrimes#include <unistd.h>
97161815Scsjp#include <stdarg.h>
9821646Sdavidn
9974874Smarkm#include <security/pam_appl.h>
10091745Sdes#include <security/openpam.h>
10174874Smarkm
102113262Sdes#define PAM_END() do {							\
103113262Sdes	int local_ret;							\
104113262Sdes	if (pamh != NULL) {						\
105113262Sdes		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
106113262Sdes		if (local_ret != PAM_SUCCESS)				\
107113262Sdes			syslog(LOG_ERR, "pam_setcred: %s",		\
108113262Sdes				pam_strerror(pamh, local_ret));		\
109113262Sdes		if (asthem) {						\
110113262Sdes			local_ret = pam_close_session(pamh, 0);		\
111113262Sdes			if (local_ret != PAM_SUCCESS)			\
112113262Sdes				syslog(LOG_ERR, "pam_close_session: %s",\
113113262Sdes					pam_strerror(pamh, local_ret));	\
114113262Sdes		}							\
115113262Sdes		local_ret = pam_end(pamh, local_ret);			\
116113262Sdes		if (local_ret != PAM_SUCCESS)				\
117113262Sdes			syslog(LOG_ERR, "pam_end: %s",			\
118113262Sdes				pam_strerror(pamh, local_ret));		\
119113262Sdes	}								\
12077220Smarkm} while (0)
12174874Smarkm
12274874Smarkm
123113262Sdes#define PAM_SET_ITEM(what, item) do {					\
124113262Sdes	int local_ret;							\
125113262Sdes	local_ret = pam_set_item(pamh, what, item);			\
126113262Sdes	if (local_ret != PAM_SUCCESS) {					\
127113262Sdes		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
128113262Sdes			pam_strerror(pamh, local_ret));			\
129113262Sdes		errx(1, "pam_set_item(" #what "): %s",			\
130113262Sdes			pam_strerror(pamh, local_ret));			\
131130409Smarkm		/* NOTREACHED */					\
132113262Sdes	}								\
13377220Smarkm} while (0)
1343702Spst
13577220Smarkmenum tristate { UNSET, YES, NO };
1361590Srgrimes
13777220Smarkmstatic pam_handle_t *pamh = NULL;
13877220Smarkmstatic char	**environ_pam;
13977220Smarkm
14077220Smarkmstatic char	*ontty(void);
141130409Smarkmstatic int	chshell(const char *);
142130409Smarkmstatic void	usage(void) __dead2;
143130409Smarkmstatic void	export_pam_environment(void);
14477220Smarkmstatic int	ok_to_export(const char *);
14577220Smarkm
14677220Smarkmextern char	**environ;
14777220Smarkm
1481590Srgrimesint
14977220Smarkmmain(int argc, char *argv[])
1501590Srgrimes{
151130409Smarkm	static char	*cleanenv;
152220055Sume	struct passwd	*pwd = NULL;
15391745Sdes	struct pam_conv	conv = { openpam_ttyconv, NULL };
15477220Smarkm	enum tristate	iscsh;
15577220Smarkm	login_cap_t	*lc;
15683373Smarkm	union {
15783373Smarkm		const char	**a;
15883373Smarkm		char		* const *b;
15997377Sdes	}		np;
16077220Smarkm	uid_t		ruid;
161153985Sbrian	pid_t		child_pid, child_pgrp, pid;
162130409Smarkm	int		asme, ch, asthem, fastlogin, prio, i, retcode,
163113262Sdes			statusp, setmaclabel;
164130409Smarkm	u_int		setwhat;
165130409Smarkm	char		*username, *class, shellbuf[MAXPATHLEN];
16683373Smarkm	const char	*p, *user, *shell, *mytty, **nargv;
167179547Sdwmalone	const void	*v;
168112695Sdavidxu	struct sigaction sa, sa_int, sa_quit, sa_pipe;
169112695Sdavidxu	int temp, fds[2];
170161815Scsjp#ifdef USE_BSM_AUDIT
171161815Scsjp	const char	*aerr;
172161815Scsjp	au_id_t		 auid;
173161815Scsjp#endif
17498837Sdillon
175324692Sngie	p = shell = class = cleanenv = NULL;
17677220Smarkm	asme = asthem = fastlogin = statusp = 0;
17710586Sjoerg	user = "root";
17877220Smarkm	iscsh = UNSET;
179105758Srwatson	setmaclabel = 0;
18077220Smarkm
181105758Srwatson	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
18277220Smarkm		switch ((char)ch) {
1831590Srgrimes		case 'f':
1841590Srgrimes			fastlogin = 1;
1851590Srgrimes			break;
1861590Srgrimes		case '-':
1871590Srgrimes		case 'l':
1881590Srgrimes			asme = 0;
1891590Srgrimes			asthem = 1;
1901590Srgrimes			break;
1911590Srgrimes		case 'm':
1921590Srgrimes			asme = 1;
1931590Srgrimes			asthem = 0;
1941590Srgrimes			break;
195105758Srwatson		case 's':
196105758Srwatson			setmaclabel = 1;
197105758Srwatson			break;
19830793Sguido		case 'c':
19930793Sguido			class = optarg;
20030793Sguido			break;
2011590Srgrimes		case '?':
2021590Srgrimes		default:
20328099Scharnier			usage();
204130409Smarkm		/* NOTREACHED */
20528099Scharnier		}
20639538Sroberto
20739538Sroberto	if (optind < argc)
20810586Sjoerg		user = argv[optind++];
20910586Sjoerg
21028612Sjoerg	if (user == NULL)
21128612Sjoerg		usage();
212130409Smarkm	/* NOTREACHED */
21328612Sjoerg
214140392Srwatson	/*
215140392Srwatson	 * Try to provide more helpful debugging output if su(1) is running
216140392Srwatson	 * non-setuid, or was run from a file system not mounted setuid.
217140392Srwatson	 */
218140392Srwatson	if (geteuid() != 0)
219140392Srwatson		errx(1, "not running setuid");
220140392Srwatson
221161815Scsjp#ifdef USE_BSM_AUDIT
222161815Scsjp	if (getauid(&auid) < 0 && errno != ENOSYS) {
223161815Scsjp		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
224161815Scsjp		errx(1, "Permission denied");
225161815Scsjp	}
226161815Scsjp#endif
227161815Scsjp	if (strlen(user) > MAXLOGNAME - 1) {
228161815Scsjp#ifdef USE_BSM_AUDIT
229161815Scsjp		if (audit_submit(AUE_su, auid,
230190700Scsjp		    EPERM, 1, "username too long: '%s'", user))
231161815Scsjp			errx(1, "Permission denied");
232161815Scsjp#endif
233161815Scsjp		errx(1, "username too long");
234161815Scsjp	}
235161815Scsjp
236130409Smarkm	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
23777220Smarkm	if (nargv == NULL)
23877220Smarkm		errx(1, "malloc failure");
23977220Smarkm
24010586Sjoerg	nargv[argc + 3] = NULL;
24110586Sjoerg	for (i = argc; i >= optind; i--)
24277220Smarkm		nargv[i + 3] = argv[i];
24383373Smarkm	np.a = &nargv[i + 3];
24410586Sjoerg
2451590Srgrimes	argv += optind;
2461590Srgrimes
2471590Srgrimes	errno = 0;
2481590Srgrimes	prio = getpriority(PRIO_PROCESS, 0);
2491590Srgrimes	if (errno)
2501590Srgrimes		prio = 0;
25177220Smarkm
25277220Smarkm	setpriority(PRIO_PROCESS, 0, -2);
25374874Smarkm	openlog("su", LOG_CONS, LOG_AUTH);
2541590Srgrimes
25577220Smarkm	/* get current login name, real uid and shell */
2561590Srgrimes	ruid = getuid();
2571590Srgrimes	username = getlogin();
258220055Sume	if (username != NULL)
259220055Sume		pwd = getpwnam(username);
260220055Sume	if (pwd == NULL || pwd->pw_uid != ruid)
2611590Srgrimes		pwd = getpwuid(ruid);
262161815Scsjp	if (pwd == NULL) {
263161815Scsjp#ifdef USE_BSM_AUDIT
264190700Scsjp		if (audit_submit(AUE_su, auid, EPERM, 1,
265161815Scsjp		    "unable to determine invoking subject: '%s'", username))
266161815Scsjp			errx(1, "Permission denied");
267161815Scsjp#endif
2681590Srgrimes		errx(1, "who are you?");
269161815Scsjp	}
27077220Smarkm
2711590Srgrimes	username = strdup(pwd->pw_name);
2721590Srgrimes	if (username == NULL)
27377220Smarkm		err(1, "strdup failure");
27477220Smarkm
27521646Sdavidn	if (asme) {
27621646Sdavidn		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
27777220Smarkm			/* must copy - pwd memory is recycled */
27877220Smarkm			shell = strncpy(shellbuf, pwd->pw_shell,
27977220Smarkm			    sizeof(shellbuf));
28077220Smarkm			shellbuf[sizeof(shellbuf) - 1] = '\0';
28177220Smarkm		}
28277220Smarkm		else {
2831590Srgrimes			shell = _PATH_BSHELL;
2841590Srgrimes			iscsh = NO;
2851590Srgrimes		}
28621646Sdavidn	}
2871590Srgrimes
28877220Smarkm	/* Do the whole PAM startup thing */
28974874Smarkm	retcode = pam_start("su", user, &conv, &pamh);
29074874Smarkm	if (retcode != PAM_SUCCESS) {
29174874Smarkm		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
29274874Smarkm		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
29374874Smarkm	}
29474874Smarkm
295110456Sdes	PAM_SET_ITEM(PAM_RUSER, username);
29681529Smarkm
29774874Smarkm	mytty = ttyname(STDERR_FILENO);
29874874Smarkm	if (!mytty)
29974874Smarkm		mytty = "tty";
30077220Smarkm	PAM_SET_ITEM(PAM_TTY, mytty);
30177220Smarkm
30277220Smarkm	retcode = pam_authenticate(pamh, 0);
30374874Smarkm	if (retcode != PAM_SUCCESS) {
304161815Scsjp#ifdef USE_BSM_AUDIT
305190700Scsjp		if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
306161815Scsjp		    username, user, mytty))
307161815Scsjp			errx(1, "Permission denied");
308161815Scsjp#endif
309105386Smarkm		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
310105386Smarkm		    username, user, mytty);
31177220Smarkm		errx(1, "Sorry");
31274874Smarkm	}
313161815Scsjp#ifdef USE_BSM_AUDIT
314161815Scsjp	if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
315161815Scsjp		errx(1, "Permission denied");
316161815Scsjp#endif
317179547Sdwmalone	retcode = pam_get_item(pamh, PAM_USER, &v);
31877220Smarkm	if (retcode == PAM_SUCCESS)
319179547Sdwmalone		user = v;
32077220Smarkm	else
32177220Smarkm		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
32277220Smarkm		    pam_strerror(pamh, retcode));
323124166Sdes	pwd = getpwnam(user);
324161815Scsjp	if (pwd == NULL) {
325161815Scsjp#ifdef USE_BSM_AUDIT
326190700Scsjp		if (audit_submit(AUE_su, auid, EPERM, 1,
327161815Scsjp		    "unknown subject: %s", user))
328161815Scsjp			errx(1, "Permission denied");
329161815Scsjp#endif
330124166Sdes		errx(1, "unknown login: %s", user);
331161815Scsjp	}
33274874Smarkm
33377220Smarkm	retcode = pam_acct_mgmt(pamh, 0);
33477220Smarkm	if (retcode == PAM_NEW_AUTHTOK_REQD) {
33577220Smarkm		retcode = pam_chauthtok(pamh,
33677220Smarkm			PAM_CHANGE_EXPIRED_AUTHTOK);
33774874Smarkm		if (retcode != PAM_SUCCESS) {
338161815Scsjp#ifdef USE_BSM_AUDIT
339161815Scsjp			aerr = pam_strerror(pamh, retcode);
340161815Scsjp			if (aerr == NULL)
341161815Scsjp				aerr = "Unknown PAM error";
342190700Scsjp			if (audit_submit(AUE_su, auid, EPERM, 1,
343161815Scsjp			    "pam_chauthtok: %s", aerr))
344161815Scsjp				errx(1, "Permission denied");
345161815Scsjp#endif
34677220Smarkm			syslog(LOG_ERR, "pam_chauthtok: %s",
34777220Smarkm			    pam_strerror(pamh, retcode));
34874874Smarkm			errx(1, "Sorry");
34974874Smarkm		}
35074874Smarkm	}
35177220Smarkm	if (retcode != PAM_SUCCESS) {
352161815Scsjp#ifdef USE_BSM_AUDIT
353190700Scsjp		if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
354161815Scsjp		    pam_strerror(pamh, retcode)))
355161815Scsjp			errx(1, "Permission denied");
356161815Scsjp#endif
35777220Smarkm		syslog(LOG_ERR, "pam_acct_mgmt: %s",
35877220Smarkm			pam_strerror(pamh, retcode));
35977220Smarkm		errx(1, "Sorry");
36077220Smarkm	}
36174874Smarkm
362124166Sdes	/* get target login information */
36377220Smarkm	if (class == NULL)
36430793Sguido		lc = login_getpwclass(pwd);
36577220Smarkm	else {
366161815Scsjp		if (ruid != 0) {
367161815Scsjp#ifdef USE_BSM_AUDIT
368190700Scsjp			if (audit_submit(AUE_su, auid, EPERM, 1,
369161815Scsjp			    "only root may use -c"))
370161815Scsjp				errx(1, "Permission denied");
371161815Scsjp#endif
37230793Sguido			errx(1, "only root may use -c");
373161815Scsjp		}
37430793Sguido		lc = login_getclass(class);
37530793Sguido		if (lc == NULL)
376254259Strasz			err(1, "login_getclass");
377254259Strasz		if (lc->lc_class == NULL || strcmp(class, lc->lc_class) != 0)
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		}
472172749Sdavidxu		tcsetpgrp(STDERR_FILENO, getpgrp());
473113262Sdes		if (pid == -1)
47477220Smarkm			err(1, "waitpid");
47581528Smarkm		PAM_END();
476130409Smarkm		exit(WEXITSTATUS(statusp));
47774874Smarkm	case -1:
478130409Smarkm		PAM_END();
47977220Smarkm		err(1, "fork");
48074874Smarkm	case 0:
481112695Sdavidxu		close(fds[1]);
482112695Sdavidxu		read(fds[0], &temp, 1);
483112695Sdavidxu		close(fds[0]);
484112695Sdavidxu		sigaction(SIGPIPE, &sa_pipe, NULL);
48598837Sdillon		sigaction(SIGINT, &sa_int, NULL);
48698837Sdillon		sigaction(SIGQUIT, &sa_quit, NULL);
487112695Sdavidxu
48877220Smarkm		/*
48977220Smarkm		 * Set all user context except for: Environmental variables
49077220Smarkm		 * Umask Login records (wtmp, etc) Path
49177220Smarkm		 */
49277220Smarkm		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
493105758Srwatson			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
494105758Srwatson			   LOGIN_SETMAC);
49577220Smarkm		/*
496105758Srwatson		 * If -s is present, also set the MAC label.
497105758Srwatson		 */
498105758Srwatson		if (setmaclabel)
499105758Srwatson			setwhat |= LOGIN_SETMAC;
500105758Srwatson		/*
50177220Smarkm		 * Don't touch resource/priority settings if -m has been used
50277220Smarkm		 * or -l and -c hasn't, and we're not su'ing to root.
50377220Smarkm		 */
50477220Smarkm		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
50577220Smarkm			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
50677220Smarkm		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
50777220Smarkm			err(1, "setusercontext");
50874874Smarkm
50977220Smarkm		if (!asme) {
51077220Smarkm			if (asthem) {
51177220Smarkm				p = getenv("TERM");
51277220Smarkm				environ = &cleanenv;
513113262Sdes			}
51469427Srwatson
515113262Sdes			if (asthem || pwd->pw_uid)
516113262Sdes				setenv("USER", pwd->pw_name, 1);
517113262Sdes			setenv("HOME", pwd->pw_dir, 1);
518113262Sdes			setenv("SHELL", shell, 1);
519113262Sdes
520113262Sdes			if (asthem) {
52177220Smarkm				/*
52277220Smarkm				 * Add any environmental variables that the
52377220Smarkm				 * PAM modules may have set.
52477220Smarkm				 */
52577220Smarkm				environ_pam = pam_getenvlist(pamh);
52677220Smarkm				if (environ_pam)
52777220Smarkm					export_pam_environment();
5281590Srgrimes
52977220Smarkm				/* set the su'd user's environment & umask */
53077220Smarkm				setusercontext(lc, pwd, pwd->pw_uid,
53177220Smarkm					LOGIN_SETPATH | LOGIN_SETUMASK |
53277220Smarkm					LOGIN_SETENV);
53377220Smarkm				if (p)
53477220Smarkm					setenv("TERM", p, 1);
535162761Sluoqi
536162761Sluoqi				p = pam_getenv(pamh, "HOME");
537162761Sluoqi				if (chdir(p ? p : pwd->pw_dir) < 0)
538162761Sluoqi					errx(1, "no directory");
53977220Smarkm			}
54077220Smarkm		}
54177220Smarkm		login_close(lc);
54274874Smarkm
54377220Smarkm		if (iscsh == YES) {
54477220Smarkm			if (fastlogin)
54583373Smarkm				*np.a-- = "-f";
54677220Smarkm			if (asme)
54783373Smarkm				*np.a-- = "-m";
5481590Srgrimes		}
54977220Smarkm		/* csh strips the first character... */
55083373Smarkm		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
55174874Smarkm
55277220Smarkm		if (ruid != 0)
55377220Smarkm			syslog(LOG_NOTICE, "%s to %s%s", username, user,
55477220Smarkm			    ontty());
55574874Smarkm
55683373Smarkm		execv(shell, np.b);
55777220Smarkm		err(1, "%s", shell);
5581590Srgrimes	}
5591590Srgrimes}
5601590Srgrimes
561130409Smarkmstatic void
56277220Smarkmexport_pam_environment(void)
56374874Smarkm{
56474874Smarkm	char	**pp;
565171195Sscf	char	*p;
56674874Smarkm
56774874Smarkm	for (pp = environ_pam; *pp != NULL; pp++) {
568171195Sscf		if (ok_to_export(*pp)) {
569171195Sscf			p = strchr(*pp, '=');
570171195Sscf			*p = '\0';
571171195Sscf			setenv(*pp, p + 1, 1);
572171195Sscf		}
573169177Sache		free(*pp);
57474874Smarkm	}
57574874Smarkm}
57674874Smarkm
57774874Smarkm/*
57874874Smarkm * Sanity checks on PAM environmental variables:
57974874Smarkm * - Make sure there is an '=' in the string.
58074874Smarkm * - Make sure the string doesn't run on too long.
58174874Smarkm * - Do not export certain variables.  This list was taken from the
58274874Smarkm *   Solaris pam_putenv(3) man page.
583113262Sdes * Note that if the user is chrooted, PAM may have a better idea than we
584113262Sdes * do of where her home directory is.
58574874Smarkm */
58674874Smarkmstatic int
58777220Smarkmok_to_export(const char *s)
58874874Smarkm{
58974874Smarkm	static const char *noexport[] = {
590113262Sdes		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
59174874Smarkm		"IFS", "PATH", NULL
59274874Smarkm	};
59374874Smarkm	const char **pp;
59474874Smarkm	size_t n;
59574874Smarkm
59674874Smarkm	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
59774874Smarkm		return 0;
59874874Smarkm	if (strncmp(s, "LD_", 3) == 0)
59974874Smarkm		return 0;
60074874Smarkm	for (pp = noexport; *pp != NULL; pp++) {
60174874Smarkm		n = strlen(*pp);
60274874Smarkm		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
60374874Smarkm			return 0;
60474874Smarkm	}
60574874Smarkm	return 1;
60674874Smarkm}
60774874Smarkm
60828099Scharnierstatic void
60977220Smarkmusage(void)
61028099Scharnier{
61181702Sru
612105758Srwatson	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
61381702Sru	exit(1);
614130409Smarkm	/* NOTREACHED */
61528099Scharnier}
61628099Scharnier
61777220Smarkmstatic int
618130409Smarkmchshell(const char *sh)
6191590Srgrimes{
62077220Smarkm	int r;
6211590Srgrimes	char *cp;
6221590Srgrimes
62377220Smarkm	r = 0;
62421646Sdavidn	setusershell();
625121236Scognet	while ((cp = getusershell()) != NULL && !r)
626121236Scognet	    r = (strcmp(cp, sh) == 0);
62721646Sdavidn	endusershell();
62821646Sdavidn	return r;
6291590Srgrimes}
6301590Srgrimes
63177220Smarkmstatic char *
63277220Smarkmontty(void)
6331590Srgrimes{
6341590Srgrimes	char *p;
6351590Srgrimes	static char buf[MAXPATHLEN + 4];
6361590Srgrimes
6371590Srgrimes	buf[0] = 0;
63814440Smarkm	p = ttyname(STDERR_FILENO);
63914440Smarkm	if (p)
6401590Srgrimes		snprintf(buf, sizeof(buf), " on %s", p);
64177220Smarkm	return buf;
6421590Srgrimes}
643