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