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] = '\0'; 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 /* FALLTHROUGH */ 1061 1062 default: 1063 if (state == WHITESPACE) { 1064 *answer++ = ptr; 1065 state = ARGUMENT; 1066 } 1067 *ptr++ = c; 1068 } 1069 1070 /* Attempt at overflow, exit */ 1071 if (input_line - p >= MAXLINE - 1 || 1072 ptr >= &envbuf[sizeof (envbuf) - 1]) { 1073 audit_error = ADT_FAIL_VALUE_INPUT_OVERFLOW; 1074 login_exit(1); 1075 } 1076 } 1077 1078 /* 1079 * If we left loop because an EOF was received or we've overflown 1080 * args[], exit immediately. 1081 */ 1082 login_exit(0); 1083 /* NOTREACHED */ 1084 } 1085 1086 /* 1087 * get_user_name - Gets the user name either passed in, or from the 1088 * login: prompt. 1089 */ 1090 1091 static void 1092 get_user_name(void) 1093 { 1094 FILE *fp; 1095 1096 if ((fp = fopen(ISSUEFILE, "r")) != NULL) { 1097 char *ptr, buffer[BUFSIZ]; 1098 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) { 1099 (void) fputs(ptr, stdout); 1100 } 1101 (void) fclose(fp); 1102 } 1103 1104 /* 1105 * if TTYPROMPT is not set, use our own prompt 1106 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT 1107 * and let the module do the prompting. 1108 */ 1109 1110 if ((ttyprompt == NULL) || (*ttyprompt == '\0')) 1111 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg); 1112 else 1113 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt); 1114 1115 envp = &zero; /* XXX: is this right? */ 1116 } 1117 1118 1119 /* 1120 * Check_for_dueling_unix - Check to see if the another login is talking 1121 * to the line we've got open as a login port 1122 * Exits if we're talking to another unix system 1123 */ 1124 1125 static void 1126 check_for_dueling_unix(char *inputline) 1127 { 1128 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) || 1129 EQN(incorrectmsg, inputline)) { 1130 (void) printf("Looking at a login line.\n"); 1131 login_exit(8); 1132 } 1133 } 1134 1135 /* 1136 * logins_disabled - if the file /etc/nologin exists and the user is not 1137 * root then do not permit them to login 1138 */ 1139 static int 1140 logins_disabled(char *user_name) 1141 { 1142 FILE *nlfd; 1143 int c; 1144 if (!EQN("root", user_name) && 1145 ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) { 1146 while ((c = getc(nlfd)) != EOF) 1147 (void) putchar(c); 1148 (void) fflush(stdout); 1149 (void) sleep(5); 1150 return (TRUE); 1151 } 1152 return (FALSE); 1153 } 1154 1155 #define DEFAULT_CONSOLE "/dev/console" 1156 1157 /* 1158 * check_for_console - Checks if we're getting a root login on the 1159 * console, or a login from the global zone. Exits if not. 1160 * 1161 * If CONSOLE is set to /dev/console in /etc/default/login, then root logins 1162 * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global 1163 * zones, but checking them does no harm. 1164 */ 1165 static void 1166 check_for_console(void) 1167 { 1168 const char *consoles[] = { "/dev/console", "/dev/vt/", NULL }; 1169 int i; 1170 1171 if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE || 1172 Console == NULL) 1173 return; 1174 1175 if (strcmp(Console, DEFAULT_CONSOLE) == 0) { 1176 for (i = 0; consoles[i] != NULL; i ++) { 1177 if (strncmp(ttyn, consoles[i], 1178 strlen(consoles[i])) == 0) 1179 return; 1180 } 1181 } else { 1182 if (strcmp(ttyn, Console) == 0) 1183 return; 1184 } 1185 1186 (void) printf("Not on system console\n"); 1187 1188 audit_error = ADT_FAIL_VALUE_CONSOLE; 1189 login_exit(10); 1190 1191 } 1192 1193 /* 1194 * List of environment variables or environment variable prefixes that should 1195 * not be propagated across logins, such as when the login -p option is used. 1196 */ 1197 static const char *const illegal[] = { 1198 "SHELL=", 1199 "HOME=", 1200 "LOGNAME=", 1201 #ifndef NO_MAIL 1202 "MAIL=", 1203 #endif 1204 "CDPATH=", 1205 "IFS=", 1206 "PATH=", 1207 "LD_", 1208 "SMF_", 1209 NULL 1210 }; 1211 1212 /* 1213 * legalenvvar - Is it legal to insert this environmental variable? 1214 */ 1215 1216 static int 1217 legalenvvar(char *s) 1218 { 1219 const char *const *p; 1220 1221 for (p = &illegal[0]; *p; p++) { 1222 if (strncmp(s, *p, strlen(*p)) == 0) 1223 return (0); 1224 } 1225 1226 return (1); 1227 } 1228 1229 1230 /* 1231 * getstr - Get a string from standard input 1232 * Calls exit if read(2) fails. 1233 */ 1234 1235 static void 1236 getstr(char *buf, int cnt, char *err) 1237 { 1238 char c; 1239 1240 do { 1241 if (read(0, &c, 1) != 1) 1242 login_exit(1); 1243 *buf++ = c; 1244 } while (--cnt > 1 && c != 0); 1245 1246 *buf = 0; 1247 err = err; /* For lint */ 1248 } 1249 1250 1251 /* 1252 * defaults - read defaults 1253 */ 1254 1255 static void 1256 defaults(void) 1257 { 1258 int flags; 1259 char *ptr; 1260 1261 if (defopen(Pndefault) == 0) { 1262 /* 1263 * ignore case 1264 */ 1265 flags = defcntl(DC_GETFLAGS, 0); 1266 TURNOFF(flags, DC_CASE); 1267 (void) defcntl(DC_SETFLAGS, flags); 1268 1269 if ((Console = defread("CONSOLE=")) != NULL) 1270 Console = strdup(Console); 1271 1272 if ((Altshell = defread("ALTSHELL=")) != NULL) 1273 Altshell = strdup(Altshell); 1274 1275 if ((ptr = defread("PASSREQ=")) != NULL && 1276 strcasecmp("YES", ptr) == 0) 1277 Passreqflag = 1; 1278 1279 if ((Def_tz = defread("TIMEZONE=")) != NULL) 1280 Def_tz = strdup(Def_tz); 1281 1282 if ((Def_hertz = defread("HZ=")) != NULL) 1283 Def_hertz = strdup(Def_hertz); 1284 1285 if ((Def_path = defread("PATH=")) != NULL) 1286 Def_path = strdup(Def_path); 1287 1288 if ((Def_supath = defread("SUPATH=")) != NULL) 1289 Def_supath = strdup(Def_supath); 1290 1291 if ((ptr = defread("ULIMIT=")) != NULL) 1292 Def_ulimit = atol(ptr); 1293 1294 if ((ptr = defread("TIMEOUT=")) != NULL) 1295 Def_timeout = (unsigned)atoi(ptr); 1296 1297 if ((ptr = defread("UMASK=")) != NULL) 1298 if (sscanf(ptr, "%lo", &Umask) != 1) 1299 Umask = DEFUMASK; 1300 1301 if ((ptr = defread("SLEEPTIME=")) != NULL) { 1302 if (is_number(ptr)) 1303 Sleeptime = atoi(ptr); 1304 } 1305 1306 if ((ptr = defread("DISABLETIME=")) != NULL) { 1307 if (is_number(ptr)) 1308 Disabletime = atoi(ptr); 1309 } 1310 1311 if ((ptr = defread("SYSLOG=")) != NULL) 1312 dosyslog = strcmp(ptr, "YES") == 0; 1313 1314 if ((ptr = defread("RETRIES=")) != NULL) { 1315 if (is_number(ptr)) 1316 retry = atoi(ptr); 1317 } 1318 1319 if ((ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) { 1320 if (is_number(ptr)) 1321 flogin = atoi(ptr); 1322 else 1323 flogin = retry; 1324 } else 1325 flogin = retry; 1326 (void) defopen((char *)NULL); 1327 } 1328 } 1329 1330 1331 /* 1332 * get_options(argc, argv) 1333 * - parse the cmd line. 1334 * - return 0 if successful, -1 if failed. 1335 * Calls login_exit() on misuse of -r, -h, and -z flags 1336 */ 1337 1338 static int 1339 get_options(int argc, char *argv[]) 1340 { 1341 int c; 1342 int errflg = 0; 1343 char sflagname[NMAX+1]; 1344 const char *flags_message = "Only one of -r, -h and -z allowed\n"; 1345 1346 while ((c = getopt(argc, argv, "u:s:R:f:h:r:pad:t:U:z:")) != -1) { 1347 switch (c) { 1348 case 'a': 1349 break; 1350 1351 case 'd': 1352 /* 1353 * Must be root to pass in device name 1354 * otherwise we exit() as punishment for trying. 1355 */ 1356 if (getuid() != 0 || geteuid() != 0) { 1357 audit_error = ADT_FAIL_VALUE_DEVICE_PERM; 1358 login_exit(1); /* sigh */ 1359 /*NOTREACHED*/ 1360 } 1361 ttyn = optarg; 1362 break; 1363 1364 case 'h': 1365 if (hflag || rflag || zflag) { 1366 (void) fprintf(stderr, flags_message); 1367 login_exit(1); 1368 } 1369 hflag = B_TRUE; 1370 SCPYL(remote_host, optarg); 1371 if (argv[optind]) { 1372 if (argv[optind][0] != '-') { 1373 SCPYL(terminal, argv[optind]); 1374 optind++; 1375 } else { 1376 /* 1377 * Allow "login -h hostname -" to 1378 * skip setting up an username as "-". 1379 */ 1380 if (argv[optind][1] == '\0') 1381 optind++; 1382 } 1383 1384 } 1385 SCPYL(progname, "telnet"); 1386 break; 1387 1388 case 'r': 1389 if (hflag || rflag || zflag) { 1390 (void) fprintf(stderr, flags_message); 1391 login_exit(1); 1392 } 1393 rflag = B_TRUE; 1394 SCPYL(remote_host, optarg); 1395 SCPYL(progname, "rlogin"); 1396 break; 1397 1398 case 'p': 1399 pflag = B_TRUE; 1400 break; 1401 1402 case 'f': 1403 /* 1404 * Must be root to bypass authentication 1405 * otherwise we exit() as punishment for trying. 1406 */ 1407 if (getuid() != 0 || geteuid() != 0) { 1408 audit_error = ADT_FAIL_VALUE_AUTH_BYPASS; 1409 1410 login_exit(1); /* sigh */ 1411 /*NOTREACHED*/ 1412 } 1413 /* save fflag user name for future use */ 1414 SCPYL(user_name, optarg); 1415 fflag = B_TRUE; 1416 break; 1417 case 'u': 1418 if (!strlen(optarg)) { 1419 (void) fprintf(stderr, 1420 "Empty string supplied with -u\n"); 1421 login_exit(1); 1422 } 1423 SCPYL(identity, optarg); 1424 uflag = B_TRUE; 1425 break; 1426 case 's': 1427 if (!strlen(optarg)) { 1428 (void) fprintf(stderr, 1429 "Empty string supplied with -s\n"); 1430 login_exit(1); 1431 } 1432 SCPYL(sflagname, optarg); 1433 sflag = B_TRUE; 1434 break; 1435 case 'R': 1436 if (!strlen(optarg)) { 1437 (void) fprintf(stderr, 1438 "Empty string supplied with -R\n"); 1439 login_exit(1); 1440 } 1441 SCPYL(repository, optarg); 1442 Rflag = B_TRUE; 1443 break; 1444 case 't': 1445 if (!strlen(optarg)) { 1446 (void) fprintf(stderr, 1447 "Empty string supplied with -t\n"); 1448 login_exit(1); 1449 } 1450 SCPYL(terminal, optarg); 1451 tflag = B_TRUE; 1452 break; 1453 case 'U': 1454 /* 1455 * Kerberized rlogind may fork us with 1456 * -U "" if the rlogin client used the "-a" 1457 * option to send a NULL username. This is done 1458 * to force login to prompt for a user/password. 1459 * However, if Kerberos auth was used, we dont need 1460 * to prompt, so we will accept the option and 1461 * handle the situation later. 1462 */ 1463 SCPYL(rusername, optarg); 1464 Uflag = B_TRUE; 1465 break; 1466 case 'z': 1467 if (hflag || rflag || zflag) { 1468 (void) fprintf(stderr, flags_message); 1469 login_exit(1); 1470 } 1471 (void) snprintf(zone_name, sizeof (zone_name), 1472 "zone:%s", optarg); 1473 SCPYL(progname, "zlogin"); 1474 zflag = B_TRUE; 1475 break; 1476 default: 1477 errflg++; 1478 break; 1479 } /* end switch */ 1480 } /* end while */ 1481 1482 /* 1483 * If the 's svcname' flag was used, override the progname 1484 * value that is to be used in the pam_start call. 1485 */ 1486 if (sflag) 1487 SCPYL(progname, sflagname); 1488 1489 /* 1490 * get the prompt set by ttymon 1491 */ 1492 ttyprompt = getenv("TTYPROMPT"); 1493 1494 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) { 1495 /* 1496 * if ttyprompt is set, there should be data on 1497 * the stream already. 1498 */ 1499 if ((envp = getargs(inputline)) != (char **)NULL) { 1500 /* 1501 * don't get name if name passed as argument. 1502 */ 1503 SCPYL(user_name, *envp++); 1504 } 1505 } else if (optind < argc) { 1506 SCPYL(user_name, argv[optind]); 1507 (void) SCPYL(inputline, user_name); 1508 (void) strlcat(inputline, " \n", sizeof (inputline)); 1509 envp = &argv[optind+1]; 1510 1511 if (!fflag) 1512 SCPYL(lusername, user_name); 1513 } 1514 1515 if (errflg) 1516 return (-1); 1517 return (0); 1518 } 1519 1520 /* 1521 * usage - Print usage message 1522 * 1523 */ 1524 static void 1525 usage(void) 1526 { 1527 (void) fprintf(stderr, 1528 "usage:\n" 1529 " login [-p] [-d device] [-R repository] [-s service]\n" 1530 "\t[-t terminal] [-u identity] [-U ruser]\n" 1531 "\t[-h hostname [terminal] | -r hostname] [name [environ]...]\n"); 1532 1533 } 1534 1535 /* 1536 * doremoteterm - Sets the appropriate ioctls for a remote terminal 1537 */ 1538 static char *speeds[] = { 1539 "0", "50", "75", "110", "134", "150", "200", "300", 1540 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400", 1541 "57600", "76800", "115200", "153600", "230400", "307200", "460800", 1542 "921600", "1000000", "1152000", "1500000", "2000000", "2500000", 1543 "3000000", "3500000", "4000000" 1544 }; 1545 1546 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0])) 1547 1548 1549 static void 1550 doremoteterm(char *term) 1551 { 1552 struct termios tp; 1553 char *cp = strchr(term, '/'), **cpp; 1554 char *speed; 1555 1556 (void) ioctl(0, TCGETS, &tp); 1557 1558 if (cp) { 1559 *cp++ = '\0'; 1560 speed = cp; 1561 cp = strchr(speed, '/'); 1562 1563 if (cp) 1564 *cp++ = '\0'; 1565 1566 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) { 1567 if (strcmp(*cpp, speed) == 0) { 1568 (void) cfsetospeed(&tp, cpp-speeds); 1569 break; 1570 } 1571 } 1572 } 1573 1574 tp.c_lflag |= ECHO|ICANON; 1575 tp.c_iflag |= IGNPAR|ICRNL; 1576 1577 (void) ioctl(0, TCSETS, &tp); 1578 1579 } 1580 1581 /* 1582 * Process_rlogin - Does the work that rlogin and telnet 1583 * need done 1584 */ 1585 static void 1586 process_rlogin(void) 1587 { 1588 /* 1589 * If a Kerberized rlogin was initiated, then these fields 1590 * must be read by rlogin daemon itself and passed down via 1591 * cmd line args. 1592 */ 1593 if (!Uflag && !strlen(rusername)) 1594 getstr(rusername, sizeof (rusername), "remuser"); 1595 if (!strlen(lusername)) 1596 getstr(lusername, sizeof (lusername), "locuser"); 1597 if (!tflag && !strlen(terminal)) 1598 getstr(terminal, sizeof (terminal), "Terminal type"); 1599 1600 if (strlen(terminal)) 1601 doremoteterm(terminal); 1602 1603 /* fflag has precedence over stuff passed by rlogind */ 1604 if (fflag || getuid()) { 1605 pwd = &nouser; 1606 return; 1607 } else { 1608 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS) 1609 login_exit(1); 1610 1611 pwd = getpwnam(lusername); 1612 if (pwd == NULL) { 1613 pwd = &nouser; 1614 return; 1615 } 1616 } 1617 1618 /* 1619 * Update PAM on the user name 1620 */ 1621 if (strlen(lusername) && 1622 pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS) 1623 login_exit(1); 1624 1625 if (strlen(rusername) && 1626 pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS) 1627 login_exit(1); 1628 1629 SCPYL(user_name, lusername); 1630 envp = &zero; 1631 lusername[0] = '\0'; 1632 } 1633 1634 /* 1635 * *** Account validation routines *** 1636 * 1637 */ 1638 1639 /* 1640 * validate_account - This is the PAM version of validate. 1641 */ 1642 1643 static void 1644 validate_account(void) 1645 { 1646 int error; 1647 int flag; 1648 int tries; /* new password retries */ 1649 1650 (void) alarm(0); /* give user time to come up with password */ 1651 1652 check_log(); 1653 1654 if (Passreqflag) 1655 flag = PAM_DISALLOW_NULL_AUTHTOK; 1656 else 1657 flag = 0; 1658 1659 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) { 1660 if (error == PAM_NEW_AUTHTOK_REQD) { 1661 tries = 1; 1662 error = PAM_AUTHTOK_ERR; 1663 while (error == PAM_AUTHTOK_ERR && 1664 tries <= DEF_ATTEMPTS) { 1665 if (tries > 1) 1666 (void) printf("Try again\n\n"); 1667 1668 (void) printf("Choose a new password.\n"); 1669 1670 error = pam_chauthtok(pamh, 1671 PAM_CHANGE_EXPIRED_AUTHTOK); 1672 if (error == PAM_TRY_AGAIN) { 1673 (void) sleep(1); 1674 error = pam_chauthtok(pamh, 1675 PAM_CHANGE_EXPIRED_AUTHTOK); 1676 } 1677 tries++; 1678 } 1679 1680 if (error != PAM_SUCCESS) { 1681 if (dosyslog) 1682 syslog(LOG_CRIT, 1683 "change password failure: %s", 1684 pam_strerror(pamh, error)); 1685 audit_error = ADT_FAIL_PAM + error; 1686 login_exit(1); 1687 } else { 1688 audit_success(ADT_passwd, pwd, zone_name); 1689 } 1690 } else { 1691 (void) printf(incorrectmsg); 1692 1693 if (dosyslog) 1694 syslog(LOG_CRIT, 1695 "login account failure: %s", 1696 pam_strerror(pamh, error)); 1697 audit_error = ADT_FAIL_PAM + error; 1698 login_exit(1); 1699 } 1700 } 1701 } 1702 1703 /* 1704 * Check_log - This is really a hack because PAM checks the log, but login 1705 * wants to know if the log is okay and PAM doesn't have 1706 * a module independent way of handing this info back. 1707 */ 1708 1709 static void 1710 check_log(void) 1711 { 1712 int fdl; 1713 long long offset; 1714 1715 offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog); 1716 1717 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) { 1718 if (llseek(fdl, offset, SEEK_SET) == offset && 1719 read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) && 1720 ll.ll_time != 0) 1721 lastlogok = 1; 1722 (void) close(fdl); 1723 } 1724 } 1725 1726 /* 1727 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to 1728 * place us in the user's home directory just in 1729 * case it was protected and the first chdir failed. 1730 * No chdir errors should happen at this point because 1731 * all failures should have happened on the first 1732 * time around. 1733 */ 1734 1735 static void 1736 chdir_to_dir_user(void) 1737 { 1738 if (chdir(pwd->pw_dir) < 0) { 1739 if (chdir("/") < 0) { 1740 (void) printf("No directory!\n"); 1741 /* 1742 * This probably won't work since we can't get to /. 1743 */ 1744 if (dosyslog) { 1745 if (remote_host[0]) { 1746 syslog(LOG_CRIT, 1747 "LOGIN FAILURES ON %s FROM %.*s ", 1748 " %.*s", ttyn, HMAX, 1749 remote_host, NMAX, pwd->pw_name); 1750 } else { 1751 syslog(LOG_CRIT, 1752 "LOGIN FAILURES ON %s, %.*s", 1753 ttyn, NMAX, pwd->pw_name); 1754 } 1755 } 1756 closelog(); 1757 (void) sleep(Disabletime); 1758 exit(1); 1759 } else { 1760 (void) printf("No directory! Logging in with home=/\n"); 1761 pwd->pw_dir = "/"; 1762 } 1763 } 1764 } 1765 1766 1767 /* 1768 * login_authenticate - Performs the main authentication work 1769 * 1. Prints the login prompt 1770 * 2. Requests and verifys the password 1771 * 3. Checks the port password 1772 */ 1773 1774 static void 1775 login_authenticate(void) 1776 { 1777 char *user; 1778 int err; 1779 int login_successful = 0; 1780 1781 do { 1782 /* if scheme broken, then nothing to do but quit */ 1783 if (pam_get_item(pamh, PAM_USER, (void **)&user) != PAM_SUCCESS) 1784 exit(1); 1785 1786 /* 1787 * only get name from utility if it is not already 1788 * supplied by pam_start or a pam_set_item. 1789 */ 1790 if (!user || !user[0]) { 1791 /* use call back to get user name */ 1792 get_user_name(); 1793 } 1794 1795 err = verify_passwd(); 1796 1797 /* 1798 * If root login and not on system console then call exit(2) 1799 */ 1800 check_for_console(); 1801 1802 switch (err) { 1803 case PAM_SUCCESS: 1804 case PAM_NEW_AUTHTOK_REQD: 1805 /* 1806 * Officially, pam_authenticate() shouldn't return this 1807 * but it's probably the right thing to return if 1808 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will 1809 * be forced to change password later in this code. 1810 */ 1811 count = 0; 1812 login_successful = 1; 1813 break; 1814 case PAM_MAXTRIES: 1815 count = retry; 1816 /*FALLTHROUGH*/ 1817 case PAM_AUTH_ERR: 1818 case PAM_AUTHINFO_UNAVAIL: 1819 case PAM_USER_UNKNOWN: 1820 audit_failure(get_audit_id(), ADT_FAIL_PAM + err, pwd, 1821 remote_host, ttyn, zone_name); 1822 log_bad_attempts(); 1823 break; 1824 case PAM_ABORT: 1825 log_bad_attempts(); 1826 (void) sleep(Disabletime); 1827 (void) printf(incorrectmsg); 1828 1829 audit_error = ADT_FAIL_PAM + err; 1830 login_exit(1); 1831 /*NOTREACHED*/ 1832 default: /* Some other PAM error */ 1833 audit_error = ADT_FAIL_PAM + err; 1834 login_exit(1); 1835 /*NOTREACHED*/ 1836 } 1837 1838 if (login_successful) 1839 break; 1840 1841 /* sleep after bad passwd */ 1842 if (count) 1843 (void) sleep(Sleeptime); 1844 (void) printf(incorrectmsg); 1845 /* force name to be null in this case */ 1846 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS) 1847 login_exit(1); 1848 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS) 1849 login_exit(1); 1850 } while (count++ < retry); 1851 1852 if (count >= retry) { 1853 audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES, pwd, 1854 remote_host, ttyn, zone_name); 1855 /* 1856 * If logging is turned on, output the 1857 * string storage area to the log file, 1858 * and sleep for Disabletime 1859 * seconds before exiting. 1860 */ 1861 if (writelog) 1862 badlogin(); 1863 if (dosyslog) { 1864 if ((pwd = getpwnam(user_name)) != NULL) { 1865 if (remote_host[0]) { 1866 syslog(LOG_CRIT, 1867 "REPEATED LOGIN FAILURES ON %s " 1868 "FROM %.*s, %.*s", 1869 ttyn, HMAX, remote_host, NMAX, 1870 user_name); 1871 } else { 1872 syslog(LOG_CRIT, 1873 "REPEATED LOGIN FAILURES ON " 1874 "%s, %.*s", 1875 ttyn, NMAX, user_name); 1876 } 1877 } else { 1878 if (remote_host[0]) { 1879 syslog(LOG_CRIT, 1880 "REPEATED LOGIN FAILURES ON %s " 1881 "FROM %.*s", 1882 ttyn, HMAX, remote_host); 1883 } else { 1884 syslog(LOG_CRIT, 1885 "REPEATED LOGIN FAILURES ON %s", 1886 ttyn); 1887 } 1888 } 1889 } 1890 (void) sleep(Disabletime); 1891 exit(1); 1892 } 1893 1894 } 1895 1896 /* 1897 * *** Credential Related routines *** 1898 * 1899 */ 1900 1901 /* 1902 * setup_credentials - sets the group ID, initializes the groups 1903 * and sets up the secretkey. 1904 * Exits if a failure occurrs. 1905 */ 1906 1907 1908 /* 1909 * setup_credentials - PAM does all the work for us on this one. 1910 */ 1911 1912 static void 1913 setup_credentials(void) 1914 { 1915 int error = 0; 1916 1917 /* set the real (and effective) GID */ 1918 if (setgid(pwd->pw_gid) == -1) { 1919 login_exit(1); 1920 } 1921 1922 /* 1923 * Initialize the supplementary group access list. 1924 */ 1925 if ((user_name[0] == '\0') || 1926 (initgroups(user_name, pwd->pw_gid) == -1)) { 1927 audit_error = ADT_FAIL_VALUE_PROGRAM; 1928 login_exit(1); 1929 } 1930 1931 if ((error = pam_setcred(pamh, zflag ? PAM_REINITIALIZE_CRED : 1932 PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 1933 audit_error = ADT_FAIL_PAM + error; 1934 login_exit(error); 1935 } 1936 1937 /* 1938 * Record successful login and fork process that records logout. 1939 * We have to do this after setting credentials because pam_setcred() 1940 * loads key audit info into the cred, but before setuid() so audit 1941 * system calls will work. 1942 */ 1943 audit_success(get_audit_id(), pwd, zone_name); 1944 } 1945 1946 static uint_t 1947 get_audit_id(void) 1948 { 1949 if (rflag) 1950 return (ADT_rlogin); 1951 else if (hflag) 1952 return (ADT_telnet); 1953 else if (zflag) 1954 return (ADT_zlogin); 1955 1956 return (ADT_login); 1957 } 1958 1959 /* 1960 * 1961 * *** Routines to get a new user set up and running *** 1962 * 1963 * Things to do when starting up a new user: 1964 * adjust_nice 1965 * update_utmpx_entry 1966 * establish_user_environment 1967 * print_banner 1968 * display_last_login_time 1969 * exec_the_shell 1970 * 1971 */ 1972 1973 1974 /* 1975 * adjust_nice - Set the nice (process priority) value if the 1976 * gecos value contains an appropriate value. 1977 */ 1978 1979 static void 1980 adjust_nice(void) 1981 { 1982 int pri, mflg, i; 1983 1984 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) { 1985 pri = 0; 1986 mflg = 0; 1987 i = 4; 1988 1989 if (pwd->pw_gecos[i] == '-') { 1990 mflg++; 1991 i++; 1992 } 1993 1994 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9') 1995 pri = (pri * 10) + pwd->pw_gecos[i++] - '0'; 1996 1997 if (mflg) 1998 pri = -pri; 1999 2000 (void) nice(pri); 2001 } 2002 } 2003 2004 /* 2005 * update_utmpx_entry - Searchs for the correct utmpx entry, making an 2006 * entry there if it finds one, otherwise exits. 2007 */ 2008 2009 static void 2010 update_utmpx_entry(int sublogin) 2011 { 2012 int err; 2013 char *user; 2014 static char *errmsg = "No utmpx entry. " 2015 "You must exec \"login\" from the lowest level \"shell\"."; 2016 int tmplen; 2017 struct utmpx *u = (struct utmpx *)0; 2018 struct utmpx utmpx; 2019 char *ttyntail; 2020 2021 /* 2022 * If we're not a sublogin then 2023 * we'll get an error back if our PID doesn't match the PID of the 2024 * entry we are updating, otherwise if its a sublogin the flags 2025 * field is set to 0, which means we just write a matching entry 2026 * (without checking the pid), or a new entry if an entry doesn't 2027 * exist. 2028 */ 2029 2030 if ((err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 2031 audit_error = ADT_FAIL_PAM + err; 2032 login_exit(1); 2033 } 2034 2035 if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) != 2036 PAM_SUCCESS) { 2037 audit_error = ADT_FAIL_PAM + err; 2038 login_exit(1); 2039 } 2040 2041 (void) memset((void *)&utmpx, 0, sizeof (utmpx)); 2042 (void) time(&utmpx.ut_tv.tv_sec); 2043 utmpx.ut_pid = getpid(); 2044 2045 if (rflag || hflag) { 2046 SCPYN(utmpx.ut_host, remote_host); 2047 tmplen = strlen(remote_host) + 1; 2048 if (tmplen < sizeof (utmpx.ut_host)) 2049 utmpx.ut_syslen = tmplen; 2050 else 2051 utmpx.ut_syslen = sizeof (utmpx.ut_host); 2052 } else if (zflag) { 2053 /* 2054 * If this is a login from another zone, put the 2055 * zone:<zonename> string in the utmpx entry. 2056 */ 2057 SCPYN(utmpx.ut_host, zone_name); 2058 tmplen = strlen(zone_name) + 1; 2059 if (tmplen < sizeof (utmpx.ut_host)) 2060 utmpx.ut_syslen = tmplen; 2061 else 2062 utmpx.ut_syslen = sizeof (utmpx.ut_host); 2063 } else { 2064 utmpx.ut_syslen = 0; 2065 } 2066 2067 SCPYN(utmpx.ut_user, user); 2068 2069 /* skip over "/dev/" */ 2070 ttyntail = basename(ttyn); 2071 2072 while ((u = getutxent()) != NULL) { 2073 if ((u->ut_type == INIT_PROCESS || 2074 u->ut_type == LOGIN_PROCESS || 2075 u->ut_type == USER_PROCESS) && 2076 ((sublogin && strncmp(u->ut_line, ttyntail, 2077 sizeof (u->ut_line)) == 0) || 2078 u->ut_pid == login_pid)) { 2079 SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1)); 2080 (void) memcpy(utmpx.ut_id, u->ut_id, 2081 sizeof (utmpx.ut_id)); 2082 utmpx.ut_exit.e_exit = u->ut_exit.e_exit; 2083 utmpx.ut_type = USER_PROCESS; 2084 (void) pututxline(&utmpx); 2085 break; 2086 } 2087 } 2088 endutxent(); 2089 2090 if (u == (struct utmpx *)NULL) { 2091 if (!sublogin) { 2092 /* 2093 * no utmpx entry already setup 2094 * (init or rlogind/telnetd) 2095 */ 2096 (void) puts(errmsg); 2097 2098 audit_error = ADT_FAIL_VALUE_PROGRAM; 2099 login_exit(1); 2100 } 2101 } else { 2102 /* Now attempt to write out this entry to the wtmp file if */ 2103 /* we were successful in getting it from the utmpx file and */ 2104 /* the wtmp file exists. */ 2105 updwtmpx(WTMPX_FILE, &utmpx); 2106 } 2107 } 2108 2109 2110 2111 /* 2112 * process_chroot_logins - Chroots to the specified subdirectory and 2113 * re executes login. 2114 */ 2115 2116 static int 2117 process_chroot_logins(void) 2118 { 2119 /* 2120 * If the shell field starts with a '*', do a chroot to the home 2121 * directory and perform a new login. 2122 */ 2123 2124 if (*pwd->pw_shell == '*') { 2125 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */ 2126 pamh = NULL; /* really done */ 2127 if (chroot(pwd->pw_dir) < 0) { 2128 (void) printf("No Root Directory\n"); 2129 2130 audit_failure(get_audit_id(), 2131 ADT_FAIL_VALUE_CHDIR_FAILED, 2132 pwd, remote_host, ttyn, zone_name); 2133 2134 return (ERROR); 2135 } 2136 /* 2137 * Set the environment flag <!sublogin> so that the next login 2138 * knows that it is a sublogin. 2139 */ 2140 envinit[0] = SUBLOGIN; 2141 envinit[1] = (char *)NULL; 2142 (void) printf("Subsystem root: %s\n", pwd->pw_dir); 2143 (void) execle("/usr/bin/login", "login", (char *)0, 2144 &envinit[0]); 2145 (void) execle("/etc/login", "login", (char *)0, &envinit[0]); 2146 (void) printf("No /usr/bin/login or /etc/login on root\n"); 2147 2148 audit_error = ADT_FAIL_VALUE_PROGRAM; 2149 2150 login_exit(1); 2151 } 2152 return (OK); 2153 } 2154 2155 /* 2156 * establish_user_environment - Set up the new users enviornment 2157 */ 2158 2159 static void 2160 establish_user_environment(char **renvp) 2161 { 2162 int i, j, k, l_index, length, idx = 0; 2163 char *endptr; 2164 char **lenvp; 2165 char **pam_env; 2166 2167 lenvp = environ; 2168 while (*lenvp++) 2169 ; 2170 2171 /* count the number of PAM environment variables set by modules */ 2172 if ((pam_env = pam_getenvlist(pamh)) != 0) { 2173 for (idx = 0; pam_env[idx] != 0; idx++) 2174 ; 2175 } 2176 2177 envinit = (char **)calloc(lenvp - environ + 10 + MAXARGS + idx, 2178 sizeof (char *)); 2179 if (envinit == NULL) { 2180 (void) printf("Calloc failed - out of swap space.\n"); 2181 login_exit(8); 2182 } 2183 2184 /* 2185 * add PAM environment variables first so they 2186 * can be overwritten at login's discretion. 2187 * check for illegal environment variables. 2188 */ 2189 idx = 0; basicenv = 0; 2190 if (pam_env != 0) { 2191 while (pam_env[idx] != 0) { 2192 if (legalenvvar(pam_env[idx])) { 2193 envinit[basicenv] = pam_env[idx]; 2194 basicenv++; 2195 } 2196 idx++; 2197 } 2198 } 2199 (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv)); 2200 2201 /* Set up environment */ 2202 if (rflag) { 2203 ENVSTRNCAT(term, terminal); 2204 } else if (hflag) { 2205 if (strlen(terminal)) { 2206 ENVSTRNCAT(term, terminal); 2207 } 2208 } else { 2209 char *tp = getenv("TERM"); 2210 2211 if ((tp != NULL) && (*tp != '\0')) 2212 ENVSTRNCAT(term, tp); 2213 } 2214 2215 ENVSTRNCAT(logname, pwd->pw_name); 2216 2217 /* 2218 * There are three places to get timezone info. init.c sets 2219 * TZ if the file /etc/default/init contains a value for TZ. 2220 * login.c looks in the file /etc/default/login for a 2221 * variable called TIMEZONE being set. If TIMEZONE has a 2222 * value, TZ is set to that value; no environment variable 2223 * TIMEZONE is set, only TZ. If neither of these methods 2224 * work to set TZ, then the library routines will default 2225 * to using the file /usr/lib/locale/TZ/localtime. 2226 * 2227 * There is a priority set up here. If /etc/default/init has 2228 * a value for TZ, that value remains top priority. If the 2229 * file /etc/default/login has TIMEZONE set, that has second 2230 * highest priority not overriding the value of TZ in 2231 * /etc/default/init. The reason for this priority is that the 2232 * file /etc/default/init is supposed to be sourced by 2233 * /etc/profile. We are doing the "sourcing" prematurely in 2234 * init.c. Additionally, a login C shell doesn't source the 2235 * file /etc/profile thus not sourcing /etc/default/init thus not 2236 * allowing an adminstrator to globally set TZ for all users 2237 */ 2238 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */ 2239 tmp_tz = Def_tz; 2240 2241 if ((Def_tz = getenv("TZ")) != NULL) { 2242 ENVSTRNCAT(timez, Def_tz); 2243 } else if (tmp_tz != NULL) { 2244 Def_tz = tmp_tz; 2245 ENVSTRNCAT(timez, Def_tz); 2246 } 2247 2248 if (Def_hertz == NULL) 2249 (void) sprintf(hertz + strlen(hertz), "%lu", HZ); 2250 else 2251 ENVSTRNCAT(hertz, Def_hertz); 2252 2253 if (Def_path == NULL) 2254 (void) strlcat(path, DEF_PATH, sizeof (path)); 2255 else 2256 ENVSTRNCAT(path, Def_path); 2257 2258 ENVSTRNCAT(home, pwd->pw_dir); 2259 2260 /* 2261 * Find the end of the basic environment 2262 */ 2263 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++) 2264 ; 2265 2266 /* 2267 * If TZ has a value, add it. 2268 */ 2269 if (strcmp(timez, "TZ=") != 0) 2270 envinit[basicenv++] = timez; 2271 2272 if (*pwd->pw_shell == '\0') { 2273 /* 2274 * If possible, use the primary default shell, 2275 * otherwise, use the secondary one. 2276 */ 2277 if (access(SHELL, X_OK) == 0) 2278 pwd->pw_shell = SHELL; 2279 else 2280 pwd->pw_shell = SHELL2; 2281 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) { 2282 envinit[basicenv++] = shell; 2283 ENVSTRNCAT(shell, pwd->pw_shell); 2284 } 2285 2286 #ifndef NO_MAIL 2287 envinit[basicenv++] = mail; 2288 (void) strlcat(mail, pwd->pw_name, sizeof (mail)); 2289 #endif 2290 2291 /* 2292 * Pick up locale environment variables, if any. 2293 */ 2294 lenvp = renvp; 2295 while (*lenvp != NULL) { 2296 j = 0; 2297 while (localeenv[j] != 0) { 2298 /* 2299 * locale_envmatch() returns 1 if 2300 * *lenvp is localenev[j] and valid. 2301 */ 2302 if (locale_envmatch(localeenv[j], *lenvp) == 1) { 2303 envinit[basicenv++] = *lenvp; 2304 break; 2305 } 2306 j++; 2307 } 2308 lenvp++; 2309 } 2310 2311 /* 2312 * If '-p' flag, then try to pass on allowable environment 2313 * variables. Note that by processing this first, what is 2314 * passed on the final "login:" line may over-ride the invocation 2315 * values. XXX is this correct? 2316 */ 2317 if (pflag) { 2318 for (lenvp = renvp; *lenvp; lenvp++) { 2319 if (!legalenvvar(*lenvp)) { 2320 continue; 2321 } 2322 /* 2323 * If this isn't 'xxx=yyy', skip it. XXX 2324 */ 2325 if ((endptr = strchr(*lenvp, '=')) == NULL) { 2326 continue; 2327 } 2328 length = endptr + 1 - *lenvp; 2329 for (j = 0; j < basicenv; j++) { 2330 if (strncmp(envinit[j], *lenvp, length) == 0) { 2331 /* 2332 * Replace previously established value 2333 */ 2334 envinit[j] = *lenvp; 2335 break; 2336 } 2337 } 2338 if (j == basicenv) { 2339 /* 2340 * It's a new definition, so add it at the end. 2341 */ 2342 envinit[basicenv++] = *lenvp; 2343 } 2344 } 2345 } 2346 2347 /* 2348 * Add in all the environment variables picked up from the 2349 * argument list to "login" or from the user response to the 2350 * "login" request, if any. 2351 */ 2352 2353 if (envp == NULL) 2354 goto switch_env; /* done */ 2355 2356 for (j = 0, k = 0, l_index = 0; 2357 *envp != NULL && j < (MAXARGS-1); 2358 j++, envp++) { 2359 2360 /* 2361 * Scan each string provided. If it doesn't have the 2362 * format xxx=yyy, then add the string "Ln=" to the beginning. 2363 */ 2364 if ((endptr = strchr(*envp, '=')) == NULL) { 2365 /* 2366 * This much to be malloc'd: 2367 * strlen(*envp) + 1 char for 'L' + 2368 * MAXARGSWIDTH + 1 char for '=' + 1 for null char; 2369 * 2370 * total = strlen(*envp) + MAXARGSWIDTH + 3 2371 */ 2372 int total = strlen(*envp) + MAXARGSWIDTH + 3; 2373 envinit[basicenv+k] = malloc(total); 2374 if (envinit[basicenv+k] == NULL) { 2375 (void) printf("%s: malloc failed\n", PROG_NAME); 2376 login_exit(1); 2377 } 2378 (void) snprintf(envinit[basicenv+k], total, "L%d=%s", 2379 l_index, *envp); 2380 2381 k++; 2382 l_index++; 2383 } else { 2384 if (!legalenvvar(*envp)) { /* this env var permited? */ 2385 continue; 2386 } else { 2387 2388 /* 2389 * Check to see whether this string replaces 2390 * any previously defined string 2391 */ 2392 for (i = 0, length = endptr + 1 - *envp; 2393 i < basicenv + k; i++) { 2394 if (strncmp(*envp, envinit[i], length) 2395 == 0) { 2396 envinit[i] = *envp; 2397 break; 2398 } 2399 } 2400 2401 /* 2402 * If it doesn't, place it at the end of 2403 * environment array. 2404 */ 2405 if (i == basicenv+k) { 2406 envinit[basicenv+k] = *envp; 2407 k++; 2408 } 2409 } 2410 } 2411 } /* for (j = 0 ... ) */ 2412 2413 switch_env: 2414 /* 2415 * Switch to the new environment. 2416 */ 2417 environ = envinit; 2418 } 2419 2420 /* 2421 * print_banner - Print the banner at start up 2422 * Do not turn on DOBANNER ifdef. This is not 2423 * relevant to SunOS. 2424 */ 2425 2426 static void 2427 print_banner(void) 2428 { 2429 #ifdef DOBANNER 2430 uname(&un); 2431 #if i386 2432 (void) printf("UNIX System V/386 Release %s\n%s\n" 2433 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n" 2434 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n", 2435 un.release, un.nodename); 2436 #elif sun 2437 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n" 2438 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n" 2439 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n" 2440 "All Rights Reserved\n", 2441 un.release, un.machine, un.nodename); 2442 #else 2443 (void) printf("UNIX System V Release %s AT&T %s\n%s\n" 2444 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n", 2445 un.release, un.machine, un.nodename); 2446 #endif /* i386 */ 2447 #endif /* DOBANNER */ 2448 } 2449 2450 /* 2451 * display_last_login_time - Advise the user the time and date 2452 * that this login-id was last used. 2453 */ 2454 2455 static void 2456 display_last_login_time(void) 2457 { 2458 if (lastlogok) { 2459 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time)); 2460 2461 if (*ll.ll_host != '\0') 2462 (void) printf("from %.*s\n", sizeof (ll.ll_host), 2463 ll.ll_host); 2464 else 2465 (void) printf("on %.*s\n", sizeof (ll.ll_line), 2466 ll.ll_line); 2467 } 2468 } 2469 2470 /* 2471 * exec_the_shell - invoke the specified shell or start up program 2472 */ 2473 2474 static void 2475 exec_the_shell(void) 2476 { 2477 char *endptr; 2478 int i; 2479 2480 (void) strlcat(minusnam, basename(pwd->pw_shell), 2481 sizeof (minusnam)); 2482 2483 /* 2484 * Exec the shell 2485 */ 2486 (void) execl(pwd->pw_shell, minusnam, (char *)0); 2487 2488 /* 2489 * pwd->pw_shell was not an executable object file, maybe it 2490 * is a shell proceedure or a command line with arguments. 2491 * If so, turn off the SHELL= environment variable. 2492 */ 2493 for (i = 0; envinit[i] != NULL; ++i) { 2494 if ((envinit[i] == shell) && 2495 ((endptr = strchr(shell, '=')) != NULL)) 2496 (*++endptr) = '\0'; 2497 } 2498 2499 if (access(pwd->pw_shell, R_OK|X_OK) == 0) { 2500 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0); 2501 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0); 2502 } 2503 2504 (void) printf("No shell\n"); 2505 } 2506 2507 /* 2508 * login_exit - Call exit() and terminate. 2509 * This function is here for PAM so cleanup can 2510 * be done before the process exits. 2511 */ 2512 static void 2513 login_exit(int exit_code) 2514 { 2515 if (pamh) 2516 (void) pam_end(pamh, PAM_ABORT); 2517 2518 if (audit_error) 2519 audit_failure(get_audit_id(), audit_error, 2520 pwd, remote_host, ttyn, zone_name); 2521 2522 exit(exit_code); 2523 /*NOTREACHED*/ 2524 } 2525 2526 /* 2527 * Check if lenv and penv matches or not. 2528 */ 2529 static int 2530 locale_envmatch(char *lenv, char *penv) 2531 { 2532 while ((*lenv == *penv) && *lenv && *penv != '=') { 2533 lenv++; 2534 penv++; 2535 } 2536 2537 /* 2538 * '/' is eliminated for security reason. 2539 */ 2540 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/') 2541 return (1); 2542 return (0); 2543 } 2544 2545 static int 2546 is_number(char *ptr) 2547 { 2548 while (*ptr != '\0') { 2549 if (!isdigit(*ptr)) 2550 return (0); 2551 ptr++; 2552 } 2553 return (1); 2554 } 2555