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