1 /*     $NetBSD: login_pam.c,v 1.28 2022/01/24 09:14:37 andvar Exp $       */
2 
3 /*-
4  * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)login.c     8.4 (Berkeley) 4/2/94";
41 #endif
42 __RCSID("$NetBSD: login_pam.c,v 1.28 2022/01/24 09:14:37 andvar Exp $");
43 #endif /* not lint */
44 
45 /*
46  * login [ name ]
47  * login -h hostname          (for telnetd, etc.)
48  * login -f name    (for pre-authenticated login: datakit, xterm, etc.)
49  */
50 
51 #include <sys/param.h>
52 #include <sys/stat.h>
53 #include <sys/time.h>
54 #include <sys/resource.h>
55 #include <sys/file.h>
56 #include <sys/wait.h>
57 #include <sys/socket.h>
58 
59 #include <err.h>
60 #include <errno.h>
61 #include <grp.h>
62 #include <pwd.h>
63 #include <setjmp.h>
64 #include <signal.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <syslog.h>
69 #include <time.h>
70 #include <ttyent.h>
71 #include <tzfile.h>
72 #include <unistd.h>
73 #include <util.h>
74 #include <login_cap.h>
75 #include <vis.h>
76 
77 #include <security/pam_appl.h>
78 #include <security/openpam.h>
79 
80 #include "pathnames.h"
81 #include "common.h"
82 
83 #if 0
84 static int           rootterm(char *);
85 #endif
86 static void          usage(void) __attribute__((__noreturn__));
87 
88 static struct pam_conv pamc = { openpam_ttyconv, NULL };
89 
90 #define   TTYGRPNAME          "tty"               /* name of group to own ttys */
91 
92 #define DEFAULT_BACKOFF 3
93 #define DEFAULT_RETRIES 10
94 
95 static struct       passwd pwres;
96 static char         pwbuf[1024];
97 static struct       group grs, *grp;
98 static char         grbuf[1024];
99 extern char **environ;
100 
101 int
main(int argc,char * argv[])102 main(int argc, char *argv[])
103 {
104           struct stat st;
105           int ask, ch, cnt, fflag, pflag, quietlog, rootlogin;
106           int auth_passed;
107           uid_t uid, saved_uid;
108           gid_t saved_gid, saved_gids[NGROUPS_MAX];
109           int nsaved_gids;
110           char *domain, *p, *ttyn;
111           char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
112           char localhost[MAXHOSTNAMELEN + 1];
113           int login_retries = DEFAULT_RETRIES,
114               login_backoff = DEFAULT_BACKOFF;
115           char *shell = NULL;
116           login_cap_t *lc = NULL;
117           pam_handle_t *pamh = NULL;
118           int pam_err;
119           sig_t oint, oabrt, oquit, oalrm;
120           const void *newuser;
121           int pam_silent = PAM_SILENT;
122           pid_t xpid, pid;
123           int status;
124           char *saved_term;
125           char **pamenv;
126 
127           tbuf[0] = '\0';
128           nested = NULL;
129 
130           oabrt = signal(SIGABRT, SIG_IGN);
131           oalrm = signal(SIGALRM, timedout);
132           oint = signal(SIGINT, SIG_IGN);
133           oquit = signal(SIGQUIT, SIG_IGN);
134 
135           (void)alarm(timeout);
136           (void)setpriority(PRIO_PROCESS, 0, 0);
137 
138           openlog("login", 0, LOG_AUTH);
139 
140           /*
141            * -p is used by getty to tell login not to destroy the environment
142            * -f is used to skip a second login authentication
143            * -h is used by other servers to pass the name of the remote host to
144            *    login so that it may be placed in utmp/utmpx and wtmp/wtmpx
145            * -a in addition to -h, a server my supply -a to pass the actual
146            *    server address.
147            */
148           domain = NULL;
149           if (gethostname(localhost, sizeof(localhost)) < 0)
150                     syslog(LOG_ERR, "couldn't get local hostname: %m");
151           else
152                     domain = strchr(localhost, '.');
153           localhost[sizeof(localhost) - 1] = '\0';
154 
155           fflag = pflag = 0;
156           have_ss = 0;
157           uid = getuid();
158           while ((ch = getopt(argc, argv, "a:fh:p")) != -1)
159                     switch (ch) {
160                     case 'a':
161                               if (uid) {
162                                         errno = EPERM;
163                                         err(EXIT_FAILURE, "-a option");
164                               }
165                               decode_ss(optarg);
166                               break;
167                     case 'f':
168                               fflag = 1;
169                               break;
170                     case 'h':
171                               if (uid) {
172                                         errno = EPERM;
173                                         err(EXIT_FAILURE, "-h option");
174                               }
175                               if (domain && (p = strchr(optarg, '.')) != NULL &&
176                                   strcasecmp(p, domain) == 0)
177                                         *p = '\0';
178                               hostname = optarg;
179                               break;
180                     case 'p':
181                               pflag = 1;
182                               break;
183                     default:
184                     case '?':
185                               usage();
186                               break;
187                     }
188 
189           setproctitle(NULL);
190           argc -= optind;
191           argv += optind;
192 
193           if (*argv) {
194                     username = trimloginname(*argv);
195                     ask = 0;
196           } else
197                     ask = 1;
198 
199 #ifdef F_CLOSEM
200           (void)fcntl(3, F_CLOSEM, 0);
201 #else
202           for (cnt = getdtablesize(); cnt > 2; cnt--)
203                     (void)close(cnt);
204 #endif
205 
206           ttyn = ttyname(STDIN_FILENO);
207           if (ttyn == NULL || *ttyn == '\0') {
208                     (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
209                     ttyn = tname;
210           }
211           if ((tty = strstr(ttyn, "/pts/")) != NULL)
212                     ++tty;
213           else if ((tty = strrchr(ttyn, '/')) != NULL)
214                     ++tty;
215           else
216                     tty = ttyn;
217 
218           if (issetugid()) {
219                     nested = strdup(user_from_uid(getuid(), 0));
220                     if (nested == NULL) {
221                     syslog(LOG_ERR, "strdup: %m");
222                     sleepexit(EXIT_FAILURE);
223                     }
224           }
225 
226           /* Get "login-retries" and "login-backoff" from default class */
227           if ((lc = login_getclass(NULL)) != NULL) {
228                     login_retries = (int)login_getcapnum(lc, "login-retries",
229                         DEFAULT_RETRIES, DEFAULT_RETRIES);
230                     login_backoff = (int)login_getcapnum(lc, "login-backoff",
231                         DEFAULT_BACKOFF, DEFAULT_BACKOFF);
232                     login_close(lc);
233                     lc = NULL;
234           }
235 
236 
237           for (cnt = 0;; ask = 1) {
238                     if (ask) {
239                               fflag = 0;
240                               username = trimloginname(getloginname());
241                     }
242                     rootlogin = 0;
243                     auth_passed = 0;
244 
245                     /*
246                      * Note if trying multiple user names; log failures for
247                      * previous user name, but don't bother logging one failure
248                      * for nonexistent name (mistyped username).
249                      */
250                     if (failures && strcmp(tbuf, username)) {
251                               if (failures > (pwd ? 0 : 1))
252                                         badlogin(tbuf);
253                               failures = 0;
254                     }
255 
256 #define PAM_END(msg) do {                                                       \
257           syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));          \
258           warnx("%s: %s", msg, pam_strerror(pamh, pam_err));                    \
259           pam_end(pamh, pam_err);                                                         \
260           sleepexit(EXIT_FAILURE);                                              \
261 } while (0)
262 
263                     pam_err = pam_start("login", username, &pamc, &pamh);
264                     if (pam_err != PAM_SUCCESS) {
265                               if (pamh != NULL)
266                                         PAM_END("pam_start");
267                               /* Things went really bad... */
268                               syslog(LOG_ERR, "pam_start failed: %s",
269                                   pam_strerror(pamh, pam_err));
270                               errx(EXIT_FAILURE, "pam_start failed");
271                     }
272 
273 #define PAM_SET_ITEM(item, var)         do {                                              \
274           pam_err = pam_set_item(pamh, (item), (var));                          \
275           if (pam_err != PAM_SUCCESS)                                           \
276                     PAM_END("pam_set_item(" # item ")");                        \
277 } while (0)
278 
279                     /*
280                      * Fill hostname tty, and nested user
281                      */
282                     PAM_SET_ITEM(PAM_RHOST, hostname);
283                     PAM_SET_ITEM(PAM_TTY, tty);
284                     if (nested)
285                               PAM_SET_ITEM(PAM_NUSER, nested);
286                     if (have_ss)
287                               PAM_SET_ITEM(PAM_SOCKADDR, &ss);
288 
289                     /*
290                      * Don't check for errors, because we don't want to give
291                      * out any information.
292                      */
293                     pwd = NULL;
294                     (void)getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf), &pwd);
295 
296                     /*
297                      * Establish the class now, before we might goto
298                      * within the next block. pwd can be NULL since it
299                      * falls back to the "default" class if it is.
300                      */
301                     lc = login_getclass(pwd ? pwd->pw_class : NULL);
302 
303                     /*
304                      * if we have a valid account name, and it doesn't have a
305                      * password, or the -f option was specified and the caller
306                      * is root or the caller isn't changing their uid, don't
307                      * authenticate.
308                      */
309                     if (pwd) {
310                               if (pwd->pw_uid == 0)
311                                         rootlogin = 1;
312 
313                               if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
314                                         /* already authenticated */
315                                         auth_passed = 1;
316                                         goto skip_auth;
317                               }
318                     }
319 
320                     (void)setpriority(PRIO_PROCESS, 0, -4);
321 
322                     switch(pam_err = pam_authenticate(pamh, pam_silent)) {
323                     case PAM_SUCCESS:
324                               /*
325                                * PAM can change the user, refresh
326                                * username, pwd, and lc.
327                                */
328                               pam_err = pam_get_item(pamh, PAM_USER, &newuser);
329                               if (pam_err != PAM_SUCCESS)
330                                         PAM_END("pam_get_item(PAM_USER)");
331 
332                               username = newuser;
333                               /*
334                                * Don't check for errors, because we don't want to give
335                                * out any information.
336                                */
337                               pwd = NULL;
338                               (void)getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf),
339                                   &pwd);
340                               lc = login_getpwclass(pwd);
341                               auth_passed = 1;
342 
343                               switch (pam_err = pam_acct_mgmt(pamh, pam_silent)) {
344                               case PAM_SUCCESS:
345                                         break;
346 
347                               case PAM_NEW_AUTHTOK_REQD:
348                                         pam_err = pam_chauthtok(pamh,
349                                             pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
350 
351                                         if (pam_err != PAM_SUCCESS)
352                                                   PAM_END("pam_chauthtok");
353                                         break;
354 
355                               case PAM_AUTH_ERR:
356                               case PAM_USER_UNKNOWN:
357                               case PAM_MAXTRIES:
358                                         auth_passed = 0;
359                                         break;
360 
361                               default:
362                                         PAM_END("pam_acct_mgmt");
363                                         break;
364                               }
365                               break;
366 
367                     case PAM_AUTH_ERR:
368                     case PAM_USER_UNKNOWN:
369                     case PAM_MAXTRIES:
370                               auth_passed = 0;
371                               break;
372 
373                     default:
374                               PAM_END("pam_authenticate");
375                               break;
376                     }
377 
378                     (void)setpriority(PRIO_PROCESS, 0, 0);
379 
380 skip_auth:
381                     /*
382                      * If the user exists and authentication passed,
383                      * get out of the loop and login the user.
384                      */
385                     if (pwd && auth_passed)
386                               break;
387 
388                     (void)printf("Login incorrect or refused on this terminal.\n");
389                     failures++;
390                     cnt++;
391                     /*
392                      * We allow login_retries tries, but after login_backoff
393                      * we start backing off.  These default to 10 and 3
394                      * respectively.
395                      */
396                     if (cnt > login_backoff) {
397                               if (cnt >= login_retries) {
398                                         badlogin(username);
399                                         pam_end(pamh, PAM_SUCCESS);
400                                         sleepexit(EXIT_FAILURE);
401                               }
402                               sleep((u_int)((cnt - login_backoff) * 5));
403                     }
404           }
405 
406           /* committed to login -- turn off timeout */
407           (void)alarm((u_int)0);
408 
409           endpwent();
410 
411         quietlog = login_getcapbool(lc, "hushlogin", 0);
412 
413           /*
414            * Temporarily give up special privileges so we can change
415            * into NFS-mounted homes that are exported for non-root
416            * access and have mode 7x0
417            */
418           saved_uid = geteuid();
419           saved_gid = getegid();
420           nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
421 
422           (void)setegid(pwd->pw_gid);
423           if (initgroups(username, pwd->pw_gid) == -1) {
424                     syslog(LOG_ERR, "initgroups failed");
425                     pam_end(pamh, PAM_SUCCESS);
426                     exit(EXIT_FAILURE);
427           }
428           (void)seteuid(pwd->pw_uid);
429 
430           if (chdir(pwd->pw_dir) != 0) {
431                 if (login_getcapbool(lc, "requirehome", 0)) {
432                               (void)printf("Home directory %s required\n",
433                                   pwd->pw_dir);
434                               pam_end(pamh, PAM_SUCCESS);
435                         exit(EXIT_FAILURE);
436                     }
437 
438                     (void)printf("No home directory %s!\n", pwd->pw_dir);
439                     if (chdir("/") == -1) {
440                               pam_end(pamh, PAM_SUCCESS);
441                               exit(EXIT_FAILURE);
442                     }
443                     pwd->pw_dir = __UNCONST("/");
444                     (void)printf("Logging in with home = \"/\".\n");
445           }
446 
447           if (!quietlog) {
448                     quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
449                     pam_silent = quietlog ? PAM_SILENT : 0;
450           }
451 
452           /* regain special privileges */
453           (void)setegid(saved_gid);
454           (void)seteuid(saved_uid);
455           if (setgroups(nsaved_gids, saved_gids) == -1) {
456                     syslog(LOG_ERR, "setgroups failed: %m");
457                     pam_end(pamh, PAM_SUCCESS);
458                     exit(EXIT_FAILURE);
459           }
460 
461           (void)getgrnam_r(TTYGRPNAME, &grs, grbuf, sizeof(grbuf), &grp);
462           (void)chown(ttyn, pwd->pw_uid,
463               (grp != NULL) ? grp->gr_gid : pwd->pw_gid);
464 
465           if (ttyaction(ttyn, "login", pwd->pw_name))
466                     (void)printf("Warning: ttyaction failed.\n");
467 
468           /* Nothing else left to fail -- really log in. */
469         update_db(quietlog, rootlogin, fflag);
470 
471           if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
472               LOGIN_SETLOGIN) != 0) {
473                     syslog(LOG_ERR, "setusercontext failed");
474                     pam_end(pamh, PAM_SUCCESS);
475                     exit(EXIT_FAILURE);
476           }
477 
478           /*
479            * Establish groups
480            */
481           if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
482                     syslog(LOG_ERR, "setusercontext failed");
483                     pam_end(pamh, PAM_SUCCESS);
484                     exit(EXIT_FAILURE);
485           }
486 
487           pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
488           if (pam_err != PAM_SUCCESS)
489                     PAM_END("pam_setcred");
490 
491           pam_err = pam_open_session(pamh, pam_silent);
492           if (pam_err != PAM_SUCCESS)
493                     PAM_END("pam_open_session");
494 
495           /*
496            * Fork because we need to call pam_closesession as root.
497            * Make sure signals cannot kill the parent.
498            * This has been handled in the beginning of main.
499            */
500 
501           switch(pid = fork()) {
502           case -1:
503                     pam_err = pam_close_session(pamh, 0);
504                     if (pam_err != PAM_SUCCESS) {
505                               syslog(LOG_ERR, "pam_close_session: %s",
506                                   pam_strerror(pamh, pam_err));
507                               warnx("pam_close_session: %s",
508                                   pam_strerror(pamh, pam_err));
509                     }
510                     syslog(LOG_ERR, "fork failed: %m");
511                     warn("fork failed");
512                     pam_end(pamh, pam_err);
513                     exit(EXIT_FAILURE);
514                     break;
515 
516           case 0: /* Child */
517                     break;
518 
519           default:
520                     /*
521                      * Parent: wait for the child to terminate
522                      * and call pam_close_session.
523                      */
524                     if ((xpid = waitpid(pid, &status, 0)) != pid) {
525                               pam_err = pam_close_session(pamh, 0);
526                               if (pam_err != PAM_SUCCESS) {
527                                         syslog(LOG_ERR,
528                                             "pam_close_session: %s",
529                                             pam_strerror(pamh, pam_err));
530                                         warnx("pam_close_session: %s",
531                                             pam_strerror(pamh, pam_err));
532                               }
533                               pam_end(pamh, pam_err);
534                               if (xpid != -1)
535                                         warnx("wrong PID: %d != %d", pid, xpid);
536                               else
537                                         warn("wait for pid %d failed", pid);
538                               exit(EXIT_FAILURE);
539                     }
540 
541                     (void)signal(SIGABRT, oabrt);
542                     (void)signal(SIGALRM, oalrm);
543                     (void)signal(SIGINT, oint);
544                     (void)signal(SIGQUIT, oquit);
545                     if ((pam_err = pam_close_session(pamh, 0)) != PAM_SUCCESS) {
546                               syslog(LOG_ERR, "pam_close_session: %s",
547                                   pam_strerror(pamh, pam_err));
548                               warnx("pam_close_session: %s",
549                                   pam_strerror(pamh, pam_err));
550                     }
551                     pam_end(pamh, PAM_SUCCESS);
552                     exit(EXIT_SUCCESS);
553                     break;
554           }
555 
556           /*
557            * The child: starting here, we don't have to care about
558            * handling PAM issues if we exit, the parent will do the
559            * job when we exit.
560          *
561            * Destroy environment unless user has requested its preservation.
562            * Try to preserve TERM anyway.
563            */
564           saved_term = getenv("TERM");
565           if (!pflag) {
566                     environ = envinit;
567                     if (saved_term)
568                               setenv("TERM", saved_term, 0);
569           }
570 
571           if (*pwd->pw_shell == '\0')
572                     pwd->pw_shell = __UNCONST(_PATH_BSHELL);
573 
574           shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
575           if (*shell == '\0')
576                     shell = pwd->pw_shell;
577 
578           if ((pwd->pw_shell = strdup(shell)) == NULL) {
579                     syslog(LOG_ERR, "Cannot alloc mem");
580                     exit(EXIT_FAILURE);
581           }
582 
583           (void)setenv("HOME", pwd->pw_dir, 1);
584           (void)setenv("SHELL", pwd->pw_shell, 1);
585           if (term[0] == '\0') {
586                     const char *tt = stypeof(tty);
587 
588                     if (tt == NULL)
589                               tt = login_getcapstr(lc, "term", NULL, NULL);
590 
591                     /* unknown term -> "su" */
592                     (void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term));
593           }
594           (void)setenv("TERM", term, 0);
595           (void)setenv("LOGNAME", pwd->pw_name, 1);
596           (void)setenv("USER", pwd->pw_name, 1);
597 
598           /*
599            * Add PAM environement
600            */
601           if ((pamenv = pam_getenvlist(pamh)) != NULL) {
602                     char **envitem;
603 
604                     for (envitem = pamenv; *envitem; envitem++) {
605                               if (putenv(*envitem) == -1)
606                                         free(*envitem);
607                     }
608 
609                     free(pamenv);
610           }
611 
612           /* This drops root privs */
613           if (setusercontext(lc, pwd, pwd->pw_uid,
614               (LOGIN_SETALL & ~LOGIN_SETLOGIN)) != 0) {
615                     syslog(LOG_ERR, "setusercontext failed");
616                     exit(EXIT_FAILURE);
617           }
618 
619           if (!quietlog) {
620                     const char *fname;
621 
622                     fname = login_getcapstr(lc, "copyright", NULL, NULL);
623                     if (fname != NULL && access(fname, F_OK) == 0)
624                               motd(fname);
625                     else
626                               (void)printf("%s", copyrightstr);
627 
628                 fname = login_getcapstr(lc, "welcome", NULL, NULL);
629                 if (fname == NULL || access(fname, F_OK) != 0)
630                         fname = _PATH_MOTDFILE;
631                 motd(fname);
632 
633                     (void)snprintf(tbuf,
634                         sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
635                     if (stat(tbuf, &st) == 0 && st.st_size != 0)
636                               (void)printf("You have %smail.\n",
637                                   (st.st_mtime > st.st_atime) ? "new " : "");
638           }
639 
640           login_close(lc);
641 
642 
643           tbuf[0] = '-';
644           (void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
645               p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
646 
647           (void)signal(SIGABRT, oabrt);
648           (void)signal(SIGALRM, oalrm);
649           (void)signal(SIGINT, oint);
650           (void)signal(SIGQUIT, oquit);
651           (void)signal(SIGTSTP, SIG_IGN);
652 
653           execlp(pwd->pw_shell, tbuf, NULL);
654           err(EXIT_FAILURE, "%s", pwd->pw_shell);
655 }
656 
657 static void
usage(void)658 usage(void)
659 {
660           (void)fprintf(stderr,
661               "Usage: %s [-fp] [-a address] [-h hostname] [username]\n",
662               getprogname());
663           exit(EXIT_FAILURE);
664 }
665 
666 #if 0
667 static int
668 rootterm(char *ttyn)
669 {
670           struct ttyent *t;
671 
672           return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
673 }
674 #endif
675