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