1 /* $NetBSD: login.c,v 1.105 2014/11/12 22:23:38 aymeric 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("@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94"; 41 #endif 42 __RCSID("$NetBSD: login.c,v 1.105 2014/11/12 22:23:38 aymeric Exp $"); 43 #endif /* not lint */ 44 45 /* 46 * login [ name ] 47 * login -h hostname (for telnetd, etc.) 48 * login -f name (for pre-authenticated login: datakit, xterm, etc.) 49 */ 50 51 #include <sys/param.h> 52 #include <sys/stat.h> 53 #include <sys/time.h> 54 #include <sys/resource.h> 55 #include <sys/file.h> 56 #include <sys/wait.h> 57 #include <sys/socket.h> 58 59 #include <err.h> 60 #include <errno.h> 61 #include <grp.h> 62 #include <pwd.h> 63 #include <setjmp.h> 64 #include <signal.h> 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <syslog.h> 69 #include <time.h> 70 #include <ttyent.h> 71 #include <tzfile.h> 72 #include <unistd.h> 73 #include <sysexits.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 <krb5/com_err.h> 87 #endif 88 #ifdef LOGIN_CAP 89 #include <login_cap.h> 90 #endif 91 #include <vis.h> 92 93 #include "pathnames.h" 94 #include "common.h" 95 96 #ifdef KERBEROS5 97 int login_krb5_forwardable_tgt = 0; 98 static int login_krb5_get_tickets = 1; 99 static int login_krb5_retain_ccache = 0; 100 #endif 101 102 static void checknologin(char *); 103 #ifdef KERBEROS5 104 int k5login(struct passwd *, char *, char *, char *); 105 void k5destroy(void); 106 int k5_read_creds(const char *); 107 int k5_write_creds(void); 108 #endif 109 #if defined(KERBEROS5) 110 static void dofork(void); 111 #endif 112 static void usage(void) __attribute__((__noreturn__)); 113 114 #define TTYGRPNAME "tty" /* name of group to own ttys */ 115 116 #define DEFAULT_BACKOFF 3 117 #define DEFAULT_RETRIES 10 118 119 #if defined(KERBEROS5) 120 int has_ccache = 0; 121 int notickets = 1; 122 extern krb5_context kcontext; 123 extern int have_forward; 124 static char *instance; 125 extern char *krb5tkfile_env; 126 extern int krb5_configured; 127 #endif 128 129 #if defined(KERBEROS5) 130 #define KERBEROS_CONFIGURED krb5_configured 131 #endif 132 133 extern char **environ; 134 135 int 136 main(int argc, char *argv[]) 137 { 138 struct group *gr; 139 struct stat st; 140 int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval; 141 uid_t uid, saved_uid; 142 gid_t saved_gid, saved_gids[NGROUPS_MAX]; 143 int nsaved_gids; 144 #ifdef notdef 145 char *domain; 146 #endif 147 char *p, *ttyn; 148 const char *pwprompt; 149 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; 150 char localhost[MAXHOSTNAMELEN + 1]; 151 int need_chpass, require_chpass; 152 int login_retries = DEFAULT_RETRIES, 153 login_backoff = DEFAULT_BACKOFF; 154 time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY; 155 char *loginname = NULL; 156 #ifdef KERBEROS5 157 int Fflag; 158 krb5_error_code kerror; 159 #endif 160 #if defined(KERBEROS5) 161 int got_tickets = 0; 162 #endif 163 #ifdef LOGIN_CAP 164 char *shell = NULL; 165 login_cap_t *lc = NULL; 166 #endif 167 168 tbuf[0] = '\0'; 169 rval = 0; 170 pwprompt = NULL; 171 nested = NULL; 172 need_chpass = require_chpass = 0; 173 174 (void)signal(SIGALRM, timedout); 175 (void)alarm(timeout); 176 (void)signal(SIGQUIT, SIG_IGN); 177 (void)signal(SIGINT, SIG_IGN); 178 (void)setpriority(PRIO_PROCESS, 0, 0); 179 180 openlog("login", 0, LOG_AUTH); 181 182 /* 183 * -p is used by getty to tell login not to destroy the environment 184 * -f is used to skip a second login authentication 185 * -h is used by other servers to pass the name of the remote host to 186 * login so that it may be placed in utmp/utmpx and wtmp/wtmpx 187 * -a in addition to -h, a server may supply -a to pass the actual 188 * server address. 189 * -s is used to force use of S/Key or equivalent. 190 */ 191 if (gethostname(localhost, sizeof(localhost)) < 0) { 192 syslog(LOG_ERR, "couldn't get local hostname: %m"); 193 strcpy(hostname, "amnesiac"); 194 } 195 #ifdef notdef 196 domain = strchr(localhost, '.'); 197 #endif 198 localhost[sizeof(localhost) - 1] = '\0'; 199 200 fflag = hflag = pflag = sflag = 0; 201 have_ss = 0; 202 #ifdef KERBEROS5 203 Fflag = 0; 204 have_forward = 0; 205 #endif 206 uid = getuid(); 207 while ((ch = getopt(argc, argv, "a:Ffh:ps")) != -1) 208 switch (ch) { 209 case 'a': 210 if (uid) 211 errx(EXIT_FAILURE, "-a option: %s", strerror(EPERM)); 212 decode_ss(optarg); 213 #ifdef notdef 214 (void)sockaddr_snprintf(optarg, 215 sizeof(struct sockaddr_storage), "%a", (void *)&ss); 216 #endif 217 break; 218 case 'F': 219 #ifdef KERBEROS5 220 Fflag = 1; 221 #endif 222 /* FALLTHROUGH */ 223 case 'f': 224 fflag = 1; 225 break; 226 case 'h': 227 if (uid) 228 errx(EXIT_FAILURE, "-h option: %s", strerror(EPERM)); 229 hflag = 1; 230 #ifdef notdef 231 if (domain && (p = strchr(optarg, '.')) != NULL && 232 strcasecmp(p, domain) == 0) 233 *p = '\0'; 234 #endif 235 hostname = optarg; 236 break; 237 case 'p': 238 pflag = 1; 239 break; 240 case 's': 241 sflag = 1; 242 break; 243 default: 244 case '?': 245 usage(); 246 break; 247 } 248 249 setproctitle(NULL); 250 argc -= optind; 251 argv += optind; 252 253 if (*argv) { 254 username = loginname = *argv; 255 ask = 0; 256 } else 257 ask = 1; 258 259 #ifdef F_CLOSEM 260 (void)fcntl(3, F_CLOSEM, 0); 261 #else 262 for (cnt = getdtablesize(); cnt > 2; cnt--) 263 (void)close(cnt); 264 #endif 265 266 ttyn = ttyname(STDIN_FILENO); 267 if (ttyn == NULL || *ttyn == '\0') { 268 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); 269 ttyn = tname; 270 } 271 if ((tty = strstr(ttyn, "/pts/")) != NULL) 272 ++tty; 273 else if ((tty = strrchr(ttyn, '/')) != NULL) 274 ++tty; 275 else 276 tty = ttyn; 277 278 if (issetugid()) { 279 nested = strdup(user_from_uid(getuid(), 0)); 280 if (nested == NULL) { 281 syslog(LOG_ERR, "strdup: %m"); 282 sleepexit(EXIT_FAILURE); 283 } 284 } 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(KERBEROS5) 321 if (login_krb5_get_tickets) 322 k5destroy(); 323 #endif 324 if (ask) { 325 fflag = 0; 326 loginname = getloginname(); 327 } 328 rootlogin = 0; 329 #ifdef KERBEROS5 330 if ((instance = strchr(loginname, '/')) != NULL) 331 *instance++ = '\0'; 332 else 333 instance = __UNCONST(""); 334 #endif 335 username = trimloginname(loginname); 336 /* 337 * Note if trying multiple user names; log failures for 338 * previous user name, but don't bother logging one failure 339 * for nonexistent name (mistyped username). 340 */ 341 if (failures && strcmp(tbuf, username)) { 342 if (failures > (pwd ? 0 : 1)) 343 badlogin(tbuf); 344 failures = 0; 345 } 346 (void)strlcpy(tbuf, username, sizeof(tbuf)); 347 348 pwd = getpwnam(username); 349 350 #ifdef LOGIN_CAP 351 /* 352 * Establish the class now, before we might goto 353 * within the next block. pwd can be NULL since it 354 * falls back to the "default" class if it is. 355 */ 356 lc = login_getclass(pwd ? pwd->pw_class : NULL); 357 #endif 358 /* 359 * if we have a valid account name, and it doesn't have a 360 * password, or the -f option was specified and the caller 361 * is root or the caller isn't changing their uid, don't 362 * authenticate. 363 */ 364 if (pwd) { 365 if (pwd->pw_uid == 0) 366 rootlogin = 1; 367 368 if (fflag && (uid == 0 || uid == pwd->pw_uid)) { 369 /* already authenticated */ 370 #ifdef KERBEROS5 371 if (login_krb5_get_tickets && Fflag) 372 k5_read_creds(username); 373 #endif 374 break; 375 } else if (pwd->pw_passwd[0] == '\0') { 376 /* pretend password okay */ 377 rval = 0; 378 goto ttycheck; 379 } 380 } 381 382 fflag = 0; 383 384 (void)setpriority(PRIO_PROCESS, 0, -4); 385 386 #ifdef SKEY 387 if (skey_haskey(username) == 0) { 388 static char skprompt[80]; 389 const char *skinfo = skey_keyinfo(username); 390 391 (void)snprintf(skprompt, sizeof(skprompt), 392 "Password [ %s ]:", 393 skinfo ? skinfo : "error getting challenge"); 394 pwprompt = skprompt; 395 } else 396 #endif 397 pwprompt = "Password:"; 398 399 p = getpass(pwprompt); 400 401 if (pwd == NULL) { 402 rval = 1; 403 goto skip; 404 } 405 #ifdef KERBEROS5 406 if (login_krb5_get_tickets && 407 k5login(pwd, instance, localhost, p) == 0) { 408 rval = 0; 409 got_tickets = 1; 410 } 411 #endif 412 #if defined(KERBEROS5) 413 if (got_tickets) 414 goto skip; 415 #endif 416 #ifdef SKEY 417 if (skey_haskey(username) == 0 && 418 skey_passcheck(username, p) != -1) { 419 rval = 0; 420 goto skip; 421 } 422 #endif 423 if (!sflag && *pwd->pw_passwd != '\0' && 424 !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) { 425 rval = 0; 426 require_chpass = 1; 427 goto skip; 428 } 429 rval = 1; 430 431 skip: 432 memset(p, 0, strlen(p)); 433 434 (void)setpriority(PRIO_PROCESS, 0, 0); 435 436 ttycheck: 437 /* 438 * If trying to log in as root without Kerberos, 439 * but with insecure terminal, refuse the login attempt. 440 */ 441 if (pwd && !rval && rootlogin && !rootterm(tty)) { 442 (void)printf("Login incorrect or refused on this " 443 "terminal.\n"); 444 if (hostname) 445 syslog(LOG_NOTICE, 446 "LOGIN %s REFUSED FROM %s ON TTY %s", 447 pwd->pw_name, hostname, tty); 448 else 449 syslog(LOG_NOTICE, 450 "LOGIN %s REFUSED ON TTY %s", 451 pwd->pw_name, tty); 452 continue; 453 } 454 455 if (pwd && !rval) 456 break; 457 458 (void)printf("Login incorrect or refused on this " 459 "terminal.\n"); 460 failures++; 461 cnt++; 462 /* 463 * We allow login_retries tries, but after login_backoff 464 * we start backing off. These default to 10 and 3 465 * respectively. 466 */ 467 if (cnt > login_backoff) { 468 if (cnt >= login_retries) { 469 badlogin(username); 470 sleepexit(EXIT_FAILURE); 471 } 472 sleep((u_int)((cnt - login_backoff) * 5)); 473 } 474 } 475 476 /* committed to login -- turn off timeout */ 477 (void)alarm((u_int)0); 478 479 endpwent(); 480 481 /* if user not super-user, check for disabled logins */ 482 #ifdef LOGIN_CAP 483 if (!login_getcapbool(lc, "ignorenologin", rootlogin)) 484 checknologin(login_getcapstr(lc, "nologin", NULL, NULL)); 485 #else 486 if (!rootlogin) 487 checknologin(NULL); 488 #endif 489 490 #ifdef LOGIN_CAP 491 quietlog = login_getcapbool(lc, "hushlogin", 0); 492 #else 493 quietlog = 0; 494 #endif 495 /* Temporarily give up special privileges so we can change */ 496 /* into NFS-mounted homes that are exported for non-root */ 497 /* access and have mode 7x0 */ 498 saved_uid = geteuid(); 499 saved_gid = getegid(); 500 nsaved_gids = getgroups(NGROUPS_MAX, saved_gids); 501 502 (void)setegid(pwd->pw_gid); 503 initgroups(username, pwd->pw_gid); 504 (void)seteuid(pwd->pw_uid); 505 506 if (chdir(pwd->pw_dir) < 0) { 507 #ifdef LOGIN_CAP 508 if (login_getcapbool(lc, "requirehome", 0)) { 509 (void)printf("Home directory %s required\n", 510 pwd->pw_dir); 511 sleepexit(EXIT_FAILURE); 512 } 513 #endif 514 (void)printf("No home directory %s!\n", pwd->pw_dir); 515 if (chdir("/") == -1) 516 exit(EXIT_FAILURE); 517 pwd->pw_dir = __UNCONST("/"); 518 (void)printf("Logging in with home = \"/\".\n"); 519 } 520 521 if (!quietlog) 522 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 523 524 /* regain special privileges */ 525 (void)seteuid(saved_uid); 526 setgroups(nsaved_gids, saved_gids); 527 (void)setegid(saved_gid); 528 529 #ifdef LOGIN_CAP 530 pw_warntime = login_getcaptime(lc, "password-warn", 531 _PASSWORD_WARNDAYS * SECSPERDAY, 532 _PASSWORD_WARNDAYS * SECSPERDAY); 533 #endif 534 535 (void)gettimeofday(&now, NULL); 536 if (pwd->pw_expire) { 537 if (now.tv_sec >= pwd->pw_expire) { 538 (void)printf("Sorry -- your account has expired.\n"); 539 sleepexit(EXIT_FAILURE); 540 } else if (pwd->pw_expire - now.tv_sec < pw_warntime && 541 !quietlog) 542 (void)printf("Warning: your account expires on %s", 543 ctime(&pwd->pw_expire)); 544 } 545 if (pwd->pw_change) { 546 if (pwd->pw_change == _PASSWORD_CHGNOW) 547 need_chpass = 1; 548 else if (now.tv_sec >= pwd->pw_change) { 549 (void)printf("Sorry -- your password has expired.\n"); 550 sleepexit(EXIT_FAILURE); 551 } else if (pwd->pw_change - now.tv_sec < pw_warntime && 552 !quietlog) 553 (void)printf("Warning: your password expires on %s", 554 ctime(&pwd->pw_change)); 555 556 } 557 /* Nothing else left to fail -- really log in. */ 558 update_db(quietlog, rootlogin, fflag); 559 560 (void)chown(ttyn, pwd->pw_uid, 561 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); 562 563 if (ttyaction(ttyn, "login", pwd->pw_name)) 564 (void)printf("Warning: ttyaction failed.\n"); 565 566 #if defined(KERBEROS5) 567 /* Fork so that we can call kdestroy */ 568 if (! login_krb5_retain_ccache && has_ccache) 569 dofork(); 570 #endif 571 572 /* Destroy environment unless user has requested its preservation. */ 573 if (!pflag) 574 environ = envinit; 575 576 #ifdef LOGIN_CAP 577 if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid, 578 LOGIN_SETLOGIN) != 0) { 579 syslog(LOG_ERR, "setusercontext failed"); 580 exit(EXIT_FAILURE); 581 } 582 if (setusercontext(lc, pwd, pwd->pw_uid, 583 (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETLOGIN))) != 0) { 584 syslog(LOG_ERR, "setusercontext failed"); 585 exit(EXIT_FAILURE); 586 } 587 #else 588 (void)setgid(pwd->pw_gid); 589 590 initgroups(username, pwd->pw_gid); 591 592 if (nested == NULL && setlogin(pwd->pw_name) < 0) 593 syslog(LOG_ERR, "setlogin() failure: %m"); 594 595 /* Discard permissions last so can't get killed and drop core. */ 596 if (rootlogin) 597 (void)setuid(0); 598 else 599 (void)setuid(pwd->pw_uid); 600 #endif 601 602 if (*pwd->pw_shell == '\0') 603 pwd->pw_shell = __UNCONST(_PATH_BSHELL); 604 #ifdef LOGIN_CAP 605 if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) { 606 if ((shell = strdup(shell)) == NULL) { 607 syslog(LOG_ERR, "Cannot alloc mem"); 608 sleepexit(EXIT_FAILURE); 609 } 610 pwd->pw_shell = shell; 611 } 612 #endif 613 614 (void)setenv("HOME", pwd->pw_dir, 1); 615 (void)setenv("SHELL", pwd->pw_shell, 1); 616 if (term[0] == '\0') { 617 const char *tt = stypeof(tty); 618 #ifdef LOGIN_CAP 619 if (tt == NULL) 620 tt = login_getcapstr(lc, "term", NULL, NULL); 621 #endif 622 /* unknown term -> "su" */ 623 (void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term)); 624 } 625 (void)setenv("TERM", term, 0); 626 (void)setenv("LOGNAME", pwd->pw_name, 1); 627 (void)setenv("USER", pwd->pw_name, 1); 628 629 #ifdef LOGIN_CAP 630 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH); 631 #else 632 (void)setenv("PATH", _PATH_DEFPATH, 0); 633 #endif 634 635 #ifdef KERBEROS5 636 if (krb5tkfile_env) 637 (void)setenv("KRB5CCNAME", krb5tkfile_env, 1); 638 #endif 639 640 /* If fflag is on, assume caller/authenticator has logged root login. */ 641 if (rootlogin && fflag == 0) { 642 if (hostname) 643 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", 644 username, tty, hostname); 645 else 646 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", 647 username, tty); 648 } 649 650 #if defined(KERBEROS5) 651 if (KERBEROS_CONFIGURED && !quietlog && notickets == 1) 652 (void)printf("Warning: no Kerberos tickets issued.\n"); 653 #endif 654 655 if (!quietlog) { 656 const char *fname; 657 #ifdef LOGIN_CAP 658 fname = login_getcapstr(lc, "copyright", NULL, NULL); 659 if (fname != NULL && access(fname, F_OK) == 0) 660 motd(fname); 661 else 662 #endif 663 (void)printf("%s", copyrightstr); 664 665 #ifdef LOGIN_CAP 666 fname = login_getcapstr(lc, "welcome", NULL, NULL); 667 if (fname == NULL || access(fname, F_OK) != 0) 668 #endif 669 fname = _PATH_MOTDFILE; 670 motd(fname); 671 672 (void)snprintf(tbuf, 673 sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); 674 if (stat(tbuf, &st) == 0 && st.st_size != 0) 675 (void)printf("You have %smail.\n", 676 (st.st_mtime > st.st_atime) ? "new " : ""); 677 } 678 679 #ifdef LOGIN_CAP 680 login_close(lc); 681 #endif 682 683 (void)signal(SIGALRM, SIG_DFL); 684 (void)signal(SIGQUIT, SIG_DFL); 685 (void)signal(SIGINT, SIG_DFL); 686 (void)signal(SIGTSTP, SIG_IGN); 687 688 tbuf[0] = '-'; 689 (void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? 690 p + 1 : pwd->pw_shell, sizeof(tbuf) - 1); 691 692 /* Wait to change password until we're unprivileged */ 693 if (need_chpass) { 694 if (!require_chpass) 695 (void)printf( 696 "Warning: your password has expired. Please change it as soon as possible.\n"); 697 else { 698 int status; 699 700 (void)printf( 701 "Your password has expired. Please choose a new one.\n"); 702 switch (fork()) { 703 case -1: 704 warn("fork"); 705 sleepexit(EXIT_FAILURE); 706 case 0: 707 execl(_PATH_BINPASSWD, "passwd", NULL); 708 _exit(EXIT_FAILURE); 709 default: 710 if (wait(&status) == -1 || 711 WEXITSTATUS(status)) 712 sleepexit(EXIT_FAILURE); 713 } 714 } 715 } 716 717 #ifdef KERBEROS5 718 if (login_krb5_get_tickets) 719 k5_write_creds(); 720 #endif 721 execlp(pwd->pw_shell, tbuf, NULL); 722 err(EXIT_FAILURE, "%s", pwd->pw_shell); 723 } 724 725 #if defined(KERBEROS5) 726 /* 727 * This routine handles cleanup stuff, and the like. 728 * It exists only in the child process. 729 */ 730 static void 731 dofork(void) 732 { 733 pid_t child, wchild; 734 735 switch (child = fork()) { 736 case 0: 737 return; /* Child process */ 738 case -1: 739 err(EXIT_FAILURE, "Can't fork"); 740 /*NOTREACHED*/ 741 default: 742 break; 743 } 744 745 /* 746 * Setup stuff? This would be things we could do in parallel 747 * with login 748 */ 749 if (chdir("/") == -1) /* Let's not keep the fs busy... */ 750 err(EXIT_FAILURE, "Can't chdir to `/'"); 751 752 /* If we're the parent, watch the child until it dies */ 753 while ((wchild = wait(NULL)) != child) 754 if (wchild == -1) 755 err(EXIT_FAILURE, "Can't wait"); 756 757 /* Cleanup stuff */ 758 /* Run kdestroy to destroy tickets */ 759 if (login_krb5_get_tickets) 760 k5destroy(); 761 762 /* Leave */ 763 exit(EXIT_SUCCESS); 764 } 765 #endif 766 767 static void 768 checknologin(char *fname) 769 { 770 int fd, nchars; 771 char tbuf[8192]; 772 773 if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { 774 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 775 (void)write(fileno(stdout), tbuf, nchars); 776 sleepexit(EXIT_SUCCESS); 777 } 778 } 779 780 static void 781 usage(void) 782 { 783 (void)fprintf(stderr, 784 "Usage: %s [-Ffps] [-a address] [-h hostname] [username]\n", 785 getprogname()); 786 exit(EXIT_FAILURE); 787 } 788