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