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