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