xref: /freebsd/usr.bin/su/su.c (revision bdcbfde31e8e9b343f113a1956384bdf30d1ed62)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software were developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 /*-
34  * Copyright (c) 1988, 1993, 1994
35  *	The Regents of the University of California.  All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. Neither the name of the University nor the names of its contributors
46  *    may be used to endorse or promote products derived from this software
47  *    without specific prior written permission.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  */
61 
62 #ifndef lint
63 static const char copyright[] =
64 "@(#) Copyright (c) 1988, 1993, 1994\n\
65 	The Regents of the University of California.  All rights reserved.\n";
66 #endif /* not lint */
67 
68 #if 0
69 #endif
70 
71 #include <sys/cdefs.h>
72 #include <sys/param.h>
73 #include <sys/time.h>
74 #include <sys/resource.h>
75 #include <sys/wait.h>
76 
77 #ifdef USE_BSM_AUDIT
78 #include <bsm/libbsm.h>
79 #include <bsm/audit_uevents.h>
80 #endif
81 
82 #include <err.h>
83 #include <errno.h>
84 #include <grp.h>
85 #include <login_cap.h>
86 #include <paths.h>
87 #include <pwd.h>
88 #include <signal.h>
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <string.h>
92 #include <syslog.h>
93 #include <unistd.h>
94 #include <stdarg.h>
95 
96 #include <security/pam_appl.h>
97 #include <security/openpam.h>
98 
99 #define PAM_END() do {							\
100 	int local_ret;							\
101 	if (pamh != NULL) {						\
102 		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
103 		if (local_ret != PAM_SUCCESS)				\
104 			syslog(LOG_ERR, "pam_setcred: %s",		\
105 				pam_strerror(pamh, local_ret));		\
106 		if (asthem) {						\
107 			local_ret = pam_close_session(pamh, 0);		\
108 			if (local_ret != PAM_SUCCESS)			\
109 				syslog(LOG_ERR, "pam_close_session: %s",\
110 					pam_strerror(pamh, local_ret));	\
111 		}							\
112 		local_ret = pam_end(pamh, local_ret);			\
113 		if (local_ret != PAM_SUCCESS)				\
114 			syslog(LOG_ERR, "pam_end: %s",			\
115 				pam_strerror(pamh, local_ret));		\
116 	}								\
117 } while (0)
118 
119 
120 #define PAM_SET_ITEM(what, item) do {					\
121 	int local_ret;							\
122 	local_ret = pam_set_item(pamh, what, item);			\
123 	if (local_ret != PAM_SUCCESS) {					\
124 		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
125 			pam_strerror(pamh, local_ret));			\
126 		errx(1, "pam_set_item(" #what "): %s",			\
127 			pam_strerror(pamh, local_ret));			\
128 		/* NOTREACHED */					\
129 	}								\
130 } while (0)
131 
132 enum tristate { UNSET, YES, NO };
133 
134 static pam_handle_t *pamh = NULL;
135 static char	**environ_pam;
136 
137 static char	*ontty(void);
138 static int	chshell(const char *);
139 static void	usage(void) __dead2;
140 static void	export_pam_environment(void);
141 static int	ok_to_export(const char *);
142 
143 extern char	**environ;
144 
145 int
146 main(int argc, char *argv[])
147 {
148 	static char	*cleanenv;
149 	struct passwd	*pwd = NULL;
150 	struct pam_conv	conv = { openpam_ttyconv, NULL };
151 	enum tristate	iscsh;
152 	login_cap_t	*lc;
153 	union {
154 		const char	**a;
155 		char		* const *b;
156 	}		np;
157 	uid_t		ruid;
158 	pid_t		child_pid, child_pgrp, pid;
159 	int		asme, ch, asthem, fastlogin, prio, i, retcode,
160 			statusp, setmaclabel;
161 	u_int		setwhat;
162 	char		*username, *class, shellbuf[MAXPATHLEN];
163 	const char	*p, *user, *shell, *mytty, **nargv;
164 	const void	*v;
165 	struct sigaction sa, sa_int, sa_quit, sa_pipe;
166 	int temp, fds[2];
167 #ifdef USE_BSM_AUDIT
168 	const char	*aerr;
169 	au_id_t		 auid;
170 #endif
171 
172 	p = shell = class = cleanenv = NULL;
173 	asme = asthem = fastlogin = statusp = 0;
174 	user = "root";
175 	iscsh = UNSET;
176 	setmaclabel = 0;
177 
178 	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
179 		switch ((char)ch) {
180 		case 'f':
181 			fastlogin = 1;
182 			break;
183 		case '-':
184 		case 'l':
185 			asme = 0;
186 			asthem = 1;
187 			break;
188 		case 'm':
189 			asme = 1;
190 			asthem = 0;
191 			break;
192 		case 's':
193 			setmaclabel = 1;
194 			break;
195 		case 'c':
196 			class = optarg;
197 			break;
198 		case '?':
199 		default:
200 			usage();
201 		/* NOTREACHED */
202 		}
203 
204 	if (optind < argc)
205 		user = argv[optind++];
206 
207 	if (user == NULL)
208 		usage();
209 	/* NOTREACHED */
210 
211 	/*
212 	 * Try to provide more helpful debugging output if su(1) is running
213 	 * non-setuid, or was run from a file system not mounted setuid.
214 	 */
215 	if (geteuid() != 0)
216 		errx(1, "not running setuid");
217 
218 #ifdef USE_BSM_AUDIT
219 	if (getauid(&auid) < 0 && errno != ENOSYS) {
220 		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
221 		errx(1, "Permission denied");
222 	}
223 #endif
224 	if (strlen(user) > MAXLOGNAME - 1) {
225 #ifdef USE_BSM_AUDIT
226 		if (audit_submit(AUE_su, auid,
227 		    EPERM, 1, "username too long: '%s'", user))
228 			errx(1, "Permission denied");
229 #endif
230 		errx(1, "username too long");
231 	}
232 
233 	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
234 	if (nargv == NULL)
235 		errx(1, "malloc failure");
236 
237 	nargv[argc + 3] = NULL;
238 	for (i = argc; i >= optind; i--)
239 		nargv[i + 3] = argv[i];
240 	np.a = &nargv[i + 3];
241 
242 	argv += optind;
243 
244 	errno = 0;
245 	prio = getpriority(PRIO_PROCESS, 0);
246 	if (errno)
247 		prio = 0;
248 
249 	setpriority(PRIO_PROCESS, 0, -2);
250 	openlog("su", LOG_CONS, LOG_AUTH);
251 
252 	/* get current login name, real uid and shell */
253 	ruid = getuid();
254 	username = getlogin();
255 	if (username != NULL)
256 		pwd = getpwnam(username);
257 	if (pwd == NULL || pwd->pw_uid != ruid)
258 		pwd = getpwuid(ruid);
259 	if (pwd == NULL) {
260 #ifdef USE_BSM_AUDIT
261 		if (audit_submit(AUE_su, auid, EPERM, 1,
262 		    "unable to determine invoking subject: '%s'", username))
263 			errx(1, "Permission denied");
264 #endif
265 		errx(1, "who are you?");
266 	}
267 
268 	username = strdup(pwd->pw_name);
269 	if (username == NULL)
270 		err(1, "strdup failure");
271 
272 	if (asme) {
273 		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
274 			/* must copy - pwd memory is recycled */
275 			strlcpy(shellbuf, pwd->pw_shell,
276 			    sizeof(shellbuf));
277 			shell = shellbuf;
278 		}
279 		else {
280 			shell = _PATH_BSHELL;
281 			iscsh = NO;
282 		}
283 	}
284 
285 	/* Do the whole PAM startup thing */
286 	retcode = pam_start("su", user, &conv, &pamh);
287 	if (retcode != PAM_SUCCESS) {
288 		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
289 		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
290 	}
291 
292 	PAM_SET_ITEM(PAM_RUSER, username);
293 
294 	mytty = ttyname(STDERR_FILENO);
295 	if (!mytty)
296 		mytty = "tty";
297 	PAM_SET_ITEM(PAM_TTY, mytty);
298 
299 	retcode = pam_authenticate(pamh, 0);
300 	if (retcode != PAM_SUCCESS) {
301 #ifdef USE_BSM_AUDIT
302 		if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
303 		    username, user, mytty))
304 			errx(1, "Permission denied");
305 #endif
306 		syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
307 		    username, user, mytty);
308 		errx(1, "Sorry");
309 	}
310 #ifdef USE_BSM_AUDIT
311 	if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
312 		errx(1, "Permission denied");
313 #endif
314 	retcode = pam_get_item(pamh, PAM_USER, &v);
315 	if (retcode == PAM_SUCCESS)
316 		user = v;
317 	else
318 		syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
319 		    pam_strerror(pamh, retcode));
320 	pwd = getpwnam(user);
321 	if (pwd == NULL) {
322 #ifdef USE_BSM_AUDIT
323 		if (audit_submit(AUE_su, auid, EPERM, 1,
324 		    "unknown subject: %s", user))
325 			errx(1, "Permission denied");
326 #endif
327 		errx(1, "unknown login: %s", user);
328 	}
329 
330 	retcode = pam_acct_mgmt(pamh, 0);
331 	if (retcode == PAM_NEW_AUTHTOK_REQD) {
332 		retcode = pam_chauthtok(pamh,
333 			PAM_CHANGE_EXPIRED_AUTHTOK);
334 		if (retcode != PAM_SUCCESS) {
335 #ifdef USE_BSM_AUDIT
336 			aerr = pam_strerror(pamh, retcode);
337 			if (aerr == NULL)
338 				aerr = "Unknown PAM error";
339 			if (audit_submit(AUE_su, auid, EPERM, 1,
340 			    "pam_chauthtok: %s", aerr))
341 				errx(1, "Permission denied");
342 #endif
343 			syslog(LOG_ERR, "pam_chauthtok: %s",
344 			    pam_strerror(pamh, retcode));
345 			errx(1, "Sorry");
346 		}
347 	}
348 	if (retcode != PAM_SUCCESS) {
349 #ifdef USE_BSM_AUDIT
350 		if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
351 		    pam_strerror(pamh, retcode)))
352 			errx(1, "Permission denied");
353 #endif
354 		syslog(LOG_ERR, "pam_acct_mgmt: %s",
355 			pam_strerror(pamh, retcode));
356 		errx(1, "Sorry");
357 	}
358 
359 	/* get target login information */
360 	if (class == NULL)
361 		lc = login_getpwclass(pwd);
362 	else {
363 		if (ruid != 0) {
364 #ifdef USE_BSM_AUDIT
365 			if (audit_submit(AUE_su, auid, EPERM, 1,
366 			    "only root may use -c"))
367 				errx(1, "Permission denied");
368 #endif
369 			errx(1, "only root may use -c");
370 		}
371 		lc = login_getclass(class);
372 		if (lc == NULL)
373 			err(1, "login_getclass");
374 		if (lc->lc_class == NULL || strcmp(class, lc->lc_class) != 0)
375 			errx(1, "unknown class: %s", class);
376 	}
377 
378 	/* if asme and non-standard target shell, must be root */
379 	if (asme) {
380 		if (ruid != 0 && !chshell(pwd->pw_shell))
381 			errx(1, "permission denied (shell)");
382 	}
383 	else if (pwd->pw_shell && *pwd->pw_shell) {
384 		shell = pwd->pw_shell;
385 		iscsh = UNSET;
386 	}
387 	else {
388 		shell = _PATH_BSHELL;
389 		iscsh = NO;
390 	}
391 
392 	/* if we're forking a csh, we want to slightly muck the args */
393 	if (iscsh == UNSET) {
394 		p = strrchr(shell, '/');
395 		if (p)
396 			++p;
397 		else
398 			p = shell;
399 		iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
400 	}
401 	setpriority(PRIO_PROCESS, 0, prio);
402 
403 	/*
404 	 * PAM modules might add supplementary groups in pam_setcred(), so
405 	 * initialize them first.
406 	 */
407 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
408 		err(1, "setusercontext");
409 
410 	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
411 	if (retcode != PAM_SUCCESS) {
412 		syslog(LOG_ERR, "pam_setcred: %s",
413 		    pam_strerror(pamh, retcode));
414 		errx(1, "failed to establish credentials.");
415 	}
416 	if (asthem) {
417 		retcode = pam_open_session(pamh, 0);
418 		if (retcode != PAM_SUCCESS) {
419 			syslog(LOG_ERR, "pam_open_session: %s",
420 			    pam_strerror(pamh, retcode));
421 			errx(1, "failed to open session.");
422 		}
423 	}
424 
425 	/*
426 	 * We must fork() before setuid() because we need to call
427 	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
428 	 */
429 	sa.sa_flags = SA_RESTART;
430 	sa.sa_handler = SIG_IGN;
431 	sigemptyset(&sa.sa_mask);
432 	sigaction(SIGINT, &sa, &sa_int);
433 	sigaction(SIGQUIT, &sa, &sa_quit);
434 	sigaction(SIGPIPE, &sa, &sa_pipe);
435 	sa.sa_handler = SIG_DFL;
436 	sigaction(SIGTSTP, &sa, NULL);
437 	statusp = 1;
438 	if (pipe(fds) == -1) {
439 		PAM_END();
440 		err(1, "pipe");
441 	}
442 	child_pid = fork();
443 	switch (child_pid) {
444 	default:
445 		sa.sa_handler = SIG_IGN;
446 		sigaction(SIGTTOU, &sa, NULL);
447 		close(fds[0]);
448 		setpgid(child_pid, child_pid);
449 		if (tcgetpgrp(STDERR_FILENO) == getpgrp())
450 			tcsetpgrp(STDERR_FILENO, child_pid);
451 		close(fds[1]);
452 		sigaction(SIGPIPE, &sa_pipe, NULL);
453 		while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
454 			if (WIFSTOPPED(statusp)) {
455 				child_pgrp = getpgid(child_pid);
456 				if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
457 					tcsetpgrp(STDERR_FILENO, getpgrp());
458 				kill(getpid(), SIGSTOP);
459 				if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
460 					child_pgrp = getpgid(child_pid);
461 					tcsetpgrp(STDERR_FILENO, child_pgrp);
462 				}
463 				kill(child_pid, SIGCONT);
464 				statusp = 1;
465 				continue;
466 			}
467 			break;
468 		}
469 		tcsetpgrp(STDERR_FILENO, getpgrp());
470 		if (pid == -1)
471 			err(1, "waitpid");
472 		PAM_END();
473 		exit(WEXITSTATUS(statusp));
474 	case -1:
475 		PAM_END();
476 		err(1, "fork");
477 	case 0:
478 		close(fds[1]);
479 		read(fds[0], &temp, 1);
480 		close(fds[0]);
481 		sigaction(SIGPIPE, &sa_pipe, NULL);
482 		sigaction(SIGINT, &sa_int, NULL);
483 		sigaction(SIGQUIT, &sa_quit, NULL);
484 
485 		/*
486 		 * Set all user context except for: Environmental variables
487 		 * Umask Login records (wtmp, etc) Path
488 		 */
489 		setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
490 			   LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
491 			   LOGIN_SETMAC);
492 		/*
493 		 * If -s is present, also set the MAC label.
494 		 */
495 		if (setmaclabel)
496 			setwhat |= LOGIN_SETMAC;
497 		/*
498 		 * Don't touch resource/priority settings if -m has been used
499 		 * or -l and -c hasn't, and we're not su'ing to root.
500 		 */
501 		if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
502 			setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
503 		if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
504 			err(1, "setusercontext");
505 
506 		if (!asme) {
507 			if (asthem) {
508 				p = getenv("TERM");
509 				environ = &cleanenv;
510 			}
511 
512 			if (asthem || pwd->pw_uid)
513 				setenv("USER", pwd->pw_name, 1);
514 			setenv("HOME", pwd->pw_dir, 1);
515 			setenv("SHELL", shell, 1);
516 
517 			if (asthem) {
518 				/*
519 				 * Add any environmental variables that the
520 				 * PAM modules may have set.
521 				 */
522 				environ_pam = pam_getenvlist(pamh);
523 				if (environ_pam)
524 					export_pam_environment();
525 
526 				/* set the su'd user's environment & umask */
527 				setusercontext(lc, pwd, pwd->pw_uid,
528 					LOGIN_SETPATH | LOGIN_SETUMASK |
529 					LOGIN_SETENV);
530 				if (p)
531 					setenv("TERM", p, 1);
532 
533 				p = pam_getenv(pamh, "HOME");
534 				if (chdir(p ? p : pwd->pw_dir) < 0)
535 					errx(1, "no directory");
536 			}
537 		}
538 		login_close(lc);
539 
540 		if (iscsh == YES) {
541 			if (fastlogin)
542 				*np.a-- = "-f";
543 			if (asme)
544 				*np.a-- = "-m";
545 		}
546 		/* csh strips the first character... */
547 		*np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
548 
549 		if (ruid != 0)
550 			syslog(LOG_NOTICE, "%s to %s%s", username, user,
551 			    ontty());
552 
553 		execv(shell, np.b);
554 		err(1, "%s", shell);
555 	}
556 }
557 
558 static void
559 export_pam_environment(void)
560 {
561 	char	**pp;
562 	char	*p;
563 
564 	for (pp = environ_pam; *pp != NULL; pp++) {
565 		if (ok_to_export(*pp)) {
566 			p = strchr(*pp, '=');
567 			*p = '\0';
568 			setenv(*pp, p + 1, 1);
569 		}
570 		free(*pp);
571 	}
572 }
573 
574 /*
575  * Sanity checks on PAM environmental variables:
576  * - Make sure there is an '=' in the string.
577  * - Make sure the string doesn't run on too long.
578  * - Do not export certain variables.  This list was taken from the
579  *   Solaris pam_putenv(3) man page.
580  * Note that if the user is chrooted, PAM may have a better idea than we
581  * do of where her home directory is.
582  */
583 static int
584 ok_to_export(const char *s)
585 {
586 	static const char *noexport[] = {
587 		"SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
588 		"IFS", "PATH", NULL
589 	};
590 	const char **pp;
591 	size_t n;
592 
593 	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
594 		return 0;
595 	if (strncmp(s, "LD_", 3) == 0)
596 		return 0;
597 	for (pp = noexport; *pp != NULL; pp++) {
598 		n = strlen(*pp);
599 		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
600 			return 0;
601 	}
602 	return 1;
603 }
604 
605 static void
606 usage(void)
607 {
608 
609 	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
610 	exit(1);
611 	/* NOTREACHED */
612 }
613 
614 static int
615 chshell(const char *sh)
616 {
617 	int r;
618 	char *cp;
619 
620 	r = 0;
621 	setusershell();
622 	while ((cp = getusershell()) != NULL && !r)
623 	    r = (strcmp(cp, sh) == 0);
624 	endusershell();
625 	return r;
626 }
627 
628 static char *
629 ontty(void)
630 {
631 	char *p;
632 	static char buf[MAXPATHLEN + 4];
633 
634 	buf[0] = 0;
635 	p = ttyname(STDERR_FILENO);
636 	if (p)
637 		snprintf(buf, sizeof(buf), " on %s", p);
638 	return buf;
639 }
640