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