su.c revision 179547
121308Sache/*
221308Sache * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
321308Sache * All rights reserved.
421308Sache *
521308Sache * Portions of this software were developed for the FreeBSD Project by
621308Sache * ThinkSec AS and NAI Labs, the Security Research Division of Network
721308Sache * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
821308Sache * ("CBOSS"), as part of the DARPA CHATS research program.
921308Sache *
1058310Sache * Redistribution and use in source and binary forms, with or without
1121308Sache * modification, are permitted provided that the following conditions
1221308Sache * are met:
1321308Sache * 1. Redistributions of source code must retain the above copyright
1421308Sache *    notice, this list of conditions and the following disclaimer.
1521308Sache * 2. Redistributions in binary form must reproduce the above copyright
1621308Sache *    notice, this list of conditions and the following disclaimer in the
1721308Sache *    documentation and/or other materials provided with the distribution.
1821308Sache *
1921308Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2021308Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2158310Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2221308Sache * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2321308Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2421308Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2521308Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2621308Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2721308Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2821308Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2921308Sache * SUCH DAMAGE.
3021308Sache */
3121308Sache/*-
3221308Sache * Copyright (c) 1988, 1993, 1994
3321308Sache *	The Regents of the University of California.  All rights reserved.
3421308Sache *
3521308Sache * Redistribution and use in source and binary forms, with or without
3621308Sache * modification, are permitted provided that the following conditions
3721308Sache * are met:
3821308Sache * 1. Redistributions of source code must retain the above copyright
3921308Sache *    notice, this list of conditions and the following disclaimer.
4021308Sache * 2. Redistributions in binary form must reproduce the above copyright
4158310Sache *    notice, this list of conditions and the following disclaimer in the
4258310Sache *    documentation and/or other materials provided with the distribution.
4358310Sache * 3. All advertising materials mentioning features or use of this software
4458310Sache *    must display the following acknowledgement:
4558310Sache *	This product includes software developed by the University of
4658310Sache *	California, Berkeley and its contributors.
4758310Sache * 4. Neither the name of the University nor the names of its contributors
4858310Sache *    may be used to endorse or promote products derived from this software
49119610Sache *    without specific prior written permission.
5021308Sache *
5121308Sache * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5221308Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5321308Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5421308Sache * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5521308Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5621308Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5721308Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5821308Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5921308Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6021308Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6121308Sache * SUCH DAMAGE.
6221308Sache */
63119610Sache
64119610Sache#ifndef lint
6521308Sachestatic const char copyright[] =
6621308Sache"@(#) Copyright (c) 1988, 1993, 1994\n\
6721308Sache	The Regents of the University of California.  All rights reserved.\n";
6821308Sache#endif /* not lint */
6921308Sache
7021308Sache#if 0
7121308Sache#ifndef lint
7221308Sachestatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
7321308Sache#endif /* not lint */
7421308Sache#endif
7521308Sache
7621308Sache#include <sys/cdefs.h>
7721308Sache__FBSDID("$FreeBSD: head/usr.bin/su/su.c 179547 2008-06-04 19:16:54Z dwmalone $");
7821308Sache
7921308Sache#include <sys/param.h>
8021308Sache#include <sys/time.h>
8147558Sache#include <sys/resource.h>
8221308Sache#include <sys/wait.h>
8321308Sache
8421308Sache#ifdef USE_BSM_AUDIT
8521308Sache#include <bsm/libbsm.h>
8621308Sache#include <bsm/audit_uevents.h>
8726497Sache#endif
8821308Sache
8921308Sache#include <err.h>
9021308Sache#include <errno.h>
9121308Sache#include <grp.h>
9221308Sache#include <login_cap.h>
9321308Sache#include <paths.h>
9447558Sache#include <pwd.h>
95119610Sache#include <signal.h>
96119610Sache#include <stdio.h>
9721308Sache#include <stdlib.h>
9821308Sache#include <string.h>
9921308Sache#include <syslog.h>
10021308Sache#include <unistd.h>
10121308Sache#include <stdarg.h>
10221308Sache
10321308Sache#include <security/pam_appl.h>
10421308Sache#include <security/openpam.h>
10521308Sache
10621308Sache#define PAM_END() do {							\
10735486Sache	int local_ret;							\
10821308Sache	if (pamh != NULL) {						\
10921308Sache		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
11021308Sache		if (local_ret != PAM_SUCCESS)				\
11121308Sache			syslog(LOG_ERR, "pam_setcred: %s",		\
11221308Sache				pam_strerror(pamh, local_ret));		\
113119610Sache		if (asthem) {						\
11475406Sache			local_ret = pam_close_session(pamh, 0);		\
11535486Sache			if (local_ret != PAM_SUCCESS)			\
11635486Sache				syslog(LOG_ERR, "pam_close_session: %s",\
11721308Sache					pam_strerror(pamh, local_ret));	\
11821308Sache		}							\
11921308Sache		local_ret = pam_end(pamh, local_ret);			\
12021308Sache		if (local_ret != PAM_SUCCESS)				\
12121308Sache			syslog(LOG_ERR, "pam_end: %s",			\
12221308Sache				pam_strerror(pamh, local_ret));		\
12321308Sache	}								\
12421308Sache} while (0)
12521308Sache
12621308Sache
12721308Sache#define PAM_SET_ITEM(what, item) do {					\
12821308Sache	int local_ret;							\
12921308Sache	local_ret = pam_set_item(pamh, what, item);			\
13021308Sache	if (local_ret != PAM_SUCCESS) {					\
13121308Sache		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
13221308Sache			pam_strerror(pamh, local_ret));			\
13321308Sache		errx(1, "pam_set_item(" #what "): %s",			\
134136644Sache			pam_strerror(pamh, local_ret));			\
13521308Sache		/* NOTREACHED */					\
13621308Sache	}								\
13721308Sache} while (0)
13821308Sache
13921308Sacheenum tristate { UNSET, YES, NO };
14021308Sache
14121308Sachestatic pam_handle_t *pamh = NULL;
14221308Sachestatic char	**environ_pam;
14321308Sache
14421308Sachestatic char	*ontty(void);
14521308Sachestatic int	chshell(const char *);
14621308Sachestatic void	usage(void) __dead2;
14721308Sachestatic void	export_pam_environment(void);
14821308Sachestatic int	ok_to_export(const char *);
14921308Sache
15021308Sacheextern char	**environ;
15121308Sache
15221308Sacheint
15321308Sachemain(int argc, char *argv[])
15421308Sache{
15521308Sache	static char	*cleanenv;
15621308Sache	struct passwd	*pwd;
15721308Sache	struct pam_conv	conv = { openpam_ttyconv, NULL };
15821308Sache	enum tristate	iscsh;
15921308Sache	login_cap_t	*lc;
16021308Sache	union {
16121308Sache		const char	**a;
16221308Sache		char		* const *b;
16321308Sache	}		np;
16421308Sache	uid_t		ruid;
16521308Sache	pid_t		child_pid, child_pgrp, pid;
16621308Sache	int		asme, ch, asthem, fastlogin, prio, i, retcode,
16721308Sache			statusp, setmaclabel;
16821308Sache	u_int		setwhat;
16921308Sache	char		*username, *class, shellbuf[MAXPATHLEN];
17021308Sache	const char	*p, *user, *shell, *mytty, **nargv;
17121308Sache	const void	*v;
17221308Sache	struct sigaction sa, sa_int, sa_quit, sa_pipe;
17321308Sache	int temp, fds[2];
17421308Sache#ifdef USE_BSM_AUDIT
17521308Sache	const char	*aerr;
17621308Sache	au_id_t		 auid;
17721308Sache#endif
17821308Sache
179157184Sache	shell = class = cleanenv = NULL;
18021308Sache	asme = asthem = fastlogin = statusp = 0;
18121308Sache	user = "root";
18221308Sache	iscsh = UNSET;
18321308Sache	setmaclabel = 0;
18421308Sache
18521308Sache	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
18621308Sache		switch ((char)ch) {
18721308Sache		case 'f':
18821308Sache			fastlogin = 1;
18975406Sache			break;
19021308Sache		case '-':
19121308Sache		case 'l':
19221308Sache			asme = 0;
19321308Sache			asthem = 1;
19475406Sache			break;
19575406Sache		case 'm':
19621308Sache			asme = 1;
19721308Sache			asthem = 0;
19821308Sache			break;
19921308Sache		case 's':
20021308Sache			setmaclabel = 1;
20121308Sache			break;
20221308Sache		case 'c':
20321308Sache			class = optarg;
20421308Sache			break;
20521308Sache		case '?':
20621308Sache		default:
20721308Sache			usage();
20821308Sache		/* NOTREACHED */
20921308Sache		}
21021308Sache
21121308Sache	if (optind < argc)
21221308Sache		user = argv[optind++];
21321308Sache
21421308Sache	if (user == NULL)
21521308Sache		usage();
21621308Sache	/* NOTREACHED */
21721308Sache
21821308Sache	/*
21921308Sache	 * Try to provide more helpful debugging output if su(1) is running
22021308Sache	 * non-setuid, or was run from a file system not mounted setuid.
22121308Sache	 */
22221308Sache	if (geteuid() != 0)
22321308Sache		errx(1, "not running setuid");
22421308Sache
22521308Sache#ifdef USE_BSM_AUDIT
22621308Sache	if (getauid(&auid) < 0 && errno != ENOSYS) {
22721308Sache		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
22821308Sache		errx(1, "Permission denied");
22921308Sache	}
23021308Sache#endif
23121308Sache	if (strlen(user) > MAXLOGNAME - 1) {
23275406Sache#ifdef USE_BSM_AUDIT
23321308Sache		if (audit_submit(AUE_su, auid,
23421308Sache		    1, EPERM, "username too long: '%s'", user))
23575406Sache			errx(1, "Permission denied");
23621308Sache#endif
23721308Sache		errx(1, "username too long");
23821308Sache	}
23921308Sache
24021308Sache	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
24175406Sache	if (nargv == NULL)
24221308Sache		errx(1, "malloc failure");
24321308Sache
24421308Sache	nargv[argc + 3] = NULL;
24521308Sache	for (i = argc; i >= optind; i--)
24675406Sache		nargv[i + 3] = argv[i];
24721308Sache	np.a = &nargv[i + 3];
24821308Sache
24921308Sache	argv += optind;
25021308Sache
25121308Sache	errno = 0;
25221308Sache	prio = getpriority(PRIO_PROCESS, 0);
25358310Sache	if (errno)
25421308Sache		prio = 0;
25521308Sache
256	setpriority(PRIO_PROCESS, 0, -2);
257	openlog("su", LOG_CONS, LOG_AUTH);
258
259	/* get current login name, real uid and shell */
260	ruid = getuid();
261	username = getlogin();
262	pwd = getpwnam(username);
263	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
264		pwd = getpwuid(ruid);
265	if (pwd == NULL) {
266#ifdef USE_BSM_AUDIT
267		if (audit_submit(AUE_su, auid, 1, EPERM,
268		    "unable to determine invoking subject: '%s'", username))
269			errx(1, "Permission denied");
270#endif
271		errx(1, "who are you?");
272	}
273
274	username = strdup(pwd->pw_name);
275	if (username == NULL)
276		err(1, "strdup failure");
277
278	if (asme) {
279		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
280			/* must copy - pwd memory is recycled */
281			shell = strncpy(shellbuf, pwd->pw_shell,
282			    sizeof(shellbuf));
283			shellbuf[sizeof(shellbuf) - 1] = '\0';
284		}
285		else {
286			shell = _PATH_BSHELL;
287			iscsh = NO;
288		}
289	}
290
291	/* Do the whole PAM startup thing */
292	retcode = pam_start("su", user, &conv, &pamh);
293	if (retcode != PAM_SUCCESS) {
294		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
295		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
296	}
297
298	PAM_SET_ITEM(PAM_RUSER, username);
299
300	mytty = ttyname(STDERR_FILENO);
301	if (!mytty)
302		mytty = "tty";
303	PAM_SET_ITEM(PAM_TTY, mytty);
304
305	retcode = pam_authenticate(pamh, 0);
306	if (retcode != PAM_SUCCESS) {
307#ifdef USE_BSM_AUDIT
308		if (audit_submit(AUE_su, auid, 1, EPERM, "bad su %s to %s on %s",
309		    username, user, mytty))
310			errx(1, "Permission denied");
311#endif
312		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
313		    username, user, mytty);
314		errx(1, "Sorry");
315	}
316#ifdef USE_BSM_AUDIT
317	if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
318		errx(1, "Permission denied");
319#endif
320	retcode = pam_get_item(pamh, PAM_USER, &v);
321	if (retcode == PAM_SUCCESS)
322		user = v;
323	else
324		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
325		    pam_strerror(pamh, retcode));
326	pwd = getpwnam(user);
327	if (pwd == NULL) {
328#ifdef USE_BSM_AUDIT
329		if (audit_submit(AUE_su, auid, 1, EPERM,
330		    "unknown subject: %s", user))
331			errx(1, "Permission denied");
332#endif
333		errx(1, "unknown login: %s", user);
334	}
335
336	retcode = pam_acct_mgmt(pamh, 0);
337	if (retcode == PAM_NEW_AUTHTOK_REQD) {
338		retcode = pam_chauthtok(pamh,
339			PAM_CHANGE_EXPIRED_AUTHTOK);
340		if (retcode != PAM_SUCCESS) {
341#ifdef USE_BSM_AUDIT
342			aerr = pam_strerror(pamh, retcode);
343			if (aerr == NULL)
344				aerr = "Unknown PAM error";
345			if (audit_submit(AUE_su, auid, 1, EPERM,
346			    "pam_chauthtok: %s", aerr))
347				errx(1, "Permission denied");
348#endif
349			syslog(LOG_ERR, "pam_chauthtok: %s",
350			    pam_strerror(pamh, retcode));
351			errx(1, "Sorry");
352		}
353	}
354	if (retcode != PAM_SUCCESS) {
355#ifdef USE_BSM_AUDIT
356		if (audit_submit(AUE_su, auid, 1, EPERM, "pam_acct_mgmt: %s",
357		    pam_strerror(pamh, retcode)))
358			errx(1, "Permission denied");
359#endif
360		syslog(LOG_ERR, "pam_acct_mgmt: %s",
361			pam_strerror(pamh, retcode));
362		errx(1, "Sorry");
363	}
364
365	/* get target login information */
366	if (class == NULL)
367		lc = login_getpwclass(pwd);
368	else {
369		if (ruid != 0) {
370#ifdef USE_BSM_AUDIT
371			if (audit_submit(AUE_su, auid, 1, EPERM,
372			    "only root may use -c"))
373				errx(1, "Permission denied");
374#endif
375			errx(1, "only root may use -c");
376		}
377		lc = login_getclass(class);
378		if (lc == NULL)
379			errx(1, "unknown class: %s", class);
380	}
381
382	/* if asme and non-standard target shell, must be root */
383	if (asme) {
384		if (ruid != 0 && !chshell(pwd->pw_shell))
385			errx(1, "permission denied (shell)");
386	}
387	else if (pwd->pw_shell && *pwd->pw_shell) {
388		shell = pwd->pw_shell;
389		iscsh = UNSET;
390	}
391	else {
392		shell = _PATH_BSHELL;
393		iscsh = NO;
394	}
395
396	/* if we're forking a csh, we want to slightly muck the args */
397	if (iscsh == UNSET) {
398		p = strrchr(shell, '/');
399		if (p)
400			++p;
401		else
402			p = shell;
403		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
404	}
405	setpriority(PRIO_PROCESS, 0, prio);
406
407	/*
408	 * PAM modules might add supplementary groups in pam_setcred(), so
409	 * initialize them first.
410	 */
411	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
412		err(1, "setusercontext");
413
414	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
415	if (retcode != PAM_SUCCESS) {
416		syslog(LOG_ERR, "pam_setcred: %s",
417		    pam_strerror(pamh, retcode));
418		errx(1, "failed to establish credentials.");
419	}
420	if (asthem) {
421		retcode = pam_open_session(pamh, 0);
422		if (retcode != PAM_SUCCESS) {
423			syslog(LOG_ERR, "pam_open_session: %s",
424			    pam_strerror(pamh, retcode));
425			errx(1, "failed to open session.");
426		}
427	}
428
429	/*
430	 * We must fork() before setuid() because we need to call
431	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
432	 */
433	sa.sa_flags = SA_RESTART;
434	sa.sa_handler = SIG_IGN;
435	sigemptyset(&sa.sa_mask);
436	sigaction(SIGINT, &sa, &sa_int);
437	sigaction(SIGQUIT, &sa, &sa_quit);
438	sigaction(SIGPIPE, &sa, &sa_pipe);
439	sa.sa_handler = SIG_DFL;
440	sigaction(SIGTSTP, &sa, NULL);
441	statusp = 1;
442	if (pipe(fds) == -1) {
443		PAM_END();
444		err(1, "pipe");
445	}
446	child_pid = fork();
447	switch (child_pid) {
448	default:
449		sa.sa_handler = SIG_IGN;
450		sigaction(SIGTTOU, &sa, NULL);
451		close(fds[0]);
452		setpgid(child_pid, child_pid);
453		if (tcgetpgrp(STDERR_FILENO) == getpgrp())
454			tcsetpgrp(STDERR_FILENO, child_pid);
455		close(fds[1]);
456		sigaction(SIGPIPE, &sa_pipe, NULL);
457		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
458			if (WIFSTOPPED(statusp)) {
459				child_pgrp = getpgid(child_pid);
460				if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
461					tcsetpgrp(STDERR_FILENO, getpgrp());
462				kill(getpid(), SIGSTOP);
463				if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
464					child_pgrp = getpgid(child_pid);
465					tcsetpgrp(STDERR_FILENO, child_pgrp);
466				}
467				kill(child_pid, SIGCONT);
468				statusp = 1;
469				continue;
470			}
471			break;
472		}
473		tcsetpgrp(STDERR_FILENO, getpgrp());
474		if (pid == -1)
475			err(1, "waitpid");
476		PAM_END();
477		exit(WEXITSTATUS(statusp));
478	case -1:
479		PAM_END();
480		err(1, "fork");
481	case 0:
482		close(fds[1]);
483		read(fds[0], &temp, 1);
484		close(fds[0]);
485		sigaction(SIGPIPE, &sa_pipe, NULL);
486		sigaction(SIGINT, &sa_int, NULL);
487		sigaction(SIGQUIT, &sa_quit, NULL);
488
489		/*
490		 * Set all user context except for: Environmental variables
491		 * Umask Login records (wtmp, etc) Path
492		 */
493		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
494			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
495			   LOGIN_SETMAC);
496		/*
497		 * If -s is present, also set the MAC label.
498		 */
499		if (setmaclabel)
500			setwhat |= LOGIN_SETMAC;
501		/*
502		 * Don't touch resource/priority settings if -m has been used
503		 * or -l and -c hasn't, and we're not su'ing to root.
504		 */
505		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
506			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
507		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
508			err(1, "setusercontext");
509
510		if (!asme) {
511			if (asthem) {
512				p = getenv("TERM");
513				environ = &cleanenv;
514			}
515
516			if (asthem || pwd->pw_uid)
517				setenv("USER", pwd->pw_name, 1);
518			setenv("HOME", pwd->pw_dir, 1);
519			setenv("SHELL", shell, 1);
520
521			if (asthem) {
522				/*
523				 * Add any environmental variables that the
524				 * PAM modules may have set.
525				 */
526				environ_pam = pam_getenvlist(pamh);
527				if (environ_pam)
528					export_pam_environment();
529
530				/* set the su'd user's environment & umask */
531				setusercontext(lc, pwd, pwd->pw_uid,
532					LOGIN_SETPATH | LOGIN_SETUMASK |
533					LOGIN_SETENV);
534				if (p)
535					setenv("TERM", p, 1);
536
537				p = pam_getenv(pamh, "HOME");
538				if (chdir(p ? p : pwd->pw_dir) < 0)
539					errx(1, "no directory");
540			}
541		}
542		login_close(lc);
543
544		if (iscsh == YES) {
545			if (fastlogin)
546				*np.a-- = "-f";
547			if (asme)
548				*np.a-- = "-m";
549		}
550		/* csh strips the first character... */
551		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
552
553		if (ruid != 0)
554			syslog(LOG_NOTICE, "%s to %s%s", username, user,
555			    ontty());
556
557		execv(shell, np.b);
558		err(1, "%s", shell);
559	}
560}
561
562static void
563export_pam_environment(void)
564{
565	char	**pp;
566	char	*p;
567
568	for (pp = environ_pam; *pp != NULL; pp++) {
569		if (ok_to_export(*pp)) {
570			p = strchr(*pp, '=');
571			*p = '\0';
572			setenv(*pp, p + 1, 1);
573		}
574		free(*pp);
575	}
576}
577
578/*
579 * Sanity checks on PAM environmental variables:
580 * - Make sure there is an '=' in the string.
581 * - Make sure the string doesn't run on too long.
582 * - Do not export certain variables.  This list was taken from the
583 *   Solaris pam_putenv(3) man page.
584 * Note that if the user is chrooted, PAM may have a better idea than we
585 * do of where her home directory is.
586 */
587static int
588ok_to_export(const char *s)
589{
590	static const char *noexport[] = {
591		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
592		"IFS", "PATH", NULL
593	};
594	const char **pp;
595	size_t n;
596
597	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
598		return 0;
599	if (strncmp(s, "LD_", 3) == 0)
600		return 0;
601	for (pp = noexport; *pp != NULL; pp++) {
602		n = strlen(*pp);
603		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
604			return 0;
605	}
606	return 1;
607}
608
609static void
610usage(void)
611{
612
613	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
614	exit(1);
615	/* NOTREACHED */
616}
617
618static int
619chshell(const char *sh)
620{
621	int r;
622	char *cp;
623
624	r = 0;
625	setusershell();
626	while ((cp = getusershell()) != NULL && !r)
627	    r = (strcmp(cp, sh) == 0);
628	endusershell();
629	return r;
630}
631
632static char *
633ontty(void)
634{
635	char *p;
636	static char buf[MAXPATHLEN + 4];
637
638	buf[0] = 0;
639	p = ttyname(STDERR_FILENO);
640	if (p)
641		snprintf(buf, sizeof(buf), " on %s", p);
642	return buf;
643}
644