su.c revision 220055
1246149Ssjg/*
2246149Ssjg * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
3246149Ssjg * All rights reserved.
4246149Ssjg *
5246149Ssjg * Portions of this software were developed for the FreeBSD Project by
6246149Ssjg * ThinkSec AS and NAI Labs, the Security Research Division of Network
7246149Ssjg * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
8246149Ssjg * ("CBOSS"), as part of the DARPA CHATS research program.
9246149Ssjg *
10246149Ssjg * Redistribution and use in source and binary forms, with or without
11246149Ssjg * modification, are permitted provided that the following conditions
12246149Ssjg * are met:
13246149Ssjg * 1. Redistributions of source code must retain the above copyright
14246149Ssjg *    notice, this list of conditions and the following disclaimer.
15246149Ssjg * 2. Redistributions in binary form must reproduce the above copyright
16246149Ssjg *    notice, this list of conditions and the following disclaimer in the
17246149Ssjg *    documentation and/or other materials provided with the distribution.
18246149Ssjg *
19246149Ssjg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20246149Ssjg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21246149Ssjg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22246149Ssjg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23246149Ssjg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24246149Ssjg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25246149Ssjg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26246149Ssjg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27246149Ssjg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28246149Ssjg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29246149Ssjg * SUCH DAMAGE.
30246149Ssjg */
31246149Ssjg/*-
32246149Ssjg * Copyright (c) 1988, 1993, 1994
33246149Ssjg *	The Regents of the University of California.  All rights reserved.
34246149Ssjg *
35246149Ssjg * Redistribution and use in source and binary forms, with or without
36246149Ssjg * modification, are permitted provided that the following conditions
37246149Ssjg * are met:
38246149Ssjg * 1. Redistributions of source code must retain the above copyright
39246149Ssjg *    notice, this list of conditions and the following disclaimer.
40246149Ssjg * 2. Redistributions in binary form must reproduce the above copyright
41246149Ssjg *    notice, this list of conditions and the following disclaimer in the
42246149Ssjg *    documentation and/or other materials provided with the distribution.
43246149Ssjg * 4. Neither the name of the University nor the names of its contributors
44246149Ssjg *    may be used to endorse or promote products derived from this software
45246149Ssjg *    without specific prior written permission.
46246149Ssjg *
47246149Ssjg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48246149Ssjg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49246149Ssjg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50246149Ssjg * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51246149Ssjg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52246149Ssjg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53246149Ssjg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54246149Ssjg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55246149Ssjg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56246149Ssjg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57246149Ssjg * SUCH DAMAGE.
58246149Ssjg */
59246149Ssjg
60246149Ssjg#ifndef lint
61246149Ssjgstatic const char copyright[] =
62246149Ssjg"@(#) Copyright (c) 1988, 1993, 1994\n\
63246149Ssjg	The Regents of the University of California.  All rights reserved.\n";
64246149Ssjg#endif /* not lint */
65246149Ssjg
66246149Ssjg#if 0
67246149Ssjg#ifndef lint
68246149Ssjgstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
69246149Ssjg#endif /* not lint */
70246149Ssjg#endif
71246149Ssjg
72246149Ssjg#include <sys/cdefs.h>
73246149Ssjg__FBSDID("$FreeBSD: head/usr.bin/su/su.c 220055 2011-03-27 12:53:20Z ume $");
74246149Ssjg
75246149Ssjg#include <sys/param.h>
76246149Ssjg#include <sys/time.h>
77246149Ssjg#include <sys/resource.h>
78246149Ssjg#include <sys/wait.h>
79246149Ssjg
80246149Ssjg#ifdef USE_BSM_AUDIT
81246149Ssjg#include <bsm/libbsm.h>
82246149Ssjg#include <bsm/audit_uevents.h>
83246149Ssjg#endif
84246149Ssjg
85246149Ssjg#include <err.h>
86246149Ssjg#include <errno.h>
87246149Ssjg#include <grp.h>
88246149Ssjg#include <login_cap.h>
89246149Ssjg#include <paths.h>
90246149Ssjg#include <pwd.h>
91246149Ssjg#include <signal.h>
92246149Ssjg#include <stdio.h>
93246149Ssjg#include <stdlib.h>
94246149Ssjg#include <string.h>
95246149Ssjg#include <syslog.h>
96246149Ssjg#include <unistd.h>
97246149Ssjg#include <stdarg.h>
98246149Ssjg
99246149Ssjg#include <security/pam_appl.h>
100246149Ssjg#include <security/openpam.h>
101246149Ssjg
102246149Ssjg#define PAM_END() do {							\
103246149Ssjg	int local_ret;							\
104246149Ssjg	if (pamh != NULL) {						\
105246149Ssjg		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
106246149Ssjg		if (local_ret != PAM_SUCCESS)				\
107246149Ssjg			syslog(LOG_ERR, "pam_setcred: %s",		\
108246149Ssjg				pam_strerror(pamh, local_ret));		\
109246149Ssjg		if (asthem) {						\
110246149Ssjg			local_ret = pam_close_session(pamh, 0);		\
111246149Ssjg			if (local_ret != PAM_SUCCESS)			\
112246149Ssjg				syslog(LOG_ERR, "pam_close_session: %s",\
113246149Ssjg					pam_strerror(pamh, local_ret));	\
114246149Ssjg		}							\
115246149Ssjg		local_ret = pam_end(pamh, local_ret);			\
116246149Ssjg		if (local_ret != PAM_SUCCESS)				\
117246149Ssjg			syslog(LOG_ERR, "pam_end: %s",			\
118246149Ssjg				pam_strerror(pamh, local_ret));		\
119246149Ssjg	}								\
120246149Ssjg} while (0)
121246149Ssjg
122246149Ssjg
123246149Ssjg#define PAM_SET_ITEM(what, item) do {					\
124246149Ssjg	int local_ret;							\
125246149Ssjg	local_ret = pam_set_item(pamh, what, item);			\
126246149Ssjg	if (local_ret != PAM_SUCCESS) {					\
127246149Ssjg		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
128246149Ssjg			pam_strerror(pamh, local_ret));			\
129246149Ssjg		errx(1, "pam_set_item(" #what "): %s",			\
130246149Ssjg			pam_strerror(pamh, local_ret));			\
131246149Ssjg		/* NOTREACHED */					\
132246149Ssjg	}								\
133246149Ssjg} while (0)
134246149Ssjg
135246149Ssjgenum tristate { UNSET, YES, NO };
136246149Ssjg
137246149Ssjgstatic pam_handle_t *pamh = NULL;
138246149Ssjgstatic char	**environ_pam;
139246149Ssjg
140246149Ssjgstatic char	*ontty(void);
141246149Ssjgstatic int	chshell(const char *);
142246149Ssjgstatic void	usage(void) __dead2;
143246149Ssjgstatic void	export_pam_environment(void);
144246149Ssjgstatic int	ok_to_export(const char *);
145246149Ssjg
146246149Ssjgextern char	**environ;
147246149Ssjg
148246149Ssjgint
149246149Ssjgmain(int argc, char *argv[])
150246149Ssjg{
151246149Ssjg	static char	*cleanenv;
152246149Ssjg	struct passwd	*pwd = NULL;
153246149Ssjg	struct pam_conv	conv = { openpam_ttyconv, NULL };
154246149Ssjg	enum tristate	iscsh;
155246149Ssjg	login_cap_t	*lc;
156246149Ssjg	union {
157246149Ssjg		const char	**a;
158246149Ssjg		char		* const *b;
159246149Ssjg	}		np;
160246149Ssjg	uid_t		ruid;
161246149Ssjg	pid_t		child_pid, child_pgrp, pid;
162246149Ssjg	int		asme, ch, asthem, fastlogin, prio, i, retcode,
163246149Ssjg			statusp, setmaclabel;
164246149Ssjg	u_int		setwhat;
165246149Ssjg	char		*username, *class, shellbuf[MAXPATHLEN];
166246149Ssjg	const char	*p, *user, *shell, *mytty, **nargv;
167246149Ssjg	const void	*v;
168246149Ssjg	struct sigaction sa, sa_int, sa_quit, sa_pipe;
169246149Ssjg	int temp, fds[2];
170246149Ssjg#ifdef USE_BSM_AUDIT
171246149Ssjg	const char	*aerr;
172246149Ssjg	au_id_t		 auid;
173246149Ssjg#endif
174246149Ssjg
175246149Ssjg	shell = class = cleanenv = NULL;
176246149Ssjg	asme = asthem = fastlogin = statusp = 0;
177246149Ssjg	user = "root";
178246149Ssjg	iscsh = UNSET;
179246149Ssjg	setmaclabel = 0;
180246149Ssjg
181246149Ssjg	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
182246149Ssjg		switch ((char)ch) {
183246149Ssjg		case 'f':
184246149Ssjg			fastlogin = 1;
185246149Ssjg			break;
186246149Ssjg		case '-':
187246149Ssjg		case 'l':
188246149Ssjg			asme = 0;
189246149Ssjg			asthem = 1;
190246149Ssjg			break;
191246149Ssjg		case 'm':
192246149Ssjg			asme = 1;
193246149Ssjg			asthem = 0;
194246149Ssjg			break;
195246149Ssjg		case 's':
196246149Ssjg			setmaclabel = 1;
197246149Ssjg			break;
198		case 'c':
199			class = optarg;
200			break;
201		case '?':
202		default:
203			usage();
204		/* NOTREACHED */
205		}
206
207	if (optind < argc)
208		user = argv[optind++];
209
210	if (user == NULL)
211		usage();
212	/* NOTREACHED */
213
214	/*
215	 * Try to provide more helpful debugging output if su(1) is running
216	 * non-setuid, or was run from a file system not mounted setuid.
217	 */
218	if (geteuid() != 0)
219		errx(1, "not running setuid");
220
221#ifdef USE_BSM_AUDIT
222	if (getauid(&auid) < 0 && errno != ENOSYS) {
223		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
224		errx(1, "Permission denied");
225	}
226#endif
227	if (strlen(user) > MAXLOGNAME - 1) {
228#ifdef USE_BSM_AUDIT
229		if (audit_submit(AUE_su, auid,
230		    EPERM, 1, "username too long: '%s'", user))
231			errx(1, "Permission denied");
232#endif
233		errx(1, "username too long");
234	}
235
236	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
237	if (nargv == NULL)
238		errx(1, "malloc failure");
239
240	nargv[argc + 3] = NULL;
241	for (i = argc; i >= optind; i--)
242		nargv[i + 3] = argv[i];
243	np.a = &nargv[i + 3];
244
245	argv += optind;
246
247	errno = 0;
248	prio = getpriority(PRIO_PROCESS, 0);
249	if (errno)
250		prio = 0;
251
252	setpriority(PRIO_PROCESS, 0, -2);
253	openlog("su", LOG_CONS, LOG_AUTH);
254
255	/* get current login name, real uid and shell */
256	ruid = getuid();
257	username = getlogin();
258	if (username != NULL)
259		pwd = getpwnam(username);
260	if (pwd == NULL || pwd->pw_uid != ruid)
261		pwd = getpwuid(ruid);
262	if (pwd == NULL) {
263#ifdef USE_BSM_AUDIT
264		if (audit_submit(AUE_su, auid, EPERM, 1,
265		    "unable to determine invoking subject: '%s'", username))
266			errx(1, "Permission denied");
267#endif
268		errx(1, "who are you?");
269	}
270
271	username = strdup(pwd->pw_name);
272	if (username == NULL)
273		err(1, "strdup failure");
274
275	if (asme) {
276		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
277			/* must copy - pwd memory is recycled */
278			shell = strncpy(shellbuf, pwd->pw_shell,
279			    sizeof(shellbuf));
280			shellbuf[sizeof(shellbuf) - 1] = '\0';
281		}
282		else {
283			shell = _PATH_BSHELL;
284			iscsh = NO;
285		}
286	}
287
288	/* Do the whole PAM startup thing */
289	retcode = pam_start("su", user, &conv, &pamh);
290	if (retcode != PAM_SUCCESS) {
291		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
292		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
293	}
294
295	PAM_SET_ITEM(PAM_RUSER, username);
296
297	mytty = ttyname(STDERR_FILENO);
298	if (!mytty)
299		mytty = "tty";
300	PAM_SET_ITEM(PAM_TTY, mytty);
301
302	retcode = pam_authenticate(pamh, 0);
303	if (retcode != PAM_SUCCESS) {
304#ifdef USE_BSM_AUDIT
305		if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
306		    username, user, mytty))
307			errx(1, "Permission denied");
308#endif
309		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
310		    username, user, mytty);
311		errx(1, "Sorry");
312	}
313#ifdef USE_BSM_AUDIT
314	if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
315		errx(1, "Permission denied");
316#endif
317	retcode = pam_get_item(pamh, PAM_USER, &v);
318	if (retcode == PAM_SUCCESS)
319		user = v;
320	else
321		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
322		    pam_strerror(pamh, retcode));
323	pwd = getpwnam(user);
324	if (pwd == NULL) {
325#ifdef USE_BSM_AUDIT
326		if (audit_submit(AUE_su, auid, EPERM, 1,
327		    "unknown subject: %s", user))
328			errx(1, "Permission denied");
329#endif
330		errx(1, "unknown login: %s", user);
331	}
332
333	retcode = pam_acct_mgmt(pamh, 0);
334	if (retcode == PAM_NEW_AUTHTOK_REQD) {
335		retcode = pam_chauthtok(pamh,
336			PAM_CHANGE_EXPIRED_AUTHTOK);
337		if (retcode != PAM_SUCCESS) {
338#ifdef USE_BSM_AUDIT
339			aerr = pam_strerror(pamh, retcode);
340			if (aerr == NULL)
341				aerr = "Unknown PAM error";
342			if (audit_submit(AUE_su, auid, EPERM, 1,
343			    "pam_chauthtok: %s", aerr))
344				errx(1, "Permission denied");
345#endif
346			syslog(LOG_ERR, "pam_chauthtok: %s",
347			    pam_strerror(pamh, retcode));
348			errx(1, "Sorry");
349		}
350	}
351	if (retcode != PAM_SUCCESS) {
352#ifdef USE_BSM_AUDIT
353		if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
354		    pam_strerror(pamh, retcode)))
355			errx(1, "Permission denied");
356#endif
357		syslog(LOG_ERR, "pam_acct_mgmt: %s",
358			pam_strerror(pamh, retcode));
359		errx(1, "Sorry");
360	}
361
362	/* get target login information */
363	if (class == NULL)
364		lc = login_getpwclass(pwd);
365	else {
366		if (ruid != 0) {
367#ifdef USE_BSM_AUDIT
368			if (audit_submit(AUE_su, auid, EPERM, 1,
369			    "only root may use -c"))
370				errx(1, "Permission denied");
371#endif
372			errx(1, "only root may use -c");
373		}
374		lc = login_getclass(class);
375		if (lc == NULL)
376			errx(1, "unknown class: %s", class);
377	}
378
379	/* if asme and non-standard target shell, must be root */
380	if (asme) {
381		if (ruid != 0 && !chshell(pwd->pw_shell))
382			errx(1, "permission denied (shell)");
383	}
384	else if (pwd->pw_shell && *pwd->pw_shell) {
385		shell = pwd->pw_shell;
386		iscsh = UNSET;
387	}
388	else {
389		shell = _PATH_BSHELL;
390		iscsh = NO;
391	}
392
393	/* if we're forking a csh, we want to slightly muck the args */
394	if (iscsh == UNSET) {
395		p = strrchr(shell, '/');
396		if (p)
397			++p;
398		else
399			p = shell;
400		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
401	}
402	setpriority(PRIO_PROCESS, 0, prio);
403
404	/*
405	 * PAM modules might add supplementary groups in pam_setcred(), so
406	 * initialize them first.
407	 */
408	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
409		err(1, "setusercontext");
410
411	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
412	if (retcode != PAM_SUCCESS) {
413		syslog(LOG_ERR, "pam_setcred: %s",
414		    pam_strerror(pamh, retcode));
415		errx(1, "failed to establish credentials.");
416	}
417	if (asthem) {
418		retcode = pam_open_session(pamh, 0);
419		if (retcode != PAM_SUCCESS) {
420			syslog(LOG_ERR, "pam_open_session: %s",
421			    pam_strerror(pamh, retcode));
422			errx(1, "failed to open session.");
423		}
424	}
425
426	/*
427	 * We must fork() before setuid() because we need to call
428	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
429	 */
430	sa.sa_flags = SA_RESTART;
431	sa.sa_handler = SIG_IGN;
432	sigemptyset(&sa.sa_mask);
433	sigaction(SIGINT, &sa, &sa_int);
434	sigaction(SIGQUIT, &sa, &sa_quit);
435	sigaction(SIGPIPE, &sa, &sa_pipe);
436	sa.sa_handler = SIG_DFL;
437	sigaction(SIGTSTP, &sa, NULL);
438	statusp = 1;
439	if (pipe(fds) == -1) {
440		PAM_END();
441		err(1, "pipe");
442	}
443	child_pid = fork();
444	switch (child_pid) {
445	default:
446		sa.sa_handler = SIG_IGN;
447		sigaction(SIGTTOU, &sa, NULL);
448		close(fds[0]);
449		setpgid(child_pid, child_pid);
450		if (tcgetpgrp(STDERR_FILENO) == getpgrp())
451			tcsetpgrp(STDERR_FILENO, child_pid);
452		close(fds[1]);
453		sigaction(SIGPIPE, &sa_pipe, NULL);
454		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
455			if (WIFSTOPPED(statusp)) {
456				child_pgrp = getpgid(child_pid);
457				if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
458					tcsetpgrp(STDERR_FILENO, getpgrp());
459				kill(getpid(), SIGSTOP);
460				if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
461					child_pgrp = getpgid(child_pid);
462					tcsetpgrp(STDERR_FILENO, child_pgrp);
463				}
464				kill(child_pid, SIGCONT);
465				statusp = 1;
466				continue;
467			}
468			break;
469		}
470		tcsetpgrp(STDERR_FILENO, getpgrp());
471		if (pid == -1)
472			err(1, "waitpid");
473		PAM_END();
474		exit(WEXITSTATUS(statusp));
475	case -1:
476		PAM_END();
477		err(1, "fork");
478	case 0:
479		close(fds[1]);
480		read(fds[0], &temp, 1);
481		close(fds[0]);
482		sigaction(SIGPIPE, &sa_pipe, NULL);
483		sigaction(SIGINT, &sa_int, NULL);
484		sigaction(SIGQUIT, &sa_quit, NULL);
485
486		/*
487		 * Set all user context except for: Environmental variables
488		 * Umask Login records (wtmp, etc) Path
489		 */
490		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
491			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
492			   LOGIN_SETMAC);
493		/*
494		 * If -s is present, also set the MAC label.
495		 */
496		if (setmaclabel)
497			setwhat |= LOGIN_SETMAC;
498		/*
499		 * Don't touch resource/priority settings if -m has been used
500		 * or -l and -c hasn't, and we're not su'ing to root.
501		 */
502		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
503			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
504		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
505			err(1, "setusercontext");
506
507		if (!asme) {
508			if (asthem) {
509				p = getenv("TERM");
510				environ = &cleanenv;
511			}
512
513			if (asthem || pwd->pw_uid)
514				setenv("USER", pwd->pw_name, 1);
515			setenv("HOME", pwd->pw_dir, 1);
516			setenv("SHELL", shell, 1);
517
518			if (asthem) {
519				/*
520				 * Add any environmental variables that the
521				 * PAM modules may have set.
522				 */
523				environ_pam = pam_getenvlist(pamh);
524				if (environ_pam)
525					export_pam_environment();
526
527				/* set the su'd user's environment & umask */
528				setusercontext(lc, pwd, pwd->pw_uid,
529					LOGIN_SETPATH | LOGIN_SETUMASK |
530					LOGIN_SETENV);
531				if (p)
532					setenv("TERM", p, 1);
533
534				p = pam_getenv(pamh, "HOME");
535				if (chdir(p ? p : pwd->pw_dir) < 0)
536					errx(1, "no directory");
537			}
538		}
539		login_close(lc);
540
541		if (iscsh == YES) {
542			if (fastlogin)
543				*np.a-- = "-f";
544			if (asme)
545				*np.a-- = "-m";
546		}
547		/* csh strips the first character... */
548		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
549
550		if (ruid != 0)
551			syslog(LOG_NOTICE, "%s to %s%s", username, user,
552			    ontty());
553
554		execv(shell, np.b);
555		err(1, "%s", shell);
556	}
557}
558
559static void
560export_pam_environment(void)
561{
562	char	**pp;
563	char	*p;
564
565	for (pp = environ_pam; *pp != NULL; pp++) {
566		if (ok_to_export(*pp)) {
567			p = strchr(*pp, '=');
568			*p = '\0';
569			setenv(*pp, p + 1, 1);
570		}
571		free(*pp);
572	}
573}
574
575/*
576 * Sanity checks on PAM environmental variables:
577 * - Make sure there is an '=' in the string.
578 * - Make sure the string doesn't run on too long.
579 * - Do not export certain variables.  This list was taken from the
580 *   Solaris pam_putenv(3) man page.
581 * Note that if the user is chrooted, PAM may have a better idea than we
582 * do of where her home directory is.
583 */
584static int
585ok_to_export(const char *s)
586{
587	static const char *noexport[] = {
588		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
589		"IFS", "PATH", NULL
590	};
591	const char **pp;
592	size_t n;
593
594	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
595		return 0;
596	if (strncmp(s, "LD_", 3) == 0)
597		return 0;
598	for (pp = noexport; *pp != NULL; pp++) {
599		n = strlen(*pp);
600		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
601			return 0;
602	}
603	return 1;
604}
605
606static void
607usage(void)
608{
609
610	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
611	exit(1);
612	/* NOTREACHED */
613}
614
615static int
616chshell(const char *sh)
617{
618	int r;
619	char *cp;
620
621	r = 0;
622	setusershell();
623	while ((cp = getusershell()) != NULL && !r)
624	    r = (strcmp(cp, sh) == 0);
625	endusershell();
626	return r;
627}
628
629static char *
630ontty(void)
631{
632	char *p;
633	static char buf[MAXPATHLEN + 4];
634
635	buf[0] = 0;
636	p = ttyname(STDERR_FILENO);
637	if (p)
638		snprintf(buf, sizeof(buf), " on %s", p);
639	return buf;
640}
641