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