xref: /dragonfly/usr.bin/su/su.c (revision 25606b42ed0593ab50ef284f752abfa6b722de31)
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