1 /*- 2 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technologies, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)login.c 8.4 (Berkeley) 4/2/94 37 * $FreeBSD: src/usr.bin/login/login.c,v 1.106 2007/07/04 00:00:40 scf Exp $ 38 * $DragonFly: src/usr.bin/login/login.c,v 1.6 2006/01/12 13:43:11 corecode Exp $ 39 */ 40 41 /* 42 * login [ name ] 43 * login -h hostname (for telnetd, etc.) 44 * login -f name (for pre-authenticated login: datakit, xterm, etc.) 45 */ 46 47 #include <sys/copyright.h> 48 #include <sys/param.h> 49 #include <sys/file.h> 50 #include <sys/stat.h> 51 #include <sys/time.h> 52 #include <sys/resource.h> 53 #include <sys/wait.h> 54 55 #include <err.h> 56 #include <errno.h> 57 #include <grp.h> 58 #include <libutil.h> 59 #include <login_cap.h> 60 #include <pwd.h> 61 #include <setjmp.h> 62 #include <signal.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <syslog.h> 67 #include <ttyent.h> 68 #include <unistd.h> 69 70 #include <security/pam_appl.h> 71 #include <security/openpam.h> 72 73 #include "login.h" 74 #include "pathnames.h" 75 76 static int auth_pam(void); 77 static void bail(int, int); 78 static int export(const char *); 79 static void export_pam_environment(void); 80 static int motd(const char *); 81 static void badlogin(char *); 82 static char *getloginname(void); 83 static void pam_syslog(const char *); 84 static void pam_cleanup(void); 85 static void refused(const char *, const char *, int); 86 static const char *stypeof(char *); 87 static void sigint(int); 88 static void timedout(int); 89 static void usage(void); 90 91 #define TTYGRPNAME "tty" /* group to own ttys */ 92 #define DEFAULT_BACKOFF 3 93 #define DEFAULT_RETRIES 10 94 #define DEFAULT_PROMPT "login: " 95 #define DEFAULT_PASSWD_PROMPT "Password:" 96 #define TERM_UNKNOWN "su" 97 #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 98 #define NO_SLEEP_EXIT 0 99 #define SLEEP_EXIT 5 100 101 /* 102 * This bounds the time given to login. Not a define so it can 103 * be patched on machines where it's too small. 104 */ 105 static u_int timeout = 300; 106 107 /* Buffer for signal handling of timeout */ 108 static jmp_buf timeout_buf; 109 110 struct passwd *pwd; 111 static int failures; 112 113 static char *envinit[1]; /* empty environment list */ 114 115 /* 116 * Command line flags and arguments 117 */ 118 static int fflag; /* -f: do not perform authentication */ 119 static int hflag; /* -h: login from remote host */ 120 static char *hostname; /* hostname from command line */ 121 static int pflag; /* -p: preserve environment */ 122 123 /* 124 * User name 125 */ 126 static char *username; /* user name */ 127 static char *olduser; /* previous user name */ 128 129 /* 130 * Prompts 131 */ 132 static char default_prompt[] = DEFAULT_PROMPT; 133 static const char *prompt; 134 static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT; 135 static const char *passwd_prompt; 136 137 static char *tty; 138 139 /* 140 * PAM data 141 */ 142 static pam_handle_t *pamh = NULL; 143 static struct pam_conv pamc = { openpam_ttyconv, NULL }; 144 static int pam_err; 145 static int pam_silent = PAM_SILENT; 146 static int pam_cred_established; 147 static int pam_session_established; 148 149 int 150 main(int argc, char **argv) 151 { 152 struct group *gr; 153 struct stat st; 154 int retries, backoff; 155 int ask, ch, cnt, quietlog, rootlogin, rval; 156 uid_t uid, euid; 157 gid_t egid; 158 char *term; 159 char *p, *ttyn; 160 char tname[sizeof(_PATH_TTY) + 10]; 161 char *arg0; 162 const char *tp; 163 const char *shell = NULL; 164 login_cap_t *lc = NULL; 165 login_cap_t *lc_user = NULL; 166 pid_t pid; 167 #ifdef USE_BSM_AUDIT 168 char auditsuccess = 1; 169 #endif 170 171 signal(SIGQUIT, SIG_IGN); 172 signal(SIGINT, SIG_IGN); 173 signal(SIGHUP, SIG_IGN); 174 if (setjmp(timeout_buf)) { 175 if (failures) 176 badlogin(username); 177 fprintf(stderr, "Login timed out after %d seconds\n", 178 timeout); 179 bail(NO_SLEEP_EXIT, 0); 180 } 181 signal(SIGALRM, timedout); 182 alarm(timeout); 183 setpriority(PRIO_PROCESS, 0, 0); 184 185 openlog("login", LOG_ODELAY, LOG_AUTH); 186 187 uid = getuid(); 188 euid = geteuid(); 189 egid = getegid(); 190 191 while ((ch = getopt(argc, argv, "fh:p")) != -1) 192 switch (ch) { 193 case 'f': 194 fflag = 1; 195 break; 196 case 'h': 197 if (uid != 0) 198 errx(1, "-h option: %s", strerror(EPERM)); 199 if (strlen(optarg) >= MAXHOSTNAMELEN) 200 errx(1, "-h option: %s: exceeds maximum " 201 "hostname size", optarg); 202 hflag = 1; 203 hostname = optarg; 204 break; 205 case 'p': 206 pflag = 1; 207 break; 208 case '?': 209 default: 210 if (uid == 0) 211 syslog(LOG_ERR, "invalid flag %c", ch); 212 usage(); 213 } 214 argc -= optind; 215 argv += optind; 216 217 if (argc > 0) { 218 username = strdup(*argv); 219 if (username == NULL) 220 err(1, "strdup()"); 221 ask = 0; 222 } else { 223 ask = 1; 224 } 225 226 setproctitle("-%s", getprogname()); 227 228 for (cnt = getdtablesize(); cnt > 2; cnt--) 229 close(cnt); 230 231 /* 232 * Get current TTY 233 */ 234 ttyn = ttyname(STDIN_FILENO); 235 if (ttyn == NULL || *ttyn == '\0') { 236 snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); 237 ttyn = tname; 238 } 239 if (strncmp(ttyn, _PATH_DEV, sizeof(_PATH_DEV) -1) == 0) 240 tty = ttyn + sizeof(_PATH_DEV) -1; 241 else 242 tty = ttyn; 243 244 /* 245 * Get "login-retries" & "login-backoff" from default class 246 */ 247 lc = login_getclass(NULL); 248 prompt = login_getcapstr(lc, "login_prompt", 249 default_prompt, default_prompt); 250 passwd_prompt = login_getcapstr(lc, "passwd_prompt", 251 default_passwd_prompt, default_passwd_prompt); 252 retries = login_getcapnum(lc, "login-retries", 253 DEFAULT_RETRIES, DEFAULT_RETRIES); 254 backoff = login_getcapnum(lc, "login-backoff", 255 DEFAULT_BACKOFF, DEFAULT_BACKOFF); 256 login_close(lc); 257 lc = NULL; 258 259 /* 260 * Try to authenticate the user until we succeed or time out. 261 */ 262 for (cnt = 0;; ask = 1) { 263 if (ask) { 264 fflag = 0; 265 if (olduser != NULL) 266 free(olduser); 267 olduser = username; 268 username = getloginname(); 269 } 270 rootlogin = 0; 271 272 /* 273 * Note if trying multiple user names; log failures for 274 * previous user name, but don't bother logging one failure 275 * for nonexistent name (mistyped username). 276 */ 277 if (failures && strcmp(olduser, username) != 0) { 278 if (failures > (pwd ? 0 : 1)) 279 badlogin(olduser); 280 } 281 282 /* 283 * Load the PAM policy and set some variables 284 */ 285 pam_err = pam_start("login", username, &pamc, &pamh); 286 if (pam_err != PAM_SUCCESS) { 287 pam_syslog("pam_start()"); 288 #ifdef USE_BSM_AUDIT 289 au_login_fail("PAM Error", 1); 290 #endif 291 bail(NO_SLEEP_EXIT, 1); 292 } 293 pam_err = pam_set_item(pamh, PAM_TTY, tty); 294 if (pam_err != PAM_SUCCESS) { 295 pam_syslog("pam_set_item(PAM_TTY)"); 296 #ifdef USE_BSM_AUDIT 297 au_login_fail("PAM Error", 1); 298 #endif 299 bail(NO_SLEEP_EXIT, 1); 300 } 301 pam_err = pam_set_item(pamh, PAM_RHOST, hostname); 302 if (pam_err != PAM_SUCCESS) { 303 pam_syslog("pam_set_item(PAM_RHOST)"); 304 #ifdef USE_BSM_AUDIT 305 au_login_fail("PAM Error", 1); 306 #endif 307 bail(NO_SLEEP_EXIT, 1); 308 } 309 310 pwd = getpwnam(username); 311 if (pwd != NULL && pwd->pw_uid == 0) 312 rootlogin = 1; 313 314 /* 315 * If the -f option was specified and the caller is 316 * root or the caller isn't changing their uid, don't 317 * authenticate. 318 */ 319 if (pwd != NULL && fflag && 320 (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) { 321 /* already authenticated */ 322 rval = 0; 323 #ifdef USE_BSM_AUDIT 324 auditsuccess = 0; /* opened a terminal window only */ 325 #endif 326 } else { 327 fflag = 0; 328 setpriority(PRIO_PROCESS, 0, -4); 329 rval = auth_pam(); 330 setpriority(PRIO_PROCESS, 0, 0); 331 } 332 333 if (pwd && rval == 0) 334 break; 335 336 pam_cleanup(); 337 338 /* 339 * We are not exiting here, but this corresponds to a failed 340 * login event, so set exitstatus to 1. 341 */ 342 #ifdef USE_BSM_AUDIT 343 au_login_fail("Login incorrect", 1); 344 #endif 345 346 printf("Login incorrect\n"); 347 failures++; 348 349 pwd = NULL; 350 351 /* 352 * Allow up to 'retry' (10) attempts, but start 353 * backing off after 'backoff' (3) attempts. 354 */ 355 if (++cnt > backoff) { 356 if (cnt >= retries) { 357 badlogin(username); 358 bail(SLEEP_EXIT, 1); 359 } 360 sleep((u_int)((cnt - backoff) * 5)); 361 } 362 } 363 364 /* committed to login -- turn off timeout */ 365 alarm((u_int)0); 366 signal(SIGHUP, SIG_DFL); 367 368 endpwent(); 369 370 #ifdef USE_BSM_AUDIT 371 /* Audit successful login. */ 372 if (auditsuccess) 373 au_login_success(); 374 #endif 375 376 /* 377 * Establish the login class. 378 */ 379 lc = login_getpwclass(pwd); 380 lc_user = login_getuserclass(pwd); 381 382 if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0))) 383 quietlog = login_getcapbool(lc, "hushlogin", 0); 384 385 /* 386 * Switching needed for NFS with root access disabled. 387 * 388 * XXX: This change fails to modify the additional groups for the 389 * process, and as such, may restrict rights normally granted 390 * through those groups. 391 */ 392 setegid(pwd->pw_gid); 393 seteuid(rootlogin ? 0 : pwd->pw_uid); 394 if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { 395 if (login_getcapbool(lc, "requirehome", 0)) 396 refused("Home directory not available", "HOMEDIR", 1); 397 if (chdir("/") < 0) 398 refused("Cannot find root directory", "ROOTDIR", 1); 399 if (!quietlog || *pwd->pw_dir) 400 printf("No home directory.\nLogging in with home = \"/\".\n"); 401 pwd->pw_dir = strdup("/"); 402 if (pwd->pw_dir == NULL) { 403 syslog(LOG_NOTICE, "strdup(): %m"); 404 bail(SLEEP_EXIT, 1); 405 } 406 } 407 seteuid(euid); 408 setegid(egid); 409 if (!quietlog) { 410 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 411 if (!quietlog) 412 pam_silent = 0; 413 } 414 415 shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); 416 if (*pwd->pw_shell == '\0') 417 pwd->pw_shell = strdup(_PATH_BSHELL); 418 if (pwd->pw_shell == NULL) { 419 syslog(LOG_NOTICE, "strdup(): %m"); 420 bail(SLEEP_EXIT, 1); 421 } 422 if (*shell == '\0') /* Not overridden */ 423 shell = pwd->pw_shell; 424 if ((shell = strdup(shell)) == NULL) { 425 syslog(LOG_NOTICE, "strdup(): %m"); 426 bail(SLEEP_EXIT, 1); 427 } 428 429 /* 430 * Set device protections, depending on what terminal the 431 * user is logged in. This feature is used on Suns to give 432 * console users better privacy. 433 */ 434 login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); 435 436 /* 437 * Clear flags of the tty. None should be set, and when the 438 * user sets them otherwise, this can cause the chown to fail. 439 * Since it isn't clear that flags are useful on character 440 * devices, we just clear them. 441 * 442 * We don't log in the case of EOPNOTSUPP because dev might be 443 * on NFS, which doesn't support chflags. 444 * 445 * We don't log in the EROFS because that means that /dev is on 446 * a read only file system and we assume that the permissions there 447 * are sane. 448 */ 449 if (ttyn != tname && chflags(ttyn, 0)) 450 if (errno != EOPNOTSUPP && errno != EROFS) 451 syslog(LOG_ERR, "chflags(%s): %m", ttyn); 452 if (ttyn != tname && chown(ttyn, pwd->pw_uid, 453 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) 454 if (errno != EROFS) 455 syslog(LOG_ERR, "chown(%s): %m", ttyn); 456 457 /* 458 * Exclude cons/vt/ptys only, assume dialup otherwise 459 * TODO: Make dialup tty determination a library call 460 * for consistency (finger etc.) 461 */ 462 if (hflag && isdialuptty(tty)) 463 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 464 465 #ifdef LOGALL 466 /* 467 * Syslog each successful login, so we don't have to watch 468 * hundreds of wtmp or lastlogin files. 469 */ 470 if (hflag) 471 syslog(LOG_INFO, "login from %s on %s as %s", 472 hostname, tty, pwd->pw_name); 473 else 474 syslog(LOG_INFO, "login on %s as %s", 475 tty, pwd->pw_name); 476 #endif 477 478 /* 479 * If fflag is on, assume caller/authenticator has logged root 480 * login. 481 */ 482 if (rootlogin && fflag == 0) { 483 if (hflag) 484 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", 485 username, tty, hostname); 486 else 487 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", 488 username, tty); 489 } 490 491 /* 492 * Destroy environment unless user has requested its 493 * preservation - but preserve TERM in all cases 494 */ 495 term = getenv("TERM"); 496 if (!pflag) 497 environ = envinit; 498 if (term != NULL) { 499 if (setenv("TERM", term, 0) == -1) 500 err(1, "setenv: cannot set TERM=%s", term); 501 } 502 503 /* 504 * PAM modules might add supplementary groups during pam_setcred(). 505 */ 506 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { 507 syslog(LOG_ERR, "setusercontext() failed - exiting"); 508 bail(NO_SLEEP_EXIT, 1); 509 } 510 511 pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED); 512 if (pam_err != PAM_SUCCESS) { 513 pam_syslog("pam_setcred()"); 514 bail(NO_SLEEP_EXIT, 1); 515 } 516 pam_cred_established = 1; 517 518 pam_err = pam_open_session(pamh, pam_silent); 519 if (pam_err != PAM_SUCCESS) { 520 pam_syslog("pam_open_session()"); 521 bail(NO_SLEEP_EXIT, 1); 522 } 523 pam_session_established = 1; 524 525 /* 526 * We must fork() before setuid() because we need to call 527 * pam_close_session() as root. 528 */ 529 pid = fork(); 530 if (pid < 0) { 531 err(1, "fork"); 532 } else if (pid != 0) { 533 /* 534 * Parent: wait for child to finish, then clean up 535 * session. 536 */ 537 int status; 538 setproctitle("-%s [pam]", getprogname()); 539 waitpid(pid, &status, 0); 540 bail(NO_SLEEP_EXIT, 0); 541 } 542 543 /* 544 * NOTICE: We are now in the child process! 545 */ 546 547 /* 548 * Add any environment variables the PAM modules may have set. 549 */ 550 export_pam_environment(); 551 552 /* 553 * We're done with PAM now; our parent will deal with the rest. 554 */ 555 pam_end(pamh, 0); 556 pamh = NULL; 557 558 /* 559 * We don't need to be root anymore, so set the login name and 560 * the UID. 561 */ 562 if (setlogin(username) != 0) { 563 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username); 564 bail(NO_SLEEP_EXIT, 1); 565 } 566 if (setusercontext(lc, pwd, pwd->pw_uid, 567 LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) { 568 syslog(LOG_ERR, "setusercontext() failed - exiting"); 569 exit(1); 570 } 571 572 if (setenv("SHELL", pwd->pw_shell, 1) == -1) 573 err(1, "setenv: cannot set SHELL=%s", pwd->pw_shell); 574 if (setenv("HOME", pwd->pw_dir, 1) == -1) 575 err(1, "setenv: cannot set HOME=%s", pwd->pw_dir); 576 /* Overwrite "term" from login.conf(5) for any known TERM */ 577 if (term == NULL && (tp = stypeof(tty)) != NULL) { 578 if (setenv("TERM", tp, 1) == -1) 579 err(1, "setenv: cannot set TERM=%s", tp); 580 } else { 581 if (setenv("TERM", TERM_UNKNOWN, 0) == -1) 582 err(1, "setenv: cannot set TERM=%s", TERM_UNKNOWN); 583 } 584 585 if (setenv("LOGNAME", username, 1) == -1) 586 err(1, "setenv: cannot set LOGNAME=%s", username); 587 if (setenv("USER", username, 1) == -1) 588 err(1, "setenv: cannot set USER=%s", username); 589 if (setenv("PATH", 590 rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0) == -1) { 591 err(1, "setenv: cannot set PATH=%s", 592 rootlogin ? _PATH_STDPATH : _PATH_DEFPATH); 593 } 594 595 if (!quietlog) { 596 const char *cw; 597 598 cw = login_getcapstr(lc, "copyright", NULL, NULL); 599 if (cw == NULL || motd(cw) == -1) 600 printf("%s", copyright); 601 602 printf("\n"); 603 604 cw = login_getcapstr(lc, "welcome", NULL, NULL); 605 if (cw != NULL && access(cw, F_OK) == 0) 606 motd(cw); 607 else 608 motd(_PATH_MOTDFILE); 609 610 if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 && 611 login_getcapbool(lc, "nocheckmail", 0) == 0) { 612 char *cx; 613 614 /* $MAIL may have been set by class. */ 615 cx = getenv("MAIL"); 616 if (cx == NULL) { 617 asprintf(&cx, "%s/%s", 618 _PATH_MAILDIR, pwd->pw_name); 619 } 620 if (cx && stat(cx, &st) == 0 && st.st_size != 0) 621 printf("You have %smail.\n", 622 (st.st_mtime > st.st_atime) ? "new " : ""); 623 if (getenv("MAIL") == NULL) 624 free(cx); 625 } 626 } 627 628 login_close(lc_user); 629 login_close(lc); 630 631 signal(SIGALRM, SIG_DFL); 632 signal(SIGQUIT, SIG_DFL); 633 signal(SIGINT, SIG_DFL); 634 signal(SIGTSTP, SIG_IGN); 635 636 /* 637 * Login shells have a leading '-' in front of argv[0] 638 */ 639 p = strrchr(pwd->pw_shell, '/'); 640 if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) { 641 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size", 642 username); 643 errx(1, "shell exceeds maximum pathname size"); 644 } else if (arg0 == NULL) { 645 err(1, "asprintf()"); 646 } 647 648 execlp(shell, arg0, NULL); 649 err(1, "%s", shell); 650 651 /* 652 * That's it, folks! 653 */ 654 } 655 656 /* 657 * Attempt to authenticate the user using PAM. Returns 0 if the user is 658 * authenticated, or 1 if not authenticated. If some sort of PAM system 659 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 660 * function returns -1. This can be used as an indication that we should 661 * fall back to a different authentication mechanism. 662 */ 663 static int 664 auth_pam(void) 665 { 666 const char *tmpl_user; 667 const void *item; 668 int rval; 669 670 pam_err = pam_authenticate(pamh, pam_silent); 671 switch (pam_err) { 672 673 case PAM_SUCCESS: 674 /* 675 * With PAM we support the concept of a "template" 676 * user. The user enters a login name which is 677 * authenticated by PAM, usually via a remote service 678 * such as RADIUS or TACACS+. If authentication 679 * succeeds, a different but related "template" name 680 * is used for setting the credentials, shell, and 681 * home directory. The name the user enters need only 682 * exist on the remote authentication server, but the 683 * template name must be present in the local password 684 * database. 685 * 686 * This is supported by two various mechanisms in the 687 * individual modules. However, from the application's 688 * point of view, the template user is always passed 689 * back as a changed value of the PAM_USER item. 690 */ 691 pam_err = pam_get_item(pamh, PAM_USER, &item); 692 if (pam_err == PAM_SUCCESS) { 693 tmpl_user = (const char *)item; 694 if (strcmp(username, tmpl_user) != 0) 695 pwd = getpwnam(tmpl_user); 696 } else { 697 pam_syslog("pam_get_item(PAM_USER)"); 698 } 699 rval = 0; 700 break; 701 702 case PAM_AUTH_ERR: 703 case PAM_USER_UNKNOWN: 704 case PAM_MAXTRIES: 705 rval = 1; 706 break; 707 708 default: 709 pam_syslog("pam_authenticate()"); 710 rval = -1; 711 break; 712 } 713 714 if (rval == 0) { 715 pam_err = pam_acct_mgmt(pamh, pam_silent); 716 switch (pam_err) { 717 case PAM_SUCCESS: 718 break; 719 case PAM_NEW_AUTHTOK_REQD: 720 pam_err = pam_chauthtok(pamh, 721 pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK); 722 if (pam_err != PAM_SUCCESS) { 723 pam_syslog("pam_chauthtok()"); 724 rval = 1; 725 } 726 break; 727 default: 728 pam_syslog("pam_acct_mgmt()"); 729 rval = 1; 730 break; 731 } 732 } 733 734 if (rval != 0) { 735 pam_end(pamh, pam_err); 736 pamh = NULL; 737 } 738 return (rval); 739 } 740 741 /* 742 * Export any environment variables PAM modules may have set 743 */ 744 static void 745 export_pam_environment(void) 746 { 747 char **pam_env; 748 char **pp; 749 750 pam_env = pam_getenvlist(pamh); 751 if (pam_env != NULL) { 752 for (pp = pam_env; *pp != NULL; pp++) { 753 export(*pp); 754 free(*pp); 755 } 756 } 757 } 758 759 /* 760 * Perform sanity checks on an environment variable: 761 * - Make sure there is an '=' in the string. 762 * - Make sure the string doesn't run on too long. 763 * - Do not export certain variables. This list was taken from the 764 * Solaris pam_putenv(3) man page. 765 * Then export it. 766 */ 767 static int 768 export(const char *s) 769 { 770 static const char *noexport[] = { 771 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH", 772 "IFS", "PATH", NULL 773 }; 774 char *p; 775 const char **pp; 776 size_t n; 777 778 if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL) 779 return (0); 780 if (strncmp(s, "LD_", 3) == 0) 781 return (0); 782 for (pp = noexport; *pp != NULL; pp++) { 783 n = strlen(*pp); 784 if (s[n] == '=' && strncmp(s, *pp, n) == 0) 785 return (0); 786 } 787 *p = '\0'; 788 if (setenv(s, p + 1, 1) == -1) 789 err(1, "setenv: cannot set %s=%s", s, p + 1); 790 *p = '='; 791 return (1); 792 } 793 794 static void 795 usage(void) 796 { 797 798 fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n"); 799 exit(1); 800 } 801 802 /* 803 * Prompt user and read login name from stdin. 804 */ 805 static char * 806 getloginname(void) 807 { 808 char *nbuf, *p; 809 int ch; 810 811 nbuf = malloc(MAXLOGNAME); 812 if (nbuf == NULL) 813 err(1, "malloc()"); 814 do { 815 printf("%s", prompt); 816 for (p = nbuf; (ch = getchar()) != '\n'; ) { 817 if (ch == EOF) { 818 badlogin(username); 819 bail(NO_SLEEP_EXIT, 0); 820 } 821 if (p < nbuf + MAXLOGNAME - 1) 822 *p++ = ch; 823 } 824 } while (p == nbuf); 825 826 *p = '\0'; 827 if (nbuf[0] == '-') { 828 pam_silent = 0; 829 memmove(nbuf, nbuf + 1, strlen(nbuf)); 830 } else { 831 pam_silent = PAM_SILENT; 832 } 833 return nbuf; 834 } 835 836 /* 837 * SIGINT handler for motd(). 838 */ 839 static volatile int motdinterrupt; 840 static void 841 sigint(int signo __unused) 842 { 843 motdinterrupt = 1; 844 } 845 846 /* 847 * Display the contents of a file (such as /etc/motd). 848 */ 849 static int 850 motd(const char *motdfile) 851 { 852 sig_t oldint; 853 FILE *f; 854 int ch; 855 856 if ((f = fopen(motdfile, "r")) == NULL) 857 return (-1); 858 motdinterrupt = 0; 859 oldint = signal(SIGINT, sigint); 860 while ((ch = fgetc(f)) != EOF && !motdinterrupt) 861 putchar(ch); 862 signal(SIGINT, oldint); 863 if (ch != EOF || ferror(f)) { 864 fclose(f); 865 return (-1); 866 } 867 fclose(f); 868 return (0); 869 } 870 871 /* 872 * SIGALRM handler, to enforce login prompt timeout. 873 * 874 * XXX This can potentially confuse the hell out of PAM. We should 875 * XXX instead implement a conversation function that returns 876 * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal 877 * XXX handler just set a flag. 878 */ 879 static void 880 timedout(int signo __unused) 881 { 882 883 longjmp(timeout_buf, signo); 884 } 885 886 static void 887 badlogin(char *name) 888 { 889 890 if (failures == 0) 891 return; 892 if (hflag) { 893 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 894 failures, failures > 1 ? "S" : "", hostname); 895 syslog(LOG_AUTHPRIV|LOG_NOTICE, 896 "%d LOGIN FAILURE%s FROM %s, %s", 897 failures, failures > 1 ? "S" : "", hostname, name); 898 } else { 899 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 900 failures, failures > 1 ? "S" : "", tty); 901 syslog(LOG_AUTHPRIV|LOG_NOTICE, 902 "%d LOGIN FAILURE%s ON %s, %s", 903 failures, failures > 1 ? "S" : "", tty, name); 904 } 905 failures = 0; 906 } 907 908 const char * 909 stypeof(char *ttyid) 910 { 911 struct ttyent *t; 912 913 if (ttyid != NULL && *ttyid != '\0') { 914 t = getttynam(ttyid); 915 if (t != NULL && t->ty_type != NULL) 916 return (t->ty_type); 917 } 918 return (NULL); 919 } 920 921 static void 922 refused(const char *msg, const char *rtype, int lout) 923 { 924 925 if (msg != NULL) 926 printf("%s.\n", msg); 927 if (hflag) 928 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s", 929 pwd->pw_name, rtype, hostname, tty); 930 else 931 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s", 932 pwd->pw_name, rtype, tty); 933 if (lout) 934 bail(SLEEP_EXIT, 1); 935 } 936 937 /* 938 * Log a PAM error 939 */ 940 static void 941 pam_syslog(const char *msg) 942 { 943 syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); 944 } 945 946 /* 947 * Shut down PAM 948 */ 949 static void 950 pam_cleanup(void) 951 { 952 953 if (pamh != NULL) { 954 if (pam_session_established) { 955 pam_err = pam_close_session(pamh, 0); 956 if (pam_err != PAM_SUCCESS) 957 pam_syslog("pam_close_session()"); 958 } 959 pam_session_established = 0; 960 if (pam_cred_established) { 961 pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED); 962 if (pam_err != PAM_SUCCESS) 963 pam_syslog("pam_setcred()"); 964 } 965 pam_cred_established = 0; 966 pam_end(pamh, pam_err); 967 pamh = NULL; 968 } 969 } 970 971 /* 972 * Exit, optionally after sleeping a few seconds 973 */ 974 void 975 bail(int sec, int eval) 976 { 977 978 pam_cleanup(); 979 #ifdef USE_BSM_AUDIT 980 if (pwd != NULL) 981 audit_logout(); 982 #endif 983 sleep(sec); 984 exit(eval); 985 } 986