login.c revision 1.77.4.2 1 /* $NetBSD: login.c,v 1.77.4.2 2006/03/17 17:18:27 riz 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.77.4.2 2006/03/17 17:18:27 riz 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 extern const char copyrightstr[];
176
177 int
178 main(argc, argv)
179 int argc;
180 char *argv[];
181 {
182 extern char **environ;
183 struct group *gr;
184 struct stat st;
185 int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
186 int Fflag;
187 uid_t uid, saved_uid;
188 gid_t saved_gid, saved_gids[NGROUPS_MAX];
189 int nsaved_gids;
190 char *domain, *p, *ttyn, *pwprompt;
191 const char *salt;
192 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
193 char localhost[MAXHOSTNAMELEN + 1];
194 int need_chpass, require_chpass;
195 int login_retries = DEFAULT_RETRIES,
196 login_backoff = DEFAULT_BACKOFF;
197 time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
198 #ifdef KERBEROS5
199 krb5_error_code kerror;
200 #endif
201 #if defined(KERBEROS) || defined(KERBEROS5)
202 int got_tickets = 0;
203 #endif
204 #ifdef LOGIN_CAP
205 char *shell = NULL;
206 login_cap_t *lc = NULL;
207 #endif
208
209 tbuf[0] = '\0';
210 rval = 0;
211 pwprompt = NULL;
212 nested = NULL;
213 need_chpass = require_chpass = 0;
214
215 (void)signal(SIGALRM, timedout);
216 (void)alarm(timeout);
217 (void)signal(SIGQUIT, SIG_IGN);
218 (void)signal(SIGINT, SIG_IGN);
219 (void)setpriority(PRIO_PROCESS, 0, 0);
220
221 openlog("login", 0, LOG_AUTH);
222
223 /*
224 * -p is used by getty to tell login not to destroy the environment
225 * -f is used to skip a second login authentication
226 * -h is used by other servers to pass the name of the remote host to
227 * login so that it may be placed in utmp/utmpx and wtmp/wtmpx
228 * -s is used to force use of S/Key or equivalent.
229 */
230 domain = NULL;
231 if (gethostname(localhost, sizeof(localhost)) < 0)
232 syslog(LOG_ERR, "couldn't get local hostname: %m");
233 else
234 domain = strchr(localhost, '.');
235 localhost[sizeof(localhost) - 1] = '\0';
236
237 Fflag = fflag = hflag = pflag = sflag = 0;
238 #ifdef KERBEROS5
239 have_forward = 0;
240 #endif
241 uid = getuid();
242 while ((ch = getopt(argc, argv, "Ffh:ps")) != -1)
243 switch (ch) {
244 case 'F':
245 Fflag = 1;
246 /* FALLTHROUGH */
247 case 'f':
248 fflag = 1;
249 break;
250 case 'h':
251 if (uid)
252 errx(1, "-h option: %s", strerror(EPERM));
253 hflag = 1;
254 if (domain && (p = strchr(optarg, '.')) != NULL &&
255 strcasecmp(p, domain) == 0)
256 *p = 0;
257 hostname = optarg;
258 break;
259 case 'p':
260 pflag = 1;
261 break;
262 case 's':
263 sflag = 1;
264 break;
265 default:
266 case '?':
267 (void)fprintf(stderr,
268 "usage: login [-fps] [-h hostname] [username]\n");
269 exit(1);
270 }
271 argc -= optind;
272 argv += optind;
273
274 if (*argv) {
275 username = *argv;
276 ask = 0;
277 } else
278 ask = 1;
279
280 for (cnt = getdtablesize(); cnt > 2; cnt--)
281 (void)close(cnt);
282
283 ttyn = ttyname(STDIN_FILENO);
284 if (ttyn == NULL || *ttyn == '\0') {
285 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
286 ttyn = tname;
287 }
288 if ((tty = strrchr(ttyn, '/')) != NULL)
289 ++tty;
290 else
291 tty = ttyn;
292
293 if (issetugid()) {
294 nested = strdup(user_from_uid(getuid(), 0));
295 if (nested == NULL) {
296 syslog(LOG_ERR, "strdup: %m");
297 sleepexit(1);
298 }
299 }
300
301 #ifdef LOGIN_CAP
302 /* Get "login-retries" and "login-backoff" from default class */
303 if ((lc = login_getclass(NULL)) != NULL) {
304 login_retries = (int)login_getcapnum(lc, "login-retries",
305 DEFAULT_RETRIES, DEFAULT_RETRIES);
306 login_backoff = (int)login_getcapnum(lc, "login-backoff",
307 DEFAULT_BACKOFF, DEFAULT_BACKOFF);
308 login_close(lc);
309 lc = NULL;
310 }
311 #endif
312
313 #ifdef KERBEROS5
314 kerror = krb5_init_context(&kcontext);
315 if (kerror) {
316 /*
317 * If Kerberos is not configured, that is, we are
318 * not using Kerberos, do not log the error message.
319 * However, if Kerberos is configured, and the
320 * context init fails for some other reason, we need
321 * to issue a no tickets warning to the user when the
322 * login succeeds.
323 */
324 if (kerror != ENXIO) { /* XXX NetBSD-local Heimdal hack */
325 syslog(LOG_NOTICE,
326 "%s when initializing Kerberos context",
327 error_message(kerror));
328 krb5_configured = 1;
329 }
330 login_krb5_get_tickets = 0;
331 }
332 #endif /* KERBEROS5 */
333
334 for (cnt = 0;; ask = 1) {
335 #if defined(KERBEROS)
336 kdestroy();
337 #endif
338 #if defined(KERBEROS5)
339 if (login_krb5_get_tickets)
340 k5destroy();
341 #endif
342 if (ask) {
343 fflag = 0;
344 getloginname();
345 }
346 rootlogin = 0;
347 #ifdef KERBEROS
348 if ((instance = strchr(username, '.')) != NULL)
349 *instance++ = '\0';
350 else
351 instance = "";
352 #endif
353 #ifdef KERBEROS5
354 if ((instance = strchr(username, '/')) != NULL)
355 *instance++ = '\0';
356 else
357 instance = "";
358 #endif
359 if (strlen(username) > MAXLOGNAME)
360 username[MAXLOGNAME] = '\0';
361
362 /*
363 * Note if trying multiple user names; log failures for
364 * previous user name, but don't bother logging one failure
365 * for nonexistent name (mistyped username).
366 */
367 if (failures && strcmp(tbuf, username)) {
368 if (failures > (pwd ? 0 : 1))
369 badlogin(tbuf);
370 failures = 0;
371 }
372 (void)strlcpy(tbuf, username, sizeof(tbuf));
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),
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)printf("Login incorrect or refused on this "
486 "terminal.\n");
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 or refused on this "
502 "terminal.\n");
503 failures++;
504 cnt++;
505 /*
506 * We allow login_retries tries, but after login_backoff
507 * we start backing off. These default to 10 and 3
508 * respectively.
509 */
510 if (cnt > login_backoff) {
511 if (cnt >= login_retries) {
512 badlogin(username);
513 sleepexit(1);
514 }
515 sleep((u_int)((cnt - login_backoff) * 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