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