]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/login/login.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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          * This needs to happen before login_getpwclass to support
385          * home directories on GSS-API authenticated NFS where the
386          * kerberos credentials need to be saved so that the kernel
387          * can authenticate to the NFS server.
388          */
389         pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
390         if (pam_err != PAM_SUCCESS) {
391                 pam_syslog("pam_setcred()");
392                 bail(NO_SLEEP_EXIT, 1);
393         }
394         pam_cred_established = 1;
395
396         /*
397          * Establish the login class.
398          */
399         lc = login_getpwclass(pwd);
400         lc_user = login_getuserclass(pwd);
401
402         if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0)))
403                 quietlog = login_getcapbool(lc, "hushlogin", 0);
404
405         /*
406          * Switching needed for NFS with root access disabled.
407          *
408          * XXX: This change fails to modify the additional groups for the
409          * process, and as such, may restrict rights normally granted
410          * through those groups.
411          */
412         (void)setegid(pwd->pw_gid);
413         (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
414         if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
415                 if (login_getcapbool(lc, "requirehome", 0))
416                         refused("Home directory not available", "HOMEDIR", 1);
417                 if (chdir("/") < 0)
418                         refused("Cannot find root directory", "ROOTDIR", 1);
419                 if (!quietlog || *pwd->pw_dir)
420                         printf("No home directory.\nLogging in with home = \"/\".\n");
421                 pwd->pw_dir = strdup("/");
422                 if (pwd->pw_dir == NULL) {
423                         syslog(LOG_NOTICE, "strdup(): %m");
424                         bail(SLEEP_EXIT, 1);
425                 }
426         }
427         (void)seteuid(euid);
428         (void)setegid(egid);
429         if (!quietlog) {
430                 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
431                 if (!quietlog)
432                         pam_silent = 0;
433         }
434
435         shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
436         if (*pwd->pw_shell == '\0')
437                 pwd->pw_shell = strdup(_PATH_BSHELL);
438         if (pwd->pw_shell == NULL) {
439                 syslog(LOG_NOTICE, "strdup(): %m");
440                 bail(SLEEP_EXIT, 1);
441         }
442         if (*shell == '\0')   /* Not overridden */
443                 shell = pwd->pw_shell;
444         if ((shell = strdup(shell)) == NULL) {
445                 syslog(LOG_NOTICE, "strdup(): %m");
446                 bail(SLEEP_EXIT, 1);
447         }
448
449         /*
450          * Set device protections, depending on what terminal the
451          * user is logged in. This feature is used on Suns to give
452          * console users better privacy.
453          */
454         login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
455
456         /*
457          * Clear flags of the tty.  None should be set, and when the
458          * user sets them otherwise, this can cause the chown to fail.
459          * Since it isn't clear that flags are useful on character
460          * devices, we just clear them.
461          *
462          * We don't log in the case of EOPNOTSUPP because dev might be
463          * on NFS, which doesn't support chflags.
464          *
465          * We don't log in the EROFS because that means that /dev is on
466          * a read only file system and we assume that the permissions there
467          * are sane.
468          */
469         if (ttyn != tname && chflags(ttyn, 0))
470                 if (errno != EOPNOTSUPP && errno != EROFS)
471                         syslog(LOG_ERR, "chflags(%s): %m", ttyn);
472         if (ttyn != tname && chown(ttyn, pwd->pw_uid,
473             (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
474                 if (errno != EROFS)
475                         syslog(LOG_ERR, "chown(%s): %m", ttyn);
476
477         /*
478          * Exclude cons/vt/ptys only, assume dialup otherwise
479          * TODO: Make dialup tty determination a library call
480          * for consistency (finger etc.)
481          */
482         if (hflag && isdialuptty(tty))
483                 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
484
485 #ifdef LOGALL
486         /*
487          * Syslog each successful login, so we don't have to watch
488          * hundreds of wtmp or lastlogin files.
489          */
490         if (hflag)
491                 syslog(LOG_INFO, "login from %s on %s as %s",
492                        hostname, tty, pwd->pw_name);
493         else
494                 syslog(LOG_INFO, "login on %s as %s",
495                        tty, pwd->pw_name);
496 #endif
497
498         /*
499          * If fflag is on, assume caller/authenticator has logged root
500          * login.
501          */
502         if (rootlogin && fflag == 0) {
503                 if (hflag)
504                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
505                             username, tty, hostname);
506                 else
507                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
508                             username, tty);
509         }
510
511         /*
512          * Destroy environment unless user has requested its
513          * preservation - but preserve TERM in all cases
514          */
515         term = getenv("TERM");
516         if (!pflag)
517                 environ = envinit;
518         if (term != NULL)
519                 setenv("TERM", term, 0);
520
521         /*
522          * PAM modules might add supplementary groups during pam_setcred().
523          */
524         if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
525                 syslog(LOG_ERR, "setusercontext() failed - exiting");
526                 bail(NO_SLEEP_EXIT, 1);
527         }
528
529         pam_err = pam_setcred(pamh, pam_silent|PAM_REINITIALIZE_CRED);
530         if (pam_err != PAM_SUCCESS) {
531                 pam_syslog("pam_setcred()");
532                 bail(NO_SLEEP_EXIT, 1);
533         }
534
535         pam_err = pam_open_session(pamh, pam_silent);
536         if (pam_err != PAM_SUCCESS) {
537                 pam_syslog("pam_open_session()");
538                 bail(NO_SLEEP_EXIT, 1);
539         }
540         pam_session_established = 1;
541
542         /*
543          * We must fork() before setuid() because we need to call
544          * pam_close_session() as root.
545          */
546         pid = fork();
547         if (pid < 0) {
548                 err(1, "fork");
549         } else if (pid != 0) {
550                 /*
551                  * Parent: wait for child to finish, then clean up
552                  * session.
553                  */
554                 int status;
555                 setproctitle("-%s [pam]", getprogname());
556                 waitpid(pid, &status, 0);
557                 bail(NO_SLEEP_EXIT, 0);
558         }
559
560         /*
561          * NOTICE: We are now in the child process!
562          */
563
564         /*
565          * Add any environment variables the PAM modules may have set.
566          */
567         export_pam_environment();
568
569         /*
570          * We're done with PAM now; our parent will deal with the rest.
571          */
572         pam_end(pamh, 0);
573         pamh = NULL;
574
575         /*
576          * We don't need to be root anymore, so set the login name and
577          * the UID.
578          */
579         if (setlogin(username) != 0) {
580                 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
581                 bail(NO_SLEEP_EXIT, 1);
582         }
583         if (setusercontext(lc, pwd, pwd->pw_uid,
584             LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
585                 syslog(LOG_ERR, "setusercontext() failed - exiting");
586                 exit(1);
587         }
588
589         (void)setenv("SHELL", pwd->pw_shell, 1);
590         (void)setenv("HOME", pwd->pw_dir, 1);
591         /* Overwrite "term" from login.conf(5) for any known TERM */
592         if (term == NULL && (tp = stypeof(tty)) != NULL)
593                 (void)setenv("TERM", tp, 1);
594         else
595                 (void)setenv("TERM", TERM_UNKNOWN, 0);
596         (void)setenv("LOGNAME", username, 1);
597         (void)setenv("USER", username, 1);
598         (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
599
600         if (!quietlog) {
601                 const char *cw;
602
603                 cw = login_getcapstr(lc, "welcome", NULL, NULL);
604                 if (cw != NULL && access(cw, F_OK) == 0)
605                         motd(cw);
606                 else
607                         motd(_PATH_MOTDFILE);
608
609                 if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 &&
610                     login_getcapbool(lc, "nocheckmail", 0) == 0) {
611                         char *cx;
612
613                         /* $MAIL may have been set by class. */
614                         cx = getenv("MAIL");
615                         if (cx == NULL) {
616                                 asprintf(&cx, "%s/%s",
617                                     _PATH_MAILDIR, pwd->pw_name);
618                         }
619                         if (cx && stat(cx, &st) == 0 && st.st_size != 0)
620                                 (void)printf("You have %smail.\n",
621                                     (st.st_mtime > st.st_atime) ? "new " : "");
622                         if (getenv("MAIL") == NULL)
623                                 free(cx);
624                 }
625         }
626
627         login_close(lc_user);
628         login_close(lc);
629
630         (void)signal(SIGALRM, SIG_DFL);
631         (void)signal(SIGQUIT, SIG_DFL);
632         (void)signal(SIGINT, SIG_DFL);
633         (void)signal(SIGTSTP, SIG_IGN);
634
635         /*
636          * Login shells have a leading '-' in front of argv[0]
637          */
638         p = strrchr(pwd->pw_shell, '/');
639         if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
640                 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
641                     username);
642                 errx(1, "shell exceeds maximum pathname size");
643         } else if (arg0 == NULL) {
644                 err(1, "asprintf()");
645         }
646
647         execlp(shell, arg0, (char *)0);
648         err(1, "%s", shell);
649
650         /*
651          * That's it, folks!
652          */
653 }
654
655 /*
656  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
657  * authenticated, or 1 if not authenticated.  If some sort of PAM system
658  * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
659  * function returns -1.  This can be used as an indication that we should
660  * fall back to a different authentication mechanism.
661  */
662 static int
663 auth_pam(void)
664 {
665         const char *tmpl_user;
666         const void *item;
667         int rval;
668
669         pam_err = pam_authenticate(pamh, pam_silent);
670         switch (pam_err) {
671
672         case PAM_SUCCESS:
673                 /*
674                  * With PAM we support the concept of a "template"
675                  * user.  The user enters a login name which is
676                  * authenticated by PAM, usually via a remote service
677                  * such as RADIUS or TACACS+.  If authentication
678                  * succeeds, a different but related "template" name
679                  * is used for setting the credentials, shell, and
680                  * home directory.  The name the user enters need only
681                  * exist on the remote authentication server, but the
682                  * template name must be present in the local password
683                  * database.
684                  *
685                  * This is supported by two various mechanisms in the
686                  * individual modules.  However, from the application's
687                  * point of view, the template user is always passed
688                  * back as a changed value of the PAM_USER item.
689                  */
690                 pam_err = pam_get_item(pamh, PAM_USER, &item);
691                 if (pam_err == PAM_SUCCESS) {
692                         tmpl_user = (const char *)item;
693                         if (strcmp(username, tmpl_user) != 0)
694                                 pwd = getpwnam(tmpl_user);
695                 } else {
696                         pam_syslog("pam_get_item(PAM_USER)");
697                 }
698                 rval = 0;
699                 break;
700
701         case PAM_AUTH_ERR:
702         case PAM_USER_UNKNOWN:
703         case PAM_MAXTRIES:
704                 rval = 1;
705                 break;
706
707         default:
708                 pam_syslog("pam_authenticate()");
709                 rval = -1;
710                 break;
711         }
712
713         if (rval == 0) {
714                 pam_err = pam_acct_mgmt(pamh, pam_silent);
715                 switch (pam_err) {
716                 case PAM_SUCCESS:
717                         break;
718                 case PAM_NEW_AUTHTOK_REQD:
719                         pam_err = pam_chauthtok(pamh,
720                             pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
721                         if (pam_err != PAM_SUCCESS) {
722                                 pam_syslog("pam_chauthtok()");
723                                 rval = 1;
724                         }
725                         break;
726                 default:
727                         pam_syslog("pam_acct_mgmt()");
728                         rval = 1;
729                         break;
730                 }
731         }
732
733         if (rval != 0) {
734                 pam_end(pamh, pam_err);
735                 pamh = NULL;
736         }
737         return (rval);
738 }
739
740 /*
741  * Export any environment variables PAM modules may have set
742  */
743 static void
744 export_pam_environment(void)
745 {
746         char **pam_env;
747         char **pp;
748
749         pam_env = pam_getenvlist(pamh);
750         if (pam_env != NULL) {
751                 for (pp = pam_env; *pp != NULL; pp++) {
752                         (void)export(*pp);
753                         free(*pp);
754                 }
755         }
756 }
757
758 /*
759  * Perform sanity checks on an environment variable:
760  * - Make sure there is an '=' in the string.
761  * - Make sure the string doesn't run on too long.
762  * - Do not export certain variables.  This list was taken from the
763  *   Solaris pam_putenv(3) man page.
764  * Then export it.
765  */
766 static int
767 export(const char *s)
768 {
769         static const char *noexport[] = {
770                 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
771                 "IFS", "PATH", NULL
772         };
773         char *p;
774         const char **pp;
775         size_t n;
776
777         if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL)
778                 return (0);
779         if (strncmp(s, "LD_", 3) == 0)
780                 return (0);
781         for (pp = noexport; *pp != NULL; pp++) {
782                 n = strlen(*pp);
783                 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
784                         return (0);
785         }
786         *p = '\0';
787         (void)setenv(s, p + 1, 1);
788         *p = '=';
789         return (1);
790 }
791
792 static void
793 usage(void)
794 {
795
796         (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
797         exit(1);
798 }
799
800 /*
801  * Prompt user and read login name from stdin.
802  */
803 static char *
804 getloginname(void)
805 {
806         char *nbuf, *p;
807         int ch;
808
809         nbuf = malloc(MAXLOGNAME);
810         if (nbuf == NULL)
811                 err(1, "malloc()");
812         do {
813                 (void)printf("%s", prompt);
814                 for (p = nbuf; (ch = getchar()) != '\n'; ) {
815                         if (ch == EOF) {
816                                 badlogin(username);
817                                 bail(NO_SLEEP_EXIT, 0);
818                         }
819                         if (p < nbuf + MAXLOGNAME - 1)
820                                 *p++ = ch;
821                 }
822         } while (p == nbuf);
823
824         *p = '\0';
825         if (nbuf[0] == '-') {
826                 pam_silent = 0;
827                 memmove(nbuf, nbuf + 1, strlen(nbuf));
828         } else {
829                 pam_silent = PAM_SILENT;
830         }
831         return nbuf;
832 }
833
834 /*
835  * SIGINT handler for motd().
836  */
837 static volatile int motdinterrupt;
838 static void
839 sigint(int signo __unused)
840 {
841         motdinterrupt = 1;
842 }
843
844 /*
845  * Display the contents of a file (such as /etc/motd).
846  */
847 static int
848 motd(const char *motdfile)
849 {
850         sig_t oldint;
851         FILE *f;
852         int ch;
853
854         if ((f = fopen(motdfile, "r")) == NULL)
855                 return (-1);
856         motdinterrupt = 0;
857         oldint = signal(SIGINT, sigint);
858         while ((ch = fgetc(f)) != EOF && !motdinterrupt)
859                 putchar(ch);
860         signal(SIGINT, oldint);
861         if (ch != EOF || ferror(f)) {
862                 fclose(f);
863                 return (-1);
864         }
865         fclose(f);
866         return (0);
867 }
868
869 /*
870  * SIGALRM handler, to enforce login prompt timeout.
871  *
872  * XXX This can potentially confuse the hell out of PAM.  We should
873  * XXX instead implement a conversation function that returns
874  * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
875  * XXX handler just set a flag.
876  */
877 static void
878 timedout(int signo __unused)
879 {
880
881         longjmp(timeout_buf, signo);
882 }
883
884 static void
885 badlogin(char *name)
886 {
887
888         if (failures == 0)
889                 return;
890         if (hflag) {
891                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
892                     failures, failures > 1 ? "S" : "", hostname);
893                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
894                     "%d LOGIN FAILURE%s FROM %s, %s",
895                     failures, failures > 1 ? "S" : "", hostname, name);
896         } else {
897                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
898                     failures, failures > 1 ? "S" : "", tty);
899                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
900                     "%d LOGIN FAILURE%s ON %s, %s",
901                     failures, failures > 1 ? "S" : "", tty, name);
902         }
903         failures = 0;
904 }
905
906 const char *
907 stypeof(char *ttyid)
908 {
909         struct ttyent *t;
910
911         if (ttyid != NULL && *ttyid != '\0') {
912                 t = getttynam(ttyid);
913                 if (t != NULL && t->ty_type != NULL)
914                         return (t->ty_type);
915         }
916         return (NULL);
917 }
918
919 static void
920 refused(const char *msg, const char *rtype, int lout)
921 {
922
923         if (msg != NULL)
924             printf("%s.\n", msg);
925         if (hflag)
926                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
927                     pwd->pw_name, rtype, hostname, tty);
928         else
929                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
930                     pwd->pw_name, rtype, tty);
931         if (lout)
932                 bail(SLEEP_EXIT, 1);
933 }
934
935 /*
936  * Log a PAM error
937  */
938 static void
939 pam_syslog(const char *msg)
940 {
941         syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
942 }
943
944 /*
945  * Shut down PAM
946  */
947 static void
948 pam_cleanup(void)
949 {
950
951         if (pamh != NULL) {
952                 if (pam_session_established) {
953                         pam_err = pam_close_session(pamh, 0);
954                         if (pam_err != PAM_SUCCESS)
955                                 pam_syslog("pam_close_session()");
956                 }
957                 pam_session_established = 0;
958                 if (pam_cred_established) {
959                         pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
960                         if (pam_err != PAM_SUCCESS)
961                                 pam_syslog("pam_setcred()");
962                 }
963                 pam_cred_established = 0;
964                 pam_end(pamh, pam_err);
965                 pamh = NULL;
966         }
967 }
968
969 /*
970  * Exit, optionally after sleeping a few seconds
971  */
972 void
973 bail(int sec, int eval)
974 {
975
976         pam_cleanup();
977 #ifdef USE_BSM_AUDIT
978         if (pwd != NULL)
979                 audit_logout();
980 #endif
981         (void)sleep(sec);
982         exit(eval);
983 }