]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/login/login.c
Update libucl to git version 8d3b186
[FreeBSD/FreeBSD.git] / usr.bin / login / login.c
1 /*-
2  * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2002 Networks Associates Technologies, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software were developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40
41 #if 0
42 #ifndef lint
43 static char sccsid[] = "@(#)login.c     8.4 (Berkeley) 4/2/94";
44 #endif
45 #endif
46
47 #include <sys/cdefs.h>
48 __FBSDID("$FreeBSD$");
49
50 /*
51  * login [ name ]
52  * login -h hostname    (for telnetd, etc.)
53  * login -f name        (for pre-authenticated login: datakit, xterm, etc.)
54  */
55
56 #include <sys/param.h>
57 #include <sys/file.h>
58 #include <sys/stat.h>
59 #include <sys/time.h>
60 #include <sys/resource.h>
61 #include <sys/wait.h>
62
63 #include <err.h>
64 #include <errno.h>
65 #include <grp.h>
66 #include <libutil.h>
67 #include <login_cap.h>
68 #include <pwd.h>
69 #include <setjmp.h>
70 #include <signal.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <syslog.h>
75 #include <ttyent.h>
76 #include <unistd.h>
77
78 #include <security/pam_appl.h>
79 #include <security/openpam.h>
80
81 #include "login.h"
82 #include "pathnames.h"
83
84 static int               auth_pam(void);
85 static void              bail(int, int);
86 static void              bail_internal(int, int, int);
87 static int               export(const char *);
88 static void              export_pam_environment(void);
89 static int               motd(const char *);
90 static void              badlogin(char *);
91 static char             *getloginname(void);
92 static void              pam_syslog(const char *);
93 static void              pam_cleanup(void);
94 static void              refused(const char *, const char *, int);
95 static const char       *stypeof(char *);
96 static void              sigint(int);
97 static void              timedout(int);
98 static void              bail_sig(int);
99 static void              usage(void);
100
101 #define TTYGRPNAME              "tty"                   /* group to own ttys */
102 #define DEFAULT_BACKOFF         3
103 #define DEFAULT_RETRIES         10
104 #define DEFAULT_PROMPT          "login: "
105 #define DEFAULT_PASSWD_PROMPT   "Password:"
106 #define TERM_UNKNOWN            "su"
107 #define DEFAULT_WARN            (2L * 7L * 86400L)      /* Two weeks */
108 #define NO_SLEEP_EXIT           0
109 #define SLEEP_EXIT              5
110
111 /*
112  * This bounds the time given to login.  Not a define so it can
113  * be patched on machines where it's too small.
114  */
115 static u_int            timeout = 300;
116
117 /* Buffer for signal handling of timeout */
118 static jmp_buf           timeout_buf;
119
120 struct passwd           *pwd;
121 static int               failures;
122
123 static char             *envinit[1];    /* empty environment list */
124
125 /*
126  * Command line flags and arguments
127  */
128 static int               fflag;         /* -f: do not perform authentication */
129 static int               hflag;         /* -h: login from remote host */
130 static char             *hostname;      /* hostname from command line */
131 static int               pflag;         /* -p: preserve environment */
132
133 /*
134  * User name
135  */
136 static char             *username;      /* user name */
137 static char             *olduser;       /* previous user name */
138
139 /*
140  * Prompts
141  */
142 static char              default_prompt[] = DEFAULT_PROMPT;
143 static const char       *prompt;
144 static char              default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
145 static const char       *passwd_prompt;
146
147 static char             *tty;
148
149 /*
150  * PAM data
151  */
152 static pam_handle_t     *pamh = NULL;
153 static struct pam_conv   pamc = { openpam_ttyconv, NULL };
154 static int               pam_err;
155 static int               pam_silent = PAM_SILENT;
156 static int               pam_cred_established;
157 static int               pam_session_established;
158
159 int
160 main(int argc, char *argv[])
161 {
162         struct group *gr;
163         struct stat st;
164         int retries, backoff;
165         int ask, ch, cnt, quietlog, rootlogin, rval;
166         uid_t uid, euid;
167         gid_t egid;
168         char *term;
169         char *p, *ttyn;
170         char tname[sizeof(_PATH_TTY) + 10];
171         char *arg0;
172         const char *tp;
173         const char *shell = NULL;
174         login_cap_t *lc = NULL;
175         login_cap_t *lc_user = NULL;
176         pid_t pid;
177         sigset_t mask, omask;
178         struct sigaction sa;
179 #ifdef USE_BSM_AUDIT
180         char auditsuccess = 1;
181 #endif
182
183         sa.sa_flags = SA_RESTART;
184         (void)sigfillset(&sa.sa_mask);
185         sa.sa_handler = SIG_IGN;
186         (void)sigaction(SIGQUIT, &sa, NULL);
187         (void)sigaction(SIGINT, &sa, NULL);
188         (void)sigaction(SIGHUP, &sa, NULL);
189         if (setjmp(timeout_buf)) {
190                 if (failures)
191                         badlogin(username);
192                 (void)fprintf(stderr, "Login timed out after %d seconds\n",
193                     timeout);
194                 bail(NO_SLEEP_EXIT, 0);
195         }
196         sa.sa_handler = timedout;
197         (void)sigaction(SIGALRM, &sa, NULL);
198         (void)alarm(timeout);
199         (void)setpriority(PRIO_PROCESS, 0, 0);
200
201         openlog("login", 0, LOG_AUTH);
202
203         uid = getuid();
204         euid = geteuid();
205         egid = getegid();
206
207         while ((ch = getopt(argc, argv, "fh:p")) != -1)
208                 switch (ch) {
209                 case 'f':
210                         fflag = 1;
211                         break;
212                 case 'h':
213                         if (uid != 0)
214                                 errx(1, "-h option: %s", strerror(EPERM));
215                         if (strlen(optarg) >= MAXHOSTNAMELEN)
216                                 errx(1, "-h option: %s: exceeds maximum "
217                                     "hostname size", optarg);
218                         hflag = 1;
219                         hostname = optarg;
220                         break;
221                 case 'p':
222                         pflag = 1;
223                         break;
224                 case '?':
225                 default:
226                         if (uid == 0)
227                                 syslog(LOG_ERR, "invalid flag %c", ch);
228                         usage();
229                 }
230         argc -= optind;
231         argv += optind;
232
233         if (argc > 0) {
234                 username = strdup(*argv);
235                 if (username == NULL)
236                         err(1, "strdup()");
237                 ask = 0;
238         } else {
239                 ask = 1;
240         }
241
242         setproctitle("-%s", getprogname());
243
244         closefrom(3);
245
246         /*
247          * Get current TTY
248          */
249         ttyn = ttyname(STDIN_FILENO);
250         if (ttyn == NULL || *ttyn == '\0') {
251                 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
252                 ttyn = tname;
253         }
254         if (strncmp(ttyn, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
255                 tty = ttyn + sizeof _PATH_DEV - 1;
256         else
257                 tty = ttyn;
258
259         /*
260          * Get "login-retries" & "login-backoff" from default class
261          */
262         lc = login_getclass(NULL);
263         prompt = login_getcapstr(lc, "login_prompt",
264             default_prompt, default_prompt);
265         passwd_prompt = login_getcapstr(lc, "passwd_prompt",
266             default_passwd_prompt, default_passwd_prompt);
267         retries = login_getcapnum(lc, "login-retries",
268             DEFAULT_RETRIES, DEFAULT_RETRIES);
269         backoff = login_getcapnum(lc, "login-backoff",
270             DEFAULT_BACKOFF, DEFAULT_BACKOFF);
271         login_close(lc);
272         lc = NULL;
273
274         /*
275          * Try to authenticate the user until we succeed or time out.
276          */
277         for (cnt = 0;; ask = 1) {
278                 if (ask) {
279                         fflag = 0;
280                         if (olduser != NULL)
281                                 free(olduser);
282                         olduser = username;
283                         username = getloginname();
284                 }
285                 rootlogin = 0;
286
287                 /*
288                  * Note if trying multiple user names; log failures for
289                  * previous user name, but don't bother logging one failure
290                  * for nonexistent name (mistyped username).
291                  */
292                 if (failures && strcmp(olduser, username) != 0) {
293                         if (failures > (pwd ? 0 : 1))
294                                 badlogin(olduser);
295                 }
296
297                 /*
298                  * Load the PAM policy and set some variables
299                  */
300                 pam_err = pam_start("login", username, &pamc, &pamh);
301                 if (pam_err != PAM_SUCCESS) {
302                         pam_syslog("pam_start()");
303 #ifdef USE_BSM_AUDIT
304                         au_login_fail("PAM Error", 1);
305 #endif
306                         bail(NO_SLEEP_EXIT, 1);
307                 }
308                 pam_err = pam_set_item(pamh, PAM_TTY, tty);
309                 if (pam_err != PAM_SUCCESS) {
310                         pam_syslog("pam_set_item(PAM_TTY)");
311 #ifdef USE_BSM_AUDIT
312                         au_login_fail("PAM Error", 1);
313 #endif
314                         bail(NO_SLEEP_EXIT, 1);
315                 }
316                 pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
317                 if (pam_err != PAM_SUCCESS) {
318                         pam_syslog("pam_set_item(PAM_RHOST)");
319 #ifdef USE_BSM_AUDIT
320                         au_login_fail("PAM Error", 1);
321 #endif
322                         bail(NO_SLEEP_EXIT, 1);
323                 }
324
325                 pwd = getpwnam(username);
326                 if (pwd != NULL && pwd->pw_uid == 0)
327                         rootlogin = 1;
328
329                 /*
330                  * If the -f option was specified and the caller is
331                  * root or the caller isn't changing their uid, don't
332                  * authenticate.
333                  */
334                 if (pwd != NULL && fflag &&
335                     (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) {
336                         /* already authenticated */
337                         rval = 0;
338 #ifdef USE_BSM_AUDIT
339                         auditsuccess = 0; /* opened a terminal window only */
340 #endif
341                 } else {
342                         fflag = 0;
343                         (void)setpriority(PRIO_PROCESS, 0, -4);
344                         rval = auth_pam();
345                         (void)setpriority(PRIO_PROCESS, 0, 0);
346                 }
347
348                 if (pwd && rval == 0)
349                         break;
350
351                 pam_cleanup();
352
353                 /*
354                  * We are not exiting here, but this corresponds to a failed
355                  * login event, so set exitstatus to 1.
356                  */
357 #ifdef USE_BSM_AUDIT
358                 au_login_fail("Login incorrect", 1);
359 #endif
360
361                 (void)printf("Login incorrect\n");
362                 failures++;
363
364                 pwd = NULL;
365
366                 /*
367                  * Allow up to 'retry' (10) attempts, but start
368                  * backing off after 'backoff' (3) attempts.
369                  */
370                 if (++cnt > backoff) {
371                         if (cnt >= retries) {
372                                 badlogin(username);
373                                 bail(SLEEP_EXIT, 1);
374                         }
375                         sleep((u_int)((cnt - backoff) * 5));
376                 }
377         }
378
379         /* committed to login -- turn off timeout */
380         (void)alarm((u_int)0);
381
382         (void)sigemptyset(&mask);
383         (void)sigaddset(&mask, SIGHUP);
384         (void)sigaddset(&mask, SIGTERM);
385         (void)sigprocmask(SIG_BLOCK, &mask, &omask);
386         sa.sa_handler = bail_sig;
387         (void)sigaction(SIGHUP, &sa, NULL);
388         (void)sigaction(SIGTERM, &sa, NULL);
389
390         endpwent();
391
392 #ifdef USE_BSM_AUDIT
393         /* Audit successful login. */
394         if (auditsuccess)
395                 au_login_success();
396 #endif
397
398         /*
399          * This needs to happen before login_getpwclass to support
400          * home directories on GSS-API authenticated NFS where the
401          * kerberos credentials need to be saved so that the kernel
402          * can authenticate to the NFS server.
403          */
404         pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
405         if (pam_err != PAM_SUCCESS) {
406                 pam_syslog("pam_setcred()");
407                 bail(NO_SLEEP_EXIT, 1);
408         }
409         pam_cred_established = 1;
410
411         /*
412          * Establish the login class.
413          */
414         lc = login_getpwclass(pwd);
415         lc_user = login_getuserclass(pwd);
416
417         if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0)))
418                 quietlog = login_getcapbool(lc, "hushlogin", 0);
419
420         /*
421          * Switching needed for NFS with root access disabled.
422          *
423          * XXX: This change fails to modify the additional groups for the
424          * process, and as such, may restrict rights normally granted
425          * through those groups.
426          */
427         (void)setegid(pwd->pw_gid);
428         (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
429         if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
430                 if (login_getcapbool(lc, "requirehome", 0))
431                         refused("Home directory not available", "HOMEDIR", 1);
432                 if (chdir("/") < 0)
433                         refused("Cannot find root directory", "ROOTDIR", 1);
434                 if (!quietlog || *pwd->pw_dir)
435                         printf("No home directory.\nLogging in with home = \"/\".\n");
436                 pwd->pw_dir = strdup("/");
437                 if (pwd->pw_dir == NULL) {
438                         syslog(LOG_NOTICE, "strdup(): %m");
439                         bail(SLEEP_EXIT, 1);
440                 }
441         }
442         (void)seteuid(euid);
443         (void)setegid(egid);
444         if (!quietlog) {
445                 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
446                 if (!quietlog)
447                         pam_silent = 0;
448         }
449
450         shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
451         if (*pwd->pw_shell == '\0')
452                 pwd->pw_shell = strdup(_PATH_BSHELL);
453         if (pwd->pw_shell == NULL) {
454                 syslog(LOG_NOTICE, "strdup(): %m");
455                 bail(SLEEP_EXIT, 1);
456         }
457         if (*shell == '\0')   /* Not overridden */
458                 shell = pwd->pw_shell;
459         if ((shell = strdup(shell)) == NULL) {
460                 syslog(LOG_NOTICE, "strdup(): %m");
461                 bail(SLEEP_EXIT, 1);
462         }
463
464         /*
465          * Set device protections, depending on what terminal the
466          * user is logged in. This feature is used on Suns to give
467          * console users better privacy.
468          */
469         login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
470
471         /*
472          * Clear flags of the tty.  None should be set, and when the
473          * user sets them otherwise, this can cause the chown to fail.
474          * Since it isn't clear that flags are useful on character
475          * devices, we just clear them.
476          *
477          * We don't log in the case of EOPNOTSUPP because dev might be
478          * on NFS, which doesn't support chflags.
479          *
480          * We don't log in the EROFS because that means that /dev is on
481          * a read only file system and we assume that the permissions there
482          * are sane.
483          */
484         if (ttyn != tname && chflags(ttyn, 0))
485                 if (errno != EOPNOTSUPP && errno != EROFS)
486                         syslog(LOG_ERR, "chflags(%s): %m", ttyn);
487         if (ttyn != tname && chown(ttyn, pwd->pw_uid,
488             (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
489                 if (errno != EROFS)
490                         syslog(LOG_ERR, "chown(%s): %m", ttyn);
491
492 #ifdef LOGALL
493         /*
494          * Syslog each successful login, so we don't have to watch
495          * hundreds of wtmp or lastlogin files.
496          */
497         if (hflag)
498                 syslog(LOG_INFO, "login from %s on %s as %s",
499                        hostname, tty, pwd->pw_name);
500         else
501                 syslog(LOG_INFO, "login on %s as %s",
502                        tty, pwd->pw_name);
503 #endif
504
505         /*
506          * If fflag is on, assume caller/authenticator has logged root
507          * login.
508          */
509         if (rootlogin && fflag == 0) {
510                 if (hflag)
511                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
512                             username, tty, hostname);
513                 else
514                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
515                             username, tty);
516         }
517
518         /*
519          * Destroy environment unless user has requested its
520          * preservation - but preserve TERM in all cases
521          */
522         term = getenv("TERM");
523         if (!pflag)
524                 environ = envinit;
525         if (term != NULL)
526                 setenv("TERM", term, 0);
527
528         /*
529          * PAM modules might add supplementary groups during pam_setcred().
530          */
531         if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
532                 syslog(LOG_ERR, "setusercontext() failed - exiting");
533                 bail(NO_SLEEP_EXIT, 1);
534         }
535
536         pam_err = pam_setcred(pamh, pam_silent|PAM_REINITIALIZE_CRED);
537         if (pam_err != PAM_SUCCESS) {
538                 pam_syslog("pam_setcred()");
539                 bail(NO_SLEEP_EXIT, 1);
540         }
541
542         pam_err = pam_open_session(pamh, pam_silent);
543         if (pam_err != PAM_SUCCESS) {
544                 pam_syslog("pam_open_session()");
545                 bail(NO_SLEEP_EXIT, 1);
546         }
547         pam_session_established = 1;
548
549         /*
550          * We must fork() before setuid() because we need to call
551          * pam_close_session() as root.
552          */
553         pid = fork();
554         if (pid < 0) {
555                 err(1, "fork");
556         } else if (pid != 0) {
557                 /*
558                  * Parent: wait for child to finish, then clean up
559                  * session.
560                  *
561                  * If we get SIGHUP or SIGTERM, clean up the session
562                  * and exit right away. This will make the terminal
563                  * inaccessible and send SIGHUP to the foreground
564                  * process group.
565                  */
566                 int status;
567                 setproctitle("-%s [pam]", getprogname());
568                 (void)sigprocmask(SIG_SETMASK, &omask, NULL);
569                 waitpid(pid, &status, 0);
570                 (void)sigprocmask(SIG_BLOCK, &mask, NULL);
571                 bail(NO_SLEEP_EXIT, 0);
572         }
573
574         /*
575          * NOTICE: We are now in the child process!
576          */
577
578         /*
579          * Add any environment variables the PAM modules may have set.
580          */
581         export_pam_environment();
582
583         /*
584          * We're done with PAM now; our parent will deal with the rest.
585          */
586         pam_end(pamh, 0);
587         pamh = NULL;
588
589         /*
590          * We don't need to be root anymore, so set the login name and
591          * the UID.
592          */
593         if (setlogin(username) != 0) {
594                 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
595                 bail(NO_SLEEP_EXIT, 1);
596         }
597         if (setusercontext(lc, pwd, pwd->pw_uid,
598             LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
599                 syslog(LOG_ERR, "setusercontext() failed - exiting");
600                 exit(1);
601         }
602
603         (void)setenv("SHELL", pwd->pw_shell, 1);
604         (void)setenv("HOME", pwd->pw_dir, 1);
605         /* Overwrite "term" from login.conf(5) for any known TERM */
606         if (term == NULL && (tp = stypeof(tty)) != NULL)
607                 (void)setenv("TERM", tp, 1);
608         else
609                 (void)setenv("TERM", TERM_UNKNOWN, 0);
610         (void)setenv("LOGNAME", username, 1);
611         (void)setenv("USER", username, 1);
612         (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
613
614         if (!quietlog) {
615                 const char *cw;
616
617                 cw = login_getcapstr(lc, "welcome", NULL, NULL);
618                 if (cw != NULL && access(cw, F_OK) == 0)
619                         motd(cw);
620                 else
621                         motd(_PATH_MOTDFILE);
622
623                 if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 &&
624                     login_getcapbool(lc, "nocheckmail", 0) == 0) {
625                         char *cx;
626
627                         /* $MAIL may have been set by class. */
628                         cx = getenv("MAIL");
629                         if (cx == NULL) {
630                                 asprintf(&cx, "%s/%s",
631                                     _PATH_MAILDIR, pwd->pw_name);
632                         }
633                         if (cx && stat(cx, &st) == 0 && st.st_size != 0)
634                                 (void)printf("You have %smail.\n",
635                                     (st.st_mtime > st.st_atime) ? "new " : "");
636                         if (getenv("MAIL") == NULL)
637                                 free(cx);
638                 }
639         }
640
641         login_close(lc_user);
642         login_close(lc);
643
644         sa.sa_handler = SIG_DFL;
645         (void)sigaction(SIGALRM, &sa, NULL);
646         (void)sigaction(SIGQUIT, &sa, NULL);
647         (void)sigaction(SIGINT, &sa, NULL);
648         (void)sigaction(SIGTERM, &sa, NULL);
649         (void)sigaction(SIGHUP, &sa, NULL);
650         sa.sa_handler = SIG_IGN;
651         (void)sigaction(SIGTSTP, &sa, NULL);
652         (void)sigprocmask(SIG_SETMASK, &omask, NULL);
653
654         /*
655          * Login shells have a leading '-' in front of argv[0]
656          */
657         p = strrchr(pwd->pw_shell, '/');
658         if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
659                 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
660                     username);
661                 errx(1, "shell exceeds maximum pathname size");
662         } else if (arg0 == NULL) {
663                 err(1, "asprintf()");
664         }
665
666         execlp(shell, arg0, (char *)0);
667         err(1, "%s", shell);
668
669         /*
670          * That's it, folks!
671          */
672 }
673
674 /*
675  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
676  * authenticated, or 1 if not authenticated.  If some sort of PAM system
677  * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
678  * function returns -1.  This can be used as an indication that we should
679  * fall back to a different authentication mechanism.
680  */
681 static int
682 auth_pam(void)
683 {
684         const char *tmpl_user;
685         const void *item;
686         int rval;
687
688         pam_err = pam_authenticate(pamh, pam_silent);
689         switch (pam_err) {
690
691         case PAM_SUCCESS:
692                 /*
693                  * With PAM we support the concept of a "template"
694                  * user.  The user enters a login name which is
695                  * authenticated by PAM, usually via a remote service
696                  * such as RADIUS or TACACS+.  If authentication
697                  * succeeds, a different but related "template" name
698                  * is used for setting the credentials, shell, and
699                  * home directory.  The name the user enters need only
700                  * exist on the remote authentication server, but the
701                  * template name must be present in the local password
702                  * database.
703                  *
704                  * This is supported by two various mechanisms in the
705                  * individual modules.  However, from the application's
706                  * point of view, the template user is always passed
707                  * back as a changed value of the PAM_USER item.
708                  */
709                 pam_err = pam_get_item(pamh, PAM_USER, &item);
710                 if (pam_err == PAM_SUCCESS) {
711                         tmpl_user = (const char *)item;
712                         if (strcmp(username, tmpl_user) != 0)
713                                 pwd = getpwnam(tmpl_user);
714                 } else {
715                         pam_syslog("pam_get_item(PAM_USER)");
716                 }
717                 rval = 0;
718                 break;
719
720         case PAM_AUTH_ERR:
721         case PAM_USER_UNKNOWN:
722         case PAM_MAXTRIES:
723                 rval = 1;
724                 break;
725
726         default:
727                 pam_syslog("pam_authenticate()");
728                 rval = -1;
729                 break;
730         }
731
732         if (rval == 0) {
733                 pam_err = pam_acct_mgmt(pamh, pam_silent);
734                 switch (pam_err) {
735                 case PAM_SUCCESS:
736                         break;
737                 case PAM_NEW_AUTHTOK_REQD:
738                         pam_err = pam_chauthtok(pamh,
739                             pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
740                         if (pam_err != PAM_SUCCESS) {
741                                 pam_syslog("pam_chauthtok()");
742                                 rval = 1;
743                         }
744                         break;
745                 default:
746                         pam_syslog("pam_acct_mgmt()");
747                         rval = 1;
748                         break;
749                 }
750         }
751
752         if (rval != 0) {
753                 pam_end(pamh, pam_err);
754                 pamh = NULL;
755         }
756         return (rval);
757 }
758
759 /*
760  * Export any environment variables PAM modules may have set
761  */
762 static void
763 export_pam_environment(void)
764 {
765         char **pam_env;
766         char **pp;
767
768         pam_env = pam_getenvlist(pamh);
769         if (pam_env != NULL) {
770                 for (pp = pam_env; *pp != NULL; pp++) {
771                         (void)export(*pp);
772                         free(*pp);
773                 }
774         }
775 }
776
777 /*
778  * Perform sanity checks on an environment variable:
779  * - Make sure there is an '=' in the string.
780  * - Make sure the string doesn't run on too long.
781  * - Do not export certain variables.  This list was taken from the
782  *   Solaris pam_putenv(3) man page.
783  * Then export it.
784  */
785 static int
786 export(const char *s)
787 {
788         static const char *noexport[] = {
789                 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
790                 "IFS", "PATH", NULL
791         };
792         char *p;
793         const char **pp;
794         size_t n;
795
796         if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL)
797                 return (0);
798         if (strncmp(s, "LD_", 3) == 0)
799                 return (0);
800         for (pp = noexport; *pp != NULL; pp++) {
801                 n = strlen(*pp);
802                 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
803                         return (0);
804         }
805         *p = '\0';
806         (void)setenv(s, p + 1, 1);
807         *p = '=';
808         return (1);
809 }
810
811 static void
812 usage(void)
813 {
814
815         (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
816         exit(1);
817 }
818
819 /*
820  * Prompt user and read login name from stdin.
821  */
822 static char *
823 getloginname(void)
824 {
825         char *nbuf, *p;
826         int ch;
827
828         nbuf = malloc(MAXLOGNAME);
829         if (nbuf == NULL)
830                 err(1, "malloc()");
831         do {
832                 (void)printf("%s", prompt);
833                 for (p = nbuf; (ch = getchar()) != '\n'; ) {
834                         if (ch == EOF) {
835                                 badlogin(username);
836                                 bail(NO_SLEEP_EXIT, 0);
837                         }
838                         if (p < nbuf + MAXLOGNAME - 1)
839                                 *p++ = ch;
840                 }
841         } while (p == nbuf);
842
843         *p = '\0';
844         if (nbuf[0] == '-') {
845                 pam_silent = 0;
846                 memmove(nbuf, nbuf + 1, strlen(nbuf));
847         } else {
848                 pam_silent = PAM_SILENT;
849         }
850         return nbuf;
851 }
852
853 /*
854  * SIGINT handler for motd().
855  */
856 static volatile int motdinterrupt;
857 static void
858 sigint(int signo __unused)
859 {
860         motdinterrupt = 1;
861 }
862
863 /*
864  * Display the contents of a file (such as /etc/motd).
865  */
866 static int
867 motd(const char *motdfile)
868 {
869         struct sigaction newint, oldint;
870         FILE *f;
871         int ch;
872
873         if ((f = fopen(motdfile, "r")) == NULL)
874                 return (-1);
875         motdinterrupt = 0;
876         newint.sa_handler = sigint;
877         newint.sa_flags = 0;
878         sigfillset(&newint.sa_mask);
879         sigaction(SIGINT, &newint, &oldint);
880         while ((ch = fgetc(f)) != EOF && !motdinterrupt)
881                 putchar(ch);
882         sigaction(SIGINT, &oldint, NULL);
883         if (ch != EOF || ferror(f)) {
884                 fclose(f);
885                 return (-1);
886         }
887         fclose(f);
888         return (0);
889 }
890
891 /*
892  * SIGALRM handler, to enforce login prompt timeout.
893  *
894  * XXX This can potentially confuse the hell out of PAM.  We should
895  * XXX instead implement a conversation function that returns
896  * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
897  * XXX handler just set a flag.
898  */
899 static void
900 timedout(int signo __unused)
901 {
902
903         longjmp(timeout_buf, signo);
904 }
905
906 static void
907 badlogin(char *name)
908 {
909
910         if (failures == 0)
911                 return;
912         if (hflag) {
913                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
914                     failures, failures > 1 ? "S" : "", hostname);
915                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
916                     "%d LOGIN FAILURE%s FROM %s, %s",
917                     failures, failures > 1 ? "S" : "", hostname, name);
918         } else {
919                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
920                     failures, failures > 1 ? "S" : "", tty);
921                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
922                     "%d LOGIN FAILURE%s ON %s, %s",
923                     failures, failures > 1 ? "S" : "", tty, name);
924         }
925         failures = 0;
926 }
927
928 const char *
929 stypeof(char *ttyid)
930 {
931         struct ttyent *t;
932
933         if (ttyid != NULL && *ttyid != '\0') {
934                 t = getttynam(ttyid);
935                 if (t != NULL && t->ty_type != NULL)
936                         return (t->ty_type);
937         }
938         return (NULL);
939 }
940
941 static void
942 refused(const char *msg, const char *rtype, int lout)
943 {
944
945         if (msg != NULL)
946             printf("%s.\n", msg);
947         if (hflag)
948                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
949                     pwd->pw_name, rtype, hostname, tty);
950         else
951                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
952                     pwd->pw_name, rtype, tty);
953         if (lout)
954                 bail(SLEEP_EXIT, 1);
955 }
956
957 /*
958  * Log a PAM error
959  */
960 static void
961 pam_syslog(const char *msg)
962 {
963         syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
964 }
965
966 /*
967  * Shut down PAM
968  */
969 static void
970 pam_cleanup(void)
971 {
972
973         if (pamh != NULL) {
974                 if (pam_session_established) {
975                         pam_err = pam_close_session(pamh, 0);
976                         if (pam_err != PAM_SUCCESS)
977                                 pam_syslog("pam_close_session()");
978                 }
979                 pam_session_established = 0;
980                 if (pam_cred_established) {
981                         pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
982                         if (pam_err != PAM_SUCCESS)
983                                 pam_syslog("pam_setcred()");
984                 }
985                 pam_cred_established = 0;
986                 pam_end(pamh, pam_err);
987                 pamh = NULL;
988         }
989 }
990
991 static void
992 bail_internal(int sec, int eval, int signo)
993 {
994         struct sigaction sa;
995
996         pam_cleanup();
997 #ifdef USE_BSM_AUDIT
998         if (pwd != NULL)
999                 audit_logout();
1000 #endif
1001         (void)sleep(sec);
1002         if (signo == 0)
1003                 exit(eval);
1004         else {
1005                 sa.sa_handler = SIG_DFL;
1006                 sa.sa_flags = 0;
1007                 (void)sigemptyset(&sa.sa_mask);
1008                 (void)sigaction(signo, &sa, NULL);
1009                 (void)sigaddset(&sa.sa_mask, signo);
1010                 (void)sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
1011                 raise(signo);
1012                 exit(128 + signo);
1013         }
1014 }
1015
1016 /*
1017  * Exit, optionally after sleeping a few seconds
1018  */
1019 static void
1020 bail(int sec, int eval)
1021 {
1022         bail_internal(sec, eval, 0);
1023 }
1024
1025 /*
1026  * Exit because of a signal.
1027  * This is not async-signal safe, so only call async-signal safe functions
1028  * while the signal is unmasked.
1029  */
1030 static void
1031 bail_sig(int signo)
1032 {
1033         bail_internal(NO_SLEEP_EXIT, 0, signo);
1034 }