1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 41 /* All Rights Reserved */ 42 43 /* 44 * For a complete reference to login(1), see the manual page. However, 45 * login has accreted some intentionally undocumented options, which are 46 * explained here: 47 * 48 * -a: This legacy flag appears to be unused. 49 * 50 * -f <username>: This flag was introduced by PSARC 1995/039 in support 51 * of Kerberos. But it's not used by Sun's Kerberos implementation. 52 * It is however employed by zlogin(1), since it allows one to tell 53 * login: "This user is authenticated." In the case of zlogin that's 54 * true because the zone always trusts the global zone. 55 * 56 * -z <zonename>: This flag is passed to login when zlogin(1) executes a 57 * zone login. This tells login(1) to skip it's normal CONSOLE check 58 * (i.e. that the root login must be on /dev/console) and tells us the 59 * name of the zone from which the login is occurring. 60 */ 61 62 #include <sys/types.h> 63 #include <sys/param.h> 64 #include <unistd.h> /* For logfile locking */ 65 #include <signal.h> 66 #include <stdio.h> 67 #include <sys/stat.h> 68 #include <string.h> 69 #include <deflt.h> 70 #include <grp.h> 71 #include <fcntl.h> 72 #include <lastlog.h> 73 #include <termio.h> 74 #include <utmpx.h> 75 #include <stdlib.h> 76 #include <wait.h> 77 #include <errno.h> 78 #include <ctype.h> 79 #include <syslog.h> 80 #include <ulimit.h> 81 #include <libgen.h> 82 #include <pwd.h> 83 #include <security/pam_appl.h> 84 #include <strings.h> 85 #include <libdevinfo.h> 86 #include <zone.h> 87 #include "login_audit.h" 88 89 #include <krb5_repository.h> 90 /* 91 * 92 * *** Defines, Macros, and String Constants *** 93 * 94 * 95 */ 96 97 #define ISSUEFILE "/etc/issue" /* file to print before prompt */ 98 #define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */ 99 100 /* 101 * These need to be defined for UTMPX management. 102 * If we add in the utility functions later, we 103 * can remove them. 104 */ 105 #define __UPDATE_ENTRY 1 106 #define __LOGIN 2 107 108 /* 109 * Intervals to sleep after failed login 110 */ 111 #ifndef SLEEPTIME 112 #define SLEEPTIME 4 /* sleeptime before login incorrect msg */ 113 #endif 114 static int Sleeptime = SLEEPTIME; 115 116 /* 117 * seconds login disabled after allowable number of unsuccessful attempts 118 */ 119 #ifndef DISABLETIME 120 #define DISABLETIME 20 121 #endif 122 static int Disabletime = DISABLETIME; 123 124 #define MAXTRYS 5 125 126 static int retry = MAXTRYS; 127 128 /* 129 * Login logging support 130 */ 131 #define LOGINLOG "/var/adm/loginlog" /* login log file */ 132 #define LNAME_SIZE 20 /* size of logged logname */ 133 #define TTYN_SIZE 15 /* size of logged tty name */ 134 #define TIME_SIZE 30 /* size of logged time string */ 135 #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3) 136 #define L_WAITTIME 5 /* waittime for log file to unlock */ 137 #define LOGTRYS 10 /* depth of 'try' logging */ 138 139 /* 140 * String manipulation macros: SCPYN, SCPYL, EQN and ENVSTRNCAT 141 * SCPYL is the safer version of SCPYN 142 */ 143 #define SCPYL(a, b) (void) strlcpy(a, b, sizeof (a)) 144 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a)) 145 #define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0) 146 #define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \ 147 (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); } 148 149 /* 150 * Other macros 151 */ 152 #define NMAX sizeof (((struct utmpx *)0)->ut_name) 153 #define HMAX sizeof (((struct utmpx *)0)->ut_host) 154 #define min(a, b) (((a) < (b)) ? (a) : (b)) 155 156 /* 157 * Various useful files and string constants 158 */ 159 #define SHELL "/usr/bin/sh" 160 #define SHELL2 "/sbin/sh" 161 #define SUBLOGIN "<!sublogin>" 162 #define LASTLOG "/var/adm/lastlog" 163 #define PROG_NAME "login" 164 #define HUSHLOGIN ".hushlogin" 165 166 /* 167 * Array and Buffer sizes 168 */ 169 #define PBUFSIZE 8 /* max significant characters in a password */ 170 #define MAXARGS 63 /* change value below if changing this */ 171 #define MAXARGSWIDTH 2 /* log10(MAXARGS) */ 172 #define MAXENV 1024 173 #define MAXLINE 2048 174 175 /* 176 * Miscellaneous constants 177 */ 178 #define ROOTUID 0 179 #define ERROR 1 180 #define OK 0 181 #define LOG_ERROR 1 182 #define DONT_LOG_ERROR 0 183 #define TRUE 1 184 #define FALSE 0 185 186 /* 187 * Counters for counting the number of failed login attempts 188 */ 189 static int trys = 0; 190 static int count = 1; 191 192 /* 193 * error value for login_exit() audit output (0 == no audit record) 194 */ 195 static int audit_error = 0; 196 197 /* 198 * Externs a plenty 199 */ 200 extern int getsecretkey(); 201 202 /* 203 * The current user name 204 */ 205 static char user_name[NMAX]; 206 static char minusnam[16] = "-"; 207 208 /* 209 * login_pid, used to find utmpx entry to update. 210 */ 211 static pid_t login_pid; 212 213 /* 214 * locale environments to be passed to shells. 215 */ 216 static char *localeenv[] = { 217 "LANG", 218 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", 219 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0}; 220 static int locale_envmatch(char *, char *); 221 222 /* 223 * Environment variable support 224 */ 225 static char shell[256] = { "SHELL=" }; 226 static char home[MAXPATHLEN] = { "HOME=" }; 227 static char term[64] = { "TERM=" }; 228 static char logname[30] = { "LOGNAME=" }; 229 static char timez[100] = { "TZ=" }; 230 static char hertz[10] = { "HZ=" }; 231 static char path[MAXPATHLEN] = { "PATH=" }; 232 static char *newenv[10+MAXARGS] = 233 {home, path, logname, hertz, term, 0, 0}; 234 static char **envinit = newenv; 235 static int basicenv; 236 static char *zero = (char *)0; 237 static char **envp; 238 #ifndef NO_MAIL 239 static char mail[30] = { "MAIL=/var/mail/" }; 240 #endif 241 extern char **environ; 242 static char inputline[MAXLINE]; 243 244 #define MAX_ID_LEN 256 245 #define MAX_REPOSITORY_LEN 256 246 #define MAX_PAMSERVICE_LEN 256 247 248 static char identity[MAX_ID_LEN]; 249 static char repository[MAX_REPOSITORY_LEN]; 250 static char progname[MAX_PAMSERVICE_LEN]; 251 252 253 /* 254 * Strings used to prompt the user. 255 */ 256 static char loginmsg[] = "login: "; 257 static char passwdmsg[] = "Password:"; 258 static char incorrectmsg[] = "Login incorrect\n"; 259 260 /* 261 * Password file support 262 */ 263 static struct passwd *pwd = NULL; 264 static char remote_host[HMAX]; 265 static char zone_name[ZONENAME_MAX]; 266 267 /* 268 * Illegal passwd entries. 269 */ 270 static struct passwd nouser = { "", "no:password", (uid_t)-1 }; 271 272 /* 273 * Log file support 274 */ 275 static char *log_entry[LOGTRYS]; 276 static int writelog = 0; 277 static int lastlogok = 0; 278 static struct lastlog ll; 279 static int dosyslog = 0; 280 static int flogin = MAXTRYS; /* flag for SYSLOG_FAILED_LOGINS */ 281 282 /* 283 * Default file toggles 284 */ 285 static char *Pndefault = "/etc/default/login"; 286 static char *Altshell = NULL; 287 static char *Console = NULL; 288 static int Passreqflag = 0; 289 290 #define DEFUMASK 022 291 static mode_t Umask = DEFUMASK; 292 static char *Def_tz = NULL; 293 static char *tmp_tz = NULL; 294 static char *Def_hertz = NULL; 295 #define SET_FSIZ 2 /* ulimit() command arg */ 296 static long Def_ulimit = 0; 297 #define MAX_TIMEOUT (15 * 60) 298 #define DEF_TIMEOUT (5 * 60) 299 static unsigned Def_timeout = DEF_TIMEOUT; 300 static char *Def_path = NULL; 301 static char *Def_supath = NULL; 302 #define DEF_PATH "/usr/bin:" /* same as PATH */ 303 #define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */ 304 305 /* 306 * Defaults for updating expired passwords 307 */ 308 #define DEF_ATTEMPTS 3 309 310 /* 311 * ttyprompt will point to the environment variable TTYPROMPT. 312 * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt. 313 */ 314 static char *ttyprompt = NULL; 315 static char *ttyn = NULL; 316 317 /* 318 * Pass inherited environment. Used by telnetd in support of the telnet 319 * ENVIRON option. 320 */ 321 static boolean_t pflag = B_FALSE; 322 static boolean_t uflag = B_FALSE; 323 static boolean_t Rflag = B_FALSE; 324 static boolean_t sflag = B_FALSE; 325 static boolean_t Uflag = B_FALSE; 326 static boolean_t tflag = B_FALSE; 327 static boolean_t hflag = B_FALSE; 328 static boolean_t rflag = B_FALSE; 329 static boolean_t zflag = B_FALSE; 330 331 /* 332 * Remote login support 333 */ 334 static char rusername[NMAX+1], lusername[NMAX+1]; 335 static char terminal[MAXPATHLEN]; 336 337 /* 338 * Pre-authentication flag support 339 */ 340 static int fflag; 341 342 static char ** getargs(char *); 343 344 static int login_conv(int, struct pam_message **, 345 struct pam_response **, void *); 346 347 static struct pam_conv pam_conv = {login_conv, NULL}; 348 static pam_handle_t *pamh; /* Authentication handle */ 349 350 /* 351 * Function declarations 352 */ 353 static void turn_on_logging(void); 354 static void defaults(void); 355 static void usage(void); 356 static void process_rlogin(void); 357 static void login_authenticate(); 358 static void setup_credentials(void); 359 static void adjust_nice(void); 360 static void update_utmpx_entry(int); 361 static void establish_user_environment(char **); 362 static void print_banner(void); 363 static void display_last_login_time(void); 364 static void exec_the_shell(void); 365 static int process_chroot_logins(void); 366 static void chdir_to_dir_user(void); 367 static void check_log(void); 368 static void validate_account(void); 369 static void doremoteterm(char *); 370 static int get_options(int, char **); 371 static void getstr(char *, int, char *); 372 static int legalenvvar(char *); 373 static void check_for_console(void); 374 static void check_for_dueling_unix(char *); 375 static void get_user_name(void); 376 static uint_t get_audit_id(void); 377 static void login_exit(int)__NORETURN; 378 static int logins_disabled(char *); 379 static void log_bad_attempts(void); 380 static int is_number(char *); 381 382 /* 383 * *** main *** 384 * 385 * The primary flow of control is directed in this routine. 386 * Control moves in line from top to bottom calling subfunctions 387 * which perform the bulk of the work. Many of these calls exit 388 * when a fatal error is encountered and do not return to main. 389 * 390 * 391 */ 392 393 int 394 main(int argc, char *argv[], char **renvp) 395 { 396 int sublogin; 397 int pam_rc; 398 399 login_pid = getpid(); 400 401 /* 402 * Set up Defaults and flags 403 */ 404 defaults(); 405 SCPYL(progname, PROG_NAME); 406 407 /* 408 * Set up default umask 409 */ 410 if (Umask > ((mode_t)0777)) 411 Umask = DEFUMASK; 412 (void) umask(Umask); 413 414 /* 415 * Set up default timeouts and delays 416 */ 417 if (Def_timeout > MAX_TIMEOUT) 418 Def_timeout = MAX_TIMEOUT; 419 if (Sleeptime < 0 || Sleeptime > 5) 420 Sleeptime = SLEEPTIME; 421 422 (void) alarm(Def_timeout); 423 424 /* 425 * Ignore SIGQUIT and SIGINT and set nice to 0 426 */ 427 (void) signal(SIGQUIT, SIG_IGN); 428 (void) signal(SIGINT, SIG_IGN); 429 (void) nice(0); 430 431 /* 432 * Set flag to disable the pid check if you find that you are 433 * a subsystem login. 434 */ 435 sublogin = 0; 436 if (*renvp && strcmp(*renvp, SUBLOGIN) == 0) 437 sublogin = 1; 438 439 /* 440 * Parse Arguments 441 */ 442 if (get_options(argc, argv) == -1) { 443 usage(); 444 audit_error = ADT_FAIL_VALUE_BAD_CMD; 445 login_exit(1); 446 } 447 448 /* 449 * if devicename is not passed as argument, call ttyname(0) 450 */ 451 if (ttyn == NULL) { 452 ttyn = ttyname(0); 453 if (ttyn == NULL) 454 ttyn = "/dev/???"; 455 } 456 457 /* 458 * Call pam_start to initiate a PAM authentication operation 459 */ 460 461 if ((pam_rc = pam_start(progname, user_name, &pam_conv, &pamh)) 462 != PAM_SUCCESS) { 463 audit_error = ADT_FAIL_PAM + pam_rc; 464 login_exit(1); 465 } 466 if ((pam_rc = pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) { 467 audit_error = ADT_FAIL_PAM + pam_rc; 468 login_exit(1); 469 } 470 if ((pam_rc = pam_set_item(pamh, PAM_RHOST, remote_host)) != 471 PAM_SUCCESS) { 472 audit_error = ADT_FAIL_PAM + pam_rc; 473 login_exit(1); 474 } 475 476 /* 477 * We currently only support special handling of the KRB5 PAM repository 478 */ 479 if ((Rflag && strlen(repository)) && 480 strcmp(repository, KRB5_REPOSITORY_NAME) == 0 && 481 (uflag && strlen(identity))) { 482 krb5_repository_data_t krb5_data; 483 pam_repository_t pam_rep_data; 484 485 krb5_data.principal = identity; 486 krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED; 487 488 pam_rep_data.type = repository; 489 pam_rep_data.scope = (void *)&krb5_data; 490 pam_rep_data.scope_len = sizeof (krb5_data); 491 492 (void) pam_set_item(pamh, PAM_REPOSITORY, 493 (void *)&pam_rep_data); 494 } 495 496 /* 497 * Open the log file which contains a record of successful and failed 498 * login attempts 499 */ 500 turn_on_logging(); 501 502 /* 503 * say "hi" to syslogd .. 504 */ 505 openlog("login", 0, LOG_AUTH); 506 507 /* 508 * Do special processing for -r (rlogin) flag 509 */ 510 if (rflag) 511 process_rlogin(); 512 513 /* 514 * validate user 515 */ 516 /* we are already authenticated. fill in what we must, then continue */ 517 if (fflag) { 518 if ((pwd = getpwnam(user_name)) == NULL) { 519 audit_error = ADT_FAIL_VALUE_USERNAME; 520 521 log_bad_attempts(); 522 (void) printf("Login failed: unknown user '%s'.\n", 523 user_name); 524 login_exit(1); 525 } 526 } else { 527 /* 528 * Perform the primary login authentication activity. 529 */ 530 login_authenticate(); 531 } 532 533 /* change root login, then we exec another login and try again */ 534 if (process_chroot_logins() != OK) 535 login_exit(1); 536 537 /* 538 * If root login and not on system console then call exit(2) 539 */ 540 check_for_console(); 541 542 /* 543 * Check to see if a shutdown is in progress, if it is and 544 * we are not root then throw the user off the system 545 */ 546 if (logins_disabled(user_name) == TRUE) { 547 audit_error = ADT_FAIL_VALUE_LOGIN_DISABLED; 548 login_exit(1); 549 } 550 551 if (pwd->pw_uid == 0) { 552 if (Def_supath != NULL) 553 Def_path = Def_supath; 554 else 555 Def_path = DEF_SUPATH; 556 } 557 558 /* 559 * Check account expiration and passwd aging 560 */ 561 validate_account(); 562 563 /* 564 * We only get here if we've been authenticated. 565 */ 566 567 /* 568 * Now we set up the environment for the new user, which includes 569 * the users ulimit, nice value, ownership of this tty, uid, gid, 570 * and environment variables. 571 */ 572 if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L) 573 (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit); 574 575 /* di_devperm_login() sends detailed errors to syslog */ 576 if (di_devperm_login((const char *)ttyn, pwd->pw_uid, pwd->pw_gid, 577 NULL) == -1) { 578 (void) fprintf(stderr, "error processing /etc/logindevperm," 579 " see syslog for more details\n"); 580 } 581 582 adjust_nice(); /* passwd file can specify nice value */ 583 584 setup_credentials(); /* Set user credentials - exits on failure */ 585 586 /* 587 * NOTE: telnetd and rlogind rely upon this updating of utmpx 588 * to indicate that the authentication completed successfully, 589 * pam_open_session was called and therefore they are required to 590 * call pam_close_session. 591 */ 592 update_utmpx_entry(sublogin); 593 594 /* set the real (and effective) UID */ 595 if (setuid(pwd->pw_uid) == -1) { 596 login_exit(1); 597 } 598 599 /* 600 * Set up the basic environment for the exec. This includes 601 * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL. 602 */ 603 chdir_to_dir_user(); 604 605 establish_user_environment(renvp); 606 607 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */ 608 pamh = NULL; 609 610 if (pwd->pw_uid == 0) { 611 if (dosyslog) { 612 if (remote_host[0]) { 613 syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", 614 ttyn, HMAX, remote_host); 615 } else 616 syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn); 617 } 618 } 619 closelog(); 620 621 (void) signal(SIGQUIT, SIG_DFL); 622 (void) signal(SIGINT, SIG_DFL); 623 624 /* 625 * Display some useful information to the new user like the banner 626 * and last login time if not a quiet login. 627 */ 628 629 if (access(HUSHLOGIN, F_OK) != 0) { 630 print_banner(); 631 display_last_login_time(); 632 } 633 634 /* 635 * Set SIGXCPU and SIGXFSZ to default disposition. 636 * Shells inherit signal disposition from parent. 637 * And the shells should have default dispositions 638 * for the two below signals. 639 */ 640 (void) signal(SIGXCPU, SIG_DFL); 641 (void) signal(SIGXFSZ, SIG_DFL); 642 643 /* 644 * Now fire off the shell of choice 645 */ 646 exec_the_shell(); 647 648 /* 649 * All done 650 */ 651 login_exit(1); 652 return (0); 653 } 654 655 656 /* 657 * *** Utility functions *** 658 */ 659 660 661 662 /* 663 * donothing & catch - Signal catching functions 664 */ 665 666 /*ARGSUSED*/ 667 static void 668 donothing(int sig) 669 { 670 if (pamh) 671 (void) pam_end(pamh, PAM_ABORT); 672 } 673 674 #ifdef notdef 675 static int intrupt; 676 677 /*ARGSUSED*/ 678 static void 679 catch(int sig) 680 { 681 ++intrupt; 682 } 683 #endif 684 685 /* 686 * *** Bad login logging support *** 687 */ 688 689 /* 690 * badlogin() - log to the log file 'trys' 691 * unsuccessful attempts 692 */ 693 694 static void 695 badlogin(void) 696 { 697 int retval, count1, fildes; 698 699 /* 700 * Tries to open the log file. If succeed, lock it and write 701 * in the failed attempts 702 */ 703 if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) { 704 705 (void) sigset(SIGALRM, donothing); 706 (void) alarm(L_WAITTIME); 707 retval = lockf(fildes, F_LOCK, 0L); 708 (void) alarm(0); 709 (void) sigset(SIGALRM, SIG_DFL); 710 if (retval == 0) { 711 for (count1 = 0; count1 < trys; count1++) 712 (void) write(fildes, log_entry[count1], 713 (unsigned)strlen(log_entry[count1])); 714 (void) lockf(fildes, F_ULOCK, 0L); 715 } 716 (void) close(fildes); 717 } 718 } 719 720 721 /* 722 * log_bad_attempts - log each bad login attempt - called from 723 * login_authenticate. Exits when the maximum attempt 724 * count is exceeded. 725 */ 726 727 static void 728 log_bad_attempts(void) 729 { 730 time_t timenow; 731 732 if (trys >= LOGTRYS) 733 return; 734 if (writelog) { 735 (void) time(&timenow); 736 (void) strncat(log_entry[trys], user_name, LNAME_SIZE); 737 (void) strncat(log_entry[trys], ":", (size_t)1); 738 (void) strncat(log_entry[trys], ttyn, TTYN_SIZE); 739 (void) strncat(log_entry[trys], ":", (size_t)1); 740 (void) strncat(log_entry[trys], ctime(&timenow), TIME_SIZE); 741 trys++; 742 } 743 if (count > flogin) { 744 if ((pwd = getpwnam(user_name)) != NULL) { 745 if (remote_host[0]) { 746 syslog(LOG_NOTICE, 747 "Login failure on %s from %.*s, " 748 "%.*s", ttyn, HMAX, remote_host, 749 NMAX, user_name); 750 } else { 751 syslog(LOG_NOTICE, 752 "Login failure on %s, %.*s", 753 ttyn, NMAX, user_name); 754 } 755 } else { 756 if (remote_host[0]) { 757 syslog(LOG_NOTICE, 758 "Login failure on %s from %.*s", 759 ttyn, HMAX, remote_host); 760 } else { 761 syslog(LOG_NOTICE, 762 "Login failure on %s", ttyn); 763 } 764 } 765 } 766 } 767 768 769 /* 770 * turn_on_logging - if the logfile exist, turn on attempt logging and 771 * initialize the string storage area 772 */ 773 774 static void 775 turn_on_logging(void) 776 { 777 struct stat dbuf; 778 int i; 779 780 if (stat(LOGINLOG, &dbuf) == 0) { 781 writelog = 1; 782 for (i = 0; i < LOGTRYS; i++) { 783 if (!(log_entry[i] = malloc((size_t)ENT_SIZE))) { 784 writelog = 0; 785 break; 786 } 787 *log_entry[i] = '\0'; 788 } 789 } 790 } 791 792 793 /* 794 * login_conv(): 795 * This is the conv (conversation) function called from 796 * a PAM authentication module to print error messages 797 * or garner information from the user. 798 */ 799 /*ARGSUSED*/ 800 static int 801 login_conv(int num_msg, struct pam_message **msg, 802 struct pam_response **response, void *appdata_ptr) 803 { 804 struct pam_message *m; 805 struct pam_response *r; 806 char *temp; 807 int k, i; 808 809 if (num_msg <= 0) 810 return (PAM_CONV_ERR); 811 812 *response = calloc(num_msg, sizeof (struct pam_response)); 813 if (*response == NULL) 814 return (PAM_BUF_ERR); 815 816 k = num_msg; 817 m = *msg; 818 r = *response; 819 while (k--) { 820 821 switch (m->msg_style) { 822 823 case PAM_PROMPT_ECHO_OFF: 824 errno = 0; 825 temp = getpassphrase(m->msg); 826 if (temp != NULL) { 827 if (errno == EINTR) 828 return (PAM_CONV_ERR); 829 830 r->resp = strdup(temp); 831 if (r->resp == NULL) { 832 /* free responses */ 833 r = *response; 834 for (i = 0; i < num_msg; i++, r++) { 835 if (r->resp) 836 free(r->resp); 837 } 838 free(*response); 839 *response = NULL; 840 return (PAM_BUF_ERR); 841 } 842 } 843 844 m++; 845 r++; 846 break; 847 848 case PAM_PROMPT_ECHO_ON: 849 if (m->msg != NULL) 850 (void) fputs(m->msg, stdout); 851 r->resp = calloc(1, PAM_MAX_RESP_SIZE); 852 if (r->resp == NULL) { 853 /* free responses */ 854 r = *response; 855 for (i = 0; i < num_msg; i++, r++) { 856 if (r->resp) 857 free(r->resp); 858 } 859 free(*response); 860 *response = NULL; 861 return (PAM_BUF_ERR); 862 } 863 /* 864 * The response might include environment variables 865 * information. We should store that information in 866 * envp if there is any; otherwise, envp is set to 867 * NULL. 868 */ 869 bzero((void *)inputline, MAXLINE); 870 871 envp = getargs(inputline); 872 873 /* If we read in any input, process it. */ 874 if (inputline[0] != '\0') { 875 int len; 876 877 if (envp != (char **)NULL) 878 /* 879 * If getargs() did not return NULL, 880 * *envp is the first string in 881 * inputline. envp++ makes envp point 882 * to environment variables information 883 * or be NULL. 884 */ 885 envp++; 886 887 (void) strncpy(r->resp, inputline, 888 PAM_MAX_RESP_SIZE-1); 889 r->resp[PAM_MAX_RESP_SIZE-1] = NULL; 890 len = strlen(r->resp); 891 if (r->resp[len-1] == '\n') 892 r->resp[len-1] = '\0'; 893 } else { 894 login_exit(1); 895 } 896 m++; 897 r++; 898 break; 899 900 case PAM_ERROR_MSG: 901 if (m->msg != NULL) { 902 (void) fputs(m->msg, stderr); 903 (void) fputs("\n", stderr); 904 } 905 m++; 906 r++; 907 break; 908 case PAM_TEXT_INFO: 909 if (m->msg != NULL) { 910 (void) fputs(m->msg, stdout); 911 (void) fputs("\n", stdout); 912 } 913 m++; 914 r++; 915 break; 916 917 default: 918 break; 919 } 920 } 921 return (PAM_SUCCESS); 922 } 923 924 /* 925 * verify_passwd - Authenticates the user. 926 * Returns: PAM_SUCCESS if authentication successful, 927 * PAM error code if authentication fails. 928 */ 929 930 static int 931 verify_passwd(void) 932 { 933 int error; 934 char *user; 935 int flag = (Passreqflag ? PAM_DISALLOW_NULL_AUTHTOK : 0); 936 937 /* 938 * PAM authenticates the user for us. 939 */ 940 error = pam_authenticate(pamh, flag); 941 942 /* get the user_name from the pam handle */ 943 (void) pam_get_item(pamh, PAM_USER, (void**)&user); 944 945 if (user == NULL || *user == '\0') 946 return (PAM_SYSTEM_ERR); 947 948 SCPYL(user_name, user); 949 check_for_dueling_unix(user_name); 950 951 if (((pwd = getpwnam(user_name)) == NULL) && 952 (error != PAM_USER_UNKNOWN)) { 953 return (PAM_SYSTEM_ERR); 954 } 955 956 return (error); 957 } 958 959 /* 960 * quotec - Called by getargs 961 */ 962 963 static int 964 quotec(void) 965 { 966 int c, i, num; 967 968 switch (c = getc(stdin)) { 969 970 case 'n': 971 c = '\n'; 972 break; 973 974 case 'r': 975 c = '\r'; 976 break; 977 978 case 'v': 979 c = '\013'; 980 break; 981 982 case 'b': 983 c = '\b'; 984 break; 985 986 case 't': 987 c = '\t'; 988 break; 989 990 case 'f': 991 c = '\f'; 992 break; 993 994 case '0': 995 case '1': 996 case '2': 997 case '3': 998 case '4': 999 case '5': 1000 case '6': 1001 case '7': 1002 for (num = 0, i = 0; i < 3; i++) { 1003 num = num * 8 + (c - '0'); 1004 if ((c = getc(stdin)) < '0' || c > '7') 1005 break; 1006 } 1007 (void) ungetc(c, stdin); 1008 c = num & 0377; 1009 break; 1010 1011 default: 1012 break; 1013 } 1014 return (c); 1015 } 1016 1017 /* 1018 * getargs - returns an input line. Exits if EOF encountered. 1019 */ 1020 #define WHITESPACE 0 1021 #define ARGUMENT 1 1022 1023 static char ** 1024 getargs(char *input_line) 1025 { 1026 static char envbuf[MAXLINE]; 1027 static char *args[MAXARGS]; 1028 char *ptr, **answer; 1029 int c; 1030 int state; 1031 char *p = input_line; 1032 1033 ptr = envbuf; 1034 answer = &args[0]; 1035 state = WHITESPACE; 1036 1037 while ((c = getc(stdin)) != EOF && answer < &args[MAXARGS-1]) { 1038 1039 *(input_line++) = c; 1040 1041 switch (c) { 1042 1043 case '\n': 1044 if (ptr == &envbuf[0]) 1045 return ((char **)NULL); 1046 *input_line = *ptr = '\0'; 1047 *answer = NULL; 1048 return (&args[0]); 1049 1050 case ' ': 1051 case '\t': 1052 if (state == ARGUMENT) { 1053 *ptr++ = '\0'; 1054 state = WHITESPACE; 1055 } 1056 break; 1057 1058 case '\\': 1059 c = quotec(); 1060 1061 default: 1062 if (state == WHITESPACE) { 1063 *answer++ = ptr; 1064 state = ARGUMENT; 1065 } 1066 *ptr++ = c; 1067 } 1068 1069 /* Attempt at overflow, exit */ 1070 if (input_line - p >= MAXLINE - 1 || 1071 ptr >= &envbuf[sizeof (envbuf) - 1]) { 1072 audit_error = ADT_FAIL_VALUE_INPUT_OVERFLOW; 1073 login_exit(1); 1074 } 1075 } 1076 1077 /* 1078 * If we left loop because an EOF was received or we've overflown 1079 * args[], exit immediately. 1080 */ 1081 login_exit(0); 1082 /* NOTREACHED */ 1083 } 1084 1085 /* 1086 * get_user_name - Gets the user name either passed in, or from the 1087 * login: prompt. 1088 */ 1089 1090 static void 1091 get_user_name(void) 1092 { 1093 FILE *fp; 1094 1095 if ((fp = fopen(ISSUEFILE, "r")) != NULL) { 1096 char *ptr, buffer[BUFSIZ]; 1097 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) { 1098 (void) fputs(ptr, stdout); 1099 } 1100 (void) fclose(fp); 1101 } 1102 1103 /* 1104 * if TTYPROMPT is not set, use our own prompt 1105 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT 1106 * and let the module do the prompting. 1107 */ 1108 1109 if ((ttyprompt == NULL) || (*ttyprompt == '\0')) 1110 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg); 1111 else 1112 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt); 1113 1114 envp = &zero; /* XXX: is this right? */ 1115 } 1116 1117 1118 /* 1119 * Check_for_dueling_unix - Check to see if the another login is talking 1120 * to the line we've got open as a login port 1121 * Exits if we're talking to another unix system 1122 */ 1123 1124 static void 1125 check_for_dueling_unix(char *inputline) 1126 { 1127 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) || 1128 EQN(incorrectmsg, inputline)) { 1129 (void) printf("Looking at a login line.\n"); 1130 login_exit(8); 1131 } 1132 } 1133 1134 /* 1135 * logins_disabled - if the file /etc/nologin exists and the user is not 1136 * root then do not permit them to login 1137 */ 1138 static int 1139 logins_disabled(char *user_name) 1140 { 1141 FILE *nlfd; 1142 int c; 1143 if (!EQN("root", user_name) && 1144 ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) { 1145 while ((c = getc(nlfd)) != EOF) 1146 (void) putchar(c); 1147 (void) fflush(stdout); 1148 (void) sleep(5); 1149 return (TRUE); 1150 } 1151 return (FALSE); 1152 } 1153 1154 #define DEFAULT_CONSOLE "/dev/console" 1155 1156 /* 1157 * check_for_console - Checks if we're getting a root login on the 1158 * console, or a login from the global zone. Exits if not. 1159 * 1160 * If CONSOLE is set to /dev/console in /etc/default/login, then root logins 1161 * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global 1162 * zones, but checking them does no harm. 1163 */ 1164 static void 1165 check_for_console(void) 1166 { 1167 const char *consoles[] = { "/dev/console", "/dev/vt/", NULL }; 1168 int i; 1169 1170 if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE || 1171 Console == NULL) 1172 return; 1173 1174 if (strcmp(Console, DEFAULT_CONSOLE) == 0) { 1175 for (i = 0; consoles[i] != NULL; i ++) { 1176 if (strncmp(ttyn, consoles[i], 1177 strlen(consoles[i])) == 0) 1178 return; 1179 } 1180 } else { 1181 if (strcmp(ttyn, Console) == 0) 1182 return; 1183 } 1184 1185 (void) printf("Not on system console\n"); 1186 1187 audit_error = ADT_FAIL_VALUE_CONSOLE; 1188 login_exit(10); 1189 1190 } 1191 1192 /* 1193 * List of environment variables or environment variable prefixes that should 1194 * not be propagated across logins, such as when the login -p option is used. 1195 */ 1196 static const char *const illegal[] = { 1197 "SHELL=", 1198 "HOME=", 1199 "LOGNAME=", 1200 #ifndef NO_MAIL 1201 "MAIL=", 1202 #endif 1203 "CDPATH=", 1204 "IFS=", 1205 "PATH=", 1206 "LD_", 1207 "SMF_", 1208 NULL 1209 }; 1210 1211 /* 1212 * legalenvvar - Is it legal to insert this environmental variable? 1213 */ 1214 1215 static int 1216 legalenvvar(char *s) 1217 { 1218 const char *const *p; 1219 1220 for (p = &illegal[0]; *p; p++) { 1221 if (strncmp(s, *p, strlen(*p)) == 0) 1222 return (0); 1223 } 1224 1225 return (1); 1226 } 1227 1228 1229 /* 1230 * getstr - Get a string from standard input 1231 * Calls exit if read(2) fails. 1232 */ 1233 1234 static void 1235 getstr(char *buf, int cnt, char *err) 1236 { 1237 char c; 1238 1239 do { 1240 if (read(0, &c, 1) != 1) 1241 login_exit(1); 1242 *buf++ = c; 1243 } while (--cnt > 1 && c != 0); 1244 1245 *buf = 0; 1246 err = err; /* For lint */ 1247 } 1248 1249 1250 /* 1251 * defaults - read defaults 1252 */ 1253 1254 static void 1255 defaults(void) 1256 { 1257 int flags; 1258 char *ptr; 1259 1260 if (defopen(Pndefault) == 0) { 1261 /* 1262 * ignore case 1263 */ 1264 flags = defcntl(DC_GETFLAGS, 0); 1265 TURNOFF(flags, DC_CASE); 1266 (void) defcntl(DC_SETFLAGS, flags); 1267 1268 if ((Console = defread("CONSOLE=")) != NULL) 1269 Console = strdup(Console); 1270 1271 if ((Altshell = defread("ALTSHELL=")) != NULL) 1272 Altshell = strdup(Altshell); 1273 1274 if ((ptr = defread("PASSREQ=")) != NULL && 1275 strcasecmp("YES", ptr) == 0) 1276 Passreqflag = 1; 1277 1278 if ((Def_tz = defread("TIMEZONE=")) != NULL) 1279 Def_tz = strdup(Def_tz); 1280 1281 if ((Def_hertz = defread("HZ=")) != NULL) 1282 Def_hertz = strdup(Def_hertz); 1283 1284 if ((Def_path = defread("PATH=")) != NULL) 1285 Def_path = strdup(Def_path); 1286 1287 if ((Def_supath = defread("SUPATH=")) != NULL) 1288 Def_supath = strdup(Def_supath); 1289 1290 if ((ptr = defread("ULIMIT=")) != NULL) 1291 Def_ulimit = atol(ptr); 1292 1293 if ((ptr = defread("TIMEOUT=")) != NULL) 1294 Def_timeout = (unsigned)atoi(ptr); 1295 1296 if ((ptr = defread("UMASK=")) != NULL) 1297 if (sscanf(ptr, "%lo", &Umask) != 1) 1298 Umask = DEFUMASK; 1299 1300 if ((ptr = defread("SLEEPTIME=")) != NULL) { 1301 if (is_number(ptr)) 1302 Sleeptime = atoi(ptr); 1303 } 1304 1305 if ((ptr = defread("DISABLETIME=")) != NULL) { 1306 if (is_number(ptr)) 1307 Disabletime = atoi(ptr); 1308 } 1309 1310 if ((ptr = defread("SYSLOG=")) != NULL) 1311 dosyslog = strcmp(ptr, "YES") == 0; 1312 1313 if ((ptr = defread("RETRIES=")) != NULL) { 1314 if (is_number(ptr)) 1315 retry = atoi(ptr); 1316 } 1317 1318 if ((ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) { 1319 if (is_number(ptr)) 1320 flogin = atoi(ptr); 1321 else 1322 flogin = retry; 1323 } else 1324 flogin = retry; 1325 (void) defopen((char *)NULL); 1326 } 1327 } 1328 1329 1330 /* 1331 * get_options(argc, argv) 1332 * - parse the cmd line. 1333 * - return 0 if successful, -1 if failed. 1334 * Calls login_exit() on misuse of -r, -h, and -z flags 1335 */ 1336 1337 static int 1338 get_options(int argc, char *argv[]) 1339 { 1340 int c; 1341 int errflg = 0; 1342 char sflagname[NMAX+1]; 1343 const char *flags_message = "Only one of -r, -h and -z allowed\n"; 1344 1345 while ((c = getopt(argc, argv, "u:s:R:f:h:r:pad:t:U:z:")) != -1) { 1346 switch (c) { 1347 case 'a': 1348 break; 1349 1350 case 'd': 1351 /* 1352 * Must be root to pass in device name 1353 * otherwise we exit() as punishment for trying. 1354 */ 1355 if (getuid() != 0 || geteuid() != 0) { 1356 audit_error = ADT_FAIL_VALUE_DEVICE_PERM; 1357 login_exit(1); /* sigh */ 1358 /*NOTREACHED*/ 1359 } 1360 ttyn = optarg; 1361 break; 1362 1363 case 'h': 1364 if (hflag || rflag || zflag) { 1365 (void) fprintf(stderr, flags_message); 1366 login_exit(1); 1367 } 1368 hflag = B_TRUE; 1369 SCPYL(remote_host, optarg); 1370 if (argv[optind]) { 1371 if (argv[optind][0] != '-') { 1372 SCPYL(terminal, argv[optind]); 1373 optind++; 1374 } else { 1375 /* 1376 * Allow "login -h hostname -" to 1377 * skip setting up an username as "-". 1378 */ 1379 if (argv[optind][1] == '\0') 1380 optind++; 1381 } 1382 1383 } 1384 SCPYL(progname, "telnet"); 1385 break; 1386 1387 case 'r': 1388 if (hflag || rflag || zflag) { 1389 (void) fprintf(stderr, flags_message); 1390 login_exit(1); 1391 } 1392 rflag = B_TRUE; 1393 SCPYL(remote_host, optarg); 1394 SCPYL(progname, "rlogin"); 1395 break; 1396 1397 case 'p': 1398 pflag = B_TRUE; 1399 break; 1400 1401 case 'f': 1402 /* 1403 * Must be root to bypass authentication 1404 * otherwise we exit() as punishment for trying. 1405 */ 1406 if (getuid() != 0 || geteuid() != 0) { 1407 audit_error = ADT_FAIL_VALUE_AUTH_BYPASS; 1408 1409 login_exit(1); /* sigh */ 1410 /*NOTREACHED*/ 1411 } 1412 /* save fflag user name for future use */ 1413 SCPYL(user_name, optarg); 1414 fflag = B_TRUE; 1415 break; 1416 case 'u': 1417 if (!strlen(optarg)) { 1418 (void) fprintf(stderr, 1419 "Empty string supplied with -u\n"); 1420 login_exit(1); 1421 } 1422 SCPYL(identity, optarg); 1423 uflag = B_TRUE; 1424 break; 1425 case 's': 1426 if (!strlen(optarg)) { 1427 (void) fprintf(stderr, 1428 "Empty string supplied with -s\n"); 1429 login_exit(1); 1430 } 1431 SCPYL(sflagname, optarg); 1432 sflag = B_TRUE; 1433 break; 1434 case 'R': 1435 if (!strlen(optarg)) { 1436 (void) fprintf(stderr, 1437 "Empty string supplied with -R\n"); 1438 login_exit(1); 1439 } 1440 SCPYL(repository, optarg); 1441 Rflag = B_TRUE; 1442 break; 1443 case 't': 1444 if (!strlen(optarg)) { 1445 (void) fprintf(stderr, 1446 "Empty string supplied with -t\n"); 1447 login_exit(1); 1448 } 1449 SCPYL(terminal, optarg); 1450 tflag = B_TRUE; 1451 break; 1452 case 'U': 1453 /* 1454 * Kerberized rlogind may fork us with 1455 * -U "" if the rlogin client used the "-a" 1456 * option to send a NULL username. This is done 1457 * to force login to prompt for a user/password. 1458 * However, if Kerberos auth was used, we dont need 1459 * to prompt, so we will accept the option and 1460 * handle the situation later. 1461 */ 1462 SCPYL(rusername, optarg); 1463 Uflag = B_TRUE; 1464 break; 1465 case 'z': 1466 if (hflag || rflag || zflag) { 1467 (void) fprintf(stderr, flags_message); 1468 login_exit(1); 1469 } 1470 (void) snprintf(zone_name, sizeof (zone_name), 1471 "zone:%s", optarg); 1472 SCPYL(progname, "zlogin"); 1473 zflag = B_TRUE; 1474 break; 1475 default: 1476 errflg++; 1477 break; 1478 } /* end switch */ 1479 } /* end while */ 1480 1481 /* 1482 * If the 's svcname' flag was used, override the progname 1483 * value that is to be used in the pam_start call. 1484 */ 1485 if (sflag) 1486 SCPYL(progname, sflagname); 1487 1488 /* 1489 * get the prompt set by ttymon 1490 */ 1491 ttyprompt = getenv("TTYPROMPT"); 1492 1493 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) { 1494 /* 1495 * if ttyprompt is set, there should be data on 1496 * the stream already. 1497 */ 1498 if ((envp = getargs(inputline)) != (char **)NULL) { 1499 /* 1500 * don't get name if name passed as argument. 1501 */ 1502 SCPYL(user_name, *envp++); 1503 } 1504 } else if (optind < argc) { 1505 SCPYL(user_name, argv[optind]); 1506 (void) SCPYL(inputline, user_name); 1507 (void) strlcat(inputline, " \n", sizeof (inputline)); 1508 envp = &argv[optind+1]; 1509 1510 if (!fflag) 1511 SCPYL(lusername, user_name); 1512 } 1513 1514 if (errflg) 1515 return (-1); 1516 return (0); 1517 } 1518 1519 /* 1520 * usage - Print usage message 1521 * 1522 */ 1523 static void 1524 usage(void) 1525 { 1526 (void) fprintf(stderr, 1527 "usage:\n" 1528 " login [-p] [-d device] [-R repository] [-s service]\n" 1529 "\t[-t terminal] [-u identity] [-U ruser]\n" 1530 "\t[-h hostname [terminal] | -r hostname] [name [environ]...]\n"); 1531 1532 } 1533 1534 /* 1535 * doremoteterm - Sets the appropriate ioctls for a remote terminal 1536 */ 1537 static char *speeds[] = { 1538 "0", "50", "75", "110", "134", "150", "200", "300", 1539 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400", 1540 "57600", "76800", "115200", "153600", "230400", "307200", "460800", 1541 "921600" 1542 }; 1543 1544 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0])) 1545 1546 1547 static void 1548 doremoteterm(char *term) 1549 { 1550 struct termios tp; 1551 char *cp = strchr(term, '/'), **cpp; 1552 char *speed; 1553 1554 (void) ioctl(0, TCGETS, &tp); 1555 1556 if (cp) { 1557 *cp++ = '\0'; 1558 speed = cp; 1559 cp = strchr(speed, '/'); 1560 1561 if (cp) 1562 *cp++ = '\0'; 1563 1564 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) 1565 if (strcmp(*cpp, speed) == 0) { 1566 (void) cfsetospeed(&tp, cpp-speeds); 1567 break; 1568 } 1569 } 1570 1571 tp.c_lflag |= ECHO|ICANON; 1572 tp.c_iflag |= IGNPAR|ICRNL; 1573 1574 (void) ioctl(0, TCSETS, &tp); 1575 1576 } 1577 1578 /* 1579 * Process_rlogin - Does the work that rlogin and telnet 1580 * need done 1581 */ 1582 static void 1583 process_rlogin(void) 1584 { 1585 /* 1586 * If a Kerberized rlogin was initiated, then these fields 1587 * must be read by rlogin daemon itself and passed down via 1588 * cmd line args. 1589 */ 1590 if (!Uflag && !strlen(rusername)) 1591 getstr(rusername, sizeof (rusername), "remuser"); 1592 if (!strlen(lusername)) 1593 getstr(lusername, sizeof (lusername), "locuser"); 1594 if (!tflag && !strlen(terminal)) 1595 getstr(terminal, sizeof (terminal), "Terminal type"); 1596 1597 if (strlen(terminal)) 1598 doremoteterm(terminal); 1599 1600 /* fflag has precedence over stuff passed by rlogind */ 1601 if (fflag || getuid()) { 1602 pwd = &nouser; 1603 return; 1604 } else { 1605 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS) 1606 login_exit(1); 1607 1608 pwd = getpwnam(lusername); 1609 if (pwd == NULL) { 1610 pwd = &nouser; 1611 return; 1612 } 1613 } 1614 1615 /* 1616 * Update PAM on the user name 1617 */ 1618 if (strlen(lusername) && 1619 pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS) 1620 login_exit(1); 1621 1622 if (strlen(rusername) && 1623 pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS) 1624 login_exit(1); 1625 1626 SCPYL(user_name, lusername); 1627 envp = &zero; 1628 lusername[0] = '\0'; 1629 } 1630 1631 /* 1632 * *** Account validation routines *** 1633 * 1634 */ 1635 1636 /* 1637 * validate_account - This is the PAM version of validate. 1638 */ 1639 1640 static void 1641 validate_account(void) 1642 { 1643 int error; 1644 int flag; 1645 int tries; /* new password retries */ 1646 1647 (void) alarm(0); /* give user time to come up with password */ 1648 1649 check_log(); 1650 1651 if (Passreqflag) 1652 flag = PAM_DISALLOW_NULL_AUTHTOK; 1653 else 1654 flag = 0; 1655 1656 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) { 1657 if (error == PAM_NEW_AUTHTOK_REQD) { 1658 tries = 1; 1659 error = PAM_AUTHTOK_ERR; 1660 while (error == PAM_AUTHTOK_ERR && 1661 tries <= DEF_ATTEMPTS) { 1662 if (tries > 1) 1663 (void) printf("Try again\n\n"); 1664 1665 (void) printf("Choose a new password.\n"); 1666 1667 error = pam_chauthtok(pamh, 1668 PAM_CHANGE_EXPIRED_AUTHTOK); 1669 if (error == PAM_TRY_AGAIN) { 1670 (void) sleep(1); 1671 error = pam_chauthtok(pamh, 1672 PAM_CHANGE_EXPIRED_AUTHTOK); 1673 } 1674 tries++; 1675 } 1676 1677 if (error != PAM_SUCCESS) { 1678 if (dosyslog) 1679 syslog(LOG_CRIT, 1680 "change password failure: %s", 1681 pam_strerror(pamh, error)); 1682 audit_error = ADT_FAIL_PAM + error; 1683 login_exit(1); 1684 } else { 1685 audit_success(ADT_passwd, pwd, zone_name); 1686 } 1687 } else { 1688 (void) printf(incorrectmsg); 1689 1690 if (dosyslog) 1691 syslog(LOG_CRIT, 1692 "login account failure: %s", 1693 pam_strerror(pamh, error)); 1694 audit_error = ADT_FAIL_PAM + error; 1695 login_exit(1); 1696 } 1697 } 1698 } 1699 1700 /* 1701 * Check_log - This is really a hack because PAM checks the log, but login 1702 * wants to know if the log is okay and PAM doesn't have 1703 * a module independent way of handing this info back. 1704 */ 1705 1706 static void 1707 check_log(void) 1708 { 1709 int fdl; 1710 long long offset; 1711 1712 offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog); 1713 1714 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) { 1715 if (llseek(fdl, offset, SEEK_SET) == offset && 1716 read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) && 1717 ll.ll_time != 0) 1718 lastlogok = 1; 1719 (void) close(fdl); 1720 } 1721 } 1722 1723 /* 1724 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to 1725 * place us in the user's home directory just in 1726 * case it was protected and the first chdir failed. 1727 * No chdir errors should happen at this point because 1728 * all failures should have happened on the first 1729 * time around. 1730 */ 1731 1732 static void 1733 chdir_to_dir_user(void) 1734 { 1735 if (chdir(pwd->pw_dir) < 0) { 1736 if (chdir("/") < 0) { 1737 (void) printf("No directory!\n"); 1738 /* 1739 * This probably won't work since we can't get to /. 1740 */ 1741 if (dosyslog) { 1742 if (remote_host[0]) { 1743 syslog(LOG_CRIT, 1744 "LOGIN FAILURES ON %s FROM %.*s ", 1745 " %.*s", ttyn, HMAX, 1746 remote_host, NMAX, pwd->pw_name); 1747 } else { 1748 syslog(LOG_CRIT, 1749 "LOGIN FAILURES ON %s, %.*s", 1750 ttyn, NMAX, pwd->pw_name); 1751 } 1752 } 1753 closelog(); 1754 (void) sleep(Disabletime); 1755 exit(1); 1756 } else { 1757 (void) printf("No directory! Logging in with home=/\n"); 1758 pwd->pw_dir = "/"; 1759 } 1760 } 1761 } 1762 1763 1764 /* 1765 * login_authenticate - Performs the main authentication work 1766 * 1. Prints the login prompt 1767 * 2. Requests and verifys the password 1768 * 3. Checks the port password 1769 */ 1770 1771 static void 1772 login_authenticate(void) 1773 { 1774 char *user; 1775 int err; 1776 int login_successful = 0; 1777 1778 do { 1779 /* if scheme broken, then nothing to do but quit */ 1780 if (pam_get_item(pamh, PAM_USER, (void **)&user) != PAM_SUCCESS) 1781 exit(1); 1782 1783 /* 1784 * only get name from utility if it is not already 1785 * supplied by pam_start or a pam_set_item. 1786 */ 1787 if (!user || !user[0]) { 1788 /* use call back to get user name */ 1789 get_user_name(); 1790 } 1791 1792 err = verify_passwd(); 1793 1794 /* 1795 * If root login and not on system console then call exit(2) 1796 */ 1797 check_for_console(); 1798 1799 switch (err) { 1800 case PAM_SUCCESS: 1801 case PAM_NEW_AUTHTOK_REQD: 1802 /* 1803 * Officially, pam_authenticate() shouldn't return this 1804 * but it's probably the right thing to return if 1805 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will 1806 * be forced to change password later in this code. 1807 */ 1808 count = 0; 1809 login_successful = 1; 1810 break; 1811 case PAM_MAXTRIES: 1812 count = retry; 1813 /*FALLTHROUGH*/ 1814 case PAM_AUTH_ERR: 1815 case PAM_AUTHINFO_UNAVAIL: 1816 case PAM_USER_UNKNOWN: 1817 audit_failure(get_audit_id(), ADT_FAIL_PAM + err, pwd, 1818 remote_host, ttyn, zone_name); 1819 log_bad_attempts(); 1820 break; 1821 case PAM_ABORT: 1822 log_bad_attempts(); 1823 (void) sleep(Disabletime); 1824 (void) printf(incorrectmsg); 1825 1826 audit_error = ADT_FAIL_PAM + err; 1827 login_exit(1); 1828 /*NOTREACHED*/ 1829 default: /* Some other PAM error */ 1830 audit_error = ADT_FAIL_PAM + err; 1831 login_exit(1); 1832 /*NOTREACHED*/ 1833 } 1834 1835 if (login_successful) 1836 break; 1837 1838 /* sleep after bad passwd */ 1839 if (count) 1840 (void) sleep(Sleeptime); 1841 (void) printf(incorrectmsg); 1842 /* force name to be null in this case */ 1843 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS) 1844 login_exit(1); 1845 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS) 1846 login_exit(1); 1847 } while (count++ < retry); 1848 1849 if (count >= retry) { 1850 audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES, pwd, 1851 remote_host, ttyn, zone_name); 1852 /* 1853 * If logging is turned on, output the 1854 * string storage area to the log file, 1855 * and sleep for Disabletime 1856 * seconds before exiting. 1857 */ 1858 if (writelog) 1859 badlogin(); 1860 if (dosyslog) { 1861 if ((pwd = getpwnam(user_name)) != NULL) { 1862 if (remote_host[0]) { 1863 syslog(LOG_CRIT, 1864 "REPEATED LOGIN FAILURES ON %s " 1865 "FROM %.*s, %.*s", 1866 ttyn, HMAX, remote_host, NMAX, 1867 user_name); 1868 } else { 1869 syslog(LOG_CRIT, 1870 "REPEATED LOGIN FAILURES ON " 1871 "%s, %.*s", 1872 ttyn, NMAX, user_name); 1873 } 1874 } else { 1875 if (remote_host[0]) { 1876 syslog(LOG_CRIT, 1877 "REPEATED LOGIN FAILURES ON %s " 1878 "FROM %.*s", 1879 ttyn, HMAX, remote_host); 1880 } else { 1881 syslog(LOG_CRIT, 1882 "REPEATED LOGIN FAILURES ON %s", 1883 ttyn); 1884 } 1885 } 1886 } 1887 (void) sleep(Disabletime); 1888 exit(1); 1889 } 1890 1891 } 1892 1893 /* 1894 * *** Credential Related routines *** 1895 * 1896 */ 1897 1898 /* 1899 * setup_credentials - sets the group ID, initializes the groups 1900 * and sets up the secretkey. 1901 * Exits if a failure occurrs. 1902 */ 1903 1904 1905 /* 1906 * setup_credentials - PAM does all the work for us on this one. 1907 */ 1908 1909 static void 1910 setup_credentials(void) 1911 { 1912 int error = 0; 1913 1914 /* set the real (and effective) GID */ 1915 if (setgid(pwd->pw_gid) == -1) { 1916 login_exit(1); 1917 } 1918 1919 /* 1920 * Initialize the supplementary group access list. 1921 */ 1922 if ((user_name[0] == '\0') || 1923 (initgroups(user_name, pwd->pw_gid) == -1)) { 1924 audit_error = ADT_FAIL_VALUE_PROGRAM; 1925 login_exit(1); 1926 } 1927 1928 if ((error = pam_setcred(pamh, zflag ? PAM_REINITIALIZE_CRED : 1929 PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 1930 audit_error = ADT_FAIL_PAM + error; 1931 login_exit(error); 1932 } 1933 1934 /* 1935 * Record successful login and fork process that records logout. 1936 * We have to do this after setting credentials because pam_setcred() 1937 * loads key audit info into the cred, but before setuid() so audit 1938 * system calls will work. 1939 */ 1940 audit_success(get_audit_id(), pwd, zone_name); 1941 } 1942 1943 static uint_t 1944 get_audit_id(void) 1945 { 1946 if (rflag) 1947 return (ADT_rlogin); 1948 else if (hflag) 1949 return (ADT_telnet); 1950 else if (zflag) 1951 return (ADT_zlogin); 1952 1953 return (ADT_login); 1954 } 1955 1956 /* 1957 * 1958 * *** Routines to get a new user set up and running *** 1959 * 1960 * Things to do when starting up a new user: 1961 * adjust_nice 1962 * update_utmpx_entry 1963 * establish_user_environment 1964 * print_banner 1965 * display_last_login_time 1966 * exec_the_shell 1967 * 1968 */ 1969 1970 1971 /* 1972 * adjust_nice - Set the nice (process priority) value if the 1973 * gecos value contains an appropriate value. 1974 */ 1975 1976 static void 1977 adjust_nice(void) 1978 { 1979 int pri, mflg, i; 1980 1981 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) { 1982 pri = 0; 1983 mflg = 0; 1984 i = 4; 1985 1986 if (pwd->pw_gecos[i] == '-') { 1987 mflg++; 1988 i++; 1989 } 1990 1991 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9') 1992 pri = (pri * 10) + pwd->pw_gecos[i++] - '0'; 1993 1994 if (mflg) 1995 pri = -pri; 1996 1997 (void) nice(pri); 1998 } 1999 } 2000 2001 /* 2002 * update_utmpx_entry - Searchs for the correct utmpx entry, making an 2003 * entry there if it finds one, otherwise exits. 2004 */ 2005 2006 static void 2007 update_utmpx_entry(int sublogin) 2008 { 2009 int err; 2010 char *user; 2011 static char *errmsg = "No utmpx entry. " 2012 "You must exec \"login\" from the lowest level \"shell\"."; 2013 int tmplen; 2014 struct utmpx *u = (struct utmpx *)0; 2015 struct utmpx utmpx; 2016 char *ttyntail; 2017 2018 /* 2019 * If we're not a sublogin then 2020 * we'll get an error back if our PID doesn't match the PID of the 2021 * entry we are updating, otherwise if its a sublogin the flags 2022 * field is set to 0, which means we just write a matching entry 2023 * (without checking the pid), or a new entry if an entry doesn't 2024 * exist. 2025 */ 2026 2027 if ((err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 2028 audit_error = ADT_FAIL_PAM + err; 2029 login_exit(1); 2030 } 2031 2032 if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) != 2033 PAM_SUCCESS) { 2034 audit_error = ADT_FAIL_PAM + err; 2035 login_exit(1); 2036 } 2037 2038 (void) memset((void *)&utmpx, 0, sizeof (utmpx)); 2039 (void) time(&utmpx.ut_tv.tv_sec); 2040 utmpx.ut_pid = getpid(); 2041 2042 if (rflag || hflag) { 2043 SCPYN(utmpx.ut_host, remote_host); 2044 tmplen = strlen(remote_host) + 1; 2045 if (tmplen < sizeof (utmpx.ut_host)) 2046 utmpx.ut_syslen = tmplen; 2047 else 2048 utmpx.ut_syslen = sizeof (utmpx.ut_host); 2049 } else if (zflag) { 2050 /* 2051 * If this is a login from another zone, put the 2052 * zone:<zonename> string in the utmpx entry. 2053 */ 2054 SCPYN(utmpx.ut_host, zone_name); 2055 tmplen = strlen(zone_name) + 1; 2056 if (tmplen < sizeof (utmpx.ut_host)) 2057 utmpx.ut_syslen = tmplen; 2058 else 2059 utmpx.ut_syslen = sizeof (utmpx.ut_host); 2060 } else { 2061 utmpx.ut_syslen = 0; 2062 } 2063 2064 SCPYN(utmpx.ut_user, user); 2065 2066 /* skip over "/dev/" */ 2067 ttyntail = basename(ttyn); 2068 2069 while ((u = getutxent()) != NULL) { 2070 if ((u->ut_type == INIT_PROCESS || 2071 u->ut_type == LOGIN_PROCESS || 2072 u->ut_type == USER_PROCESS) && 2073 ((sublogin && strncmp(u->ut_line, ttyntail, 2074 sizeof (u->ut_line)) == 0) || 2075 u->ut_pid == login_pid)) { 2076 SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1)); 2077 (void) memcpy(utmpx.ut_id, u->ut_id, 2078 sizeof (utmpx.ut_id)); 2079 utmpx.ut_exit.e_exit = u->ut_exit.e_exit; 2080 utmpx.ut_type = USER_PROCESS; 2081 (void) pututxline(&utmpx); 2082 break; 2083 } 2084 } 2085 endutxent(); 2086 2087 if (u == (struct utmpx *)NULL) { 2088 if (!sublogin) { 2089 /* 2090 * no utmpx entry already setup 2091 * (init or rlogind/telnetd) 2092 */ 2093 (void) puts(errmsg); 2094 2095 audit_error = ADT_FAIL_VALUE_PROGRAM; 2096 login_exit(1); 2097 } 2098 } else { 2099 /* Now attempt to write out this entry to the wtmp file if */ 2100 /* we were successful in getting it from the utmpx file and */ 2101 /* the wtmp file exists. */ 2102 updwtmpx(WTMPX_FILE, &utmpx); 2103 } 2104 } 2105 2106 2107 2108 /* 2109 * process_chroot_logins - Chroots to the specified subdirectory and 2110 * re executes login. 2111 */ 2112 2113 static int 2114 process_chroot_logins(void) 2115 { 2116 /* 2117 * If the shell field starts with a '*', do a chroot to the home 2118 * directory and perform a new login. 2119 */ 2120 2121 if (*pwd->pw_shell == '*') { 2122 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */ 2123 pamh = NULL; /* really done */ 2124 if (chroot(pwd->pw_dir) < 0) { 2125 (void) printf("No Root Directory\n"); 2126 2127 audit_failure(get_audit_id(), 2128 ADT_FAIL_VALUE_CHDIR_FAILED, 2129 pwd, remote_host, ttyn, zone_name); 2130 2131 return (ERROR); 2132 } 2133 /* 2134 * Set the environment flag <!sublogin> so that the next login 2135 * knows that it is a sublogin. 2136 */ 2137 envinit[0] = SUBLOGIN; 2138 envinit[1] = (char *)NULL; 2139 (void) printf("Subsystem root: %s\n", pwd->pw_dir); 2140 (void) execle("/usr/bin/login", "login", (char *)0, 2141 &envinit[0]); 2142 (void) execle("/etc/login", "login", (char *)0, &envinit[0]); 2143 (void) printf("No /usr/bin/login or /etc/login on root\n"); 2144 2145 audit_error = ADT_FAIL_VALUE_PROGRAM; 2146 2147 login_exit(1); 2148 } 2149 return (OK); 2150 } 2151 2152 /* 2153 * establish_user_environment - Set up the new users enviornment 2154 */ 2155 2156 static void 2157 establish_user_environment(char **renvp) 2158 { 2159 int i, j, k, l_index, length, idx = 0; 2160 char *endptr; 2161 char **lenvp; 2162 char **pam_env; 2163 2164 lenvp = environ; 2165 while (*lenvp++) 2166 ; 2167 2168 /* count the number of PAM environment variables set by modules */ 2169 if ((pam_env = pam_getenvlist(pamh)) != 0) { 2170 for (idx = 0; pam_env[idx] != 0; idx++) 2171 ; 2172 } 2173 2174 envinit = (char **)calloc(lenvp - environ + 10 + MAXARGS + idx, 2175 sizeof (char *)); 2176 if (envinit == NULL) { 2177 (void) printf("Calloc failed - out of swap space.\n"); 2178 login_exit(8); 2179 } 2180 2181 /* 2182 * add PAM environment variables first so they 2183 * can be overwritten at login's discretion. 2184 * check for illegal environment variables. 2185 */ 2186 idx = 0; basicenv = 0; 2187 if (pam_env != 0) { 2188 while (pam_env[idx] != 0) { 2189 if (legalenvvar(pam_env[idx])) { 2190 envinit[basicenv] = pam_env[idx]; 2191 basicenv++; 2192 } 2193 idx++; 2194 } 2195 } 2196 (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv)); 2197 2198 /* Set up environment */ 2199 if (rflag) { 2200 ENVSTRNCAT(term, terminal); 2201 } else if (hflag) { 2202 if (strlen(terminal)) { 2203 ENVSTRNCAT(term, terminal); 2204 } 2205 } else { 2206 char *tp = getenv("TERM"); 2207 2208 if ((tp != NULL) && (*tp != '\0')) 2209 ENVSTRNCAT(term, tp); 2210 } 2211 2212 ENVSTRNCAT(logname, pwd->pw_name); 2213 2214 /* 2215 * There are three places to get timezone info. init.c sets 2216 * TZ if the file /etc/default/init contains a value for TZ. 2217 * login.c looks in the file /etc/default/login for a 2218 * variable called TIMEZONE being set. If TIMEZONE has a 2219 * value, TZ is set to that value; no environment variable 2220 * TIMEZONE is set, only TZ. If neither of these methods 2221 * work to set TZ, then the library routines will default 2222 * to using the file /usr/lib/locale/TZ/localtime. 2223 * 2224 * There is a priority set up here. If /etc/default/init has 2225 * a value for TZ, that value remains top priority. If the 2226 * file /etc/default/login has TIMEZONE set, that has second 2227 * highest priority not overriding the value of TZ in 2228 * /etc/default/init. The reason for this priority is that the 2229 * file /etc/default/init is supposed to be sourced by 2230 * /etc/profile. We are doing the "sourcing" prematurely in 2231 * init.c. Additionally, a login C shell doesn't source the 2232 * file /etc/profile thus not sourcing /etc/default/init thus not 2233 * allowing an adminstrator to globally set TZ for all users 2234 */ 2235 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */ 2236 tmp_tz = Def_tz; 2237 2238 if ((Def_tz = getenv("TZ")) != NULL) { 2239 ENVSTRNCAT(timez, Def_tz); 2240 } else if (tmp_tz != NULL) { 2241 Def_tz = tmp_tz; 2242 ENVSTRNCAT(timez, Def_tz); 2243 } 2244 2245 if (Def_hertz == NULL) 2246 (void) sprintf(hertz + strlen(hertz), "%lu", HZ); 2247 else 2248 ENVSTRNCAT(hertz, Def_hertz); 2249 2250 if (Def_path == NULL) 2251 (void) strlcat(path, DEF_PATH, sizeof (path)); 2252 else 2253 ENVSTRNCAT(path, Def_path); 2254 2255 ENVSTRNCAT(home, pwd->pw_dir); 2256 2257 /* 2258 * Find the end of the basic environment 2259 */ 2260 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++) 2261 ; 2262 2263 /* 2264 * If TZ has a value, add it. 2265 */ 2266 if (strcmp(timez, "TZ=") != 0) 2267 envinit[basicenv++] = timez; 2268 2269 if (*pwd->pw_shell == '\0') { 2270 /* 2271 * If possible, use the primary default shell, 2272 * otherwise, use the secondary one. 2273 */ 2274 if (access(SHELL, X_OK) == 0) 2275 pwd->pw_shell = SHELL; 2276 else 2277 pwd->pw_shell = SHELL2; 2278 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) { 2279 envinit[basicenv++] = shell; 2280 ENVSTRNCAT(shell, pwd->pw_shell); 2281 } 2282 2283 #ifndef NO_MAIL 2284 envinit[basicenv++] = mail; 2285 (void) strlcat(mail, pwd->pw_name, sizeof (mail)); 2286 #endif 2287 2288 /* 2289 * Pick up locale environment variables, if any. 2290 */ 2291 lenvp = renvp; 2292 while (*lenvp != NULL) { 2293 j = 0; 2294 while (localeenv[j] != 0) { 2295 /* 2296 * locale_envmatch() returns 1 if 2297 * *lenvp is localenev[j] and valid. 2298 */ 2299 if (locale_envmatch(localeenv[j], *lenvp) == 1) { 2300 envinit[basicenv++] = *lenvp; 2301 break; 2302 } 2303 j++; 2304 } 2305 lenvp++; 2306 } 2307 2308 /* 2309 * If '-p' flag, then try to pass on allowable environment 2310 * variables. Note that by processing this first, what is 2311 * passed on the final "login:" line may over-ride the invocation 2312 * values. XXX is this correct? 2313 */ 2314 if (pflag) { 2315 for (lenvp = renvp; *lenvp; lenvp++) { 2316 if (!legalenvvar(*lenvp)) { 2317 continue; 2318 } 2319 /* 2320 * If this isn't 'xxx=yyy', skip it. XXX 2321 */ 2322 if ((endptr = strchr(*lenvp, '=')) == NULL) { 2323 continue; 2324 } 2325 length = endptr + 1 - *lenvp; 2326 for (j = 0; j < basicenv; j++) { 2327 if (strncmp(envinit[j], *lenvp, length) == 0) { 2328 /* 2329 * Replace previously established value 2330 */ 2331 envinit[j] = *lenvp; 2332 break; 2333 } 2334 } 2335 if (j == basicenv) { 2336 /* 2337 * It's a new definition, so add it at the end. 2338 */ 2339 envinit[basicenv++] = *lenvp; 2340 } 2341 } 2342 } 2343 2344 /* 2345 * Add in all the environment variables picked up from the 2346 * argument list to "login" or from the user response to the 2347 * "login" request, if any. 2348 */ 2349 2350 if (envp == NULL) 2351 goto switch_env; /* done */ 2352 2353 for (j = 0, k = 0, l_index = 0; 2354 *envp != NULL && j < (MAXARGS-1); 2355 j++, envp++) { 2356 2357 /* 2358 * Scan each string provided. If it doesn't have the 2359 * format xxx=yyy, then add the string "Ln=" to the beginning. 2360 */ 2361 if ((endptr = strchr(*envp, '=')) == NULL) { 2362 /* 2363 * This much to be malloc'd: 2364 * strlen(*envp) + 1 char for 'L' + 2365 * MAXARGSWIDTH + 1 char for '=' + 1 for null char; 2366 * 2367 * total = strlen(*envp) + MAXARGSWIDTH + 3 2368 */ 2369 int total = strlen(*envp) + MAXARGSWIDTH + 3; 2370 envinit[basicenv+k] = malloc(total); 2371 if (envinit[basicenv+k] == NULL) { 2372 (void) printf("%s: malloc failed\n", PROG_NAME); 2373 login_exit(1); 2374 } 2375 (void) snprintf(envinit[basicenv+k], total, "L%d=%s", 2376 l_index, *envp); 2377 2378 k++; 2379 l_index++; 2380 } else { 2381 if (!legalenvvar(*envp)) { /* this env var permited? */ 2382 continue; 2383 } else { 2384 2385 /* 2386 * Check to see whether this string replaces 2387 * any previously defined string 2388 */ 2389 for (i = 0, length = endptr + 1 - *envp; 2390 i < basicenv + k; i++) { 2391 if (strncmp(*envp, envinit[i], length) 2392 == 0) { 2393 envinit[i] = *envp; 2394 break; 2395 } 2396 } 2397 2398 /* 2399 * If it doesn't, place it at the end of 2400 * environment array. 2401 */ 2402 if (i == basicenv+k) { 2403 envinit[basicenv+k] = *envp; 2404 k++; 2405 } 2406 } 2407 } 2408 } /* for (j = 0 ... ) */ 2409 2410 switch_env: 2411 /* 2412 * Switch to the new environment. 2413 */ 2414 environ = envinit; 2415 } 2416 2417 /* 2418 * print_banner - Print the banner at start up 2419 * Do not turn on DOBANNER ifdef. This is not 2420 * relevant to SunOS. 2421 */ 2422 2423 static void 2424 print_banner(void) 2425 { 2426 #ifdef DOBANNER 2427 uname(&un); 2428 #if i386 2429 (void) printf("UNIX System V/386 Release %s\n%s\n" 2430 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n" 2431 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n", 2432 un.release, un.nodename); 2433 #elif sun 2434 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n" 2435 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n" 2436 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n" 2437 "All Rights Reserved\n", 2438 un.release, un.machine, un.nodename); 2439 #else 2440 (void) printf("UNIX System V Release %s AT&T %s\n%s\n" 2441 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n", 2442 un.release, un.machine, un.nodename); 2443 #endif /* i386 */ 2444 #endif /* DOBANNER */ 2445 } 2446 2447 /* 2448 * display_last_login_time - Advise the user the time and date 2449 * that this login-id was last used. 2450 */ 2451 2452 static void 2453 display_last_login_time(void) 2454 { 2455 if (lastlogok) { 2456 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time)); 2457 2458 if (*ll.ll_host != '\0') 2459 (void) printf("from %.*s\n", sizeof (ll.ll_host), 2460 ll.ll_host); 2461 else 2462 (void) printf("on %.*s\n", sizeof (ll.ll_line), 2463 ll.ll_line); 2464 } 2465 } 2466 2467 /* 2468 * exec_the_shell - invoke the specified shell or start up program 2469 */ 2470 2471 static void 2472 exec_the_shell(void) 2473 { 2474 char *endptr; 2475 int i; 2476 2477 (void) strlcat(minusnam, basename(pwd->pw_shell), 2478 sizeof (minusnam)); 2479 2480 /* 2481 * Exec the shell 2482 */ 2483 (void) execl(pwd->pw_shell, minusnam, (char *)0); 2484 2485 /* 2486 * pwd->pw_shell was not an executable object file, maybe it 2487 * is a shell proceedure or a command line with arguments. 2488 * If so, turn off the SHELL= environment variable. 2489 */ 2490 for (i = 0; envinit[i] != NULL; ++i) { 2491 if ((envinit[i] == shell) && 2492 ((endptr = strchr(shell, '=')) != NULL)) 2493 (*++endptr) = '\0'; 2494 } 2495 2496 if (access(pwd->pw_shell, R_OK|X_OK) == 0) { 2497 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0); 2498 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0); 2499 } 2500 2501 (void) printf("No shell\n"); 2502 } 2503 2504 /* 2505 * login_exit - Call exit() and terminate. 2506 * This function is here for PAM so cleanup can 2507 * be done before the process exits. 2508 */ 2509 static void 2510 login_exit(int exit_code) 2511 { 2512 if (pamh) 2513 (void) pam_end(pamh, PAM_ABORT); 2514 2515 if (audit_error) 2516 audit_failure(get_audit_id(), audit_error, 2517 pwd, remote_host, ttyn, zone_name); 2518 2519 exit(exit_code); 2520 /*NOTREACHED*/ 2521 } 2522 2523 /* 2524 * Check if lenv and penv matches or not. 2525 */ 2526 static int 2527 locale_envmatch(char *lenv, char *penv) 2528 { 2529 while ((*lenv == *penv) && *lenv && *penv != '=') { 2530 lenv++; 2531 penv++; 2532 } 2533 2534 /* 2535 * '/' is eliminated for security reason. 2536 */ 2537 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/') 2538 return (1); 2539 return (0); 2540 } 2541 2542 static int 2543 is_number(char *ptr) 2544 { 2545 while (*ptr != '\0') { 2546 if (!isdigit(*ptr)) 2547 return (0); 2548 ptr++; 2549 } 2550 return (1); 2551 }