login.c revision 1.69 1 /* $NetBSD: login.c,v 1.69 2002/09/20 21:01:31 itojun Exp $ */
2
3 /*-
4 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT(
39 "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
40 The Regents of the University of California. All rights reserved.\n");
41 #endif /* not lint */
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
46 #endif
47 __RCSID("$NetBSD: login.c,v 1.69 2002/09/20 21:01:31 itojun Exp $");
48 #endif /* not lint */
49
50 /*
51 * login [ name ]
52 * login -h hostname (for telnetd, etc.)
53 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
54 */
55
56 #include <sys/param.h>
57 #include <sys/stat.h>
58 #include <sys/time.h>
59 #include <sys/resource.h>
60 #include <sys/file.h>
61 #include <sys/wait.h>
62 #include <sys/socket.h>
63
64 #include <err.h>
65 #include <errno.h>
66 #include <grp.h>
67 #include <pwd.h>
68 #include <setjmp.h>
69 #include <signal.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <syslog.h>
74 #include <time.h>
75 #include <ttyent.h>
76 #include <tzfile.h>
77 #include <unistd.h>
78 #ifdef SUPPORT_UTMP
79 #include <utmp.h>
80 #endif
81 #ifdef SUPPORT_UTMPX
82 #include <utmpx.h>
83 #endif
84 #include <util.h>
85 #ifdef SKEY
86 #include <skey.h>
87 #endif
88 #ifdef KERBEROS5
89 #include <krb5/krb5.h>
90 #include <com_err.h>
91 #endif
92 #ifdef LOGIN_CAP
93 #include <login_cap.h>
94 #endif
95
96 #include "pathnames.h"
97
98 #ifdef KERBEROS5
99 int login_krb5_get_tickets = 1;
100 int login_krb4_get_tickets = 0;
101 int login_krb5_forwardable_tgt = 0;
102 int login_krb5_retain_ccache = 0;
103 #endif
104
105 void badlogin __P((char *));
106 void checknologin __P((char *));
107 #ifdef SUPPORT_UTMP
108 static void doutmp __P((void));
109 static void dolastlog __P((int));
110 #endif
111 #ifdef SUPPORT_UTMPX
112 static void doutmpx __P((void));
113 static void dolastlogx __P((int));
114 #endif
115 static void update_db __P((int));
116 void getloginname __P((void));
117 int main __P((int, char *[]));
118 void motd __P((char *));
119 int rootterm __P((char *));
120 void sigint __P((int));
121 void sleepexit __P((int));
122 const char *stypeof __P((const char *));
123 void timedout __P((int));
124 #if defined(KERBEROS)
125 int klogin __P((struct passwd *, char *, char *, char *));
126 void kdestroy __P((void));
127 #endif
128 #ifdef KERBEROS5
129 int k5login __P((struct passwd *, char *, char *, char *));
130 void k5destroy __P((void));
131 int k5_read_creds __P((char*));
132 int k5_write_creds __P((void));
133 #endif
134 #if defined(KERBEROS) || defined(KERBEROS5)
135 void dofork __P((void));
136 #endif
137
138 #define TTYGRPNAME "tty" /* name of group to own ttys */
139
140 #define DEFAULT_BACKOFF 3
141 #define DEFAULT_RETRIES 10
142
143 /*
144 * This bounds the time given to login. Not a define so it can
145 * be patched on machines where it's too small.
146 */
147 u_int timeout = 300;
148
149 #if defined(KERBEROS) || defined(KERBEROS5)
150 int notickets = 1;
151 char *instance;
152 int has_ccache = 0;
153 #endif
154 #ifdef KERBEROS
155 extern char *krbtkfile_env;
156 extern int krb_configured;
157 #endif
158 #ifdef KERBEROS5
159 extern krb5_context kcontext;
160 extern int have_forward;
161 extern char *krb5tkfile_env;
162 extern int krb5_configured;
163 #endif
164
165 #if defined(KERBEROS) && defined(KERBEROS5)
166 #define KERBEROS_CONFIGURED (krb_configured || krb5_configured)
167 #elif defined(KERBEROS)
168 #define KERBEROS_CONFIGURED krb_configured
169 #elif defined(KERBEROS5)
170 #define KERBEROS_CONFIGURED krb5_configured
171 #endif
172
173 struct passwd *pwd;
174 int failures;
175 char term[64], *envinit[1], *hostname, *username, *tty;
176 struct timeval now;
177 struct sockaddr_storage ss;
178
179 static const char copyrightstr[] = "\
180 Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002\n\
181 \tThe NetBSD Foundation, Inc. All rights reserved.\n\
182 Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994\n\
183 \tThe Regents of the University of California. All rights reserved.\n\n";
184
185 int
186 main(argc, argv)
187 int argc;
188 char *argv[];
189 {
190 extern char **environ;
191 struct group *gr;
192 struct stat st;
193 int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
194 int Fflag;
195 uid_t uid, saved_uid;
196 gid_t saved_gid, saved_gids[NGROUPS_MAX];
197 int nsaved_gids;
198 char *domain, *p, *ttyn, *pwprompt;
199 const char *salt;
200 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
201 char localhost[MAXHOSTNAMELEN + 1];
202 int need_chpass, require_chpass;
203 int login_retries = DEFAULT_RETRIES,
204 login_backoff = DEFAULT_BACKOFF;
205 time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
206 #ifdef KERBEROS5
207 krb5_error_code kerror;
208 #endif
209 #if defined(KERBEROS) || defined(KERBEROS5)
210 int got_tickets = 0;
211 #endif
212 #ifdef LOGIN_CAP
213 char *shell = NULL;
214 login_cap_t *lc = NULL;
215 #endif
216
217 tbuf[0] = '\0';
218 rval = 0;
219 pwprompt = NULL;
220 need_chpass = require_chpass = 0;
221
222 (void)signal(SIGALRM, timedout);
223 (void)alarm(timeout);
224 (void)signal(SIGQUIT, SIG_IGN);
225 (void)signal(SIGINT, SIG_IGN);
226 (void)setpriority(PRIO_PROCESS, 0, 0);
227
228 openlog("login", 0, LOG_AUTH);
229
230 /*
231 * -p is used by getty to tell login not to destroy the environment
232 * -f is used to skip a second login authentication
233 * -h is used by other servers to pass the name of the remote host to
234 * login so that it may be placed in utmp/utmpx and wtmp/wtmpx
235 * -s is used to force use of S/Key or equivalent.
236 */
237 domain = NULL;
238 if (gethostname(localhost, sizeof(localhost)) < 0)
239 syslog(LOG_ERR, "couldn't get local hostname: %m");
240 else
241 domain = strchr(localhost, '.');
242 localhost[sizeof(localhost) - 1] = '\0';
243
244 Fflag = fflag = hflag = pflag = sflag = 0;
245 #ifdef KERBEROS5
246 have_forward = 0;
247 #endif
248 uid = getuid();
249 while ((ch = getopt(argc, argv, "Ffh:ps")) != -1)
250 switch (ch) {
251 case 'F':
252 Fflag = 1;
253 /* FALLTHROUGH */
254 case 'f':
255 fflag = 1;
256 break;
257 case 'h':
258 if (uid)
259 errx(1, "-h option: %s", strerror(EPERM));
260 hflag = 1;
261 if (domain && (p = strchr(optarg, '.')) != NULL &&
262 strcasecmp(p, domain) == 0)
263 *p = 0;
264 hostname = optarg;
265 break;
266 case 'p':
267 pflag = 1;
268 break;
269 case 's':
270 sflag = 1;
271 break;
272 default:
273 case '?':
274 (void)fprintf(stderr,
275 "usage: login [-fps] [-h hostname] [username]\n");
276 exit(1);
277 }
278 argc -= optind;
279 argv += optind;
280
281 if (*argv) {
282 username = *argv;
283 ask = 0;
284 } else
285 ask = 1;
286
287 for (cnt = getdtablesize(); cnt > 2; cnt--)
288 (void)close(cnt);
289
290 ttyn = ttyname(STDIN_FILENO);
291 if (ttyn == NULL || *ttyn == '\0') {
292 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
293 ttyn = tname;
294 }
295 if ((tty = strrchr(ttyn, '/')) != NULL)
296 ++tty;
297 else
298 tty = ttyn;
299
300 #ifdef LOGIN_CAP
301 /* Get "login-retries" and "login-backoff" from default class */
302 if ((lc = login_getclass(NULL)) != NULL) {
303 login_retries = (int)login_getcapnum(lc, "login-retries",
304 DEFAULT_RETRIES, DEFAULT_RETRIES);
305 login_backoff = (int)login_getcapnum(lc, "login-backoff",
306 DEFAULT_BACKOFF, DEFAULT_BACKOFF);
307 login_close(lc);
308 lc = NULL;
309 }
310 #endif
311
312 #ifdef KERBEROS5
313 kerror = krb5_init_context(&kcontext);
314 if (kerror) {
315 /*
316 * If Kerberos is not configured, that is, we are
317 * not using Kerberos, do not log the error message.
318 * However, if Kerberos is configured, and the
319 * context init fails for some other reason, we need
320 * to issue a no tickets warning to the user when the
321 * login succeeds.
322 */
323 if (kerror != ENXIO) { /* XXX NetBSD-local Heimdal hack */
324 syslog(LOG_NOTICE,
325 "%s when initializing Kerberos context",
326 error_message(kerror));
327 krb5_configured = 1;
328 }
329 login_krb5_get_tickets = 0;
330 }
331 #endif /* KERBEROS5 */
332
333 for (cnt = 0;; ask = 1) {
334 #if defined(KERBEROS)
335 kdestroy();
336 #endif
337 #if defined(KERBEROS5)
338 if (login_krb5_get_tickets)
339 k5destroy();
340 #endif
341 if (ask) {
342 fflag = 0;
343 getloginname();
344 }
345 rootlogin = 0;
346 #ifdef KERBEROS
347 if ((instance = strchr(username, '.')) != NULL)
348 *instance++ = '\0';
349 else
350 instance = "";
351 #endif
352 #ifdef KERBEROS5
353 if ((instance = strchr(username, '/')) != NULL)
354 *instance++ = '\0';
355 else
356 instance = "";
357 #endif
358 if (strlen(username) > MAXLOGNAME)
359 username[MAXLOGNAME] = '\0';
360
361 /*
362 * Note if trying multiple user names; log failures for
363 * previous user name, but don't bother logging one failure
364 * for nonexistent name (mistyped username).
365 */
366 if (failures && strcmp(tbuf, username)) {
367 if (failures > (pwd ? 0 : 1))
368 badlogin(tbuf);
369 failures = 0;
370 }
371 (void)strncpy(tbuf, username, sizeof(tbuf) - 1);
372 tbuf[sizeof(tbuf) - 1] = '\0';
373
374 if ((pwd = getpwnam(username)) != NULL)
375 salt = pwd->pw_passwd;
376 else
377 salt = "xx";
378
379 #ifdef LOGIN_CAP
380 /*
381 * Establish the class now, before we might goto
382 * within the next block. pwd can be NULL since it
383 * falls back to the "default" class if it is.
384 */
385 lc = login_getclass(pwd ? pwd->pw_class : NULL);
386 #endif
387 /*
388 * if we have a valid account name, and it doesn't have a
389 * password, or the -f option was specified and the caller
390 * is root or the caller isn't changing their uid, don't
391 * authenticate.
392 */
393 if (pwd) {
394 if (pwd->pw_uid == 0)
395 rootlogin = 1;
396
397 if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
398 /* already authenticated */
399 #ifdef KERBEROS5
400 if (login_krb5_get_tickets && Fflag)
401 k5_read_creds(username);
402 #endif
403 break;
404 } else if (pwd->pw_passwd[0] == '\0') {
405 /* pretend password okay */
406 rval = 0;
407 goto ttycheck;
408 }
409 }
410
411 fflag = 0;
412
413 (void)setpriority(PRIO_PROCESS, 0, -4);
414
415 #ifdef SKEY
416 if (skey_haskey(username) == 0) {
417 static char skprompt[80];
418 const char *skinfo = skey_keyinfo(username);
419
420 (void)snprintf(skprompt, sizeof(skprompt)-1,
421 "Password [%s]:",
422 skinfo ? skinfo : "error getting challenge");
423 pwprompt = skprompt;
424 } else
425 #endif
426 pwprompt = "Password:";
427
428 p = getpass(pwprompt);
429
430 if (pwd == NULL) {
431 rval = 1;
432 goto skip;
433 }
434 #ifdef KERBEROS
435 if (
436 #ifdef KERBEROS5
437 /* allow a user to get both krb4 and krb5 tickets, if
438 * desired. If krb5 is compiled in, the default action
439 * is to ignore krb4 and get krb5 tickets, but the user
440 * can override this in the krb5.conf. */
441 login_krb4_get_tickets &&
442 #endif
443 klogin(pwd, instance, localhost, p) == 0) {
444 rval = 0;
445 got_tickets = 1;
446 }
447 #endif
448 #ifdef KERBEROS5
449 if (login_krb5_get_tickets &&
450 k5login(pwd, instance, localhost, p) == 0) {
451 rval = 0;
452 got_tickets = 1;
453 }
454 #endif
455 #if defined(KERBEROS) || defined(KERBEROS5)
456 if (got_tickets)
457 goto skip;
458 #endif
459 #ifdef SKEY
460 if (skey_haskey(username) == 0 &&
461 skey_passcheck(username, p) != -1) {
462 rval = 0;
463 goto skip;
464 }
465 #endif
466 if (!sflag && *pwd->pw_passwd != '\0' &&
467 !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
468 rval = 0;
469 require_chpass = 1;
470 goto skip;
471 }
472 rval = 1;
473
474 skip:
475 memset(p, 0, strlen(p));
476
477 (void)setpriority(PRIO_PROCESS, 0, 0);
478
479 ttycheck:
480 /*
481 * If trying to log in as root without Kerberos,
482 * but with insecure terminal, refuse the login attempt.
483 */
484 if (pwd && !rval && rootlogin && !rootterm(tty)) {
485 (void)fprintf(stderr,
486 "%s login refused on this terminal.\n",
487 pwd->pw_name);
488 if (hostname)
489 syslog(LOG_NOTICE,
490 "LOGIN %s REFUSED FROM %s ON TTY %s",
491 pwd->pw_name, hostname, tty);
492 else
493 syslog(LOG_NOTICE,
494 "LOGIN %s REFUSED ON TTY %s",
495 pwd->pw_name, tty);
496 continue;
497 }
498
499 if (pwd && !rval)
500 break;
501
502 (void)printf("Login incorrect\n");
503 failures++;
504 cnt++;
505 /* we allow 10 tries, but after 3 we start backing off */
506 if (cnt > login_backoff) {
507 if (cnt >= login_retries) {
508 badlogin(username);
509 sleepexit(1);
510 }
511 sleep((u_int)((cnt - 3) * 5));
512 }
513 }
514
515 /* committed to login -- turn off timeout */
516 (void)alarm((u_int)0);
517
518 endpwent();
519
520 /* if user not super-user, check for disabled logins */
521 #ifdef LOGIN_CAP
522 if (!login_getcapbool(lc, "ignorenologin", rootlogin))
523 checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
524 #else
525 if (!rootlogin)
526 checknologin(NULL);
527 #endif
528
529 #ifdef LOGIN_CAP
530 quietlog = login_getcapbool(lc, "hushlogin", 0);
531 #else
532 quietlog = 0;
533 #endif
534 /* Temporarily give up special privileges so we can change */
535 /* into NFS-mounted homes that are exported for non-root */
536 /* access and have mode 7x0 */
537 saved_uid = geteuid();
538 saved_gid = getegid();
539 nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
540
541 (void)setegid(pwd->pw_gid);
542 initgroups(username, pwd->pw_gid);
543 (void)seteuid(pwd->pw_uid);
544
545 if (chdir(pwd->pw_dir) < 0) {
546 #ifdef LOGIN_CAP
547 if (login_getcapbool(lc, "requirehome", 0)) {
548 (void)printf("Home directory %s required\n",
549 pwd->pw_dir);
550 sleepexit(1);
551 }
552 #endif
553 (void)printf("No home directory %s!\n", pwd->pw_dir);
554 if (chdir("/"))
555 exit(0);
556 pwd->pw_dir = "/";
557 (void)printf("Logging in with home = \"/\".\n");
558 }
559
560 if (!quietlog)
561 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
562
563 /* regain special privileges */
564 (void)seteuid(saved_uid);
565 setgroups(nsaved_gids, saved_gids);
566 (void)setegid(saved_gid);
567
568 #ifdef LOGIN_CAP
569 pw_warntime = login_getcaptime(lc, "password-warn",
570 _PASSWORD_WARNDAYS * SECSPERDAY,
571 _PASSWORD_WARNDAYS * SECSPERDAY);
572 #endif
573
574 (void)gettimeofday(&now, (struct timezone *)NULL);
575 if (pwd->pw_expire) {
576 if (now.tv_sec >= pwd->pw_expire) {
577 (void)printf("Sorry -- your account has expired.\n");
578 sleepexit(1);
579 } else if (pwd->pw_expire - now.tv_sec < pw_warntime &&
580 !quietlog)
581 (void)printf("Warning: your account expires on %s",
582 ctime(&pwd->pw_expire));
583 }
584 if (pwd->pw_change) {
585 if (pwd->pw_change == _PASSWORD_CHGNOW)
586 need_chpass = 1;
587 else if (now.tv_sec >= pwd->pw_change) {
588 (void)printf("Sorry -- your password has expired.\n");
589 sleepexit(1);
590 } else if (pwd->pw_change - now.tv_sec < pw_warntime &&
591 !quietlog)
592 (void)printf("Warning: your password expires on %s",
593 ctime(&pwd->pw_change));
594
595 }
596 /* Nothing else left to fail -- really log in. */
597 update_db(quietlog);
598
599 (void)chown(ttyn, pwd->pw_uid,
600 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
601
602 if (ttyaction(ttyn, "login", pwd->pw_name))
603 (void)printf("Warning: ttyaction failed.\n");
604
605 #if defined(KERBEROS) || defined(KERBEROS5)
606 /* Fork so that we can call kdestroy */
607 if (
608 #ifdef KERBEROS5
609 ! login_krb5_retain_ccache &&
610 #endif
611 has_ccache)
612 dofork();
613 #endif
614
615 /* Destroy environment unless user has requested its preservation. */
616 if (!pflag)
617 environ = envinit;
618
619 #ifdef LOGIN_CAP
620 if (setusercontext(lc, pwd, pwd->pw_uid,
621 LOGIN_SETALL & ~LOGIN_SETPATH) != 0) {
622 syslog(LOG_ERR, "setusercontext failed");
623 exit(1);
624 }
625 #else
626 (void)setgid(pwd->pw_gid);
627
628 initgroups(username, pwd->pw_gid);
629
630 if (setlogin(pwd->pw_name) < 0)
631 syslog(LOG_ERR, "setlogin() failure: %m");
632
633 /* Discard permissions last so can't get killed and drop core. */
634 if (rootlogin)
635 (void)setuid(0);
636 else
637 (void)setuid(pwd->pw_uid);
638 #endif
639
640 if (*pwd->pw_shell == '\0')
641 pwd->pw_shell = _PATH_BSHELL;
642 #ifdef LOGIN_CAP
643 if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
644 if ((shell = strdup(shell)) == NULL) {
645 syslog(LOG_ERR, "Cannot alloc mem");
646 sleepexit(1);
647 }
648 pwd->pw_shell = shell;
649 }
650 #endif
651
652 (void)setenv("HOME", pwd->pw_dir, 1);
653 (void)setenv("SHELL", pwd->pw_shell, 1);
654 if (term[0] == '\0') {
655 char *tt = (char *)stypeof(tty);
656 #ifdef LOGIN_CAP
657 if (tt == NULL)
658 tt = login_getcapstr(lc, "term", NULL, NULL);
659 #endif
660 /* unknown term -> "su" */
661 (void)strncpy(term, tt != NULL ? tt : "su", sizeof(term));
662 }
663 (void)setenv("TERM", term, 0);
664 (void)setenv("LOGNAME", pwd->pw_name, 1);
665 (void)setenv("USER", pwd->pw_name, 1);
666
667 #ifdef LOGIN_CAP
668 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
669 #else
670 (void)setenv("PATH", _PATH_DEFPATH, 0);
671 #endif
672
673 #ifdef KERBEROS
674 if (krbtkfile_env)
675 (void)setenv("KRBTKFILE", krbtkfile_env, 1);
676 #endif
677 #ifdef KERBEROS5
678 if (krb5tkfile_env)
679 (void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
680 #endif
681
682 if (tty[sizeof("tty")-1] == 'd')
683 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
684
685 /* If fflag is on, assume caller/authenticator has logged root login. */
686 if (rootlogin && fflag == 0) {
687 if (hostname)
688 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
689 username, tty, hostname);
690 else
691 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
692 username, tty);
693 }
694
695 #if defined(KERBEROS) || defined(KERBEROS5)
696 if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
697 (void)printf("Warning: no Kerberos tickets issued.\n");
698 #endif
699
700 if (!quietlog) {
701 char *fname;
702 #ifdef LOGIN_CAP
703 fname = login_getcapstr(lc, "copyright", NULL, NULL);
704 if (fname != NULL && access(fname, F_OK) == 0)
705 motd(fname);
706 else
707 #endif
708 (void)printf("%s", copyrightstr);
709
710 #ifdef LOGIN_CAP
711 fname = login_getcapstr(lc, "welcome", NULL, NULL);
712 if (fname == NULL || access(fname, F_OK) != 0)
713 #endif
714 fname = _PATH_MOTDFILE;
715 motd(fname);
716
717 (void)snprintf(tbuf,
718 sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
719 if (stat(tbuf, &st) == 0 && st.st_size != 0)
720 (void)printf("You have %smail.\n",
721 (st.st_mtime > st.st_atime) ? "new " : "");
722 }
723
724 #ifdef LOGIN_CAP
725 login_close(lc);
726 #endif
727
728 (void)signal(SIGALRM, SIG_DFL);
729 (void)signal(SIGQUIT, SIG_DFL);
730 (void)signal(SIGINT, SIG_DFL);
731 (void)signal(SIGTSTP, SIG_IGN);
732
733 tbuf[0] = '-';
734 (void)strncpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
735 p + 1 : pwd->pw_shell, sizeof(tbuf) - 2);
736
737 /* Wait to change password until we're unprivileged */
738 if (need_chpass) {
739 if (!require_chpass)
740 (void)printf(
741 "Warning: your password has expired. Please change it as soon as possible.\n");
742 else {
743 int status;
744
745 (void)printf(
746 "Your password has expired. Please choose a new one.\n");
747 switch (fork()) {
748 case -1:
749 warn("fork");
750 sleepexit(1);
751 case 0:
752 execl(_PATH_BINPASSWD, "passwd", 0);
753 _exit(1);
754 default:
755 if (wait(&status) == -1 ||
756 WEXITSTATUS(status))
757 sleepexit(1);
758 }
759 }
760 }
761
762 #ifdef KERBEROS5
763 if (login_krb5_get_tickets)
764 k5_write_creds();
765 #endif
766 execlp(pwd->pw_shell, tbuf, 0);
767 err(1, "%s", pwd->pw_shell);
768 }
769
770 #if defined(KERBEROS) || defined(KERBEROS5)
771 #define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */
772 #else
773 #define NBUFSIZ (MAXLOGNAME + 1)
774 #endif
775
776 #if defined(KERBEROS) || defined(KERBEROS5)
777 /*
778 * This routine handles cleanup stuff, and the like.
779 * It exists only in the child process.
780 */
781 #include <sys/wait.h>
782 void
783 dofork()
784 {
785 int child;
786
787 if (!(child = fork()))
788 return; /* Child process */
789
790 /*
791 * Setup stuff? This would be things we could do in parallel
792 * with login
793 */
794 (void)chdir("/"); /* Let's not keep the fs busy... */
795
796 /* If we're the parent, watch the child until it dies */
797 while (wait(0) != child)
798 ;
799
800 /* Cleanup stuff */
801 /* Run kdestroy to destroy tickets */
802 #ifdef KERBEROS
803 kdestroy();
804 #endif
805 #ifdef KERBEROS5
806 if (login_krb5_get_tickets)
807 k5destroy();
808 #endif
809
810 /* Leave */
811 exit(0);
812 }
813 #endif
814
815 void
816 getloginname()
817 {
818 int ch;
819 char *p;
820 static char nbuf[NBUFSIZ];
821
822 for (;;) {
823 (void)printf("login: ");
824 for (p = nbuf; (ch = getchar()) != '\n'; ) {
825 if (ch == EOF) {
826 badlogin(username);
827 exit(0);
828 }
829 if (p < nbuf + (NBUFSIZ - 1))
830 *p++ = ch;
831 }
832 if (p > nbuf) {
833 if (nbuf[0] == '-')
834 (void)fprintf(stderr,
835 "login names may not start with '-'.\n");
836 else {
837 *p = '\0';
838 username = nbuf;
839 break;
840 }
841 }
842 }
843 }
844
845 int
846 rootterm(ttyn)
847 char *ttyn;
848 {
849 struct ttyent *t;
850
851 return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
852 }
853
854 jmp_buf motdinterrupt;
855
856 void
857 motd(fname)
858 char *fname;
859 {
860 int fd, nchars;
861 sig_t oldint;
862 char tbuf[8192];
863
864 if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
865 return;
866 oldint = signal(SIGINT, sigint);
867 if (setjmp(motdinterrupt) == 0)
868 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
869 (void)write(fileno(stdout), tbuf, nchars);
870 (void)signal(SIGINT, oldint);
871 (void)close(fd);
872 }
873
874 /* ARGSUSED */
875 void
876 sigint(signo)
877 int signo;
878 {
879
880 longjmp(motdinterrupt, 1);
881 }
882
883 /* ARGSUSED */
884 void
885 timedout(signo)
886 int signo;
887 {
888
889 (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
890 exit(0);
891 }
892
893 void
894 checknologin(fname)
895 char *fname;
896 {
897 int fd, nchars;
898 char tbuf[8192];
899
900 if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
901 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
902 (void)write(fileno(stdout), tbuf, nchars);
903 sleepexit(0);
904 }
905 }
906
907 static void
908 update_db(int quietlog)
909 {
910 if (hostname != NULL) {
911 socklen_t len = sizeof(ss);
912 (void)getpeername(STDIN_FILENO, (struct sockaddr *)&ss, &len);
913 }
914 (void)gettimeofday(&now, NULL);
915 #ifdef SUPPORT_UTMPX
916 doutmpx();
917 dolastlogx(quietlog);
918 quietlog = 1;
919 #endif
920 #ifdef SUPPORT_UTMP
921 doutmp();
922 dolastlog(quietlog);
923 #endif
924 }
925
926 #ifdef SUPPORT_UTMPX
927 static void
928 doutmpx()
929 {
930 struct utmpx utmpx;
931 char *t;
932
933 memset((void *)&utmpx, 0, sizeof(utmpx));
934 utmpx.ut_tv = now;
935 (void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name));
936 if (hostname) {
937 (void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host));
938 utmpx.ut_ss = ss;
939 }
940 (void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line));
941 utmpx.ut_type = USER_PROCESS;
942 utmpx.ut_pid = getpid();
943 t = tty + strlen(tty);
944 if (t - tty >= sizeof(utmpx.ut_id)) {
945 (void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id),
946 sizeof(utmpx.ut_id));
947 } else {
948 (void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id));
949 }
950 if (pututxline(&utmpx) == NULL)
951 syslog(LOG_NOTICE, "Cannot update utmpx %m");
952 endutxent();
953 if (updwtmpx(_PATH_WTMPX, &utmpx) != 0)
954 syslog(LOG_NOTICE, "Cannot update wtmpx %m");
955 }
956
957 static void
958 dolastlogx(quiet)
959 int quiet;
960 {
961 struct lastlogx ll;
962 if (getlastlogx(pwd->pw_uid, &ll) != NULL) {
963 time_t t = (time_t)ll.ll_tv.tv_sec;
964 (void)printf("Last login: %.24s ", ctime(&t));
965 if (*ll.ll_host != '\0')
966 (void)printf("from %.*s ",
967 (int)sizeof(ll.ll_host),
968 ll.ll_host);
969 (void)printf("on %.*s\n",
970 (int)sizeof(ll.ll_line),
971 ll.ll_line);
972 }
973 ll.ll_tv = now;
974 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
975 if (hostname) {
976 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
977 ll.ll_ss = ss;
978 }
979 if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0)
980 syslog(LOG_NOTICE, "Cannot update lastlogx %m");
981 }
982 #endif
983
984 #ifdef SUPPORT_UTMP
985 static void
986 doutmp()
987 {
988 struct utmp utmp;
989
990 (void)memset((void *)&utmp, 0, sizeof(utmp));
991 utmp.ut_time = now.tv_sec;
992 (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
993 if (hostname)
994 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
995 (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
996 login(&utmp);
997 }
998
999 static void
1000 dolastlog(quiet)
1001 int quiet;
1002 {
1003 struct lastlog ll;
1004 int fd;
1005
1006 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
1007 (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
1008 if (!quiet) {
1009 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1010 ll.ll_time != 0) {
1011 (void)printf("Last login: %.24s ",
1012 ctime(&ll.ll_time));
1013 if (*ll.ll_host != '\0')
1014 (void)printf("from %.*s ",
1015 (int)sizeof(ll.ll_host),
1016 ll.ll_host);
1017 (void)printf("on %.*s\n",
1018 (int)sizeof(ll.ll_line), ll.ll_line);
1019 }
1020 (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
1021 SEEK_SET);
1022 }
1023 memset((void *)&ll, 0, sizeof(ll));
1024 ll.ll_time = now.tv_sec;
1025 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
1026 if (hostname)
1027 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1028 (void)write(fd, (char *)&ll, sizeof(ll));
1029 (void)close(fd);
1030 }
1031 }
1032 #endif
1033
1034 void
1035 badlogin(name)
1036 char *name;
1037 {
1038
1039 if (failures == 0)
1040 return;
1041 if (hostname) {
1042 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
1043 failures, failures > 1 ? "S" : "", hostname);
1044 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1045 "%d LOGIN FAILURE%s FROM %s, %s",
1046 failures, failures > 1 ? "S" : "", hostname, name);
1047 } else {
1048 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
1049 failures, failures > 1 ? "S" : "", tty);
1050 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1051 "%d LOGIN FAILURE%s ON %s, %s",
1052 failures, failures > 1 ? "S" : "", tty, name);
1053 }
1054 }
1055
1056 const char *
1057 stypeof(ttyid)
1058 const char *ttyid;
1059 {
1060 struct ttyent *t;
1061
1062 return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
1063 }
1064
1065 void
1066 sleepexit(eval)
1067 int eval;
1068 {
1069
1070 (void)sleep(5);
1071 exit(eval);
1072 }
1073