1/* $NetBSD: login.c,v 1.104 2014/03/16 00:33:13 dholland 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 40static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94"; 41#endif 42__RCSID("$NetBSD: login.c,v 1.104 2014/03/16 00:33:13 dholland 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 97int login_krb5_forwardable_tgt = 0; 98static int login_krb5_get_tickets = 1; 99static int login_krb5_retain_ccache = 0; 100#endif 101 102static void checknologin(char *); 103#ifdef KERBEROS5 104int k5login(struct passwd *, char *, char *, char *); 105void k5destroy(void); 106int k5_read_creds(const char *); 107int k5_write_creds(void); 108#endif 109#if defined(KERBEROS5) 110static void dofork(void); 111#endif 112static 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) 120int has_ccache = 0; 121int notickets = 1; 122extern krb5_context kcontext; 123extern int have_forward; 124static char *instance; 125extern char *krb5tkfile_env; 126extern int krb5_configured; 127#endif 128 129#if defined(KERBEROS5) 130#define KERBEROS_CONFIGURED krb5_configured 131#endif 132 133extern char **environ; 134 135int 136main(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 */ 730static void 731dofork(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 767static void 768checknologin(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 780static void 781usage(void) 782{ 783 (void)fprintf(stderr, 784 "Usage: %s [-Ffps] [-a address] [-h hostname] [username]\n", 785 getprogname()); 786 exit(EXIT_FAILURE); 787} 788