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