su.c revision 200420
1264790Sbapt/*
2264790Sbapt * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
3264790Sbapt * All rights reserved.
4264790Sbapt *
5264790Sbapt * Portions of this software were developed for the FreeBSD Project by
6264790Sbapt * ThinkSec AS and NAI Labs, the Security Research Division of Network
7264790Sbapt * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
8264790Sbapt * ("CBOSS"), as part of the DARPA CHATS research program.
9264790Sbapt *
10264790Sbapt * Redistribution and use in source and binary forms, with or without
11264790Sbapt * modification, are permitted provided that the following conditions
12264790Sbapt * are met:
13264790Sbapt * 1. Redistributions of source code must retain the above copyright
14264790Sbapt *    notice, this list of conditions and the following disclaimer.
15264790Sbapt * 2. Redistributions in binary form must reproduce the above copyright
16264790Sbapt *    notice, this list of conditions and the following disclaimer in the
17264790Sbapt *    documentation and/or other materials provided with the distribution.
18264790Sbapt *
19264790Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20264790Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21264790Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22264790Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23264790Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24264790Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25264790Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26264790Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27264790Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28264790Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29264790Sbapt * SUCH DAMAGE.
30264790Sbapt */
31264790Sbapt/*-
32264790Sbapt * Copyright (c) 1988, 1993, 1994
33264790Sbapt *	The Regents of the University of California.  All rights reserved.
34264790Sbapt *
35264790Sbapt * Redistribution and use in source and binary forms, with or without
36264790Sbapt * modification, are permitted provided that the following conditions
37264790Sbapt * are met:
38264790Sbapt * 1. Redistributions of source code must retain the above copyright
39264790Sbapt *    notice, this list of conditions and the following disclaimer.
40264790Sbapt * 2. Redistributions in binary form must reproduce the above copyright
41264790Sbapt *    notice, this list of conditions and the following disclaimer in the
42264790Sbapt *    documentation and/or other materials provided with the distribution.
43264790Sbapt * 3. All advertising materials mentioning features or use of this software
44264790Sbapt *    must display the following acknowledgement:
45264790Sbapt *	This product includes software developed by the University of
46264790Sbapt *	California, Berkeley and its contributors.
47264790Sbapt * 4. Neither the name of the University nor the names of its contributors
48264790Sbapt *    may be used to endorse or promote products derived from this software
49264790Sbapt *    without specific prior written permission.
50264790Sbapt *
51264790Sbapt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52264790Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53264790Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54264790Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55264790Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56264790Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57264790Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58264790Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59264790Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60264790Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61264790Sbapt * SUCH DAMAGE.
62264790Sbapt */
63264790Sbapt
64264790Sbapt#ifndef lint
65264790Sbaptstatic const char copyright[] =
66264790Sbapt"@(#) Copyright (c) 1988, 1993, 1994\n\
67264790Sbapt	The Regents of the University of California.  All rights reserved.\n";
68264790Sbapt#endif /* not lint */
69264790Sbapt
70264790Sbapt#if 0
71264790Sbapt#ifndef lint
72264790Sbaptstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
73264790Sbapt#endif /* not lint */
74264790Sbapt#endif
75264790Sbapt
76264790Sbapt#include <sys/cdefs.h>
77264790Sbapt__FBSDID("$FreeBSD: head/usr.bin/su/su.c 200420 2009-12-11 23:35:38Z delphij $");
78264790Sbapt
79264790Sbapt#include <sys/param.h>
80264790Sbapt#include <sys/time.h>
81264790Sbapt#include <sys/resource.h>
82264790Sbapt#include <sys/wait.h>
83264790Sbapt
84264790Sbapt#ifdef USE_BSM_AUDIT
85264790Sbapt#include <bsm/libbsm.h>
86264790Sbapt#include <bsm/audit_uevents.h>
87264790Sbapt#endif
88264790Sbapt
89264790Sbapt#include <err.h>
90264790Sbapt#include <errno.h>
91264790Sbapt#include <login_cap.h>
92264790Sbapt#include <paths.h>
93264790Sbapt#include <pwd.h>
94264790Sbapt#include <signal.h>
95264790Sbapt#include <stdio.h>
96264790Sbapt#include <stdlib.h>
97264790Sbapt#include <string.h>
98264790Sbapt#include <syslog.h>
99264790Sbapt#include <unistd.h>
100264790Sbapt#include <stdarg.h>
101264790Sbapt
102264790Sbapt#include <security/pam_appl.h>
103264790Sbapt#include <security/openpam.h>
104264790Sbapt
105264790Sbapt#define PAM_END() do {							\
106264790Sbapt	int local_ret;							\
107264790Sbapt	if (pamh != NULL) {						\
108264790Sbapt		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
109264790Sbapt		if (local_ret != PAM_SUCCESS)				\
110264790Sbapt			syslog(LOG_ERR, "pam_setcred: %s",		\
111264790Sbapt				pam_strerror(pamh, local_ret));		\
112264790Sbapt		if (asthem) {						\
113264790Sbapt			local_ret = pam_close_session(pamh, 0);		\
114264790Sbapt			if (local_ret != PAM_SUCCESS)			\
115264790Sbapt				syslog(LOG_ERR, "pam_close_session: %s",\
116264790Sbapt					pam_strerror(pamh, local_ret));	\
117264790Sbapt		}							\
118264790Sbapt		local_ret = pam_end(pamh, local_ret);			\
119264790Sbapt		if (local_ret != PAM_SUCCESS)				\
120264790Sbapt			syslog(LOG_ERR, "pam_end: %s",			\
121264790Sbapt				pam_strerror(pamh, local_ret));		\
122264790Sbapt	}								\
123264790Sbapt} while (0)
124264790Sbapt
125264790Sbapt
126264790Sbapt#define PAM_SET_ITEM(what, item) do {					\
127264790Sbapt	int local_ret;							\
128264790Sbapt	local_ret = pam_set_item(pamh, what, item);			\
129264790Sbapt	if (local_ret != PAM_SUCCESS) {					\
130264790Sbapt		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
131264790Sbapt			pam_strerror(pamh, local_ret));			\
132264790Sbapt		errx(1, "pam_set_item(" #what "): %s",			\
133264790Sbapt			pam_strerror(pamh, local_ret));			\
134264790Sbapt		/* NOTREACHED */					\
135264790Sbapt	}								\
136264790Sbapt} while (0)
137264790Sbapt
138264790Sbaptenum tristate { UNSET, YES, NO };
139264790Sbapt
140264790Sbaptstatic pam_handle_t *pamh = NULL;
141264790Sbaptstatic char	**environ_pam;
142264790Sbapt
143264790Sbaptstatic char	*ontty(void);
144264790Sbaptstatic int	chshell(const char *);
145264790Sbaptstatic void	usage(void) __dead2;
146264790Sbaptstatic void	export_pam_environment(void);
147264790Sbaptstatic int	ok_to_export(const char *);
148264790Sbapt
149264790Sbaptextern char	**environ;
150264790Sbapt
151264790Sbaptint
152264790Sbaptmain(int argc, char *argv[])
153264790Sbapt{
154264790Sbapt	static char	*cleanenv;
155264790Sbapt	struct passwd	*pwd;
156264790Sbapt	struct pam_conv	conv = { openpam_ttyconv, NULL };
157264790Sbapt	enum tristate	iscsh;
158264790Sbapt	login_cap_t	*lc;
159264790Sbapt	union {
160264790Sbapt		const char	**a;
161264790Sbapt		char		* const *b;
162264790Sbapt	}		np;
163264790Sbapt	uid_t		ruid;
164264790Sbapt	pid_t		child_pid, child_pgrp, pid;
165264790Sbapt	int		asme, ch, asthem, fastlogin, prio, i, retcode,
166264790Sbapt			statusp, setmaclabel;
167264790Sbapt	u_int		setwhat;
168264790Sbapt	char		*username, *class, shellbuf[MAXPATHLEN];
169264790Sbapt	const char	*p, *user, *shell, *mytty, **nargv;
170264790Sbapt	const void	*v;
171264790Sbapt	struct sigaction sa, sa_int, sa_quit, sa_pipe;
172264790Sbapt	int temp, fds[2];
173264790Sbapt#ifdef USE_BSM_AUDIT
174264790Sbapt	const char	*aerr;
175264790Sbapt	au_id_t		 auid;
176264790Sbapt#endif
177264790Sbapt
178264790Sbapt	shell = class = cleanenv = NULL;
179264790Sbapt	asme = asthem = fastlogin = statusp = 0;
180264790Sbapt	user = "root";
181264790Sbapt	iscsh = UNSET;
182264790Sbapt	setmaclabel = 0;
183264790Sbapt
184264790Sbapt	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
185264790Sbapt		switch ((char)ch) {
186264790Sbapt		case 'f':
187264790Sbapt			fastlogin = 1;
188264790Sbapt			break;
189264790Sbapt		case '-':
190264790Sbapt		case 'l':
191264790Sbapt			asme = 0;
192264790Sbapt			asthem = 1;
193264790Sbapt			break;
194264790Sbapt		case 'm':
195264790Sbapt			asme = 1;
196264790Sbapt			asthem = 0;
197264790Sbapt			break;
198264790Sbapt		case 's':
199264790Sbapt			setmaclabel = 1;
200264790Sbapt			break;
201264790Sbapt		case 'c':
202264790Sbapt			class = optarg;
203264790Sbapt			break;
204264790Sbapt		case '?':
205264790Sbapt		default:
206264790Sbapt			usage();
207264790Sbapt		/* NOTREACHED */
208264790Sbapt		}
209264790Sbapt
210264790Sbapt	if (optind < argc)
211264790Sbapt		user = argv[optind++];
212264790Sbapt
213264790Sbapt	if (user == NULL)
214264790Sbapt		usage();
215264790Sbapt	/* NOTREACHED */
216264790Sbapt
217264790Sbapt	/*
218264790Sbapt	 * Try to provide more helpful debugging output if su(1) is running
219264790Sbapt	 * non-setuid, or was run from a file system not mounted setuid.
220264790Sbapt	 */
221264790Sbapt	if (geteuid() != 0)
222264790Sbapt		errx(1, "not running setuid");
223264790Sbapt
224264790Sbapt#ifdef USE_BSM_AUDIT
225264790Sbapt	if (getauid(&auid) < 0 && errno != ENOSYS) {
226264790Sbapt		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
227264790Sbapt		errx(1, "Permission denied");
228264790Sbapt	}
229264790Sbapt#endif
230264790Sbapt	if (strlen(user) > MAXLOGNAME - 1) {
231264790Sbapt#ifdef USE_BSM_AUDIT
232264790Sbapt		if (audit_submit(AUE_su, auid,
233264790Sbapt		    EPERM, 1, "username too long: '%s'", user))
234264790Sbapt			errx(1, "Permission denied");
235264790Sbapt#endif
236264790Sbapt		errx(1, "username too long");
237264790Sbapt	}
238264790Sbapt
239264790Sbapt	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
240264790Sbapt	if (nargv == NULL)
241264790Sbapt		errx(1, "malloc failure");
242264790Sbapt
243264790Sbapt	nargv[argc + 3] = NULL;
244264790Sbapt	for (i = argc; i >= optind; i--)
245264790Sbapt		nargv[i + 3] = argv[i];
246264790Sbapt	np.a = &nargv[i + 3];
247264790Sbapt
248264790Sbapt	argv += optind;
249264790Sbapt
250264790Sbapt	errno = 0;
251264790Sbapt	prio = getpriority(PRIO_PROCESS, 0);
252264790Sbapt	if (errno)
253264790Sbapt		prio = 0;
254264790Sbapt
255264790Sbapt	setpriority(PRIO_PROCESS, 0, -2);
256264790Sbapt	openlog("su", LOG_CONS, LOG_AUTH);
257264790Sbapt
258264790Sbapt	/* get current login name, real uid and shell */
259264790Sbapt	ruid = getuid();
260264790Sbapt	username = getlogin();
261264790Sbapt	pwd = getpwnam(username);
262264790Sbapt	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
263264790Sbapt		pwd = getpwuid(ruid);
264264790Sbapt	if (pwd == NULL) {
265264790Sbapt#ifdef USE_BSM_AUDIT
266264790Sbapt		if (audit_submit(AUE_su, auid, EPERM, 1,
267264790Sbapt		    "unable to determine invoking subject: '%s'", username))
268264790Sbapt			errx(1, "Permission denied");
269264790Sbapt#endif
270264790Sbapt		errx(1, "who are you?");
271264790Sbapt	}
272264790Sbapt
273264790Sbapt	username = strdup(pwd->pw_name);
274264790Sbapt	if (username == NULL)
275264790Sbapt		err(1, "strdup failure");
276264790Sbapt
277264790Sbapt	if (asme) {
278264790Sbapt		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
279264790Sbapt			/* must copy - pwd memory is recycled */
280264790Sbapt			shell = strncpy(shellbuf, pwd->pw_shell,
281264790Sbapt			    sizeof(shellbuf));
282264790Sbapt			shellbuf[sizeof(shellbuf) - 1] = '\0';
283264790Sbapt		}
284264790Sbapt		else {
285264790Sbapt			shell = _PATH_BSHELL;
286264790Sbapt			iscsh = NO;
287264790Sbapt		}
288264790Sbapt	}
289264790Sbapt
290264790Sbapt	/* Do the whole PAM startup thing */
291264790Sbapt	retcode = pam_start("su", user, &conv, &pamh);
292264790Sbapt	if (retcode != PAM_SUCCESS) {
293264790Sbapt		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
294264790Sbapt		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
295264790Sbapt	}
296264790Sbapt
297264790Sbapt	PAM_SET_ITEM(PAM_RUSER, username);
298264790Sbapt
299264790Sbapt	mytty = ttyname(STDERR_FILENO);
300264790Sbapt	if (!mytty)
301264790Sbapt		mytty = "tty";
302264790Sbapt	PAM_SET_ITEM(PAM_TTY, mytty);
303264790Sbapt
304264790Sbapt	retcode = pam_authenticate(pamh, 0);
305264790Sbapt	if (retcode != PAM_SUCCESS) {
306264790Sbapt#ifdef USE_BSM_AUDIT
307264790Sbapt		if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
308264790Sbapt		    username, user, mytty))
309264790Sbapt			errx(1, "Permission denied");
310264790Sbapt#endif
311264790Sbapt		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
312264790Sbapt		    username, user, mytty);
313264790Sbapt		errx(1, "Sorry");
314264790Sbapt	}
315264790Sbapt#ifdef USE_BSM_AUDIT
316264790Sbapt	if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
317264790Sbapt		errx(1, "Permission denied");
318264790Sbapt#endif
319264790Sbapt	retcode = pam_get_item(pamh, PAM_USER, &v);
320264790Sbapt	if (retcode == PAM_SUCCESS)
321264790Sbapt		user = v;
322264790Sbapt	else
323264790Sbapt		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
324264790Sbapt		    pam_strerror(pamh, retcode));
325264790Sbapt	pwd = getpwnam(user);
326264790Sbapt	if (pwd == NULL) {
327264790Sbapt#ifdef USE_BSM_AUDIT
328264790Sbapt		if (audit_submit(AUE_su, auid, EPERM, 1,
329264790Sbapt		    "unknown subject: %s", user))
330264790Sbapt			errx(1, "Permission denied");
331264790Sbapt#endif
332264790Sbapt		errx(1, "unknown login: %s", user);
333264790Sbapt	}
334264790Sbapt
335264790Sbapt	retcode = pam_acct_mgmt(pamh, 0);
336264790Sbapt	if (retcode == PAM_NEW_AUTHTOK_REQD) {
337264790Sbapt		retcode = pam_chauthtok(pamh,
338264790Sbapt			PAM_CHANGE_EXPIRED_AUTHTOK);
339264790Sbapt		if (retcode != PAM_SUCCESS) {
340264790Sbapt#ifdef USE_BSM_AUDIT
341264790Sbapt			aerr = pam_strerror(pamh, retcode);
342264790Sbapt			if (aerr == NULL)
343264790Sbapt				aerr = "Unknown PAM error";
344264790Sbapt			if (audit_submit(AUE_su, auid, EPERM, 1,
345264790Sbapt			    "pam_chauthtok: %s", aerr))
346264790Sbapt				errx(1, "Permission denied");
347264790Sbapt#endif
348264790Sbapt			syslog(LOG_ERR, "pam_chauthtok: %s",
349264790Sbapt			    pam_strerror(pamh, retcode));
350264790Sbapt			errx(1, "Sorry");
351264790Sbapt		}
352264790Sbapt	}
353264790Sbapt	if (retcode != PAM_SUCCESS) {
354264790Sbapt#ifdef USE_BSM_AUDIT
355264790Sbapt		if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
356264790Sbapt		    pam_strerror(pamh, retcode)))
357264790Sbapt			errx(1, "Permission denied");
358264790Sbapt#endif
359264790Sbapt		syslog(LOG_ERR, "pam_acct_mgmt: %s",
360264790Sbapt			pam_strerror(pamh, retcode));
361264790Sbapt		errx(1, "Sorry");
362264790Sbapt	}
363264790Sbapt
364264790Sbapt	/* get target login information */
365264790Sbapt	if (class == NULL)
366264790Sbapt		lc = login_getpwclass(pwd);
367264790Sbapt	else {
368264790Sbapt		if (ruid != 0) {
369264790Sbapt#ifdef USE_BSM_AUDIT
370264790Sbapt			if (audit_submit(AUE_su, auid, EPERM, 1,
371264790Sbapt			    "only root may use -c"))
372264790Sbapt				errx(1, "Permission denied");
373264790Sbapt#endif
374264790Sbapt			errx(1, "only root may use -c");
375264790Sbapt		}
376264790Sbapt		lc = login_getclass(class);
377264790Sbapt		if (lc == NULL)
378264790Sbapt			errx(1, "unknown class: %s", class);
379264790Sbapt	}
380264790Sbapt
381264790Sbapt	/* if asme and non-standard target shell, must be root */
382264790Sbapt	if (asme) {
383264790Sbapt		if (ruid != 0 && !chshell(pwd->pw_shell))
384264790Sbapt			errx(1, "permission denied (shell)");
385264790Sbapt	}
386264790Sbapt	else if (pwd->pw_shell && *pwd->pw_shell) {
387264790Sbapt		shell = pwd->pw_shell;
388264790Sbapt		iscsh = UNSET;
389264790Sbapt	}
390264790Sbapt	else {
391264790Sbapt		shell = _PATH_BSHELL;
392264790Sbapt		iscsh = NO;
393264790Sbapt	}
394264790Sbapt
395264790Sbapt	/* if we're forking a csh, we want to slightly muck the args */
396264790Sbapt	if (iscsh == UNSET) {
397264790Sbapt		p = strrchr(shell, '/');
398264790Sbapt		if (p)
399264790Sbapt			++p;
400264790Sbapt		else
401264790Sbapt			p = shell;
402264790Sbapt		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
403264790Sbapt	}
404264790Sbapt	setpriority(PRIO_PROCESS, 0, prio);
405264790Sbapt
406264790Sbapt	/*
407264790Sbapt	 * PAM modules might add supplementary groups in pam_setcred(), so
408264790Sbapt	 * initialize them first.
409264790Sbapt	 */
410264790Sbapt	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
411264790Sbapt		err(1, "setusercontext");
412264790Sbapt
413264790Sbapt	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
414264790Sbapt	if (retcode != PAM_SUCCESS) {
415264790Sbapt		syslog(LOG_ERR, "pam_setcred: %s",
416264790Sbapt		    pam_strerror(pamh, retcode));
417264790Sbapt		errx(1, "failed to establish credentials.");
418264790Sbapt	}
419264790Sbapt	if (asthem) {
420264790Sbapt		retcode = pam_open_session(pamh, 0);
421264790Sbapt		if (retcode != PAM_SUCCESS) {
422264790Sbapt			syslog(LOG_ERR, "pam_open_session: %s",
423264790Sbapt			    pam_strerror(pamh, retcode));
424264790Sbapt			errx(1, "failed to open session.");
425264790Sbapt		}
426264790Sbapt	}
427264790Sbapt
428264790Sbapt	/*
429264790Sbapt	 * We must fork() before setuid() because we need to call
430264790Sbapt	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
431264790Sbapt	 */
432264790Sbapt	sa.sa_flags = SA_RESTART;
433264790Sbapt	sa.sa_handler = SIG_IGN;
434264790Sbapt	sigemptyset(&sa.sa_mask);
435264790Sbapt	sigaction(SIGINT, &sa, &sa_int);
436264790Sbapt	sigaction(SIGQUIT, &sa, &sa_quit);
437264790Sbapt	sigaction(SIGPIPE, &sa, &sa_pipe);
438264790Sbapt	sa.sa_handler = SIG_DFL;
439264790Sbapt	sigaction(SIGTSTP, &sa, NULL);
440264790Sbapt	statusp = 1;
441264790Sbapt	if (pipe(fds) == -1) {
442264790Sbapt		PAM_END();
443264790Sbapt		err(1, "pipe");
444264790Sbapt	}
445264790Sbapt	child_pid = fork();
446264790Sbapt	switch (child_pid) {
447264790Sbapt	default:
448264790Sbapt		sa.sa_handler = SIG_IGN;
449264790Sbapt		sigaction(SIGTTOU, &sa, NULL);
450264790Sbapt		close(fds[0]);
451264790Sbapt		setpgid(child_pid, child_pid);
452264790Sbapt		if (tcgetpgrp(STDERR_FILENO) == getpgrp())
453264790Sbapt			tcsetpgrp(STDERR_FILENO, child_pid);
454264790Sbapt		close(fds[1]);
455264790Sbapt		sigaction(SIGPIPE, &sa_pipe, NULL);
456264790Sbapt		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
457264790Sbapt			if (WIFSTOPPED(statusp)) {
458264790Sbapt				child_pgrp = getpgid(child_pid);
459264790Sbapt				if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
460264790Sbapt					tcsetpgrp(STDERR_FILENO, getpgrp());
461264790Sbapt				kill(getpid(), SIGSTOP);
462264790Sbapt				if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
463264790Sbapt					child_pgrp = getpgid(child_pid);
464264790Sbapt					tcsetpgrp(STDERR_FILENO, child_pgrp);
465264790Sbapt				}
466264790Sbapt				kill(child_pid, SIGCONT);
467264790Sbapt				statusp = 1;
468264790Sbapt				continue;
469264790Sbapt			}
470264790Sbapt			break;
471264790Sbapt		}
472264790Sbapt		tcsetpgrp(STDERR_FILENO, getpgrp());
473264790Sbapt		if (pid == -1)
474264790Sbapt			err(1, "waitpid");
475264790Sbapt		PAM_END();
476264790Sbapt		exit(WEXITSTATUS(statusp));
477264790Sbapt	case -1:
478264790Sbapt		PAM_END();
479264790Sbapt		err(1, "fork");
480264790Sbapt	case 0:
481264790Sbapt		close(fds[1]);
482264790Sbapt		read(fds[0], &temp, 1);
483264790Sbapt		close(fds[0]);
484264790Sbapt		sigaction(SIGPIPE, &sa_pipe, NULL);
485264790Sbapt		sigaction(SIGINT, &sa_int, NULL);
486264790Sbapt		sigaction(SIGQUIT, &sa_quit, NULL);
487264790Sbapt
488264790Sbapt		/*
489264790Sbapt		 * Set all user context except for: Environmental variables
490264790Sbapt		 * Umask Login records (wtmp, etc) Path
491264790Sbapt		 */
492264790Sbapt		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
493264790Sbapt			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
494264790Sbapt			   LOGIN_SETMAC);
495264790Sbapt		/*
496264790Sbapt		 * If -s is present, also set the MAC label.
497264790Sbapt		 */
498264790Sbapt		if (setmaclabel)
499264790Sbapt			setwhat |= LOGIN_SETMAC;
500264790Sbapt		/*
501264790Sbapt		 * Don't touch resource/priority settings if -m has been used
502264790Sbapt		 * or -l and -c hasn't, and we're not su'ing to root.
503264790Sbapt		 */
504264790Sbapt		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
505264790Sbapt			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
506264790Sbapt		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
507264790Sbapt			err(1, "setusercontext");
508264790Sbapt
509264790Sbapt		if (!asme) {
510264790Sbapt			if (asthem) {
511264790Sbapt				p = getenv("TERM");
512264790Sbapt				environ = &cleanenv;
513264790Sbapt			}
514264790Sbapt
515264790Sbapt			if (asthem || pwd->pw_uid)
516264790Sbapt				setenv("USER", pwd->pw_name, 1);
517264790Sbapt			setenv("HOME", pwd->pw_dir, 1);
518264790Sbapt			setenv("SHELL", shell, 1);
519264790Sbapt
520264790Sbapt			if (asthem) {
521264790Sbapt				/*
522264790Sbapt				 * Add any environmental variables that the
523264790Sbapt				 * PAM modules may have set.
524264790Sbapt				 */
525264790Sbapt				environ_pam = pam_getenvlist(pamh);
526264790Sbapt				if (environ_pam)
527264790Sbapt					export_pam_environment();
528264790Sbapt
529264790Sbapt				/* set the su'd user's environment & umask */
530264790Sbapt				setusercontext(lc, pwd, pwd->pw_uid,
531264790Sbapt					LOGIN_SETPATH | LOGIN_SETUMASK |
532264790Sbapt					LOGIN_SETENV);
533264790Sbapt				if (p)
534264790Sbapt					setenv("TERM", p, 1);
535264790Sbapt
536264790Sbapt				p = pam_getenv(pamh, "HOME");
537264790Sbapt				if (chdir(p ? p : pwd->pw_dir) < 0)
538264790Sbapt					errx(1, "no directory");
539264790Sbapt			}
540264790Sbapt		}
541264790Sbapt		login_close(lc);
542264790Sbapt
543264790Sbapt		if (iscsh == YES) {
544264790Sbapt			if (fastlogin)
545264790Sbapt				*np.a-- = "-f";
546264790Sbapt			if (asme)
547264790Sbapt				*np.a-- = "-m";
548264790Sbapt		}
549264790Sbapt		/* csh strips the first character... */
550264790Sbapt		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
551264790Sbapt
552264790Sbapt		if (ruid != 0)
553264790Sbapt			syslog(LOG_NOTICE, "%s to %s%s", username, user,
554264790Sbapt			    ontty());
555264790Sbapt
556264790Sbapt		execv(shell, np.b);
557264790Sbapt		err(1, "%s", shell);
558264790Sbapt	}
559264790Sbapt}
560264790Sbapt
561264790Sbaptstatic void
562264790Sbaptexport_pam_environment(void)
563264790Sbapt{
564264790Sbapt	char	**pp;
565264790Sbapt	char	*p;
566264790Sbapt
567264790Sbapt	for (pp = environ_pam; *pp != NULL; pp++) {
568264790Sbapt		if (ok_to_export(*pp)) {
569264790Sbapt			p = strchr(*pp, '=');
570264790Sbapt			*p = '\0';
571264790Sbapt			setenv(*pp, p + 1, 1);
572264790Sbapt		}
573264790Sbapt		free(*pp);
574264790Sbapt	}
575264790Sbapt}
576264790Sbapt
577264790Sbapt/*
578264790Sbapt * Sanity checks on PAM environmental variables:
579264790Sbapt * - Make sure there is an '=' in the string.
580264790Sbapt * - Make sure the string doesn't run on too long.
581264790Sbapt * - Do not export certain variables.  This list was taken from the
582264790Sbapt *   Solaris pam_putenv(3) man page.
583264790Sbapt * Note that if the user is chrooted, PAM may have a better idea than we
584264790Sbapt * do of where her home directory is.
585264790Sbapt */
586264790Sbaptstatic int
587264790Sbaptok_to_export(const char *s)
588264790Sbapt{
589264790Sbapt	static const char *noexport[] = {
590264790Sbapt		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
591264790Sbapt		"IFS", "PATH", NULL
592264790Sbapt	};
593264790Sbapt	const char **pp;
594264790Sbapt	size_t n;
595264790Sbapt
596264790Sbapt	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
597264790Sbapt		return 0;
598264790Sbapt	if (strncmp(s, "LD_", 3) == 0)
599264790Sbapt		return 0;
600264790Sbapt	for (pp = noexport; *pp != NULL; pp++) {
601264790Sbapt		n = strlen(*pp);
602264790Sbapt		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
603264790Sbapt			return 0;
604264790Sbapt	}
605264790Sbapt	return 1;
606264790Sbapt}
607264790Sbapt
608264790Sbaptstatic void
609264790Sbaptusage(void)
610264790Sbapt{
611264790Sbapt
612264790Sbapt	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
613264790Sbapt	exit(1);
614264790Sbapt	/* NOTREACHED */
615264790Sbapt}
616264790Sbapt
617264790Sbaptstatic int
618264790Sbaptchshell(const char *sh)
619264790Sbapt{
620264790Sbapt	int r;
621264790Sbapt	char *cp;
622264790Sbapt
623264790Sbapt	r = 0;
624264790Sbapt	setusershell();
625264790Sbapt	while ((cp = getusershell()) != NULL && !r)
626264790Sbapt	    r = (strcmp(cp, sh) == 0);
627264790Sbapt	endusershell();
628264790Sbapt	return r;
629264790Sbapt}
630264790Sbapt
631264790Sbaptstatic char *
632264790Sbaptontty(void)
633264790Sbapt{
634264790Sbapt	char *p;
635264790Sbapt	static char buf[MAXPATHLEN + 4];
636264790Sbapt
637264790Sbapt	buf[0] = 0;
638264790Sbapt	p = ttyname(STDERR_FILENO);
639264790Sbapt	if (p)
640264790Sbapt		snprintf(buf, sizeof(buf), " on %s", p);
641264790Sbapt	return buf;
642264790Sbapt}
643264790Sbapt