su.c revision 105362
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1988, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
497377Sdes * Copyright (c) 2002 Networks Associates Technologies, Inc.
597377Sdes * All rights reserved.
61590Srgrimes *
797377Sdes * Portions of this software were developed for the FreeBSD Project by
897377Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network
997377Sdes * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
1097377Sdes * ("CBOSS"), as part of the DARPA CHATS research program.
1197377Sdes *
121590Srgrimes * Redistribution and use in source and binary forms, with or without
131590Srgrimes * modification, are permitted provided that the following conditions
141590Srgrimes * are met:
151590Srgrimes * 1. Redistributions of source code must retain the above copyright
161590Srgrimes *    notice, this list of conditions and the following disclaimer.
171590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
181590Srgrimes *    notice, this list of conditions and the following disclaimer in the
191590Srgrimes *    documentation and/or other materials provided with the distribution.
201590Srgrimes * 3. All advertising materials mentioning features or use of this software
211590Srgrimes *    must display the following acknowledgement:
221590Srgrimes *	This product includes software developed by the University of
231590Srgrimes *	California, Berkeley and its contributors.
241590Srgrimes * 4. Neither the name of the University nor the names of its contributors
251590Srgrimes *    may be used to endorse or promote products derived from this software
261590Srgrimes *    without specific prior written permission.
271590Srgrimes *
281590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
291590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
301590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
311590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
321590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
331590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
341590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
351590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
361590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
371590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
381590Srgrimes * SUCH DAMAGE.
391590Srgrimes */
401590Srgrimes
411590Srgrimes#ifndef lint
4214440Smarkmstatic const char copyright[] =
431590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\
441590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
451590Srgrimes#endif /* not lint */
461590Srgrimes
471590Srgrimes#ifndef lint
4828099Scharnier#if 0
491590Srgrimesstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
5028099Scharnier#endif
5114440Smarkmstatic const char rcsid[] =
5250477Speter  "$FreeBSD: head/usr.bin/su/su.c 105362 2002-10-17 23:32:44Z tjr $";
531590Srgrimes#endif /* not lint */
541590Srgrimes
551590Srgrimes#include <sys/param.h>
561590Srgrimes#include <sys/time.h>
571590Srgrimes#include <sys/resource.h>
5877220Smarkm#include <sys/wait.h>
591590Srgrimes
601590Srgrimes#include <err.h>
611590Srgrimes#include <errno.h>
621590Srgrimes#include <grp.h>
6377220Smarkm#include <libutil.h>
6477220Smarkm#include <login_cap.h>
651590Srgrimes#include <paths.h>
661590Srgrimes#include <pwd.h>
6777220Smarkm#include <signal.h>
681590Srgrimes#include <stdio.h>
691590Srgrimes#include <stdlib.h>
701590Srgrimes#include <string.h>
711590Srgrimes#include <syslog.h>
721590Srgrimes#include <unistd.h>
7321646Sdavidn
7474874Smarkm#include <security/pam_appl.h>
7591745Sdes#include <security/openpam.h>
7674874Smarkm
7781528Smarkm#define PAM_END() do {						\
7877220Smarkm	int local_ret;						\
7977220Smarkm	if (pamh != NULL && creds_set) {			\
8077220Smarkm		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);	\
8177220Smarkm		if (local_ret != PAM_SUCCESS)			\
8277220Smarkm			syslog(LOG_ERR, "pam_setcred: %s",	\
8377220Smarkm				pam_strerror(pamh, local_ret));	\
8477220Smarkm		local_ret = pam_end(pamh, local_ret);		\
8577220Smarkm		if (local_ret != PAM_SUCCESS)			\
8677220Smarkm			syslog(LOG_ERR, "pam_end: %s",		\
8777220Smarkm				pam_strerror(pamh, local_ret));	\
8877220Smarkm	}							\
8977220Smarkm} while (0)
9074874Smarkm
9174874Smarkm
9277220Smarkm#define PAM_SET_ITEM(what, item) do {				\
9377220Smarkm	int local_ret;						\
9477220Smarkm	local_ret = pam_set_item(pamh, what, item);		\
9577220Smarkm	if (local_ret != PAM_SUCCESS) {				\
9677220Smarkm		syslog(LOG_ERR, "pam_set_item(" #what "): %s",	\
9777220Smarkm			pam_strerror(pamh, local_ret));		\
9877220Smarkm		errx(1, "pam_set_item(" #what "): %s",		\
9977220Smarkm			pam_strerror(pamh, local_ret));		\
10077220Smarkm	}							\
10177220Smarkm} while (0)
1023702Spst
10377220Smarkmenum tristate { UNSET, YES, NO };
1041590Srgrimes
10577220Smarkmstatic pam_handle_t *pamh = NULL;
10677220Smarkmstatic int	creds_set = 0;
10777220Smarkmstatic char	**environ_pam;
10877220Smarkm
10977220Smarkmstatic char	*ontty(void);
11077220Smarkmstatic int	chshell(char *);
11177220Smarkmstatic void	usage(void);
11277220Smarkmstatic int	export_pam_environment(void);
11377220Smarkmstatic int	ok_to_export(const char *);
11477220Smarkm
11577220Smarkmextern char	**environ;
11677220Smarkm
1171590Srgrimesint
11877220Smarkmmain(int argc, char *argv[])
1191590Srgrimes{
12077220Smarkm	struct passwd	*pwd;
12191745Sdes	struct pam_conv	conv = { openpam_ttyconv, NULL };
12277220Smarkm	enum tristate	iscsh;
12377220Smarkm	login_cap_t	*lc;
12483373Smarkm	union {
12583373Smarkm		const char	**a;
12683373Smarkm		char		* const *b;
12797377Sdes	}		np;
12877220Smarkm	uid_t		ruid;
12977220Smarkm	int		asme, ch, asthem, fastlogin, prio, i, setwhat, retcode,
130101446Sache			statusp, child_pid, child_pgrp, ret_pid;
13189746Sdes	char		*username, *cleanenv, *class, shellbuf[MAXPATHLEN];
13283373Smarkm	const char	*p, *user, *shell, *mytty, **nargv;
1331590Srgrimes
13498837Sdillon	struct sigaction sa, sa_int, sa_quit, sa_tstp;
13598837Sdillon
13677220Smarkm	shell = class = cleanenv = NULL;
13777220Smarkm	asme = asthem = fastlogin = statusp = 0;
13810586Sjoerg	user = "root";
13977220Smarkm	iscsh = UNSET;
14077220Smarkm
14181703Sru	while ((ch = getopt(argc, argv, "-flmc:")) != -1)
14277220Smarkm		switch ((char)ch) {
1431590Srgrimes		case 'f':
1441590Srgrimes			fastlogin = 1;
1451590Srgrimes			break;
1461590Srgrimes		case '-':
1471590Srgrimes		case 'l':
1481590Srgrimes			asme = 0;
1491590Srgrimes			asthem = 1;
1501590Srgrimes			break;
1511590Srgrimes		case 'm':
1521590Srgrimes			asme = 1;
1531590Srgrimes			asthem = 0;
1541590Srgrimes			break;
15530793Sguido		case 'c':
15630793Sguido			class = optarg;
15730793Sguido			break;
1581590Srgrimes		case '?':
1591590Srgrimes		default:
16028099Scharnier			usage();
16128099Scharnier		}
16239538Sroberto
16339538Sroberto	if (optind < argc)
16410586Sjoerg		user = argv[optind++];
16510586Sjoerg
16628612Sjoerg	if (user == NULL)
16728612Sjoerg		usage();
16828612Sjoerg
16977220Smarkm	if (strlen(user) > MAXLOGNAME - 1)
17077220Smarkm		errx(1, "username too long");
17110586Sjoerg
17277220Smarkm	nargv = malloc(sizeof(char *) * (argc + 4));
17377220Smarkm	if (nargv == NULL)
17477220Smarkm		errx(1, "malloc failure");
17577220Smarkm
17610586Sjoerg	nargv[argc + 3] = NULL;
17710586Sjoerg	for (i = argc; i >= optind; i--)
17877220Smarkm		nargv[i + 3] = argv[i];
17983373Smarkm	np.a = &nargv[i + 3];
18010586Sjoerg
1811590Srgrimes	argv += optind;
1821590Srgrimes
1831590Srgrimes	errno = 0;
1841590Srgrimes	prio = getpriority(PRIO_PROCESS, 0);
1851590Srgrimes	if (errno)
1861590Srgrimes		prio = 0;
18777220Smarkm
18877220Smarkm	setpriority(PRIO_PROCESS, 0, -2);
18974874Smarkm	openlog("su", LOG_CONS, LOG_AUTH);
1901590Srgrimes
19177220Smarkm	/* get current login name, real uid and shell */
1921590Srgrimes	ruid = getuid();
1931590Srgrimes	username = getlogin();
19477220Smarkm	pwd = getpwnam(username);
19577220Smarkm	if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
1961590Srgrimes		pwd = getpwuid(ruid);
1971590Srgrimes	if (pwd == NULL)
1981590Srgrimes		errx(1, "who are you?");
19977220Smarkm
2001590Srgrimes	username = strdup(pwd->pw_name);
2011590Srgrimes	if (username == NULL)
20277220Smarkm		err(1, "strdup failure");
20377220Smarkm
20421646Sdavidn	if (asme) {
20521646Sdavidn		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
20677220Smarkm			/* must copy - pwd memory is recycled */
20777220Smarkm			shell = strncpy(shellbuf, pwd->pw_shell,
20877220Smarkm			    sizeof(shellbuf));
20977220Smarkm			shellbuf[sizeof(shellbuf) - 1] = '\0';
21077220Smarkm		}
21177220Smarkm		else {
2121590Srgrimes			shell = _PATH_BSHELL;
2131590Srgrimes			iscsh = NO;
2141590Srgrimes		}
21521646Sdavidn	}
2161590Srgrimes
21777220Smarkm	/* Do the whole PAM startup thing */
21874874Smarkm	retcode = pam_start("su", user, &conv, &pamh);
21974874Smarkm	if (retcode != PAM_SUCCESS) {
22074874Smarkm		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
22174874Smarkm		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
22274874Smarkm	}
22374874Smarkm
22481529Smarkm	PAM_SET_ITEM(PAM_RUSER, getlogin());
22581529Smarkm
22674874Smarkm	mytty = ttyname(STDERR_FILENO);
22774874Smarkm	if (!mytty)
22874874Smarkm		mytty = "tty";
22977220Smarkm	PAM_SET_ITEM(PAM_TTY, mytty);
23077220Smarkm
23177220Smarkm	retcode = pam_authenticate(pamh, 0);
23274874Smarkm	if (retcode != PAM_SUCCESS) {
23377220Smarkm		syslog(LOG_ERR, "pam_authenticate: %s",
23477220Smarkm		    pam_strerror(pamh, retcode));
23577220Smarkm		errx(1, "Sorry");
23674874Smarkm	}
23777220Smarkm	retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
23877220Smarkm	if (retcode == PAM_SUCCESS)
23977220Smarkm		user = p;
24077220Smarkm	else
24177220Smarkm		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
24277220Smarkm		    pam_strerror(pamh, retcode));
24374874Smarkm
24477220Smarkm	retcode = pam_acct_mgmt(pamh, 0);
24577220Smarkm	if (retcode == PAM_NEW_AUTHTOK_REQD) {
24677220Smarkm		retcode = pam_chauthtok(pamh,
24777220Smarkm			PAM_CHANGE_EXPIRED_AUTHTOK);
24874874Smarkm		if (retcode != PAM_SUCCESS) {
24977220Smarkm			syslog(LOG_ERR, "pam_chauthtok: %s",
25077220Smarkm			    pam_strerror(pamh, retcode));
25174874Smarkm			errx(1, "Sorry");
25274874Smarkm		}
25374874Smarkm	}
25477220Smarkm	if (retcode != PAM_SUCCESS) {
25577220Smarkm		syslog(LOG_ERR, "pam_acct_mgmt: %s",
25677220Smarkm			pam_strerror(pamh, retcode));
25777220Smarkm		errx(1, "Sorry");
25877220Smarkm	}
25974874Smarkm
2601590Srgrimes	/* get target login information, default to root */
26177220Smarkm	pwd = getpwnam(user);
26277220Smarkm	if (pwd == NULL)
2639502Swollman		errx(1, "unknown login: %s", user);
26477220Smarkm	if (class == NULL)
26530793Sguido		lc = login_getpwclass(pwd);
26677220Smarkm	else {
26777220Smarkm		if (ruid != 0)
26830793Sguido			errx(1, "only root may use -c");
26930793Sguido		lc = login_getclass(class);
27030793Sguido		if (lc == NULL)
27130793Sguido			errx(1, "unknown class: %s", class);
27230793Sguido	}
2731590Srgrimes
27477220Smarkm	/* if asme and non-standard target shell, must be root */
2751590Srgrimes	if (asme) {
27677220Smarkm		if (ruid != 0 && !chshell(pwd->pw_shell))
2771590Srgrimes			errx(1, "permission denied (shell).");
27877220Smarkm	}
27977220Smarkm	else if (pwd->pw_shell && *pwd->pw_shell) {
2801590Srgrimes		shell = pwd->pw_shell;
2811590Srgrimes		iscsh = UNSET;
28277220Smarkm	}
28377220Smarkm	else {
2841590Srgrimes		shell = _PATH_BSHELL;
2851590Srgrimes		iscsh = NO;
2861590Srgrimes	}
2871590Srgrimes
2881590Srgrimes	/* if we're forking a csh, we want to slightly muck the args */
2891590Srgrimes	if (iscsh == UNSET) {
29014440Smarkm		p = strrchr(shell, '/');
29114440Smarkm		if (p)
2921590Srgrimes			++p;
2931590Srgrimes		else
2941590Srgrimes			p = shell;
29577220Smarkm		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
2961590Srgrimes	}
29777220Smarkm	setpriority(PRIO_PROCESS, 0, prio);
2981590Srgrimes
29921646Sdavidn	/*
30077220Smarkm	 * PAM modules might add supplementary groups in pam_setcred(), so
30177220Smarkm	 * initialize them first.
30274874Smarkm	 */
30374874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
30474874Smarkm		err(1, "setusercontext");
30574874Smarkm
30674874Smarkm	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
30777220Smarkm	if (retcode != PAM_SUCCESS)
30877220Smarkm		syslog(LOG_ERR, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s",
30977220Smarkm		    pam_strerror(pamh, retcode));
31077220Smarkm	else
31177220Smarkm		creds_set = 1;
31274874Smarkm
31374874Smarkm	/*
31474874Smarkm	 * We must fork() before setuid() because we need to call
31574874Smarkm	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
31674874Smarkm	 */
31798837Sdillon	sa.sa_flags = SA_RESTART;
318105362Stjr	sa.sa_handler = SIG_IGN;
31998837Sdillon	sigemptyset(&sa.sa_mask);
32098837Sdillon	sigaction(SIGINT, &sa, &sa_int);
32198837Sdillon	sigaction(SIGQUIT, &sa, &sa_quit);
32298837Sdillon	sigaction(SIGTSTP, &sa, &sa_tstp);
32374874Smarkm
32474874Smarkm	statusp = 1;
32577220Smarkm	child_pid = fork();
32677220Smarkm	switch (child_pid) {
32774874Smarkm	default:
32877220Smarkm		while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
32977220Smarkm			if (WIFSTOPPED(statusp)) {
33077220Smarkm				kill(getpid(), SIGSTOP);
331101722Sache				child_pgrp = getpgid(child_pid);
332101749Sache				if (tcgetpgrp(1) == getpgrp()) {
333101722Sache					tcsetpgrp(1, child_pgrp);
334101722Sache					kill(child_pid, SIGCONT);
335101722Sache				}
33677220Smarkm				statusp = 1;
33777220Smarkm				continue;
33877220Smarkm			}
33977220Smarkm			break;
34074874Smarkm		}
34177220Smarkm		if (ret_pid == -1)
34277220Smarkm			err(1, "waitpid");
34381528Smarkm		PAM_END();
34477220Smarkm		exit(statusp);
34574874Smarkm	case -1:
34677220Smarkm		err(1, "fork");
34781528Smarkm		PAM_END();
34877220Smarkm		exit(1);
34974874Smarkm	case 0:
35098837Sdillon		sigaction(SIGINT, &sa_int, NULL);
35198837Sdillon		sigaction(SIGQUIT, &sa_quit, NULL);
35298837Sdillon		sigaction(SIGTSTP, &sa_tstp, NULL);
35377220Smarkm		/*
35477220Smarkm		 * Set all user context except for: Environmental variables
35577220Smarkm		 * Umask Login records (wtmp, etc) Path
35677220Smarkm		 */
35777220Smarkm		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
35877220Smarkm			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP);
35977220Smarkm		/*
36077220Smarkm		 * Don't touch resource/priority settings if -m has been used
36177220Smarkm		 * or -l and -c hasn't, and we're not su'ing to root.
36277220Smarkm		 */
36377220Smarkm		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
36477220Smarkm			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
36577220Smarkm		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
36677220Smarkm			err(1, "setusercontext");
36774874Smarkm
36877220Smarkm		if (!asme) {
36977220Smarkm			if (asthem) {
37077220Smarkm				p = getenv("TERM");
37177220Smarkm				environ = &cleanenv;
37269427Srwatson
37377220Smarkm				/*
37477220Smarkm				 * Add any environmental variables that the
37577220Smarkm				 * PAM modules may have set.
37677220Smarkm				 */
37777220Smarkm				environ_pam = pam_getenvlist(pamh);
37877220Smarkm				if (environ_pam)
37977220Smarkm					export_pam_environment();
3801590Srgrimes
38177220Smarkm				/* set the su'd user's environment & umask */
38277220Smarkm				setusercontext(lc, pwd, pwd->pw_uid,
38377220Smarkm					LOGIN_SETPATH | LOGIN_SETUMASK |
38477220Smarkm					LOGIN_SETENV);
38577220Smarkm				if (p)
38677220Smarkm					setenv("TERM", p, 1);
38777220Smarkm				if (chdir(pwd->pw_dir) < 0)
38877220Smarkm					errx(1, "no directory");
38977220Smarkm			}
39077220Smarkm			if (asthem || pwd->pw_uid)
39177220Smarkm				setenv("USER", pwd->pw_name, 1);
39277220Smarkm			setenv("HOME", pwd->pw_dir, 1);
39377220Smarkm			setenv("SHELL", shell, 1);
39477220Smarkm		}
39577220Smarkm		login_close(lc);
39674874Smarkm
39777220Smarkm		if (iscsh == YES) {
39877220Smarkm			if (fastlogin)
39983373Smarkm				*np.a-- = "-f";
40077220Smarkm			if (asme)
40183373Smarkm				*np.a-- = "-m";
4021590Srgrimes		}
40377220Smarkm		/* csh strips the first character... */
40483373Smarkm		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
40574874Smarkm
40677220Smarkm		if (ruid != 0)
40777220Smarkm			syslog(LOG_NOTICE, "%s to %s%s", username, user,
40877220Smarkm			    ontty());
40974874Smarkm
41083373Smarkm		execv(shell, np.b);
41177220Smarkm		err(1, "%s", shell);
4121590Srgrimes	}
4131590Srgrimes}
4141590Srgrimes
41574874Smarkmstatic int
41677220Smarkmexport_pam_environment(void)
41774874Smarkm{
41874874Smarkm	char	**pp;
41974874Smarkm
42074874Smarkm	for (pp = environ_pam; *pp != NULL; pp++) {
42174874Smarkm		if (ok_to_export(*pp))
42277220Smarkm			putenv(*pp);
42374874Smarkm		free(*pp);
42474874Smarkm	}
42574874Smarkm	return PAM_SUCCESS;
42674874Smarkm}
42774874Smarkm
42874874Smarkm/*
42974874Smarkm * Sanity checks on PAM environmental variables:
43074874Smarkm * - Make sure there is an '=' in the string.
43174874Smarkm * - Make sure the string doesn't run on too long.
43274874Smarkm * - Do not export certain variables.  This list was taken from the
43374874Smarkm *   Solaris pam_putenv(3) man page.
43474874Smarkm */
43574874Smarkmstatic int
43677220Smarkmok_to_export(const char *s)
43774874Smarkm{
43874874Smarkm	static const char *noexport[] = {
43974874Smarkm		"SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
44074874Smarkm		"IFS", "PATH", NULL
44174874Smarkm	};
44274874Smarkm	const char **pp;
44374874Smarkm	size_t n;
44474874Smarkm
44574874Smarkm	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
44674874Smarkm		return 0;
44774874Smarkm	if (strncmp(s, "LD_", 3) == 0)
44874874Smarkm		return 0;
44974874Smarkm	for (pp = noexport; *pp != NULL; pp++) {
45074874Smarkm		n = strlen(*pp);
45174874Smarkm		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
45274874Smarkm			return 0;
45374874Smarkm	}
45474874Smarkm	return 1;
45574874Smarkm}
45674874Smarkm
45728099Scharnierstatic void
45877220Smarkmusage(void)
45928099Scharnier{
46081702Sru
46181971Smarkm	fprintf(stderr, "usage: su [-] [-flm] [-c class] [login [args]]\n");
46281702Sru	exit(1);
46328099Scharnier}
46428099Scharnier
46577220Smarkmstatic int
46677220Smarkmchshell(char *sh)
4671590Srgrimes{
46877220Smarkm	int r;
4691590Srgrimes	char *cp;
4701590Srgrimes
47177220Smarkm	r = 0;
47221646Sdavidn	setusershell();
47377220Smarkm	do {
47477220Smarkm		cp = getusershell();
47577220Smarkm		r = strcmp(cp, sh);
47677220Smarkm	} while (!r && cp != NULL);
47721646Sdavidn	endusershell();
47821646Sdavidn	return r;
4791590Srgrimes}
4801590Srgrimes
48177220Smarkmstatic char *
48277220Smarkmontty(void)
4831590Srgrimes{
4841590Srgrimes	char *p;
4851590Srgrimes	static char buf[MAXPATHLEN + 4];
4861590Srgrimes
4871590Srgrimes	buf[0] = 0;
48814440Smarkm	p = ttyname(STDERR_FILENO);
48914440Smarkm	if (p)
4901590Srgrimes		snprintf(buf, sizeof(buf), " on %s", p);
49177220Smarkm	return buf;
4921590Srgrimes}
493