Initial import from FreeBSD RELENG_4:
[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  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #if 0
35 static char copyright[] =
36 "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)login.c     8.4 (Berkeley) 4/2/94";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD: src/usr.bin/login/login.c,v 1.51.2.15 2003/04/29 14:10:41 des Exp $";
46 #endif /* not lint */
47
48 /*
49  * login [ name ]
50  * login -h hostname    (for telnetd, etc.)
51  * login -f name        (for pre-authenticated login: datakit, xterm, etc.)
52  */
53
54 #include <sys/copyright.h>
55 #include <sys/param.h>
56 #include <sys/stat.h>
57 #include <sys/socket.h>
58 #include <sys/time.h>
59 #include <sys/resource.h>
60 #include <sys/file.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63
64 #include <err.h>
65 #include <errno.h>
66 #include <grp.h>
67 #include <libutil.h>
68 #include <login_cap.h>
69 #include <netdb.h>
70 #include <pwd.h>
71 #include <setjmp.h>
72 #include <signal.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <syslog.h>
77 #include <ttyent.h>
78 #include <unistd.h>
79 #include <utmp.h>
80
81 #ifdef USE_PAM
82 #include <security/pam_appl.h>
83 #include <security/pam_misc.h>
84 #include <sys/wait.h>
85 #endif /* USE_PAM */
86
87 #include "pathnames.h"
88
89 /* wrapper for KAME-special getnameinfo() */
90 #ifndef NI_WITHSCOPEID
91 #define NI_WITHSCOPEID  0
92 #endif
93
94 void     badlogin __P((char *));
95 void     checknologin __P((void));
96 void     dolastlog __P((int));
97 void     getloginname __P((void));
98 void     motd __P((char *));
99 int      rootterm __P((char *));
100 void     sigint __P((int));
101 void     sleepexit __P((int));
102 void     refused __P((char *,char *,int));
103 char    *stypeof __P((char *));
104 void     timedout __P((int));
105 int      login_access __P((char *, char *));
106 void     login_fbtab __P((char *, uid_t, gid_t));
107
108 #ifdef USE_PAM
109 static int auth_pam __P((void));
110 static int export_pam_environment __P((void));
111 static int ok_to_export __P((const char *));
112
113 static pam_handle_t *pamh = NULL;
114 static char **environ_pam;
115
116 #define PAM_END { \
117         if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
118                 syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); \
119         if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) \
120                 syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); \
121         if ((e = pam_end(pamh, e)) != PAM_SUCCESS) \
122                 syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); \
123 }
124 #endif /* USE_PAM */
125 static int auth_traditional __P((void));
126 extern void login __P((struct utmp *));
127 static void usage __P((void));
128
129 #define TTYGRPNAME      "tty"           /* name of group to own ttys */
130 #define DEFAULT_BACKOFF 3
131 #define DEFAULT_RETRIES 10
132 #define DEFAULT_PROMPT          "login: "
133 #define DEFAULT_PASSWD_PROMPT   "Password:"
134
135 /*
136  * This bounds the time given to login.  Not a define so it can
137  * be patched on machines where it's too small.
138  */
139 u_int   timeout = 300;
140
141 /* Buffer for signal handling of timeout */
142 jmp_buf timeout_buf;
143
144 struct  passwd *pwd;
145 int     failures;
146 char    *term, *envinit[1], *hostname, *passwd_prompt, *prompt, *tty, *username;
147 char    full_hostname[MAXHOSTNAMELEN];
148
149 int
150 main(argc, argv)
151         int argc;
152         char *argv[];
153 {
154         extern char **environ;
155         struct group *gr;
156         struct stat st;
157         struct timeval tp;
158         struct utmp utmp;
159         int rootok, retries, backoff;
160         int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
161         int changepass;
162         time_t warntime;
163         uid_t uid, euid;
164         gid_t egid;
165         char *p, *ttyn;
166         char tbuf[MAXPATHLEN + 2];
167         char tname[sizeof(_PATH_TTY) + 10];
168         char *shell = NULL;
169         login_cap_t *lc = NULL;
170 #ifdef USE_PAM
171         pid_t pid;
172         int e;
173 #endif /* USE_PAM */
174
175         (void)signal(SIGQUIT, SIG_IGN);
176         (void)signal(SIGINT, SIG_IGN);
177         (void)signal(SIGHUP, SIG_IGN);
178         if (setjmp(timeout_buf)) {
179                 if (failures)
180                         badlogin(tbuf);
181                 (void)fprintf(stderr, "Login timed out after %d seconds\n",
182                     timeout);
183                 exit(0);
184         }
185         (void)signal(SIGALRM, timedout);
186         (void)alarm(timeout);
187         (void)setpriority(PRIO_PROCESS, 0, 0);
188
189         openlog("login", LOG_ODELAY, LOG_AUTH);
190
191         /*
192          * -p is used by getty to tell login not to destroy the environment
193          * -f is used to skip a second login authentication
194          * -h is used by other servers to pass the name of the remote
195          *    host to login so that it may be placed in utmp and wtmp
196          */
197         *full_hostname = '\0';
198         term = NULL;
199
200         fflag = hflag = pflag = 0;
201         uid = getuid();
202         euid = geteuid();
203         egid = getegid();
204         while ((ch = getopt(argc, argv, "fh:p")) != -1)
205                 switch (ch) {
206                 case 'f':
207                         fflag = 1;
208                         break;
209                 case 'h':
210                         if (uid)
211                                 errx(1, "-h option: %s", strerror(EPERM));
212                         hflag = 1;
213                         if (strlcpy(full_hostname, optarg,
214                             sizeof(full_hostname)) >= sizeof(full_hostname))
215                                 errx(1, "-h option: %s: exceeds maximum "
216                                     "hostname size", optarg);
217
218                         trimdomain(optarg, UT_HOSTSIZE);
219
220                         if (strlen(optarg) > UT_HOSTSIZE) {
221                                 struct addrinfo hints, *res;
222                                 int ga_err;
223                                 
224                                 memset(&hints, 0, sizeof(hints));
225                                 hints.ai_family = AF_UNSPEC;
226                                 ga_err = getaddrinfo(optarg, NULL, &hints,
227                                     &res);
228                                 if (ga_err == 0) {
229                                         char hostbuf[MAXHOSTNAMELEN];
230
231                                         getnameinfo(res->ai_addr,
232                                             res->ai_addrlen,
233                                             hostbuf,
234                                             sizeof(hostbuf), NULL, 0,
235                                             NI_NUMERICHOST|
236                                             NI_WITHSCOPEID);
237                                         optarg = strdup(hostbuf);
238                                         if (optarg == NULL) {
239                                                 syslog(LOG_NOTICE,
240                                                     "strdup(): %m");
241                                                 sleepexit(1);
242                                         }
243                                 } else
244                                         optarg = "invalid hostname";
245                                 if (res != NULL)
246                                         freeaddrinfo(res);
247                         }
248                         hostname = optarg;
249                         break;
250                 case 'p':
251                         pflag = 1;
252                         break;
253                 case '?':
254                 default:
255                         if (!uid)
256                                 syslog(LOG_ERR, "invalid flag %c", ch);
257                         usage();
258                 }
259         argc -= optind;
260         argv += optind;
261
262         if (*argv) {
263                 username = *argv;
264                 ask = 0;
265         } else
266                 ask = 1;
267
268         setproctitle("-%s", getprogname());
269
270         for (cnt = getdtablesize(); cnt > 2; cnt--)
271                 (void)close(cnt);
272
273         ttyn = ttyname(STDIN_FILENO);
274         if (ttyn == NULL || *ttyn == '\0') {
275                 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
276                 ttyn = tname;
277         }
278         if ((tty = strrchr(ttyn, '/')) != NULL)
279                 ++tty;
280         else
281                 tty = ttyn;
282
283         /*
284          * Get "login-retries" & "login-backoff" from default class
285          */
286         lc = login_getclass(NULL);
287         prompt = login_getcapstr(lc, "login_prompt",
288             DEFAULT_PROMPT, DEFAULT_PROMPT);
289         passwd_prompt = login_getcapstr(lc, "passwd_prompt",
290             DEFAULT_PASSWD_PROMPT, DEFAULT_PASSWD_PROMPT);
291         retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES,
292             DEFAULT_RETRIES);
293         backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF,
294             DEFAULT_BACKOFF);
295         login_close(lc);
296         lc = NULL;
297
298         for (cnt = 0;; ask = 1) {
299                 if (ask) {
300                         fflag = 0;
301                         getloginname();
302                 }
303                 rootlogin = 0;
304                 rootok = rootterm(tty); /* Default (auth may change) */
305
306                 if (strlen(username) > UT_NAMESIZE)
307                         username[UT_NAMESIZE] = '\0';
308
309                 /*
310                  * Note if trying multiple user names; log failures for
311                  * previous user name, but don't bother logging one failure
312                  * for nonexistent name (mistyped username).
313                  */
314                 if (failures && strcmp(tbuf, username)) {
315                         if (failures > (pwd ? 0 : 1))
316                                 badlogin(tbuf);
317                 }
318                 (void)strlcpy(tbuf, username, sizeof(tbuf));
319
320                 pwd = getpwnam(username);
321
322                 /*
323                  * if we have a valid account name, and it doesn't have a
324                  * password, or the -f option was specified and the caller
325                  * is root or the caller isn't changing their uid, don't
326                  * authenticate.
327                  */
328                 if (pwd != NULL) {
329                         if (pwd->pw_uid == 0)
330                                 rootlogin = 1;
331
332                         if (fflag && (uid == (uid_t)0 ||
333                             uid == (uid_t)pwd->pw_uid)) {
334                                 /* already authenticated */
335                                 break;
336                         } else if (pwd->pw_passwd[0] == '\0') {
337                                 if (!rootlogin || rootok) {
338                                         /* pretend password okay */
339                                         rval = 0;
340                                         goto ttycheck;
341                                 }
342                         }
343                 }
344
345                 fflag = 0;
346
347                 (void)setpriority(PRIO_PROCESS, 0, -4);
348
349 #ifdef USE_PAM
350                 /*
351                  * Try to authenticate using PAM.  If a PAM system error
352                  * occurs, perhaps because of a botched configuration,
353                  * then fall back to using traditional Unix authentication.
354                  */
355                 if ((rval = auth_pam()) == -1)
356 #endif /* USE_PAM */
357                         rval = auth_traditional();
358
359                 (void)setpriority(PRIO_PROCESS, 0, 0);
360
361 #ifdef USE_PAM
362                 /*
363                  * PAM authentication may have changed "pwd" to the
364                  * entry for the template user.  Check again to see if
365                  * this is a root login after all.
366                  */
367                 if (pwd != NULL && pwd->pw_uid == 0)
368                         rootlogin = 1;
369 #endif /* USE_PAM */
370
371         ttycheck:
372                 /*
373                  * If trying to log in as root without Kerberos,
374                  * but with insecure terminal, refuse the login attempt.
375                  */
376                 if (pwd && !rval) {
377                         if (rootlogin && !rootok)
378                                 refused(NULL, "NOROOT", 0);
379                         else    /* valid password & authenticated */
380                                 break;
381                 }
382
383                 (void)printf("Login incorrect\n");
384                 failures++;
385
386                 /*
387                  * we allow up to 'retry' (10) tries,
388                  * but after 'backoff' (3) we start backing off
389                  */
390                 if (++cnt > backoff) {
391                         if (cnt >= retries) {
392                                 badlogin(username);
393                                 sleepexit(1);
394                         }
395                         sleep((u_int)((cnt - backoff) * 5));
396                 }
397         }
398
399         /* committed to login -- turn off timeout */
400         (void)alarm((u_int)0);
401         (void)signal(SIGHUP, SIG_DFL);
402
403         endpwent();
404
405         /*
406          * Establish the login class.
407          */
408         lc = login_getpwclass(pwd);
409
410         /* if user not super-user, check for disabled logins */
411         if (!rootlogin)
412                 auth_checknologin(lc);
413
414         quietlog = login_getcapbool(lc, "hushlogin", 0);
415         /* Switching needed for NFS with root access disabled */
416         (void)setegid(pwd->pw_gid);
417         (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
418         if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
419                 if (login_getcapbool(lc, "requirehome", 0))
420                         refused("Home directory not available", "HOMEDIR", 1);
421                 if (chdir("/") < 0) 
422                         refused("Cannot find root directory", "ROOTDIR", 1);
423                 if (!quietlog || *pwd->pw_dir)
424                         printf("No home directory.\nLogging in with home = \"/\".\n");
425                 pwd->pw_dir = "/";
426         }
427         (void)seteuid(euid);
428         (void)setegid(egid);
429         if (!quietlog)
430                 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
431
432         if (pwd->pw_change || pwd->pw_expire)
433                 (void)gettimeofday(&tp, (struct timezone *)NULL);
434
435 #define DEFAULT_WARN  (2L * 7L * 86400L)  /* Two weeks */
436
437         warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN,
438             DEFAULT_WARN);
439
440         if (pwd->pw_expire) {
441                 if (tp.tv_sec >= pwd->pw_expire) {
442                         refused("Sorry -- your account has expired", "EXPIRED",
443                             1);
444                 } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
445                         (void)printf("Warning: your account expires on %s",
446                             ctime(&pwd->pw_expire));
447         }
448
449         warntime = login_getcaptime(lc, "warnpassword", DEFAULT_WARN,
450             DEFAULT_WARN);
451
452         changepass = 0;
453         if (pwd->pw_change) {
454                 if (tp.tv_sec >= pwd->pw_change) {
455                         (void)printf("Sorry -- your password has expired.\n");
456                         changepass = 1;
457                         syslog(LOG_INFO, "%s Password expired - forcing change",
458                             pwd->pw_name);
459                 } else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog)
460                         (void)printf("Warning: your password expires on %s",
461                             ctime(&pwd->pw_change));
462         }
463
464         if (lc != NULL) {
465                 if (hostname) {
466                         struct addrinfo hints, *res;
467                         int ga_err;
468
469                         memset(&hints, 0, sizeof(hints));
470                         hints.ai_family = AF_UNSPEC;
471                         ga_err = getaddrinfo(full_hostname, NULL, &hints,
472                                              &res);
473                         if (ga_err == 0) {
474                                 char hostbuf[MAXHOSTNAMELEN];
475
476                                 getnameinfo(res->ai_addr, res->ai_addrlen,
477                                     hostbuf, sizeof(hostbuf), NULL, 0,
478                                     NI_NUMERICHOST|NI_WITHSCOPEID);
479                                 if ((optarg = strdup(hostbuf)) == NULL) {
480                                         syslog(LOG_NOTICE, "strdup(): %m");
481                                         sleepexit(1);
482                                 }
483                         } else
484                                 optarg = NULL;
485                         if (res != NULL)
486                                 freeaddrinfo(res);
487                         if (!auth_hostok(lc, full_hostname, optarg))
488                                 refused("Permission denied", "HOST", 1);
489                 }
490
491                 if (!auth_ttyok(lc, tty))
492                         refused("Permission denied", "TTY", 1);
493
494                 if (!auth_timeok(lc, time(NULL)))
495                         refused("Logins not available right now", "TIME", 1);
496         }
497         shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
498         if (*pwd->pw_shell == '\0')
499                 pwd->pw_shell = _PATH_BSHELL;
500         if (*shell == '\0')   /* Not overridden */
501                 shell = pwd->pw_shell;
502         if ((shell = strdup(shell)) == NULL) {
503                 syslog(LOG_NOTICE, "strdup(): %m");
504                 sleepexit(1);
505         }
506
507 #ifdef LOGIN_ACCESS
508         if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
509                 refused("Permission denied", "ACCESS", 1);
510 #endif /* LOGIN_ACCESS */
511
512         /* Nothing else left to fail -- really log in. */
513         memset((void *)&utmp, 0, sizeof(utmp));
514         (void)time(&utmp.ut_time);
515         (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
516         if (hostname)
517                 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
518         (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
519         login(&utmp);
520
521         dolastlog(quietlog);
522
523         /*
524          * Set device protections, depending on what terminal the
525          * user is logged in. This feature is used on Suns to give
526          * console users better privacy.
527          */
528         login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
529
530         /*
531          * Clear flags of the tty.  None should be set, and when the
532          * user sets them otherwise, this can cause the chown to fail.
533          * Since it isn't clear that flags are useful on character
534          * devices, we just clear them.
535          */
536         if (ttyn != tname && chflags(ttyn, 0) && errno != EOPNOTSUPP)
537                 syslog(LOG_ERR, "chflags(%s): %m", ttyn);
538         if (ttyn != tname && chown(ttyn, pwd->pw_uid,
539             (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
540                 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
541
542
543         /*
544          * Preserve TERM if it happens to be already set.
545          */
546         if ((term = getenv("TERM")) != NULL) {
547                 if ((term = strdup(term)) == NULL) {
548                         syslog(LOG_NOTICE,
549                             "strdup(): %m");
550                         sleepexit(1);
551                 }
552         }
553
554         /*
555          * Exclude cons/vt/ptys only, assume dialup otherwise
556          * TODO: Make dialup tty determination a library call
557          * for consistency (finger etc.)
558          */
559         if (hostname==NULL && isdialuptty(tty))
560                 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
561
562 #ifdef LOGALL
563         /*
564          * Syslog each successful login, so we don't have to watch hundreds
565          * of wtmp or lastlogin files.
566          */
567         if (hostname)
568                 syslog(LOG_INFO, "login from %s on %s as %s",
569                        full_hostname, tty, pwd->pw_name);
570         else
571                 syslog(LOG_INFO, "login on %s as %s",
572                        tty, pwd->pw_name);
573 #endif
574
575         /*
576          * If fflag is on, assume caller/authenticator has logged root login.
577          */
578         if (rootlogin && fflag == 0)
579         {
580                 if (hostname)
581                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
582                             username, tty, full_hostname);
583                 else
584                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
585                             username, tty);
586         }
587
588         /*
589          * Destroy environment unless user has requested its preservation.
590          * We need to do this before setusercontext() because that may
591          * set or reset some environment variables.
592          */
593         if (!pflag)
594                 environ = envinit;
595
596         /*
597          * PAM modules might add supplementary groups during pam_setcred().
598          */
599         if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
600                 syslog(LOG_ERR, "setusercontext() failed - exiting");
601                 exit(1);
602         }
603
604 #ifdef USE_PAM
605         if (pamh) {
606                 if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
607                         syslog(LOG_ERR, "pam_open_session: %s",
608                             pam_strerror(pamh, e));
609                 } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
610                     != PAM_SUCCESS) {
611                         syslog(LOG_ERR, "pam_setcred: %s",
612                             pam_strerror(pamh, e));
613                 }
614
615                 /*
616                  * Add any environmental variables that the
617                  * PAM modules may have set.
618                  * Call *after* opening session!
619                  */
620                 if (pamh) {
621                   environ_pam = pam_getenvlist(pamh);
622                   if (environ_pam)
623                         export_pam_environment();
624                 }
625
626                 /*
627                  * We must fork() before setuid() because we need to call
628                  * pam_close_session() as root.
629                  */
630                 pid = fork();
631                 if (pid < 0) {
632                         err(1, "fork");
633                         PAM_END;
634                         exit(0);
635                 } else if (pid) {
636                         /* parent - wait for child to finish, then cleanup
637                            session */
638                         setproctitle("-%s [pam]", getprogname());
639                         wait(NULL);
640                         PAM_END;
641                         exit(0);
642                 } else {
643                         if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
644                                 syslog(LOG_ERR, "pam_end: %s",
645                                     pam_strerror(pamh, e));
646                 }
647         }
648 #endif /* USE_PAM */
649
650         /*
651          * We don't need to be root anymore, so
652          * set the user and session context
653          */
654         if (setlogin(username) != 0) {
655                 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
656                 exit(1);
657         }
658         if (setusercontext(lc, pwd, pwd->pw_uid,
659             LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
660                 syslog(LOG_ERR, "setusercontext() failed - exiting");
661                 exit(1);
662         }
663
664         (void)setenv("SHELL", pwd->pw_shell, 1);
665         (void)setenv("HOME", pwd->pw_dir, 1);
666         if (term != NULL && *term != '\0')
667                 (void)setenv("TERM", term, 1);          /* Preset overrides */
668         else {
669                 (void)setenv("TERM", stypeof(tty), 0);  /* Fallback doesn't */
670         }
671         (void)setenv("LOGNAME", username, 1);
672         (void)setenv("USER", username, 1);
673         (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
674
675         if (!quietlog) {
676                 char    *cw;
677
678                 cw = login_getcapstr(lc, "copyright", NULL, NULL);
679                 if (cw != NULL && access(cw, F_OK) == 0)
680                         motd(cw);
681                 else
682                     (void)printf("%s\n\t%s %s\n",
683         "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
684         "The Regents of the University of California. ",
685         "All rights reserved.");
686
687                 (void)printf("\n");
688
689                 cw = login_getcapstr(lc, "welcome", NULL, NULL);
690                 if (cw == NULL || access(cw, F_OK) != 0)
691                         cw = _PATH_MOTDFILE;
692                 motd(cw);
693
694                 cw = getenv("MAIL");    /* $MAIL may have been set by class */
695                 if (cw != NULL)
696                         strlcpy(tbuf, cw, sizeof(tbuf));
697                 else
698                         snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR,
699                             pwd->pw_name);
700                 if (stat(tbuf, &st) == 0 && st.st_size != 0)
701                         (void)printf("You have %smail.\n",
702                             (st.st_mtime > st.st_atime) ? "new " : "");
703         }
704
705         login_close(lc);
706
707         (void)signal(SIGALRM, SIG_DFL);
708         (void)signal(SIGQUIT, SIG_DFL);
709         (void)signal(SIGINT, SIG_DFL);
710         (void)signal(SIGTSTP, SIG_IGN);
711
712         if (changepass) {
713                 if (system(_PATH_CHPASS) != 0)
714                         sleepexit(1);
715         }
716
717         /*
718          * Login shells have a leading '-' in front of argv[0]
719          */
720         if (snprintf(tbuf, sizeof(tbuf), "-%s",
721             (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell) >=
722             sizeof(tbuf)) {
723                 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
724                     username);
725                 errx(1, "shell exceeds maximum pathname size");
726         }
727
728         execlp(shell, tbuf, (char *)0);
729         err(1, "%s", shell);
730 }
731
732 static int
733 auth_traditional()
734 {
735         int rval;
736         char *p;
737         char *ep;
738         char *salt;
739
740         rval = 1;
741         salt = pwd != NULL ? pwd->pw_passwd : "xx";
742
743         p = getpass(passwd_prompt);
744         ep = crypt(p, salt);
745
746         if (pwd) {
747                 if (!p[0] && pwd->pw_passwd[0])
748                         ep = ":";
749                 if (strcmp(ep, pwd->pw_passwd) == 0)
750                         rval = 0;
751         }
752
753         /* clear entered password */
754         memset(p, 0, strlen(p));
755         return rval;
756 }
757
758 #ifdef USE_PAM
759 /*
760  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
761  * authenticated, or 1 if not authenticated.  If some sort of PAM system
762  * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
763  * function returns -1.  This can be used as an indication that we should
764  * fall back to a different authentication mechanism.
765  */
766 static int
767 auth_pam()
768 {
769         const char *tmpl_user;
770         const void *item;
771         int rval;
772         int e;
773         static struct pam_conv conv = { misc_conv, NULL };
774
775         if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) {
776                 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
777                 return -1;
778         }
779         if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
780                 syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
781                     pam_strerror(pamh, e));
782                 return -1;
783         }
784         if (hostname != NULL &&
785             (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
786                 syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
787                     pam_strerror(pamh, e));
788                 return -1;
789         }
790         e = pam_authenticate(pamh, 0);
791         switch (e) {
792
793         case PAM_SUCCESS:
794                 /*
795                  * With PAM we support the concept of a "template"
796                  * user.  The user enters a login name which is
797                  * authenticated by PAM, usually via a remote service
798                  * such as RADIUS or TACACS+.  If authentication
799                  * succeeds, a different but related "template" name
800                  * is used for setting the credentials, shell, and
801                  * home directory.  The name the user enters need only
802                  * exist on the remote authentication server, but the
803                  * template name must be present in the local password
804                  * database.
805                  *
806                  * This is supported by two various mechanisms in the
807                  * individual modules.  However, from the application's
808                  * point of view, the template user is always passed
809                  * back as a changed value of the PAM_USER item.
810                  */
811                 if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
812                     PAM_SUCCESS) {
813                         tmpl_user = (const char *) item;
814                         if (strcmp(username, tmpl_user) != 0)
815                                 pwd = getpwnam(tmpl_user);
816                 } else
817                         syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
818                             pam_strerror(pamh, e));
819                 rval = 0;
820                 break;
821
822         case PAM_AUTH_ERR:
823         case PAM_USER_UNKNOWN:
824         case PAM_MAXTRIES:
825                 rval = 1;
826                 break;
827
828         default:
829                 syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
830                 rval = -1;
831                 break;
832         }
833
834         if (rval == 0) {
835                 e = pam_acct_mgmt(pamh, 0);
836                 if (e == PAM_NEW_AUTHTOK_REQD) {
837                         e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
838                         if (e != PAM_SUCCESS) {
839                                 syslog(LOG_ERR, "pam_chauthtok: %s",
840                                     pam_strerror(pamh, e));
841                                 rval = 1;
842                         }
843                 } else if (e != PAM_SUCCESS) {
844                         rval = 1;
845                 }
846         }
847
848         if (rval != 0) {
849                 if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
850                         syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
851                 }
852                 pamh = NULL;
853         }
854         return rval;
855 }
856
857 static int
858 export_pam_environment()
859 {
860         char    **pp;
861
862         for (pp = environ_pam; *pp != NULL; pp++) {
863                 if (ok_to_export(*pp))
864                         (void) putenv(*pp);
865                 free(*pp);
866         }
867         return PAM_SUCCESS;
868 }
869
870 /*
871  * Sanity checks on PAM environmental variables:
872  * - Make sure there is an '=' in the string.
873  * - Make sure the string doesn't run on too long.
874  * - Do not export certain variables.  This list was taken from the
875  *   Solaris pam_putenv(3) man page.
876  */
877 static int
878 ok_to_export(s)
879         const char *s;
880 {
881         static const char *noexport[] = {
882                 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
883                 "IFS", "PATH", NULL
884         };
885         const char **pp;
886         size_t n;
887
888         if (strlen(s) > 1024 || strchr(s, '=') == NULL)
889                 return 0;
890         if (strncmp(s, "LD_", 3) == 0)
891                 return 0;
892         for (pp = noexport; *pp != NULL; pp++) {
893                 n = strlen(*pp);
894                 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
895                         return 0;
896         }
897         return 1;
898 }
899 #endif /* USE_PAM */
900
901 static void
902 usage()
903 {
904
905         (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
906         exit(1);
907 }
908
909 /*
910  * Allow for authentication style and/or kerberos instance
911  */
912
913 #define NBUFSIZ         UT_NAMESIZE + 64
914
915 void
916 getloginname()
917 {
918         int ch;
919         char *p;
920         static char nbuf[NBUFSIZ];
921
922         for (;;) {
923                 (void)printf("%s", prompt);
924                 for (p = nbuf; (ch = getchar()) != '\n'; ) {
925                         if (ch == EOF) {
926                                 badlogin(username);
927                                 exit(0);
928                         }
929                         if (p < nbuf + (NBUFSIZ - 1))
930                                 *p++ = ch;
931                 }
932                 if (p > nbuf) {
933                         if (nbuf[0] == '-')
934                                 (void)fprintf(stderr,
935                                     "login names may not start with '-'.\n");
936                         else {
937                                 *p = '\0';
938                                 username = nbuf;
939                                 break;
940                         }
941                 }
942         }
943 }
944
945 int
946 rootterm(ttyn)
947         char *ttyn;
948 {
949         struct ttyent *t;
950
951         return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
952 }
953
954 volatile int motdinterrupt;
955
956 void
957 sigint(signo)
958         int signo __unused;
959 {
960         motdinterrupt = 1;
961 }
962
963 void
964 motd(motdfile)
965         char *motdfile;
966 {
967         int fd, nchars;
968         sig_t oldint;
969         char tbuf[256];
970
971         if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
972                 return;
973         motdinterrupt = 0;
974         oldint = signal(SIGINT, sigint);
975         while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
976                 (void)write(fileno(stdout), tbuf, nchars);
977         (void)signal(SIGINT, oldint);
978         (void)close(fd);
979 }
980
981 /* ARGSUSED */
982 void
983 timedout(signo)
984         int signo;
985 {
986
987         longjmp(timeout_buf, signo);
988 }
989
990
991 void
992 dolastlog(quiet)
993         int quiet;
994 {
995         struct lastlog ll;
996         int fd;
997
998         if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
999                 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
1000                 if (!quiet) {
1001                         if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1002                             ll.ll_time != 0) {
1003                                 (void)printf("Last login: %.*s ",
1004                                     24-5, (char *)ctime(&ll.ll_time));
1005                                 if (*ll.ll_host != '\0')
1006                                         (void)printf("from %.*s\n",
1007                                             (int)sizeof(ll.ll_host),
1008                                             ll.ll_host);
1009                                 else
1010                                         (void)printf("on %.*s\n",
1011                                             (int)sizeof(ll.ll_line),
1012                                             ll.ll_line);
1013                         }
1014                         (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
1015                 }
1016                 memset((void *)&ll, 0, sizeof(ll));
1017                 (void)time(&ll.ll_time);
1018                 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
1019                 if (hostname)
1020                         (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1021                 (void)write(fd, (char *)&ll, sizeof(ll));
1022                 (void)close(fd);
1023         } else {
1024                 syslog(LOG_ERR, "cannot open %s: %m", _PATH_LASTLOG);
1025         }
1026 }
1027
1028 void
1029 badlogin(name)
1030         char *name;
1031 {
1032
1033         if (failures == 0)
1034                 return;
1035         if (hostname) {
1036                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
1037                     failures, failures > 1 ? "S" : "", full_hostname);
1038                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1039                     "%d LOGIN FAILURE%s FROM %s, %s",
1040                     failures, failures > 1 ? "S" : "", full_hostname, name);
1041         } else {
1042                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
1043                     failures, failures > 1 ? "S" : "", tty);
1044                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1045                     "%d LOGIN FAILURE%s ON %s, %s",
1046                     failures, failures > 1 ? "S" : "", tty, name);
1047         }
1048         failures = 0;
1049 }
1050
1051 #undef  UNKNOWN
1052 #define UNKNOWN "su"
1053
1054 char *
1055 stypeof(ttyid)
1056         char *ttyid;
1057 {
1058         struct ttyent *t;
1059
1060         if (ttyid != NULL && *ttyid != '\0') {
1061                 t = getttynam(ttyid);
1062                 if (t != NULL && t->ty_type != NULL)
1063                         return (t->ty_type);
1064         }
1065         return (UNKNOWN);
1066 }
1067
1068 void
1069 refused(msg, rtype, lout)
1070         char *msg;
1071         char *rtype;
1072         int lout;
1073 {
1074
1075         if (msg != NULL)
1076             printf("%s.\n", msg);
1077         if (hostname)
1078                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1079                     pwd->pw_name, rtype, full_hostname, tty);
1080         else
1081                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
1082                     pwd->pw_name, rtype, tty);
1083         if (lout)
1084                 sleepexit(1);
1085 }
1086
1087 void
1088 sleepexit(eval)
1089         int eval;
1090 {
1091
1092         (void)sleep(5);
1093         exit(eval);
1094 }