xref: /freebsd/usr.bin/su/su.c (revision 3157ba21)
1 /*
2  * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
3  * All rights reserved.
4  *
5  * Portions of this software were developed for the FreeBSD Project by
6  * ThinkSec AS and NAI Labs, the Security Research Division of Network
7  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
8  * ("CBOSS"), as part of the DARPA CHATS research program.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 /*-
32  * Copyright (c) 1988, 1993, 1994
33  *	The Regents of the University of California.  All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  * 3. All advertising materials mentioning features or use of this software
44  *    must display the following acknowledgement:
45  *	This product includes software developed by the University of
46  *	California, Berkeley and its contributors.
47  * 4. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63 
64 #ifndef lint
65 static const char copyright[] =
66 "@(#) Copyright (c) 1988, 1993, 1994\n\
67 	The Regents of the University of California.  All rights reserved.\n";
68 #endif /* not lint */
69 
70 #if 0
71 #ifndef lint
72 static char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
73 #endif /* not lint */
74 #endif
75 
76 #include <sys/cdefs.h>
77 __FBSDID("$FreeBSD$");
78 
79 #include <sys/param.h>
80 #include <sys/time.h>
81 #include <sys/resource.h>
82 #include <sys/wait.h>
83 
84 #ifdef USE_BSM_AUDIT
85 #include <bsm/libbsm.h>
86 #include <bsm/audit_uevents.h>
87 #endif
88 
89 #include <err.h>
90 #include <errno.h>
91 #include <grp.h>
92 #include <login_cap.h>
93 #include <paths.h>
94 #include <pwd.h>
95 #include <signal.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <string.h>
99 #include <syslog.h>
100 #include <unistd.h>
101 #include <stdarg.h>
102 
103 #include <security/pam_appl.h>
104 #include <security/openpam.h>
105 
106 #define PAM_END() do {							\
107 	int local_ret;							\
108 	if (pamh != NULL) {						\
109 		local_ret = pam_setcred(pamh, PAM_DELETE_CRED);		\
110 		if (local_ret != PAM_SUCCESS)				\
111 			syslog(LOG_ERR, "pam_setcred: %s",		\
112 				pam_strerror(pamh, local_ret));		\
113 		if (asthem) {						\
114 			local_ret = pam_close_session(pamh, 0);		\
115 			if (local_ret != PAM_SUCCESS)			\
116 				syslog(LOG_ERR, "pam_close_session: %s",\
117 					pam_strerror(pamh, local_ret));	\
118 		}							\
119 		local_ret = pam_end(pamh, local_ret);			\
120 		if (local_ret != PAM_SUCCESS)				\
121 			syslog(LOG_ERR, "pam_end: %s",			\
122 				pam_strerror(pamh, local_ret));		\
123 	}								\
124 } while (0)
125 
126 
127 #define PAM_SET_ITEM(what, item) do {					\
128 	int local_ret;							\
129 	local_ret = pam_set_item(pamh, what, item);			\
130 	if (local_ret != PAM_SUCCESS) {					\
131 		syslog(LOG_ERR, "pam_set_item(" #what "): %s",		\
132 			pam_strerror(pamh, local_ret));			\
133 		errx(1, "pam_set_item(" #what "): %s",			\
134 			pam_strerror(pamh, local_ret));			\
135 		/* NOTREACHED */					\
136 	}								\
137 } while (0)
138 
139 enum tristate { UNSET, YES, NO };
140 
141 static pam_handle_t *pamh = NULL;
142 static char	**environ_pam;
143 
144 static char	*ontty(void);
145 static int	chshell(const char *);
146 static void	usage(void) __dead2;
147 static void	export_pam_environment(void);
148 static int	ok_to_export(const char *);
149 
150 extern char	**environ;
151 
152 int
153 main(int argc, char *argv[])
154 {
155 	static char	*cleanenv;
156 	struct passwd	*pwd;
157 	struct pam_conv	conv = { openpam_ttyconv, NULL };
158 	enum tristate	iscsh;
159 	login_cap_t	*lc;
160 	union {
161 		const char	**a;
162 		char		* const *b;
163 	}		np;
164 	uid_t		ruid;
165 	pid_t		child_pid, child_pgrp, pid;
166 	int		asme, ch, asthem, fastlogin, prio, i, retcode,
167 			statusp, setmaclabel;
168 	u_int		setwhat;
169 	char		*username, *class, shellbuf[MAXPATHLEN];
170 	const char	*p, *user, *shell, *mytty, **nargv;
171 	const void	*v;
172 	struct sigaction sa, sa_int, sa_quit, sa_pipe;
173 	int temp, fds[2];
174 #ifdef USE_BSM_AUDIT
175 	const char	*aerr;
176 	au_id_t		 auid;
177 #endif
178 
179 	shell = class = cleanenv = NULL;
180 	asme = asthem = fastlogin = statusp = 0;
181 	user = "root";
182 	iscsh = UNSET;
183 	setmaclabel = 0;
184 
185 	while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
186 		switch ((char)ch) {
187 		case 'f':
188 			fastlogin = 1;
189 			break;
190 		case '-':
191 		case 'l':
192 			asme = 0;
193 			asthem = 1;
194 			break;
195 		case 'm':
196 			asme = 1;
197 			asthem = 0;
198 			break;
199 		case 's':
200 			setmaclabel = 1;
201 			break;
202 		case 'c':
203 			class = optarg;
204 			break;
205 		case '?':
206 		default:
207 			usage();
208 		/* NOTREACHED */
209 		}
210 
211 	if (optind < argc)
212 		user = argv[optind++];
213 
214 	if (user == NULL)
215 		usage();
216 	/* NOTREACHED */
217 
218 	/*
219 	 * Try to provide more helpful debugging output if su(1) is running
220 	 * non-setuid, or was run from a file system not mounted setuid.
221 	 */
222 	if (geteuid() != 0)
223 		errx(1, "not running setuid");
224 
225 #ifdef USE_BSM_AUDIT
226 	if (getauid(&auid) < 0 && errno != ENOSYS) {
227 		syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
228 		errx(1, "Permission denied");
229 	}
230 #endif
231 	if (strlen(user) > MAXLOGNAME - 1) {
232 #ifdef USE_BSM_AUDIT
233 		if (audit_submit(AUE_su, auid,
234 		    EPERM, 1, "username too long: '%s'", user))
235 			errx(1, "Permission denied");
236 #endif
237 		errx(1, "username too long");
238 	}
239 
240 	nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
241 	if (nargv == NULL)
242 		errx(1, "malloc failure");
243 
244 	nargv[argc + 3] = NULL;
245 	for (i = argc; i >= optind; i--)
246 		nargv[i + 3] = argv[i];
247 	np.a = &nargv[i + 3];
248 
249 	argv += optind;
250 
251 	errno = 0;
252 	prio = getpriority(PRIO_PROCESS, 0);
253 	if (errno)
254 		prio = 0;
255 
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, EPERM, 1,
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, EPERM, 1, "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, EPERM, 1,
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, EPERM, 1,
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, EPERM, 1, "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, EPERM, 1,
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 
562 static void
563 export_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  */
587 static int
588 ok_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 
609 static void
610 usage(void)
611 {
612 
613 	fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
614 	exit(1);
615 	/* NOTREACHED */
616 }
617 
618 static int
619 chshell(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 
632 static char *
633 ontty(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