]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.bin/login/login.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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", LOG_ODELAY, 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         /*
493          * Exclude cons/vt/ptys only, assume dialup otherwise
494          * TODO: Make dialup tty determination a library call
495          * for consistency (finger etc.)
496          */
497         if (hflag && isdialuptty(tty))
498                 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
499
500 #ifdef LOGALL
501         /*
502          * Syslog each successful login, so we don't have to watch
503          * hundreds of wtmp or lastlogin files.
504          */
505         if (hflag)
506                 syslog(LOG_INFO, "login from %s on %s as %s",
507                        hostname, tty, pwd->pw_name);
508         else
509                 syslog(LOG_INFO, "login on %s as %s",
510                        tty, pwd->pw_name);
511 #endif
512
513         /*
514          * If fflag is on, assume caller/authenticator has logged root
515          * login.
516          */
517         if (rootlogin && fflag == 0) {
518                 if (hflag)
519                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
520                             username, tty, hostname);
521                 else
522                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
523                             username, tty);
524         }
525
526         /*
527          * Destroy environment unless user has requested its
528          * preservation - but preserve TERM in all cases
529          */
530         term = getenv("TERM");
531         if (!pflag)
532                 environ = envinit;
533         if (term != NULL)
534                 setenv("TERM", term, 0);
535
536         /*
537          * PAM modules might add supplementary groups during pam_setcred().
538          */
539         if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
540                 syslog(LOG_ERR, "setusercontext() failed - exiting");
541                 bail(NO_SLEEP_EXIT, 1);
542         }
543
544         pam_err = pam_setcred(pamh, pam_silent|PAM_REINITIALIZE_CRED);
545         if (pam_err != PAM_SUCCESS) {
546                 pam_syslog("pam_setcred()");
547                 bail(NO_SLEEP_EXIT, 1);
548         }
549
550         pam_err = pam_open_session(pamh, pam_silent);
551         if (pam_err != PAM_SUCCESS) {
552                 pam_syslog("pam_open_session()");
553                 bail(NO_SLEEP_EXIT, 1);
554         }
555         pam_session_established = 1;
556
557         /*
558          * We must fork() before setuid() because we need to call
559          * pam_close_session() as root.
560          */
561         pid = fork();
562         if (pid < 0) {
563                 err(1, "fork");
564         } else if (pid != 0) {
565                 /*
566                  * Parent: wait for child to finish, then clean up
567                  * session.
568                  *
569                  * If we get SIGHUP or SIGTERM, clean up the session
570                  * and exit right away. This will make the terminal
571                  * inaccessible and send SIGHUP to the foreground
572                  * process group.
573                  */
574                 int status;
575                 setproctitle("-%s [pam]", getprogname());
576                 (void)sigprocmask(SIG_SETMASK, &omask, NULL);
577                 waitpid(pid, &status, 0);
578                 (void)sigprocmask(SIG_BLOCK, &mask, NULL);
579                 bail(NO_SLEEP_EXIT, 0);
580         }
581
582         /*
583          * NOTICE: We are now in the child process!
584          */
585
586         /*
587          * Add any environment variables the PAM modules may have set.
588          */
589         export_pam_environment();
590
591         /*
592          * We're done with PAM now; our parent will deal with the rest.
593          */
594         pam_end(pamh, 0);
595         pamh = NULL;
596
597         /*
598          * We don't need to be root anymore, so set the login name and
599          * the UID.
600          */
601         if (setlogin(username) != 0) {
602                 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
603                 bail(NO_SLEEP_EXIT, 1);
604         }
605         if (setusercontext(lc, pwd, pwd->pw_uid,
606             LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
607                 syslog(LOG_ERR, "setusercontext() failed - exiting");
608                 exit(1);
609         }
610
611         (void)setenv("SHELL", pwd->pw_shell, 1);
612         (void)setenv("HOME", pwd->pw_dir, 1);
613         /* Overwrite "term" from login.conf(5) for any known TERM */
614         if (term == NULL && (tp = stypeof(tty)) != NULL)
615                 (void)setenv("TERM", tp, 1);
616         else
617                 (void)setenv("TERM", TERM_UNKNOWN, 0);
618         (void)setenv("LOGNAME", username, 1);
619         (void)setenv("USER", username, 1);
620         (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
621
622         if (!quietlog) {
623                 const char *cw;
624
625                 cw = login_getcapstr(lc, "welcome", NULL, NULL);
626                 if (cw != NULL && access(cw, F_OK) == 0)
627                         motd(cw);
628                 else
629                         motd(_PATH_MOTDFILE);
630
631                 if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 &&
632                     login_getcapbool(lc, "nocheckmail", 0) == 0) {
633                         char *cx;
634
635                         /* $MAIL may have been set by class. */
636                         cx = getenv("MAIL");
637                         if (cx == NULL) {
638                                 asprintf(&cx, "%s/%s",
639                                     _PATH_MAILDIR, pwd->pw_name);
640                         }
641                         if (cx && stat(cx, &st) == 0 && st.st_size != 0)
642                                 (void)printf("You have %smail.\n",
643                                     (st.st_mtime > st.st_atime) ? "new " : "");
644                         if (getenv("MAIL") == NULL)
645                                 free(cx);
646                 }
647         }
648
649         login_close(lc_user);
650         login_close(lc);
651
652         sa.sa_handler = SIG_DFL;
653         (void)sigaction(SIGALRM, &sa, NULL);
654         (void)sigaction(SIGQUIT, &sa, NULL);
655         (void)sigaction(SIGINT, &sa, NULL);
656         (void)sigaction(SIGTERM, &sa, NULL);
657         (void)sigaction(SIGHUP, &sa, NULL);
658         sa.sa_handler = SIG_IGN;
659         (void)sigaction(SIGTSTP, &sa, NULL);
660         (void)sigprocmask(SIG_SETMASK, &omask, NULL);
661
662         /*
663          * Login shells have a leading '-' in front of argv[0]
664          */
665         p = strrchr(pwd->pw_shell, '/');
666         if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
667                 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
668                     username);
669                 errx(1, "shell exceeds maximum pathname size");
670         } else if (arg0 == NULL) {
671                 err(1, "asprintf()");
672         }
673
674         execlp(shell, arg0, (char *)0);
675         err(1, "%s", shell);
676
677         /*
678          * That's it, folks!
679          */
680 }
681
682 /*
683  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
684  * authenticated, or 1 if not authenticated.  If some sort of PAM system
685  * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
686  * function returns -1.  This can be used as an indication that we should
687  * fall back to a different authentication mechanism.
688  */
689 static int
690 auth_pam(void)
691 {
692         const char *tmpl_user;
693         const void *item;
694         int rval;
695
696         pam_err = pam_authenticate(pamh, pam_silent);
697         switch (pam_err) {
698
699         case PAM_SUCCESS:
700                 /*
701                  * With PAM we support the concept of a "template"
702                  * user.  The user enters a login name which is
703                  * authenticated by PAM, usually via a remote service
704                  * such as RADIUS or TACACS+.  If authentication
705                  * succeeds, a different but related "template" name
706                  * is used for setting the credentials, shell, and
707                  * home directory.  The name the user enters need only
708                  * exist on the remote authentication server, but the
709                  * template name must be present in the local password
710                  * database.
711                  *
712                  * This is supported by two various mechanisms in the
713                  * individual modules.  However, from the application's
714                  * point of view, the template user is always passed
715                  * back as a changed value of the PAM_USER item.
716                  */
717                 pam_err = pam_get_item(pamh, PAM_USER, &item);
718                 if (pam_err == PAM_SUCCESS) {
719                         tmpl_user = (const char *)item;
720                         if (strcmp(username, tmpl_user) != 0)
721                                 pwd = getpwnam(tmpl_user);
722                 } else {
723                         pam_syslog("pam_get_item(PAM_USER)");
724                 }
725                 rval = 0;
726                 break;
727
728         case PAM_AUTH_ERR:
729         case PAM_USER_UNKNOWN:
730         case PAM_MAXTRIES:
731                 rval = 1;
732                 break;
733
734         default:
735                 pam_syslog("pam_authenticate()");
736                 rval = -1;
737                 break;
738         }
739
740         if (rval == 0) {
741                 pam_err = pam_acct_mgmt(pamh, pam_silent);
742                 switch (pam_err) {
743                 case PAM_SUCCESS:
744                         break;
745                 case PAM_NEW_AUTHTOK_REQD:
746                         pam_err = pam_chauthtok(pamh,
747                             pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
748                         if (pam_err != PAM_SUCCESS) {
749                                 pam_syslog("pam_chauthtok()");
750                                 rval = 1;
751                         }
752                         break;
753                 default:
754                         pam_syslog("pam_acct_mgmt()");
755                         rval = 1;
756                         break;
757                 }
758         }
759
760         if (rval != 0) {
761                 pam_end(pamh, pam_err);
762                 pamh = NULL;
763         }
764         return (rval);
765 }
766
767 /*
768  * Export any environment variables PAM modules may have set
769  */
770 static void
771 export_pam_environment(void)
772 {
773         char **pam_env;
774         char **pp;
775
776         pam_env = pam_getenvlist(pamh);
777         if (pam_env != NULL) {
778                 for (pp = pam_env; *pp != NULL; pp++) {
779                         (void)export(*pp);
780                         free(*pp);
781                 }
782         }
783 }
784
785 /*
786  * Perform sanity checks on an environment variable:
787  * - Make sure there is an '=' in the string.
788  * - Make sure the string doesn't run on too long.
789  * - Do not export certain variables.  This list was taken from the
790  *   Solaris pam_putenv(3) man page.
791  * Then export it.
792  */
793 static int
794 export(const char *s)
795 {
796         static const char *noexport[] = {
797                 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
798                 "IFS", "PATH", NULL
799         };
800         char *p;
801         const char **pp;
802         size_t n;
803
804         if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL)
805                 return (0);
806         if (strncmp(s, "LD_", 3) == 0)
807                 return (0);
808         for (pp = noexport; *pp != NULL; pp++) {
809                 n = strlen(*pp);
810                 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
811                         return (0);
812         }
813         *p = '\0';
814         (void)setenv(s, p + 1, 1);
815         *p = '=';
816         return (1);
817 }
818
819 static void
820 usage(void)
821 {
822
823         (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
824         exit(1);
825 }
826
827 /*
828  * Prompt user and read login name from stdin.
829  */
830 static char *
831 getloginname(void)
832 {
833         char *nbuf, *p;
834         int ch;
835
836         nbuf = malloc(MAXLOGNAME);
837         if (nbuf == NULL)
838                 err(1, "malloc()");
839         do {
840                 (void)printf("%s", prompt);
841                 for (p = nbuf; (ch = getchar()) != '\n'; ) {
842                         if (ch == EOF) {
843                                 badlogin(username);
844                                 bail(NO_SLEEP_EXIT, 0);
845                         }
846                         if (p < nbuf + MAXLOGNAME - 1)
847                                 *p++ = ch;
848                 }
849         } while (p == nbuf);
850
851         *p = '\0';
852         if (nbuf[0] == '-') {
853                 pam_silent = 0;
854                 memmove(nbuf, nbuf + 1, strlen(nbuf));
855         } else {
856                 pam_silent = PAM_SILENT;
857         }
858         return nbuf;
859 }
860
861 /*
862  * SIGINT handler for motd().
863  */
864 static volatile int motdinterrupt;
865 static void
866 sigint(int signo __unused)
867 {
868         motdinterrupt = 1;
869 }
870
871 /*
872  * Display the contents of a file (such as /etc/motd).
873  */
874 static int
875 motd(const char *motdfile)
876 {
877         struct sigaction newint, oldint;
878         FILE *f;
879         int ch;
880
881         if ((f = fopen(motdfile, "r")) == NULL)
882                 return (-1);
883         motdinterrupt = 0;
884         newint.sa_handler = sigint;
885         newint.sa_flags = 0;
886         sigfillset(&newint.sa_mask);
887         sigaction(SIGINT, &newint, &oldint);
888         while ((ch = fgetc(f)) != EOF && !motdinterrupt)
889                 putchar(ch);
890         sigaction(SIGINT, &oldint, NULL);
891         if (ch != EOF || ferror(f)) {
892                 fclose(f);
893                 return (-1);
894         }
895         fclose(f);
896         return (0);
897 }
898
899 /*
900  * SIGALRM handler, to enforce login prompt timeout.
901  *
902  * XXX This can potentially confuse the hell out of PAM.  We should
903  * XXX instead implement a conversation function that returns
904  * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
905  * XXX handler just set a flag.
906  */
907 static void
908 timedout(int signo __unused)
909 {
910
911         longjmp(timeout_buf, signo);
912 }
913
914 static void
915 badlogin(char *name)
916 {
917
918         if (failures == 0)
919                 return;
920         if (hflag) {
921                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
922                     failures, failures > 1 ? "S" : "", hostname);
923                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
924                     "%d LOGIN FAILURE%s FROM %s, %s",
925                     failures, failures > 1 ? "S" : "", hostname, name);
926         } else {
927                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
928                     failures, failures > 1 ? "S" : "", tty);
929                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
930                     "%d LOGIN FAILURE%s ON %s, %s",
931                     failures, failures > 1 ? "S" : "", tty, name);
932         }
933         failures = 0;
934 }
935
936 const char *
937 stypeof(char *ttyid)
938 {
939         struct ttyent *t;
940
941         if (ttyid != NULL && *ttyid != '\0') {
942                 t = getttynam(ttyid);
943                 if (t != NULL && t->ty_type != NULL)
944                         return (t->ty_type);
945         }
946         return (NULL);
947 }
948
949 static void
950 refused(const char *msg, const char *rtype, int lout)
951 {
952
953         if (msg != NULL)
954             printf("%s.\n", msg);
955         if (hflag)
956                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
957                     pwd->pw_name, rtype, hostname, tty);
958         else
959                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
960                     pwd->pw_name, rtype, tty);
961         if (lout)
962                 bail(SLEEP_EXIT, 1);
963 }
964
965 /*
966  * Log a PAM error
967  */
968 static void
969 pam_syslog(const char *msg)
970 {
971         syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
972 }
973
974 /*
975  * Shut down PAM
976  */
977 static void
978 pam_cleanup(void)
979 {
980
981         if (pamh != NULL) {
982                 if (pam_session_established) {
983                         pam_err = pam_close_session(pamh, 0);
984                         if (pam_err != PAM_SUCCESS)
985                                 pam_syslog("pam_close_session()");
986                 }
987                 pam_session_established = 0;
988                 if (pam_cred_established) {
989                         pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
990                         if (pam_err != PAM_SUCCESS)
991                                 pam_syslog("pam_setcred()");
992                 }
993                 pam_cred_established = 0;
994                 pam_end(pamh, pam_err);
995                 pamh = NULL;
996         }
997 }
998
999 static void
1000 bail_internal(int sec, int eval, int signo)
1001 {
1002         struct sigaction sa;
1003
1004         pam_cleanup();
1005 #ifdef USE_BSM_AUDIT
1006         if (pwd != NULL)
1007                 audit_logout();
1008 #endif
1009         (void)sleep(sec);
1010         if (signo == 0)
1011                 exit(eval);
1012         else {
1013                 sa.sa_handler = SIG_DFL;
1014                 sa.sa_flags = 0;
1015                 (void)sigemptyset(&sa.sa_mask);
1016                 (void)sigaction(signo, &sa, NULL);
1017                 (void)sigaddset(&sa.sa_mask, signo);
1018                 (void)sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
1019                 raise(signo);
1020                 exit(128 + signo);
1021         }
1022 }
1023
1024 /*
1025  * Exit, optionally after sleeping a few seconds
1026  */
1027 static void
1028 bail(int sec, int eval)
1029 {
1030         bail_internal(sec, eval, 0);
1031 }
1032
1033 /*
1034  * Exit because of a signal.
1035  * This is not async-signal safe, so only call async-signal safe functions
1036  * while the signal is unmasked.
1037  */
1038 static void
1039 bail_sig(int signo)
1040 {
1041         bail_internal(NO_SLEEP_EXIT, 0, signo);
1042 }