]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/login/login.c
This commit was generated by cvs2svn to compensate for changes in r78844,
[FreeBSD/FreeBSD.git] / usr.bin / login / login.c
1 /*-
2  * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
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$";
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                         strncpy(full_hostname, optarg, sizeof(full_hostname)-1);
214
215                         trimdomain(optarg, UT_HOSTSIZE);
216
217                         if (strlen(optarg) > UT_HOSTSIZE) {
218                                 struct addrinfo hints, *res;
219                                 int ga_err;
220                                 
221                                 memset(&hints, 0, sizeof(hints));
222                                 hints.ai_family = AF_UNSPEC;
223                                 ga_err = getaddrinfo(optarg, NULL, &hints,
224                                     &res);
225                                 if (ga_err == 0) {
226                                         char hostbuf[MAXHOSTNAMELEN];
227
228                                         getnameinfo(res->ai_addr,
229                                             res->ai_addrlen,
230                                             hostbuf,
231                                             sizeof(hostbuf), NULL, 0,
232                                             NI_NUMERICHOST|
233                                             NI_WITHSCOPEID);
234                                         optarg = strdup(hostbuf);
235                                 } else
236                                         optarg = "invalid hostname";
237                                 if (res != NULL)
238                                         freeaddrinfo(res);
239                         }
240                         hostname = optarg;
241                         break;
242                 case 'p':
243                         pflag = 1;
244                         break;
245                 case '?':
246                 default:
247                         if (!uid)
248                                 syslog(LOG_ERR, "invalid flag %c", ch);
249                         usage();
250                 }
251         argc -= optind;
252         argv += optind;
253
254         if (*argv) {
255                 username = *argv;
256                 ask = 0;
257         } else
258                 ask = 1;
259
260         for (cnt = getdtablesize(); cnt > 2; cnt--)
261                 (void)close(cnt);
262
263         ttyn = ttyname(STDIN_FILENO);
264         if (ttyn == NULL || *ttyn == '\0') {
265                 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
266                 ttyn = tname;
267         }
268         if ((tty = strrchr(ttyn, '/')) != NULL)
269                 ++tty;
270         else
271                 tty = ttyn;
272
273         /*
274          * Get "login-retries" & "login-backoff" from default class
275          */
276         lc = login_getclass(NULL);
277         prompt = login_getcapstr(lc, "prompt", DEFAULT_PROMPT, DEFAULT_PROMPT);
278         passwd_prompt = login_getcapstr(lc, "passwd_prompt",
279             DEFAULT_PASSWD_PROMPT, DEFAULT_PASSWD_PROMPT);
280         retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES,
281             DEFAULT_RETRIES);
282         backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF,
283             DEFAULT_BACKOFF);
284         login_close(lc);
285         lc = NULL;
286
287         for (cnt = 0;; ask = 1) {
288                 if (ask) {
289                         fflag = 0;
290                         getloginname();
291                 }
292                 rootlogin = 0;
293                 rootok = rootterm(tty); /* Default (auth may change) */
294
295                 if (strlen(username) > UT_NAMESIZE)
296                         username[UT_NAMESIZE] = '\0';
297
298                 /*
299                  * Note if trying multiple user names; log failures for
300                  * previous user name, but don't bother logging one failure
301                  * for nonexistent name (mistyped username).
302                  */
303                 if (failures && strcmp(tbuf, username)) {
304                         if (failures > (pwd ? 0 : 1))
305                                 badlogin(tbuf);
306                 }
307                 (void)strncpy(tbuf, username, sizeof tbuf-1);
308                 tbuf[sizeof tbuf-1] = '\0';
309
310                 pwd = getpwnam(username);
311
312                 /*
313                  * if we have a valid account name, and it doesn't have a
314                  * password, or the -f option was specified and the caller
315                  * is root or the caller isn't changing their uid, don't
316                  * authenticate.
317                  */
318                 if (pwd != NULL) {
319                         if (pwd->pw_uid == 0)
320                                 rootlogin = 1;
321
322                         if (fflag && (uid == (uid_t)0 ||
323                             uid == (uid_t)pwd->pw_uid)) {
324                                 /* already authenticated */
325                                 break;
326                         } else if (pwd->pw_passwd[0] == '\0') {
327                                 if (!rootlogin || rootok) {
328                                         /* pretend password okay */
329                                         rval = 0;
330                                         goto ttycheck;
331                                 }
332                         }
333                 }
334
335                 fflag = 0;
336
337                 (void)setpriority(PRIO_PROCESS, 0, -4);
338
339 #ifdef USE_PAM
340                 /*
341                  * Try to authenticate using PAM.  If a PAM system error
342                  * occurs, perhaps because of a botched configuration,
343                  * then fall back to using traditional Unix authentication.
344                  */
345                 if ((rval = auth_pam()) == -1)
346 #endif /* USE_PAM */
347                         rval = auth_traditional();
348
349                 (void)setpriority(PRIO_PROCESS, 0, 0);
350
351 #ifdef USE_PAM
352                 /*
353                  * PAM authentication may have changed "pwd" to the
354                  * entry for the template user.  Check again to see if
355                  * this is a root login after all.
356                  */
357                 if (pwd != NULL && pwd->pw_uid == 0)
358                         rootlogin = 1;
359 #endif /* USE_PAM */
360
361         ttycheck:
362                 /*
363                  * If trying to log in as root without Kerberos,
364                  * but with insecure terminal, refuse the login attempt.
365                  */
366                 if (pwd && !rval) {
367                         if (rootlogin && !rootok)
368                                 refused(NULL, "NOROOT", 0);
369                         else    /* valid password & authenticated */
370                                 break;
371                 }
372
373                 (void)printf("Login incorrect\n");
374                 failures++;
375
376                 /*
377                  * we allow up to 'retry' (10) tries,
378                  * but after 'backoff' (3) we start backing off
379                  */
380                 if (++cnt > backoff) {
381                         if (cnt >= retries) {
382                                 badlogin(username);
383                                 sleepexit(1);
384                         }
385                         sleep((u_int)((cnt - backoff) * 5));
386                 }
387         }
388
389         /* committed to login -- turn off timeout */
390         (void)alarm((u_int)0);
391         (void)signal(SIGHUP, SIG_DFL);
392
393         endpwent();
394
395         /*
396          * Establish the login class.
397          */
398         lc = login_getpwclass(pwd);
399
400         /* if user not super-user, check for disabled logins */
401         if (!rootlogin)
402                 auth_checknologin(lc);
403
404         quietlog = login_getcapbool(lc, "hushlogin", 0);
405         /* Switching needed for NFS with root access disabled */
406         (void)setegid(pwd->pw_gid);
407         (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
408         if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
409                 if (login_getcapbool(lc, "requirehome", 0))
410                         refused("Home directory not available", "HOMEDIR", 1);
411                 if (chdir("/") < 0) 
412                         refused("Cannot find root directory", "ROOTDIR", 1);
413                 if (!quietlog || *pwd->pw_dir)
414                         printf("No home directory.\nLogging in with home = \"/\".\n");
415                 pwd->pw_dir = "/";
416         }
417         (void)seteuid(euid);
418         (void)setegid(egid);
419         if (!quietlog)
420                 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
421
422         if (pwd->pw_change || pwd->pw_expire)
423                 (void)gettimeofday(&tp, (struct timezone *)NULL);
424
425 #define DEFAULT_WARN  (2L * 7L * 86400L)  /* Two weeks */
426
427         warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN,
428             DEFAULT_WARN);
429
430         if (pwd->pw_expire) {
431                 if (tp.tv_sec >= pwd->pw_expire) {
432                         refused("Sorry -- your account has expired", "EXPIRED",
433                             1);
434                 } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
435                         (void)printf("Warning: your account expires on %s",
436                             ctime(&pwd->pw_expire));
437         }
438
439         warntime = login_getcaptime(lc, "warnpassword", DEFAULT_WARN,
440             DEFAULT_WARN);
441
442         changepass = 0;
443         if (pwd->pw_change) {
444                 if (tp.tv_sec >= pwd->pw_change) {
445                         (void)printf("Sorry -- your password has expired.\n");
446                         changepass = 1;
447                         syslog(LOG_INFO, "%s Password expired - forcing change",
448                             pwd->pw_name);
449                 } else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog)
450                         (void)printf("Warning: your password expires on %s",
451                             ctime(&pwd->pw_change));
452         }
453
454         if (lc != NULL) {
455                 if (hostname) {
456                         struct addrinfo hints, *res;
457                         int ga_err;
458
459                         memset(&hints, 0, sizeof(hints));
460                         hints.ai_family = AF_UNSPEC;
461                         ga_err = getaddrinfo(full_hostname, NULL, &hints,
462                                              &res);
463                         if (ga_err == 0) {
464                                 char hostbuf[MAXHOSTNAMELEN];
465
466                                 getnameinfo(res->ai_addr, res->ai_addrlen,
467                                     hostbuf, sizeof(hostbuf), NULL, 0,
468                                     NI_NUMERICHOST|NI_WITHSCOPEID);
469                                 optarg = strdup(hostbuf);
470                         } else
471                                 optarg = NULL;
472                         if (res != NULL)
473                                 freeaddrinfo(res);
474                         if (!auth_hostok(lc, full_hostname, optarg))
475                                 refused("Permission denied", "HOST", 1);
476                 }
477
478                 if (!auth_ttyok(lc, tty))
479                         refused("Permission denied", "TTY", 1);
480
481                 if (!auth_timeok(lc, time(NULL)))
482                         refused("Logins not available right now", "TIME", 1);
483         }
484         shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
485         if (*pwd->pw_shell == '\0')
486                 pwd->pw_shell = _PATH_BSHELL;
487         if (*shell == '\0')   /* Not overridden */
488                 shell = pwd->pw_shell;
489         if ((shell = strdup(shell)) == NULL) {
490                 syslog(LOG_NOTICE, "memory allocation error");
491                 sleepexit(1);
492         }
493
494 #ifdef LOGIN_ACCESS
495         if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
496                 refused("Permission denied", "ACCESS", 1);
497 #endif /* LOGIN_ACCESS */
498
499         /* Nothing else left to fail -- really log in. */
500         memset((void *)&utmp, 0, sizeof(utmp));
501         (void)time(&utmp.ut_time);
502         (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
503         if (hostname)
504                 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
505         (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
506         login(&utmp);
507
508         dolastlog(quietlog);
509
510         /*
511          * Set device protections, depending on what terminal the
512          * user is logged in. This feature is used on Suns to give
513          * console users better privacy.
514          */
515         login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
516
517         /*
518          * Clear flags of the tty.  None should be set, and when the
519          * user sets them otherwise, this can cause the chown to fail.
520          * Since it isn't clear that flags are useful on character
521          * devices, we just clear them.
522          */
523         if (chflags(ttyn, 0) && errno != EOPNOTSUPP)
524                 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
525         if (chown(ttyn, pwd->pw_uid,
526             (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
527                 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
528
529
530         /*
531          * Preserve TERM if it happens to be already set.
532          */
533         if ((term = getenv("TERM")) != NULL)
534                 term = strdup(term);
535
536         /*
537          * Exclude cons/vt/ptys only, assume dialup otherwise
538          * TODO: Make dialup tty determination a library call
539          * for consistency (finger etc.)
540          */
541         if (hostname==NULL && isdialuptty(tty))
542                 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
543
544 #ifdef LOGALL
545         /*
546          * Syslog each successful login, so we don't have to watch hundreds
547          * of wtmp or lastlogin files.
548          */
549         if (hostname)
550                 syslog(LOG_INFO, "login from %s on %s as %s",
551                        full_hostname, tty, pwd->pw_name);
552         else
553                 syslog(LOG_INFO, "login on %s as %s",
554                        tty, pwd->pw_name);
555 #endif
556
557         /*
558          * If fflag is on, assume caller/authenticator has logged root login.
559          */
560         if (rootlogin && fflag == 0)
561         {
562                 if (hostname)
563                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
564                             username, tty, full_hostname);
565                 else
566                         syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
567                             username, tty);
568         }
569
570         /*
571          * Destroy environment unless user has requested its preservation.
572          * We need to do this before setusercontext() because that may
573          * set or reset some environment variables.
574          */
575         if (!pflag)
576                 environ = envinit;
577
578 #ifdef USE_PAM
579         /*
580          * Add any environmental variables that the
581          * PAM modules may have set.
582          */
583         if (pamh) {
584                 environ_pam = pam_getenvlist(pamh);
585                 if (environ_pam)
586                         export_pam_environment();
587         }
588 #endif /* USE_PAM */
589
590         /*
591          * PAM modules might add supplementary groups during pam_setcred().
592          */
593         if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
594                 syslog(LOG_ERR, "setusercontext() failed - exiting");
595                 exit(1);
596         }
597
598 #ifdef USE_PAM
599         if (pamh) {
600                 if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
601                         syslog(LOG_ERR, "pam_open_session: %s",
602                             pam_strerror(pamh, e));
603                 } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
604                     != PAM_SUCCESS) {
605                         syslog(LOG_ERR, "pam_setcred: %s",
606                             pam_strerror(pamh, e));
607                 }
608
609                 /*
610                  * We must fork() before setuid() because we need to call
611                  * pam_close_session() as root.
612                  */
613                 pid = fork();
614                 if (pid < 0) {
615                         err(1, "fork");
616                         PAM_END;
617                         exit(0);
618                 } else if (pid) {
619                         /* parent - wait for child to finish, then cleanup
620                            session */
621                         wait(NULL);
622                         PAM_END;
623                         exit(0);
624                 } else {
625                         if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
626                                 syslog(LOG_ERR, "pam_end: %s",
627                                     pam_strerror(pamh, e));
628                 }
629         }
630 #endif /* USE_PAM */
631
632         /*
633          * We don't need to be root anymore, so
634          * set the user and session context
635          */
636         if (setlogin(username) != 0) {
637                 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
638                 exit(1);
639         }
640         if (setusercontext(lc, pwd, pwd->pw_uid,
641             LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
642                 syslog(LOG_ERR, "setusercontext() failed - exiting");
643                 exit(1);
644         }
645
646         (void)setenv("SHELL", pwd->pw_shell, 1);
647         (void)setenv("HOME", pwd->pw_dir, 1);
648         if (term != NULL && *term != '\0')
649                 (void)setenv("TERM", term, 1);          /* Preset overrides */
650         else {
651                 (void)setenv("TERM", stypeof(tty), 0);  /* Fallback doesn't */
652         }
653         (void)setenv("LOGNAME", username, 1);
654         (void)setenv("USER", username, 1);
655         (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
656
657         if (!quietlog) {
658                 char    *cw;
659
660                 cw = login_getcapstr(lc, "copyright", NULL, NULL);
661                 if (cw != NULL && access(cw, F_OK) == 0)
662                         motd(cw);
663                 else
664                     (void)printf("%s\n\t%s %s\n",
665         "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
666         "The Regents of the University of California. ",
667         "All rights reserved.");
668
669                 (void)printf("\n");
670
671                 cw = login_getcapstr(lc, "welcome", NULL, NULL);
672                 if (cw == NULL || access(cw, F_OK) != 0)
673                         cw = _PATH_MOTDFILE;
674                 motd(cw);
675
676                 cw = getenv("MAIL");    /* $MAIL may have been set by class */
677                 if (cw != NULL) {
678                         strncpy(tbuf, cw, sizeof(tbuf));
679                         tbuf[sizeof(tbuf)-1] = '\0';
680                 } else
681                         snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR,
682                             pwd->pw_name);
683                 if (stat(tbuf, &st) == 0 && st.st_size != 0)
684                         (void)printf("You have %smail.\n",
685                             (st.st_mtime > st.st_atime) ? "new " : "");
686         }
687
688         login_close(lc);
689
690         (void)signal(SIGALRM, SIG_DFL);
691         (void)signal(SIGQUIT, SIG_DFL);
692         (void)signal(SIGINT, SIG_DFL);
693         (void)signal(SIGTSTP, SIG_IGN);
694
695         if (changepass) {
696                 if (system(_PATH_CHPASS) != 0)
697                         sleepexit(1);
698         }
699
700         /*
701          * Login shells have a leading '-' in front of argv[0]
702          */
703         tbuf[0] = '-';
704         (void)strcpy(tbuf + 1,
705             (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell);
706
707         execlp(shell, tbuf, 0);
708         err(1, "%s", shell);
709 }
710
711 static int
712 auth_traditional()
713 {
714         int rval;
715         char *p;
716         char *ep;
717         char *salt;
718
719         rval = 1;
720         salt = pwd != NULL ? pwd->pw_passwd : "xx";
721
722         p = getpass(passwd_prompt);
723         ep = crypt(p, salt);
724
725         if (pwd) {
726                 if (!p[0] && pwd->pw_passwd[0])
727                         ep = ":";
728                 if (strcmp(ep, pwd->pw_passwd) == 0)
729                         rval = 0;
730         }
731
732         /* clear entered password */
733         memset(p, 0, strlen(p));
734         return rval;
735 }
736
737 #ifdef USE_PAM
738 /*
739  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
740  * authenticated, or 1 if not authenticated.  If some sort of PAM system
741  * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
742  * function returns -1.  This can be used as an indication that we should
743  * fall back to a different authentication mechanism.
744  */
745 static int
746 auth_pam()
747 {
748         const char *tmpl_user;
749         const void *item;
750         int rval;
751         int e;
752         static struct pam_conv conv = { misc_conv, NULL };
753
754         if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) {
755                 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
756                 return -1;
757         }
758         if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
759                 syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
760                     pam_strerror(pamh, e));
761                 return -1;
762         }
763         if (hostname != NULL &&
764             (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
765                 syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
766                     pam_strerror(pamh, e));
767                 return -1;
768         }
769         e = pam_authenticate(pamh, 0);
770         switch (e) {
771
772         case PAM_SUCCESS:
773                 /*
774                  * With PAM we support the concept of a "template"
775                  * user.  The user enters a login name which is
776                  * authenticated by PAM, usually via a remote service
777                  * such as RADIUS or TACACS+.  If authentication
778                  * succeeds, a different but related "template" name
779                  * is used for setting the credentials, shell, and
780                  * home directory.  The name the user enters need only
781                  * exist on the remote authentication server, but the
782                  * template name must be present in the local password
783                  * database.
784                  *
785                  * This is supported by two various mechanisms in the
786                  * individual modules.  However, from the application's
787                  * point of view, the template user is always passed
788                  * back as a changed value of the PAM_USER item.
789                  */
790                 if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
791                     PAM_SUCCESS) {
792                         tmpl_user = (const char *) item;
793                         if (strcmp(username, tmpl_user) != 0)
794                                 pwd = getpwnam(tmpl_user);
795                 } else
796                         syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
797                             pam_strerror(pamh, e));
798                 rval = 0;
799                 break;
800
801         case PAM_AUTH_ERR:
802         case PAM_USER_UNKNOWN:
803         case PAM_MAXTRIES:
804                 rval = 1;
805                 break;
806
807         default:
808                 syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
809                 rval = -1;
810                 break;
811         }
812
813         if (rval == 0) {
814                 e = pam_acct_mgmt(pamh, 0);
815                 if (e == PAM_NEW_AUTHTOK_REQD) {
816                         e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
817                         if (e != PAM_SUCCESS) {
818                                 syslog(LOG_ERR, "pam_chauthtok: %s",
819                                     pam_strerror(pamh, e));
820                                 rval = 1;
821                         }
822                 } else if (e != PAM_SUCCESS) {
823                         rval = 1;
824                 }
825         }
826
827         if (rval != 0) {
828                 if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
829                         syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
830                 }
831                 pamh = NULL;
832         }
833         return rval;
834 }
835
836 static int
837 export_pam_environment()
838 {
839         char    **pp;
840
841         for (pp = environ_pam; *pp != NULL; pp++) {
842                 if (ok_to_export(*pp))
843                         (void) putenv(*pp);
844                 free(*pp);
845         }
846         return PAM_SUCCESS;
847 }
848
849 /*
850  * Sanity checks on PAM environmental variables:
851  * - Make sure there is an '=' in the string.
852  * - Make sure the string doesn't run on too long.
853  * - Do not export certain variables.  This list was taken from the
854  *   Solaris pam_putenv(3) man page.
855  */
856 static int
857 ok_to_export(s)
858         const char *s;
859 {
860         static const char *noexport[] = {
861                 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
862                 "IFS", "PATH", NULL
863         };
864         const char **pp;
865         size_t n;
866
867         if (strlen(s) > 1024 || strchr(s, '=') == NULL)
868                 return 0;
869         if (strncmp(s, "LD_", 3) == 0)
870                 return 0;
871         for (pp = noexport; *pp != NULL; pp++) {
872                 n = strlen(*pp);
873                 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
874                         return 0;
875         }
876         return 1;
877 }
878 #endif /* USE_PAM */
879
880 static void
881 usage()
882 {
883
884         (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
885         exit(1);
886 }
887
888 /*
889  * Allow for authentication style and/or kerberos instance
890  */
891
892 #define NBUFSIZ         UT_NAMESIZE + 64
893
894 void
895 getloginname()
896 {
897         int ch;
898         char *p;
899         static char nbuf[NBUFSIZ];
900
901         for (;;) {
902                 (void)printf(prompt);
903                 for (p = nbuf; (ch = getchar()) != '\n'; ) {
904                         if (ch == EOF) {
905                                 badlogin(username);
906                                 exit(0);
907                         }
908                         if (p < nbuf + (NBUFSIZ - 1))
909                                 *p++ = ch;
910                 }
911                 if (p > nbuf) {
912                         if (nbuf[0] == '-')
913                                 (void)fprintf(stderr,
914                                     "login names may not start with '-'.\n");
915                         else {
916                                 *p = '\0';
917                                 username = nbuf;
918                                 break;
919                         }
920                 }
921         }
922 }
923
924 int
925 rootterm(ttyn)
926         char *ttyn;
927 {
928         struct ttyent *t;
929
930         return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
931 }
932
933 volatile int motdinterrupt;
934
935 /* ARGSUSED */
936 void
937 sigint(signo)
938         int signo;
939 {
940         motdinterrupt = 1;
941 }
942
943 void
944 motd(motdfile)
945         char *motdfile;
946 {
947         int fd, nchars;
948         sig_t oldint;
949         char tbuf[256];
950
951         if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
952                 return;
953         motdinterrupt = 0;
954         oldint = signal(SIGINT, sigint);
955         while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
956                 (void)write(fileno(stdout), tbuf, nchars);
957         (void)signal(SIGINT, oldint);
958         (void)close(fd);
959 }
960
961 /* ARGSUSED */
962 void
963 timedout(signo)
964         int signo;
965 {
966
967         longjmp(timeout_buf, signo);
968 }
969
970
971 void
972 dolastlog(quiet)
973         int quiet;
974 {
975         struct lastlog ll;
976         int fd;
977
978         if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
979                 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
980                 if (!quiet) {
981                         if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
982                             ll.ll_time != 0) {
983                                 (void)printf("Last login: %.*s ",
984                                     24-5, (char *)ctime(&ll.ll_time));
985                                 if (*ll.ll_host != '\0')
986                                         (void)printf("from %.*s\n",
987                                             (int)sizeof(ll.ll_host),
988                                             ll.ll_host);
989                                 else
990                                         (void)printf("on %.*s\n",
991                                             (int)sizeof(ll.ll_line),
992                                             ll.ll_line);
993                         }
994                         (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
995                 }
996                 memset((void *)&ll, 0, sizeof(ll));
997                 (void)time(&ll.ll_time);
998                 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
999                 if (hostname)
1000                         (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1001                 (void)write(fd, (char *)&ll, sizeof(ll));
1002                 (void)close(fd);
1003         }
1004 }
1005
1006 void
1007 badlogin(name)
1008         char *name;
1009 {
1010
1011         if (failures == 0)
1012                 return;
1013         if (hostname) {
1014                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
1015                     failures, failures > 1 ? "S" : "", full_hostname);
1016                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1017                     "%d LOGIN FAILURE%s FROM %s, %s",
1018                     failures, failures > 1 ? "S" : "", full_hostname, name);
1019         } else {
1020                 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
1021                     failures, failures > 1 ? "S" : "", tty);
1022                 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1023                     "%d LOGIN FAILURE%s ON %s, %s",
1024                     failures, failures > 1 ? "S" : "", tty, name);
1025         }
1026         failures = 0;
1027 }
1028
1029 #undef  UNKNOWN
1030 #define UNKNOWN "su"
1031
1032 char *
1033 stypeof(ttyid)
1034         char *ttyid;
1035 {
1036         struct ttyent *t;
1037
1038         return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
1039 }
1040
1041 void
1042 refused(msg, rtype, lout)
1043         char *msg;
1044         char *rtype;
1045         int lout;
1046 {
1047
1048         if (msg != NULL)
1049             printf("%s.\n", msg);
1050         if (hostname)
1051                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1052                     pwd->pw_name, rtype, full_hostname, tty);
1053         else
1054                 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
1055                     pwd->pw_name, rtype, tty);
1056         if (lout)
1057                 sleepexit(1);
1058 }
1059
1060 void
1061 sleepexit(eval)
1062         int eval;
1063 {
1064
1065         (void)sleep(5);
1066         exit(eval);
1067 }