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