xref: /freebsd/usr.bin/su/su.c (revision 0b8224d1cc9dc6c9778ba04a75b2c8d47e5d7481)
18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
4c8fbd1ecSRobert Watson  * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
5a1bdb05cSDag-Erling Smørgrav  * All rights reserved.
6a1bdb05cSDag-Erling Smørgrav  *
7a1bdb05cSDag-Erling Smørgrav  * Portions of this software were developed for the FreeBSD Project by
8a1bdb05cSDag-Erling Smørgrav  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9a1bdb05cSDag-Erling Smørgrav  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10a1bdb05cSDag-Erling Smørgrav  * ("CBOSS"), as part of the DARPA CHATS research program.
119b50d902SRodney W. Grimes  *
129b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
139b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
149b50d902SRodney W. Grimes  * are met:
159b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
169b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
179b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
189b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
199b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
20c8fbd1ecSRobert Watson  *
21c8fbd1ecSRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22c8fbd1ecSRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23c8fbd1ecSRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24c8fbd1ecSRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25c8fbd1ecSRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26c8fbd1ecSRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27c8fbd1ecSRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28c8fbd1ecSRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29c8fbd1ecSRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30c8fbd1ecSRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31c8fbd1ecSRobert Watson  * SUCH DAMAGE.
32c8fbd1ecSRobert Watson  */
33c8fbd1ecSRobert Watson /*-
34c8fbd1ecSRobert Watson  * Copyright (c) 1988, 1993, 1994
35c8fbd1ecSRobert Watson  *	The Regents of the University of California.  All rights reserved.
36c8fbd1ecSRobert Watson  *
37c8fbd1ecSRobert Watson  * Redistribution and use in source and binary forms, with or without
38c8fbd1ecSRobert Watson  * modification, are permitted provided that the following conditions
39c8fbd1ecSRobert Watson  * are met:
40c8fbd1ecSRobert Watson  * 1. Redistributions of source code must retain the above copyright
41c8fbd1ecSRobert Watson  *    notice, this list of conditions and the following disclaimer.
42c8fbd1ecSRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
43c8fbd1ecSRobert Watson  *    notice, this list of conditions and the following disclaimer in the
44c8fbd1ecSRobert Watson  *    documentation and/or other materials provided with the distribution.
45fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
469b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
479b50d902SRodney W. Grimes  *    without specific prior written permission.
489b50d902SRodney W. Grimes  *
499b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
509b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
519b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
529b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
539b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
549b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
559b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
569b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
579b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
589b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
599b50d902SRodney W. Grimes  * SUCH DAMAGE.
609b50d902SRodney W. Grimes  */
619b50d902SRodney W. Grimes 
629b50d902SRodney W. Grimes #include <sys/param.h>
639b50d902SRodney W. Grimes #include <sys/time.h>
649b50d902SRodney W. Grimes #include <sys/resource.h>
655b3771f1SMark Murray #include <sys/wait.h>
669b50d902SRodney W. Grimes 
676319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
686319ad28SChristian S.J. Peron #include <bsm/libbsm.h>
696319ad28SChristian S.J. Peron #include <bsm/audit_uevents.h>
706319ad28SChristian S.J. Peron #endif
716319ad28SChristian S.J. Peron 
729b50d902SRodney W. Grimes #include <err.h>
739b50d902SRodney W. Grimes #include <errno.h>
74821df508SXin LI #include <grp.h>
755b3771f1SMark Murray #include <login_cap.h>
769b50d902SRodney W. Grimes #include <paths.h>
779b50d902SRodney W. Grimes #include <pwd.h>
785b3771f1SMark Murray #include <signal.h>
799b50d902SRodney W. Grimes #include <stdio.h>
809b50d902SRodney W. Grimes #include <stdlib.h>
819b50d902SRodney W. Grimes #include <string.h>
829b50d902SRodney W. Grimes #include <syslog.h>
839b50d902SRodney W. Grimes #include <unistd.h>
846319ad28SChristian S.J. Peron #include <stdarg.h>
8591bcac64SDavid Nugent 
865bc9d93dSMark Murray #include <security/pam_appl.h>
8717e623acSDag-Erling Smørgrav #include <security/openpam.h>
885bc9d93dSMark Murray 
898cc3b02fSMark Murray #define PAM_END() do {							\
905b3771f1SMark Murray 	int local_ret;							\
9160b28daaSDag-Erling Smørgrav 	if (pamh != NULL) {						\
925b3771f1SMark Murray 		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
935b3771f1SMark Murray 		if (local_ret != PAM_SUCCESS)				\
945b3771f1SMark Murray 			syslog(LOG_ERR, "pam_setcred: %s",		\
955b3771f1SMark Murray 				pam_strerror(pamh, local_ret));		\
9660b28daaSDag-Erling Smørgrav 		if (asthem) {						\
9760b28daaSDag-Erling Smørgrav 			local_ret = pam_close_session(pamh, 0);		\
9860b28daaSDag-Erling Smørgrav 			if (local_ret != PAM_SUCCESS)			\
9960b28daaSDag-Erling Smørgrav 				syslog(LOG_ERR, "pam_close_session: %s",\
10060b28daaSDag-Erling Smørgrav 					pam_strerror(pamh, local_ret));	\
10160b28daaSDag-Erling Smørgrav 		}							\
1025b3771f1SMark Murray 		local_ret = pam_end(pamh, local_ret);			\
1035b3771f1SMark Murray 		if (local_ret != PAM_SUCCESS)				\
1045b3771f1SMark Murray 			syslog(LOG_ERR, "pam_end: %s",			\
1055b3771f1SMark Murray 				pam_strerror(pamh, local_ret));		\
1065bc9d93dSMark Murray 	}								\
1075b3771f1SMark Murray } while (0)
1085b3771f1SMark Murray 
1095b3771f1SMark Murray 
1105b3771f1SMark Murray #define PAM_SET_ITEM(what, item) do {					\
1115b3771f1SMark Murray 	int local_ret;							\
1125b3771f1SMark Murray 	local_ret = pam_set_item(pamh, what, item);			\
1135b3771f1SMark Murray 	if (local_ret != PAM_SUCCESS) {					\
1145b3771f1SMark Murray 		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
1155b3771f1SMark Murray 			pam_strerror(pamh, local_ret));			\
1165b3771f1SMark Murray 		errx(1, "pam_set_item(" #what "): %s",			\
1175b3771f1SMark Murray 			pam_strerror(pamh, local_ret));			\
118953cb3ecSMark Murray 		/* NOTREACHED */					\
1195bc9d93dSMark Murray 	}								\
1205b3771f1SMark Murray } while (0)
1212ddadf84SPaul Traina 
1225b3771f1SMark Murray enum tristate { UNSET, YES, NO };
1235b3771f1SMark Murray 
1245b3771f1SMark Murray static pam_handle_t *pamh = NULL;
1255b3771f1SMark Murray static char	**environ_pam;
1265b3771f1SMark Murray 
1275b3771f1SMark Murray static char	*ontty(void);
128953cb3ecSMark Murray static int	chshell(const char *);
129953cb3ecSMark Murray static void	usage(void) __dead2;
130953cb3ecSMark Murray static void	export_pam_environment(void);
1315b3771f1SMark Murray static int	ok_to_export(const char *);
1325b3771f1SMark Murray 
1335b3771f1SMark Murray extern char	**environ;
1349b50d902SRodney W. Grimes 
1359b50d902SRodney W. Grimes int
main(int argc,char * argv[])1365b3771f1SMark Murray main(int argc, char *argv[])
1379b50d902SRodney W. Grimes {
138953cb3ecSMark Murray 	static char	*cleanenv;
1393db78cf0SHajimu UMEMOTO 	struct passwd	*pwd = NULL;
14017e623acSDag-Erling Smørgrav 	struct pam_conv	conv = { openpam_ttyconv, NULL };
1415b3771f1SMark Murray 	enum tristate	iscsh;
1425b3771f1SMark Murray 	login_cap_t	*lc;
14369ebfe34SMark Murray 	union {
14469ebfe34SMark Murray 		const char	**a;
14569ebfe34SMark Murray 		char		* const *b;
14669ebfe34SMark Murray 	}		np;
1479b50d902SRodney W. Grimes 	uid_t		ruid;
148cd17a1f7SBrian Somers 	pid_t		child_pid, child_pgrp, pid;
149953cb3ecSMark Murray 	int		asme, ch, asthem, fastlogin, prio, i, retcode,
15060b28daaSDag-Erling Smørgrav 			statusp, setmaclabel;
151953cb3ecSMark Murray 	u_int		setwhat;
152953cb3ecSMark Murray 	char		*username, *class, shellbuf[MAXPATHLEN];
15369ebfe34SMark Murray 	const char	*p, *user, *shell, *mytty, **nargv;
154f258a139SDavid Malone 	const void	*v;
155a75fd4bfSDavid Xu 	struct sigaction sa, sa_int, sa_quit, sa_pipe;
156a75fd4bfSDavid Xu 	int temp, fds[2];
1576319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
1586319ad28SChristian S.J. Peron 	const char	*aerr;
1596319ad28SChristian S.J. Peron 	au_id_t		 auid;
1606319ad28SChristian S.J. Peron #endif
161b22ac97bSMatthew Dillon 
162838c6d51SEnji Cooper 	p = shell = class = cleanenv = NULL;
1635b3771f1SMark Murray 	asme = asthem = fastlogin = statusp = 0;
16476ba1af2SJoerg Wunsch 	user = "root";
1655b3771f1SMark Murray 	iscsh = UNSET;
1661494905bSRobert Watson 	setmaclabel = 0;
1675b3771f1SMark Murray 
1681494905bSRobert Watson 	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
1699b50d902SRodney W. Grimes 		switch ((char)ch) {
1709b50d902SRodney W. Grimes 		case 'f':
1719b50d902SRodney W. Grimes 			fastlogin = 1;
1729b50d902SRodney W. Grimes 			break;
1739b50d902SRodney W. Grimes 		case '-':
1749b50d902SRodney W. Grimes 		case 'l':
1759b50d902SRodney W. Grimes 			asme = 0;
1769b50d902SRodney W. Grimes 			asthem = 1;
1779b50d902SRodney W. Grimes 			break;
1789b50d902SRodney W. Grimes 		case 'm':
1799b50d902SRodney W. Grimes 			asme = 1;
1809b50d902SRodney W. Grimes 			asthem = 0;
1819b50d902SRodney W. Grimes 			break;
1821494905bSRobert Watson 		case 's':
1831494905bSRobert Watson 			setmaclabel = 1;
1841494905bSRobert Watson 			break;
1857a853dffSGuido van Rooij 		case 'c':
1867a853dffSGuido van Rooij 			class = optarg;
1877a853dffSGuido van Rooij 			break;
1889b50d902SRodney W. Grimes 		case '?':
1899b50d902SRodney W. Grimes 		default:
1907be91299SPhilippe Charnier 			usage();
191953cb3ecSMark Murray 		/* NOTREACHED */
1929b50d902SRodney W. Grimes 		}
193d6bc2e88SOllivier Robert 
194d6bc2e88SOllivier Robert 	if (optind < argc)
19576ba1af2SJoerg Wunsch 		user = argv[optind++];
19676ba1af2SJoerg Wunsch 
19784c2e301SJoerg Wunsch 	if (user == NULL)
19884c2e301SJoerg Wunsch 		usage();
199953cb3ecSMark Murray 	/* NOTREACHED */
20084c2e301SJoerg Wunsch 
201c8fbd1ecSRobert Watson 	/*
202c8fbd1ecSRobert Watson 	 * Try to provide more helpful debugging output if su(1) is running
203c8fbd1ecSRobert Watson 	 * non-setuid, or was run from a file system not mounted setuid.
204c8fbd1ecSRobert Watson 	 */
205c8fbd1ecSRobert Watson 	if (geteuid() != 0)
206c8fbd1ecSRobert Watson 		errx(1, "not running setuid");
207c8fbd1ecSRobert Watson 
2086319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
2096319ad28SChristian S.J. Peron 	if (getauid(&auid) < 0 && errno != ENOSYS) {
2106319ad28SChristian S.J. Peron 		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
2116319ad28SChristian S.J. Peron 		errx(1, "Permission denied");
2126319ad28SChristian S.J. Peron 	}
2136319ad28SChristian S.J. Peron #endif
2146319ad28SChristian S.J. Peron 	if (strlen(user) > MAXLOGNAME - 1) {
2156319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
2166319ad28SChristian S.J. Peron 		if (audit_submit(AUE_su, auid,
217ce8c6d71SChristian S.J. Peron 		    EPERM, 1, "username too long: '%s'", user))
2186319ad28SChristian S.J. Peron 			errx(1, "Permission denied");
2196319ad28SChristian S.J. Peron #endif
2206319ad28SChristian S.J. Peron 		errx(1, "username too long");
2216319ad28SChristian S.J. Peron 	}
2226319ad28SChristian S.J. Peron 
223953cb3ecSMark Murray 	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
2245b3771f1SMark Murray 	if (nargv == NULL)
22576ba1af2SJoerg Wunsch 		errx(1, "malloc failure");
22676ba1af2SJoerg Wunsch 
22776ba1af2SJoerg Wunsch 	nargv[argc + 3] = NULL;
22876ba1af2SJoerg Wunsch 	for (i = argc; i >= optind; i--)
22976ba1af2SJoerg Wunsch 		nargv[i + 3] = argv[i];
23069ebfe34SMark Murray 	np.a = &nargv[i + 3];
23176ba1af2SJoerg Wunsch 
2329b50d902SRodney W. Grimes 	argv += optind;
2339b50d902SRodney W. Grimes 
2349b50d902SRodney W. Grimes 	errno = 0;
2359b50d902SRodney W. Grimes 	prio = getpriority(PRIO_PROCESS, 0);
2369b50d902SRodney W. Grimes 	if (errno)
2379b50d902SRodney W. Grimes 		prio = 0;
2385b3771f1SMark Murray 
2395b3771f1SMark Murray 	setpriority(PRIO_PROCESS, 0, -2);
2405bc9d93dSMark Murray 	openlog("su", LOG_CONS, LOG_AUTH);
2419b50d902SRodney W. Grimes 
2425b3771f1SMark Murray 	/* get current login name, real uid and shell */
2439b50d902SRodney W. Grimes 	ruid = getuid();
2449b50d902SRodney W. Grimes 	username = getlogin();
2453db78cf0SHajimu UMEMOTO 	if (username != NULL)
2465b3771f1SMark Murray 		pwd = getpwnam(username);
2473db78cf0SHajimu UMEMOTO 	if (pwd == NULL || pwd->pw_uid != ruid)
2489b50d902SRodney W. Grimes 		pwd = getpwuid(ruid);
2496319ad28SChristian S.J. Peron 	if (pwd == NULL) {
2506319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
251ce8c6d71SChristian S.J. Peron 		if (audit_submit(AUE_su, auid, EPERM, 1,
2526319ad28SChristian S.J. Peron 		    "unable to determine invoking subject: '%s'", username))
2536319ad28SChristian S.J. Peron 			errx(1, "Permission denied");
2546319ad28SChristian S.J. Peron #endif
2559b50d902SRodney W. Grimes 		errx(1, "who are you?");
2566319ad28SChristian S.J. Peron 	}
2575b3771f1SMark Murray 
2585b3771f1SMark Murray 	username = strdup(pwd->pw_name);
2599b50d902SRodney W. Grimes 	if (username == NULL)
2605b3771f1SMark Murray 		err(1, "strdup failure");
2615b3771f1SMark Murray 
26291bcac64SDavid Nugent 	if (asme) {
26391bcac64SDavid Nugent 		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
2645b3771f1SMark Murray 			/* must copy - pwd memory is recycled */
265*aff7b6c7SXin LI 			strlcpy(shellbuf, pwd->pw_shell,
2665b3771f1SMark Murray 			    sizeof(shellbuf));
267*aff7b6c7SXin LI 			shell = shellbuf;
2685b3771f1SMark Murray 		}
2695b3771f1SMark Murray 		else {
2709b50d902SRodney W. Grimes 			shell = _PATH_BSHELL;
2719b50d902SRodney W. Grimes 			iscsh = NO;
2729b50d902SRodney W. Grimes 		}
27391bcac64SDavid Nugent 	}
27491bcac64SDavid Nugent 
2755b3771f1SMark Murray 	/* Do the whole PAM startup thing */
2765bc9d93dSMark Murray 	retcode = pam_start("su", user, &conv, &pamh);
2775bc9d93dSMark Murray 	if (retcode != PAM_SUCCESS) {
2785bc9d93dSMark Murray 		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
2795bc9d93dSMark Murray 		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
2805bc9d93dSMark Murray 	}
2815bc9d93dSMark Murray 
28284c03427SDag-Erling Smørgrav 	PAM_SET_ITEM(PAM_RUSER, username);
283788222e4SMark Murray 
2845bc9d93dSMark Murray 	mytty = ttyname(STDERR_FILENO);
2855bc9d93dSMark Murray 	if (!mytty)
2865bc9d93dSMark Murray 		mytty = "tty";
2875b3771f1SMark Murray 	PAM_SET_ITEM(PAM_TTY, mytty);
2885bc9d93dSMark Murray 
2895bc9d93dSMark Murray 	retcode = pam_authenticate(pamh, 0);
2905bc9d93dSMark Murray 	if (retcode != PAM_SUCCESS) {
2916319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
292ce8c6d71SChristian S.J. Peron 		if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
2936319ad28SChristian S.J. Peron 		    username, user, mytty))
2946319ad28SChristian S.J. Peron 			errx(1, "Permission denied");
2956319ad28SChristian S.J. Peron #endif
296ed5fc39fSMark Murray 		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
297ed5fc39fSMark Murray 		    username, user, mytty);
2985bc9d93dSMark Murray 		errx(1, "Sorry");
2995bc9d93dSMark Murray 	}
3006319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
3016319ad28SChristian S.J. Peron 	if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
3026319ad28SChristian S.J. Peron 		errx(1, "Permission denied");
3036319ad28SChristian S.J. Peron #endif
304f258a139SDavid Malone 	retcode = pam_get_item(pamh, PAM_USER, &v);
3055b3771f1SMark Murray 	if (retcode == PAM_SUCCESS)
306f258a139SDavid Malone 		user = v;
3075b3771f1SMark Murray 	else
3085bc9d93dSMark Murray 		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
3095bc9d93dSMark Murray 		    pam_strerror(pamh, retcode));
31060f4b54dSDag-Erling Smørgrav 	pwd = getpwnam(user);
3116319ad28SChristian S.J. Peron 	if (pwd == NULL) {
3126319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
313ce8c6d71SChristian S.J. Peron 		if (audit_submit(AUE_su, auid, EPERM, 1,
3146319ad28SChristian S.J. Peron 		    "unknown subject: %s", user))
3156319ad28SChristian S.J. Peron 			errx(1, "Permission denied");
3166319ad28SChristian S.J. Peron #endif
31760f4b54dSDag-Erling Smørgrav 		errx(1, "unknown login: %s", user);
3186319ad28SChristian S.J. Peron 	}
3195bc9d93dSMark Murray 
3205bc9d93dSMark Murray 	retcode = pam_acct_mgmt(pamh, 0);
3215bc9d93dSMark Murray 	if (retcode == PAM_NEW_AUTHTOK_REQD) {
3225b3771f1SMark Murray 		retcode = pam_chauthtok(pamh,
3235b3771f1SMark Murray 			PAM_CHANGE_EXPIRED_AUTHTOK);
3245bc9d93dSMark Murray 		if (retcode != PAM_SUCCESS) {
3256319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
3266319ad28SChristian S.J. Peron 			aerr = pam_strerror(pamh, retcode);
3276319ad28SChristian S.J. Peron 			if (aerr == NULL)
3286319ad28SChristian S.J. Peron 				aerr = "Unknown PAM error";
329ce8c6d71SChristian S.J. Peron 			if (audit_submit(AUE_su, auid, EPERM, 1,
3306319ad28SChristian S.J. Peron 			    "pam_chauthtok: %s", aerr))
3316319ad28SChristian S.J. Peron 				errx(1, "Permission denied");
3326319ad28SChristian S.J. Peron #endif
3335b3771f1SMark Murray 			syslog(LOG_ERR, "pam_chauthtok: %s",
3345b3771f1SMark Murray 			    pam_strerror(pamh, retcode));
3355bc9d93dSMark Murray 			errx(1, "Sorry");
3365bc9d93dSMark Murray 		}
3375bc9d93dSMark Murray 	}
3385bc9d93dSMark Murray 	if (retcode != PAM_SUCCESS) {
3396319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
340ce8c6d71SChristian S.J. Peron 		if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
3416319ad28SChristian S.J. Peron 		    pam_strerror(pamh, retcode)))
3426319ad28SChristian S.J. Peron 			errx(1, "Permission denied");
3436319ad28SChristian S.J. Peron #endif
3445b3771f1SMark Murray 		syslog(LOG_ERR, "pam_acct_mgmt: %s",
3455b3771f1SMark Murray 			pam_strerror(pamh, retcode));
3465bc9d93dSMark Murray 		errx(1, "Sorry");
3475bc9d93dSMark Murray 	}
3485bc9d93dSMark Murray 
34960f4b54dSDag-Erling Smørgrav 	/* get target login information */
3505b3771f1SMark Murray 	if (class == NULL)
3515d0bfe39SDavid Nugent 		lc = login_getpwclass(pwd);
3525b3771f1SMark Murray 	else {
3536319ad28SChristian S.J. Peron 		if (ruid != 0) {
3546319ad28SChristian S.J. Peron #ifdef USE_BSM_AUDIT
355ce8c6d71SChristian S.J. Peron 			if (audit_submit(AUE_su, auid, EPERM, 1,
3566319ad28SChristian S.J. Peron 			    "only root may use -c"))
3576319ad28SChristian S.J. Peron 				errx(1, "Permission denied");
3586319ad28SChristian S.J. Peron #endif
3597a853dffSGuido van Rooij 			errx(1, "only root may use -c");
3606319ad28SChristian S.J. Peron 		}
3617a853dffSGuido van Rooij 		lc = login_getclass(class);
3627a853dffSGuido van Rooij 		if (lc == NULL)
363d790b965SEdward Tomasz Napierala 			err(1, "login_getclass");
364d790b965SEdward Tomasz Napierala 		if (lc->lc_class == NULL || strcmp(class, lc->lc_class) != 0)
3657a853dffSGuido van Rooij 			errx(1, "unknown class: %s", class);
3667a853dffSGuido van Rooij 	}
3679b50d902SRodney W. Grimes 
3689b50d902SRodney W. Grimes 	/* if asme and non-standard target shell, must be root */
3695b3771f1SMark Murray 	if (asme) {
3705b3771f1SMark Murray 		if (ruid != 0 && !chshell(pwd->pw_shell))
371d6d62d8dSPhilippe Charnier 			errx(1, "permission denied (shell)");
3725b3771f1SMark Murray 	}
3735b3771f1SMark Murray 	else if (pwd->pw_shell && *pwd->pw_shell) {
3749b50d902SRodney W. Grimes 		shell = pwd->pw_shell;
3759b50d902SRodney W. Grimes 		iscsh = UNSET;
3765b3771f1SMark Murray 	}
3775b3771f1SMark Murray 	else {
3789b50d902SRodney W. Grimes 		shell = _PATH_BSHELL;
3799b50d902SRodney W. Grimes 		iscsh = NO;
3809b50d902SRodney W. Grimes 	}
3819b50d902SRodney W. Grimes 
3829b50d902SRodney W. Grimes 	/* if we're forking a csh, we want to slightly muck the args */
3839b50d902SRodney W. Grimes 	if (iscsh == UNSET) {
3845a453b0eSMark Murray 		p = strrchr(shell, '/');
3855a453b0eSMark Murray 		if (p)
3869b50d902SRodney W. Grimes 			++p;
3879b50d902SRodney W. Grimes 		else
3889b50d902SRodney W. Grimes 			p = shell;
3895b3771f1SMark Murray 		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
3909b50d902SRodney W. Grimes 	}
3915b3771f1SMark Murray 	setpriority(PRIO_PROCESS, 0, prio);
39291bcac64SDavid Nugent 
3935bc9d93dSMark Murray 	/*
3945b3771f1SMark Murray 	 * PAM modules might add supplementary groups in pam_setcred(), so
3955b3771f1SMark Murray 	 * initialize them first.
3965bc9d93dSMark Murray 	 */
3975bc9d93dSMark Murray 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
3985bc9d93dSMark Murray 		err(1, "setusercontext");
3995bc9d93dSMark Murray 
4005bc9d93dSMark Murray 	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
40160b28daaSDag-Erling Smørgrav 	if (retcode != PAM_SUCCESS) {
40260b28daaSDag-Erling Smørgrav 		syslog(LOG_ERR, "pam_setcred: %s",
4035b3771f1SMark Murray 		    pam_strerror(pamh, retcode));
40460b28daaSDag-Erling Smørgrav 		errx(1, "failed to establish credentials.");
40560b28daaSDag-Erling Smørgrav 	}
40660b28daaSDag-Erling Smørgrav 	if (asthem) {
40760b28daaSDag-Erling Smørgrav 		retcode = pam_open_session(pamh, 0);
40860b28daaSDag-Erling Smørgrav 		if (retcode != PAM_SUCCESS) {
40960b28daaSDag-Erling Smørgrav 			syslog(LOG_ERR, "pam_open_session: %s",
41060b28daaSDag-Erling Smørgrav 			    pam_strerror(pamh, retcode));
41160b28daaSDag-Erling Smørgrav 			errx(1, "failed to open session.");
41260b28daaSDag-Erling Smørgrav 		}
41360b28daaSDag-Erling Smørgrav 	}
4145bc9d93dSMark Murray 
4155bc9d93dSMark Murray 	/*
4165bc9d93dSMark Murray 	 * We must fork() before setuid() because we need to call
4175bc9d93dSMark Murray 	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
4185bc9d93dSMark Murray 	 */
419b22ac97bSMatthew Dillon 	sa.sa_flags = SA_RESTART;
420505b2816STim J. Robbins 	sa.sa_handler = SIG_IGN;
421b22ac97bSMatthew Dillon 	sigemptyset(&sa.sa_mask);
422b22ac97bSMatthew Dillon 	sigaction(SIGINT, &sa, &sa_int);
423b22ac97bSMatthew Dillon 	sigaction(SIGQUIT, &sa, &sa_quit);
424a75fd4bfSDavid Xu 	sigaction(SIGPIPE, &sa, &sa_pipe);
425658d3a6bSDavid Xu 	sa.sa_handler = SIG_DFL;
426658d3a6bSDavid Xu 	sigaction(SIGTSTP, &sa, NULL);
4275bc9d93dSMark Murray 	statusp = 1;
428a75fd4bfSDavid Xu 	if (pipe(fds) == -1) {
429a75fd4bfSDavid Xu 		PAM_END();
430953cb3ecSMark Murray 		err(1, "pipe");
431a75fd4bfSDavid Xu 	}
4325b3771f1SMark Murray 	child_pid = fork();
4335b3771f1SMark Murray 	switch (child_pid) {
4345bc9d93dSMark Murray 	default:
435bcf123b3SDavid Xu 		sa.sa_handler = SIG_IGN;
436bcf123b3SDavid Xu 		sigaction(SIGTTOU, &sa, NULL);
437a75fd4bfSDavid Xu 		close(fds[0]);
438cd17a1f7SBrian Somers 		setpgid(child_pid, child_pid);
439cd17a1f7SBrian Somers 		if (tcgetpgrp(STDERR_FILENO) == getpgrp())
440cd17a1f7SBrian Somers 			tcsetpgrp(STDERR_FILENO, child_pid);
441a75fd4bfSDavid Xu 		close(fds[1]);
442a75fd4bfSDavid Xu 		sigaction(SIGPIPE, &sa_pipe, NULL);
44360b28daaSDag-Erling Smørgrav 		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
4445bc9d93dSMark Murray 			if (WIFSTOPPED(statusp)) {
445cd17a1f7SBrian Somers 				child_pgrp = getpgid(child_pid);
446cd17a1f7SBrian Somers 				if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
447cd17a1f7SBrian Somers 					tcsetpgrp(STDERR_FILENO, getpgrp());
448d039c62bSBrian Somers 				kill(getpid(), SIGSTOP);
449cd17a1f7SBrian Somers 				if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
450cd17a1f7SBrian Somers 					child_pgrp = getpgid(child_pid);
451cd17a1f7SBrian Somers 					tcsetpgrp(STDERR_FILENO, child_pgrp);
452cd17a1f7SBrian Somers 				}
453d039c62bSBrian Somers 				kill(child_pid, SIGCONT);
4545bc9d93dSMark Murray 				statusp = 1;
4555bc9d93dSMark Murray 				continue;
4565bc9d93dSMark Murray 			}
4575bc9d93dSMark Murray 			break;
4585bc9d93dSMark Murray 		}
459cd17a1f7SBrian Somers 		tcsetpgrp(STDERR_FILENO, getpgrp());
46060b28daaSDag-Erling Smørgrav 		if (pid == -1)
4615bc9d93dSMark Murray 			err(1, "waitpid");
4628cc3b02fSMark Murray 		PAM_END();
463953cb3ecSMark Murray 		exit(WEXITSTATUS(statusp));
4645bc9d93dSMark Murray 	case -1:
4658cc3b02fSMark Murray 		PAM_END();
466953cb3ecSMark Murray 		err(1, "fork");
4675bc9d93dSMark Murray 	case 0:
468a75fd4bfSDavid Xu 		close(fds[1]);
469a75fd4bfSDavid Xu 		read(fds[0], &temp, 1);
470a75fd4bfSDavid Xu 		close(fds[0]);
471a75fd4bfSDavid Xu 		sigaction(SIGPIPE, &sa_pipe, NULL);
472b22ac97bSMatthew Dillon 		sigaction(SIGINT, &sa_int, NULL);
473b22ac97bSMatthew Dillon 		sigaction(SIGQUIT, &sa_quit, NULL);
474a75fd4bfSDavid Xu 
475e292984cSRobert Watson 		/*
4765b3771f1SMark Murray 		 * Set all user context except for: Environmental variables
4775b3771f1SMark Murray 		 * Umask Login records (wtmp, etc) Path
478e292984cSRobert Watson 		 */
479e292984cSRobert Watson 		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
4801494905bSRobert Watson 			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
4811494905bSRobert Watson 			   LOGIN_SETMAC);
4821494905bSRobert Watson 		/*
4831494905bSRobert Watson 		 * If -s is present, also set the MAC label.
4841494905bSRobert Watson 		 */
4851494905bSRobert Watson 		if (setmaclabel)
4861494905bSRobert Watson 			setwhat |= LOGIN_SETMAC;
48791bcac64SDavid Nugent 		/*
4885b3771f1SMark Murray 		 * Don't touch resource/priority settings if -m has been used
4895b3771f1SMark Murray 		 * or -l and -c hasn't, and we're not su'ing to root.
49091bcac64SDavid Nugent 		 */
4917a853dffSGuido van Rooij 		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
492a564e855SDavid Nugent 			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
49391bcac64SDavid Nugent 		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
49491bcac64SDavid Nugent 			err(1, "setusercontext");
4959b50d902SRodney W. Grimes 
4969b50d902SRodney W. Grimes 		if (!asme) {
4979b50d902SRodney W. Grimes 			if (asthem) {
4989b50d902SRodney W. Grimes 				p = getenv("TERM");
4995bc9d93dSMark Murray 				environ = &cleanenv;
50060b28daaSDag-Erling Smørgrav 			}
5015bc9d93dSMark Murray 
50260b28daaSDag-Erling Smørgrav 			if (asthem || pwd->pw_uid)
50360b28daaSDag-Erling Smørgrav 				setenv("USER", pwd->pw_name, 1);
50460b28daaSDag-Erling Smørgrav 			setenv("HOME", pwd->pw_dir, 1);
50560b28daaSDag-Erling Smørgrav 			setenv("SHELL", shell, 1);
50660b28daaSDag-Erling Smørgrav 
50760b28daaSDag-Erling Smørgrav 			if (asthem) {
5085bc9d93dSMark Murray 				/*
5095bc9d93dSMark Murray 				 * Add any environmental variables that the
5105bc9d93dSMark Murray 				 * PAM modules may have set.
5115bc9d93dSMark Murray 				 */
5125bc9d93dSMark Murray 				environ_pam = pam_getenvlist(pamh);
5135bc9d93dSMark Murray 				if (environ_pam)
5145bc9d93dSMark Murray 					export_pam_environment();
5155bc9d93dSMark Murray 
51691bcac64SDavid Nugent 				/* set the su'd user's environment & umask */
5175b3771f1SMark Murray 				setusercontext(lc, pwd, pwd->pw_uid,
5185b3771f1SMark Murray 					LOGIN_SETPATH | LOGIN_SETUMASK |
5195b3771f1SMark Murray 					LOGIN_SETENV);
52040a8a5cfSJoerg Wunsch 				if (p)
5215b3771f1SMark Murray 					setenv("TERM", p, 1);
5225a8e64eeSLuoqi Chen 
5235a8e64eeSLuoqi Chen 				p = pam_getenv(pamh, "HOME");
5245a8e64eeSLuoqi Chen 				if (chdir(p ? p : pwd->pw_dir) < 0)
5255a8e64eeSLuoqi Chen 					errx(1, "no directory");
5269b50d902SRodney W. Grimes 			}
5279b50d902SRodney W. Grimes 		}
5285bc9d93dSMark Murray 		login_close(lc);
5295bc9d93dSMark Murray 
5309b50d902SRodney W. Grimes 		if (iscsh == YES) {
5319b50d902SRodney W. Grimes 			if (fastlogin)
53269ebfe34SMark Murray 				*np.a-- = "-f";
5339b50d902SRodney W. Grimes 			if (asme)
53469ebfe34SMark Murray 				*np.a-- = "-m";
5359b50d902SRodney W. Grimes 		}
5369b50d902SRodney W. Grimes 		/* csh strips the first character... */
53769ebfe34SMark Murray 		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
5389b50d902SRodney W. Grimes 
5399b50d902SRodney W. Grimes 		if (ruid != 0)
5405b3771f1SMark Murray 			syslog(LOG_NOTICE, "%s to %s%s", username, user,
5415b3771f1SMark Murray 			    ontty());
5429b50d902SRodney W. Grimes 
54369ebfe34SMark Murray 		execv(shell, np.b);
5449b50d902SRodney W. Grimes 		err(1, "%s", shell);
5459b50d902SRodney W. Grimes 	}
5465bc9d93dSMark Murray }
5475bc9d93dSMark Murray 
548953cb3ecSMark Murray static void
export_pam_environment(void)5495b3771f1SMark Murray export_pam_environment(void)
5505bc9d93dSMark Murray {
5515bc9d93dSMark Murray 	char	**pp;
5522966d28cSSean Farley 	char	*p;
5535bc9d93dSMark Murray 
5545bc9d93dSMark Murray 	for (pp = environ_pam; *pp != NULL; pp++) {
5552966d28cSSean Farley 		if (ok_to_export(*pp)) {
5562966d28cSSean Farley 			p = strchr(*pp, '=');
5572966d28cSSean Farley 			*p = '\0';
5582966d28cSSean Farley 			setenv(*pp, p + 1, 1);
5592966d28cSSean Farley 		}
5605bc9d93dSMark Murray 		free(*pp);
5615bc9d93dSMark Murray 	}
5625bc9d93dSMark Murray }
5635bc9d93dSMark Murray 
5645bc9d93dSMark Murray /*
5655bc9d93dSMark Murray  * Sanity checks on PAM environmental variables:
5665bc9d93dSMark Murray  * - Make sure there is an '=' in the string.
5675bc9d93dSMark Murray  * - Make sure the string doesn't run on too long.
5685bc9d93dSMark Murray  * - Do not export certain variables.  This list was taken from the
5695bc9d93dSMark Murray  *   Solaris pam_putenv(3) man page.
57060b28daaSDag-Erling Smørgrav  * Note that if the user is chrooted, PAM may have a better idea than we
57160b28daaSDag-Erling Smørgrav  * do of where her home directory is.
5725bc9d93dSMark Murray  */
5735bc9d93dSMark Murray static int
ok_to_export(const char * s)5745b3771f1SMark Murray ok_to_export(const char *s)
5755bc9d93dSMark Murray {
5765bc9d93dSMark Murray 	static const char *noexport[] = {
57760b28daaSDag-Erling Smørgrav 		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
5785bc9d93dSMark Murray 		"IFS", "PATH", NULL
5795bc9d93dSMark Murray 	};
5805bc9d93dSMark Murray 	const char **pp;
5815bc9d93dSMark Murray 	size_t n;
5825bc9d93dSMark Murray 
5835bc9d93dSMark Murray 	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
5845bc9d93dSMark Murray 		return 0;
5855bc9d93dSMark Murray 	if (strncmp(s, "LD_", 3) == 0)
5865bc9d93dSMark Murray 		return 0;
5875bc9d93dSMark Murray 	for (pp = noexport; *pp != NULL; pp++) {
5885bc9d93dSMark Murray 		n = strlen(*pp);
5895bc9d93dSMark Murray 		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
5905bc9d93dSMark Murray 			return 0;
5915bc9d93dSMark Murray 	}
5925bc9d93dSMark Murray 	return 1;
5935bc9d93dSMark Murray }
5949b50d902SRodney W. Grimes 
5957be91299SPhilippe Charnier static void
usage(void)5965b3771f1SMark Murray usage(void)
5977be91299SPhilippe Charnier {
598d1433646SRuslan Ermilov 
5991494905bSRobert Watson 	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
600d1433646SRuslan Ermilov 	exit(1);
601953cb3ecSMark Murray 	/* NOTREACHED */
6027be91299SPhilippe Charnier }
6037be91299SPhilippe Charnier 
6045b3771f1SMark Murray static int
chshell(const char * sh)605953cb3ecSMark Murray chshell(const char *sh)
6069b50d902SRodney W. Grimes {
6075b3771f1SMark Murray 	int r;
6089b50d902SRodney W. Grimes 	char *cp;
6099b50d902SRodney W. Grimes 
6105b3771f1SMark Murray 	r = 0;
61191bcac64SDavid Nugent 	setusershell();
612f6a43a2bSOlivier Houchard 	while ((cp = getusershell()) != NULL && !r)
613f6a43a2bSOlivier Houchard 	    r = (strcmp(cp, sh) == 0);
61491bcac64SDavid Nugent 	endusershell();
61591bcac64SDavid Nugent 	return r;
6169b50d902SRodney W. Grimes }
6179b50d902SRodney W. Grimes 
6185b3771f1SMark Murray static char *
ontty(void)6195b3771f1SMark Murray ontty(void)
6209b50d902SRodney W. Grimes {
6219b50d902SRodney W. Grimes 	char *p;
6229b50d902SRodney W. Grimes 	static char buf[MAXPATHLEN + 4];
6239b50d902SRodney W. Grimes 
6249b50d902SRodney W. Grimes 	buf[0] = 0;
6255a453b0eSMark Murray 	p = ttyname(STDERR_FILENO);
6265a453b0eSMark Murray 	if (p)
6279b50d902SRodney W. Grimes 		snprintf(buf, sizeof(buf), " on %s", p);
6285b3771f1SMark Murray 	return buf;
6299b50d902SRodney W. Grimes }
630