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