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