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 2005 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, 0); 1662 if (error == PAM_TRY_AGAIN) { 1663 (void) sleep(1); 1664 error = pam_chauthtok(pamh, 0); 1665 } 1666 tries++; 1667 } 1668 1669 if (error != PAM_SUCCESS) { 1670 if (dosyslog) 1671 syslog(LOG_CRIT, 1672 "change password failure: %s", 1673 pam_strerror(pamh, error)); 1674 audit_error = ADT_FAIL_PAM + error; 1675 login_exit(1); 1676 } else { 1677 audit_success(ADT_passwd, pwd, zone_name); 1678 } 1679 } else { 1680 (void) printf(incorrectmsg); 1681 1682 if (dosyslog) 1683 syslog(LOG_CRIT, 1684 "login account failure: %s", 1685 pam_strerror(pamh, error)); 1686 audit_error = ADT_FAIL_PAM + error; 1687 login_exit(1); 1688 } 1689 } 1690 } 1691 1692 /* 1693 * Check_log - This is really a hack because PAM checks the log, but login 1694 * wants to know if the log is okay and PAM doesn't have 1695 * a module independent way of handing this info back. 1696 */ 1697 1698 static void 1699 check_log(void) 1700 { 1701 int fdl; 1702 long long offset; 1703 1704 offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog); 1705 1706 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) { 1707 if (llseek(fdl, offset, SEEK_SET) == offset && 1708 read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) && 1709 ll.ll_time != 0) 1710 lastlogok = 1; 1711 (void) close(fdl); 1712 } 1713 } 1714 1715 /* 1716 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to 1717 * place us in the user's home directory just in 1718 * case it was protected and the first chdir failed. 1719 * No chdir errors should happen at this point because 1720 * all failures should have happened on the first 1721 * time around. 1722 */ 1723 1724 static void 1725 chdir_to_dir_user(void) 1726 { 1727 if (chdir(pwd->pw_dir) < 0) { 1728 if (chdir("/") < 0) { 1729 (void) printf("No directory!\n"); 1730 /* 1731 * This probably won't work since we can't get to /. 1732 */ 1733 if (dosyslog) { 1734 if (remote_host[0]) { 1735 syslog(LOG_CRIT, 1736 "LOGIN FAILURES ON %s FROM %.*s ", 1737 " %.*s", ttyn, HMAX, 1738 remote_host, NMAX, pwd->pw_name); 1739 } else { 1740 syslog(LOG_CRIT, 1741 "LOGIN FAILURES ON %s, %.*s", 1742 ttyn, NMAX, pwd->pw_name); 1743 } 1744 } 1745 closelog(); 1746 (void) sleep(Disabletime); 1747 exit(1); 1748 } else { 1749 (void) printf("No directory! Logging in with home=/\n"); 1750 pwd->pw_dir = "/"; 1751 } 1752 } 1753 } 1754 1755 1756 /* ONC_PLUS EXTRACT START */ 1757 /* 1758 * login_authenticate - Performs the main authentication work 1759 * 1. Prints the login prompt 1760 * 2. Requests and verifys the password 1761 * 3. Checks the port password 1762 */ 1763 1764 static void 1765 login_authenticate(void) 1766 { 1767 char *user; 1768 int err; 1769 int login_successful = 0; 1770 1771 do { 1772 /* if scheme broken, then nothing to do but quit */ 1773 if (pam_get_item(pamh, PAM_USER, (void **)&user) 1774 != PAM_SUCCESS) 1775 exit(1); 1776 1777 /* 1778 * only get name from utility if it is not already 1779 * supplied by pam_start or a pam_set_item. 1780 */ 1781 if (!user || !user[0]) { 1782 /* use call back to get user name */ 1783 get_user_name(); 1784 } 1785 1786 err = verify_passwd(); 1787 1788 /* 1789 * If root login and not on system console then call exit(2) 1790 */ 1791 check_for_console(); 1792 1793 switch (err) { 1794 case PAM_SUCCESS: 1795 case PAM_NEW_AUTHTOK_REQD: 1796 /* 1797 * Officially, pam_authenticate() shouldn't return this 1798 * but it's probably the right thing to return if 1799 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will 1800 * be forced to change password later in this code. 1801 */ 1802 count = 0; 1803 login_successful = 1; 1804 break; 1805 case PAM_MAXTRIES: 1806 count = retry; 1807 /*FALLTHROUGH*/ 1808 case PAM_AUTH_ERR: 1809 case PAM_AUTHINFO_UNAVAIL: 1810 case PAM_USER_UNKNOWN: 1811 audit_failure(get_audit_id(), ADT_FAIL_PAM + err, pwd, 1812 remote_host, ttyn, zone_name); 1813 log_bad_attempts(); 1814 break; 1815 case PAM_ABORT: 1816 log_bad_attempts(); 1817 (void) sleep(Disabletime); 1818 (void) printf(incorrectmsg); 1819 1820 audit_error = ADT_FAIL_PAM + err; 1821 login_exit(1); 1822 /*NOTREACHED*/ 1823 default: /* Some other PAM error */ 1824 audit_error = ADT_FAIL_PAM + err; 1825 login_exit(1); 1826 /*NOTREACHED*/ 1827 } 1828 1829 if (login_successful) 1830 break; 1831 1832 /* sleep after bad passwd */ 1833 if (count) 1834 (void) sleep(Sleeptime); 1835 (void) printf(incorrectmsg); 1836 /* force name to be null in this case */ 1837 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS) 1838 login_exit(1); 1839 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS) 1840 login_exit(1); 1841 } while (count++ < retry); 1842 1843 if (count >= retry) { 1844 audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES, pwd, 1845 remote_host, ttyn, zone_name); 1846 /* 1847 * If logging is turned on, output the 1848 * string storage area to the log file, 1849 * and sleep for Disabletime 1850 * seconds before exiting. 1851 */ 1852 if (writelog) 1853 badlogin(); 1854 if (dosyslog) { 1855 if ((pwd = getpwnam(user_name)) != NULL) { 1856 if (remote_host[0]) { 1857 syslog(LOG_CRIT, 1858 "REPEATED LOGIN FAILURES ON %s FROM " 1859 "%.*s, %.*s", 1860 ttyn, HMAX, remote_host, NMAX, 1861 user_name); 1862 } else { 1863 syslog(LOG_CRIT, 1864 "REPEATED LOGIN FAILURES ON %s, %.*s", 1865 ttyn, NMAX, user_name); 1866 } 1867 } else { 1868 if (remote_host[0]) { 1869 syslog(LOG_CRIT, 1870 "REPEATED LOGIN FAILURES ON %s FROM " 1871 "%.*s", 1872 ttyn, HMAX, remote_host); 1873 } else { 1874 syslog(LOG_CRIT, 1875 "REPEATED LOGIN FAILURES ON %s", ttyn); 1876 } 1877 } 1878 } 1879 (void) sleep(Disabletime); 1880 exit(1); 1881 } 1882 1883 } 1884 1885 /* 1886 * *** Credential Related routines *** 1887 * 1888 */ 1889 1890 /* 1891 * setup_credentials - sets the group ID, initializes the groups 1892 * and sets up the secretkey. 1893 * Exits if a failure occurrs. 1894 */ 1895 1896 1897 /* 1898 * setup_credentials - PAM does all the work for us on this one. 1899 */ 1900 1901 static void 1902 setup_credentials(void) 1903 { 1904 int error = 0; 1905 1906 /* set the real (and effective) GID */ 1907 if (setgid(pwd->pw_gid) == -1) { 1908 login_exit(1); 1909 } 1910 1911 /* 1912 * Initialize the supplementary group access list. 1913 */ 1914 if ((user_name[0] == '\0') || 1915 (initgroups(user_name, pwd->pw_gid) == -1)) { 1916 audit_error = ADT_FAIL_VALUE_PROGRAM; 1917 login_exit(1); 1918 } 1919 1920 if ((error = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 1921 audit_error = ADT_FAIL_PAM + error; 1922 login_exit(error); 1923 } 1924 1925 /* 1926 * Record successful login and fork process that records logout. 1927 * We have to do this after setting credentials because pam_setcred() 1928 * loads key audit info into the cred, but before setuid() so audit 1929 * system calls will work. 1930 */ 1931 audit_success(get_audit_id(), pwd, zone_name); 1932 1933 /* set the real (and effective) UID */ 1934 if (setuid(pwd->pw_uid) == -1) { 1935 login_exit(1); 1936 } 1937 1938 } 1939 /* ONC_PLUS EXTRACT END */ 1940 1941 static uint_t 1942 get_audit_id(void) 1943 { 1944 if (rflag) 1945 return (ADT_rlogin); 1946 else if (hflag) 1947 return (ADT_telnet); 1948 else if (zflag) 1949 return (ADT_zlogin); 1950 1951 return (ADT_login); 1952 } 1953 1954 /* 1955 * 1956 * *** Routines to get a new user set up and running *** 1957 * 1958 * Things to do when starting up a new user: 1959 * adjust_nice 1960 * update_utmpx_entry 1961 * establish_user_environment 1962 * print_banner 1963 * display_last_login_time 1964 * exec_the_shell 1965 * 1966 */ 1967 1968 1969 /* 1970 * adjust_nice - Set the nice (process priority) value if the 1971 * gecos value contains an appropriate value. 1972 */ 1973 1974 static void 1975 adjust_nice(void) 1976 { 1977 int pri, mflg, i; 1978 1979 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) { 1980 pri = 0; 1981 mflg = 0; 1982 i = 4; 1983 1984 if (pwd->pw_gecos[i] == '-') { 1985 mflg++; 1986 i++; 1987 } 1988 1989 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9') 1990 pri = (pri * 10) + pwd->pw_gecos[i++] - '0'; 1991 1992 if (mflg) 1993 pri = -pri; 1994 1995 (void) nice(pri); 1996 } 1997 } 1998 1999 /* ONC_PLUS EXTRACT START */ 2000 /* 2001 * update_utmpx_entry - Searchs for the correct utmpx entry, making an 2002 * entry there if it finds one, otherwise exits. 2003 */ 2004 2005 static void 2006 update_utmpx_entry(int sublogin) 2007 { 2008 int err; 2009 char *user; 2010 static char *errmsg = "No utmpx entry. " 2011 "You must exec \"login\" from the lowest level \"shell\"."; 2012 int tmplen; 2013 struct utmpx *u = (struct utmpx *)0; 2014 struct utmpx utmpx; 2015 char *ttyntail; 2016 2017 /* 2018 * If we're not a sublogin then 2019 * we'll get an error back if our PID doesn't match the PID of the 2020 * entry we are updating, otherwise if its a sublogin the flags 2021 * field is set to 0, which means we just write a matching entry 2022 * (without checking the pid), or a new entry if an entry doesn't 2023 * exist. 2024 */ 2025 2026 if ((err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 2027 audit_error = ADT_FAIL_PAM + err; 2028 login_exit(1); 2029 } 2030 2031 if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) != 2032 PAM_SUCCESS) { 2033 audit_error = ADT_FAIL_PAM + err; 2034 login_exit(1); 2035 } 2036 /* ONC_PLUS EXTRACT END */ 2037 2038 (void) memset((void *)&utmpx, 0, sizeof (utmpx)); 2039 (void) time(&utmpx.ut_tv.tv_sec); 2040 utmpx.ut_pid = getpid(); 2041 2042 if (rflag || hflag) { 2043 SCPYN(utmpx.ut_host, remote_host); 2044 tmplen = strlen(remote_host) + 1; 2045 if (tmplen < sizeof (utmpx.ut_host)) 2046 utmpx.ut_syslen = tmplen; 2047 else 2048 utmpx.ut_syslen = sizeof (utmpx.ut_host); 2049 } else if (zflag) { 2050 /* 2051 * If this is a login from another zone, put the 2052 * zone:<zonename> string in the utmpx entry. 2053 */ 2054 SCPYN(utmpx.ut_host, zone_name); 2055 tmplen = strlen(zone_name) + 1; 2056 if (tmplen < sizeof (utmpx.ut_host)) 2057 utmpx.ut_syslen = tmplen; 2058 else 2059 utmpx.ut_syslen = sizeof (utmpx.ut_host); 2060 } else { 2061 utmpx.ut_syslen = 0; 2062 } 2063 2064 SCPYN(utmpx.ut_user, user); 2065 2066 /* skip over "/dev/" */ 2067 ttyntail = basename(ttyn); 2068 2069 while ((u = getutxent()) != NULL) { 2070 if ((u->ut_type == INIT_PROCESS || 2071 u->ut_type == LOGIN_PROCESS || 2072 u->ut_type == USER_PROCESS) && 2073 ((sublogin && strncmp(u->ut_line, ttyntail, 2074 sizeof (u->ut_line)) == 0) || 2075 u->ut_pid == utmpx.ut_pid)) { 2076 SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1)); 2077 (void) memcpy(utmpx.ut_id, u->ut_id, 2078 sizeof (utmpx.ut_id)); 2079 utmpx.ut_exit.e_exit = u->ut_exit.e_exit; 2080 utmpx.ut_type = USER_PROCESS; 2081 (void) pututxline(&utmpx); 2082 break; 2083 } 2084 } 2085 endutxent(); 2086 2087 if (u == (struct utmpx *)NULL) { 2088 if (!sublogin) { 2089 /* 2090 * no utmpx entry already setup 2091 * (init or rlogind/telnetd) 2092 */ 2093 (void) puts(errmsg); 2094 2095 audit_error = ADT_FAIL_VALUE_PROGRAM; 2096 login_exit(1); 2097 } 2098 } else { 2099 /* Now attempt to write out this entry to the wtmp file if */ 2100 /* we were successful in getting it from the utmpx file and */ 2101 /* the wtmp file exists. */ 2102 updwtmpx(WTMPX_FILE, &utmpx); 2103 } 2104 /* ONC_PLUS EXTRACT START */ 2105 } 2106 2107 2108 2109 /* 2110 * process_chroot_logins - Chroots to the specified subdirectory and 2111 * re executes login. 2112 */ 2113 2114 static int 2115 process_chroot_logins(void) 2116 { 2117 /* 2118 * If the shell field starts with a '*', do a chroot to the home 2119 * directory and perform a new login. 2120 */ 2121 2122 if (*pwd->pw_shell == '*') { 2123 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */ 2124 pamh = NULL; /* really done */ 2125 if (chroot(pwd->pw_dir) < 0) { 2126 (void) printf("No Root Directory\n"); 2127 2128 audit_failure(get_audit_id(), 2129 ADT_FAIL_VALUE_CHDIR_FAILED, 2130 pwd, remote_host, ttyn, zone_name); 2131 2132 return (ERROR); 2133 } 2134 /* 2135 * Set the environment flag <!sublogin> so that the next login 2136 * knows that it is a sublogin. 2137 */ 2138 /* ONC_PLUS EXTRACT END */ 2139 envinit[0] = SUBLOGIN; 2140 envinit[1] = (char *)NULL; 2141 (void) printf("Subsystem root: %s\n", pwd->pw_dir); 2142 (void) execle("/usr/bin/login", "login", (char *)0, 2143 &envinit[0]); 2144 (void) execle("/etc/login", "login", (char *)0, &envinit[0]); 2145 (void) printf("No /usr/bin/login or /etc/login on root\n"); 2146 2147 audit_error = ADT_FAIL_VALUE_PROGRAM; 2148 2149 login_exit(1); 2150 } 2151 return (OK); 2152 /* ONC_PLUS EXTRACT START */ 2153 } 2154 2155 /* 2156 * establish_user_environment - Set up the new users enviornment 2157 */ 2158 2159 static void 2160 establish_user_environment(char **renvp) 2161 { 2162 int i, j, k, l_index, length, idx = 0; 2163 char *endptr; 2164 char **lenvp; 2165 char **pam_env; 2166 2167 lenvp = environ; 2168 while (*lenvp++) 2169 ; 2170 2171 /* count the number of PAM environment variables set by modules */ 2172 if ((pam_env = pam_getenvlist(pamh)) != 0) { 2173 for (idx = 0; pam_env[idx] != 0; idx++) 2174 ; 2175 } 2176 2177 envinit = (char **)calloc(lenvp - environ + 10 2178 + MAXARGS + idx, sizeof (char *)); 2179 if (envinit == NULL) { 2180 (void) printf("Calloc failed - out of swap space.\n"); 2181 login_exit(8); 2182 } 2183 2184 /* 2185 * add PAM environment variables first so they 2186 * can be overwritten at login's discretion. 2187 * check for illegal environment variables. 2188 */ 2189 idx = 0; basicenv = 0; 2190 if (pam_env != 0) { 2191 while (pam_env[idx] != 0) { 2192 if (legalenvvar(pam_env[idx])) { 2193 envinit[basicenv] = pam_env[idx]; 2194 basicenv++; 2195 } 2196 idx++; 2197 } 2198 } 2199 (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv)); 2200 /* ONC_PLUS EXTRACT END */ 2201 2202 /* Set up environment */ 2203 if (rflag) { 2204 ENVSTRNCAT(term, terminal); 2205 } else if (hflag) { 2206 if (strlen(terminal)) { 2207 ENVSTRNCAT(term, terminal); 2208 } 2209 } else { 2210 char *tp = getenv("TERM"); 2211 2212 if ((tp != NULL) && (*tp != '\0')) 2213 ENVSTRNCAT(term, tp); 2214 } 2215 2216 ENVSTRNCAT(logname, pwd->pw_name); 2217 2218 /* 2219 * There are three places to get timezone info. init.c sets 2220 * TZ if the file /etc/TIMEZONE contains a value for TZ. 2221 * login.c looks in the file /etc/default/login for a 2222 * variable called TIMEZONE being set. If TIMEZONE has a 2223 * value, TZ is set to that value; no environment variable 2224 * TIMEZONE is set, only TZ. If neither of these methods 2225 * work to set TZ, then the library routines will default 2226 * to using the file /usr/lib/locale/TZ/localtime. 2227 * 2228 * There is a priority set up here. If /etc/TIMEZONE has 2229 * a value for TZ, that value remains top priority. If the 2230 * file /etc/default/login has TIMEZONE set, that has second 2231 * highest priority not overriding the value of TZ in 2232 * /etc/TIMEZONE. The reason for this priority is that the 2233 * file /etc/TIMEZONE is supposed to be sourced by 2234 * /etc/profile. We are doing the "sourcing" prematurely in 2235 * init.c. Additionally, a login C shell doesn't source the 2236 * file /etc/profile thus not sourcing /etc/TIMEZONE thus not 2237 * allowing an adminstrator to globally set TZ for all users 2238 */ 2239 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */ 2240 tmp_tz = Def_tz; 2241 2242 if ((Def_tz = getenv("TZ")) != NULL) { 2243 ENVSTRNCAT(timez, Def_tz); 2244 } else if (tmp_tz != NULL) { 2245 Def_tz = tmp_tz; 2246 ENVSTRNCAT(timez, Def_tz); 2247 } 2248 2249 if (Def_hertz == NULL) 2250 (void) sprintf(hertz + strlen(hertz), "%lu", HZ); 2251 else 2252 ENVSTRNCAT(hertz, Def_hertz); 2253 2254 if (Def_path == NULL) 2255 (void) strlcat(path, DEF_PATH, sizeof (path)); 2256 else 2257 ENVSTRNCAT(path, Def_path); 2258 2259 ENVSTRNCAT(home, pwd->pw_dir); 2260 2261 /* 2262 * Find the end of the basic environment 2263 */ 2264 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++) 2265 ; 2266 2267 /* 2268 * If TZ has a value, add it. 2269 */ 2270 if (strcmp(timez, "TZ=") != 0) 2271 envinit[basicenv++] = timez; 2272 2273 if (*pwd->pw_shell == '\0') { 2274 /* 2275 * If possible, use the primary default shell, 2276 * otherwise, use the secondary one. 2277 */ 2278 if (access(SHELL, X_OK) == 0) 2279 pwd->pw_shell = SHELL; 2280 else 2281 pwd->pw_shell = SHELL2; 2282 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) { 2283 envinit[basicenv++] = shell; 2284 ENVSTRNCAT(shell, pwd->pw_shell); 2285 } 2286 2287 #ifndef NO_MAIL 2288 envinit[basicenv++] = mail; 2289 (void) strlcat(mail, pwd->pw_name, sizeof (mail)); 2290 #endif 2291 2292 /* 2293 * Pick up locale environment variables, if any. 2294 */ 2295 lenvp = renvp; 2296 while (*lenvp != NULL) { 2297 j = 0; 2298 while (localeenv[j] != 0) { 2299 /* 2300 * locale_envmatch() returns 1 if 2301 * *lenvp is localenev[j] and valid. 2302 */ 2303 if (locale_envmatch(localeenv[j], *lenvp) == 1) { 2304 envinit[basicenv++] = *lenvp; 2305 break; 2306 } 2307 j++; 2308 } 2309 lenvp++; 2310 } 2311 2312 /* 2313 * If '-p' flag, then try to pass on allowable environment 2314 * variables. Note that by processing this first, what is 2315 * passed on the final "login:" line may over-ride the invocation 2316 * values. XXX is this correct? 2317 */ 2318 if (pflag) { 2319 for (lenvp = renvp; *lenvp; lenvp++) { 2320 if (!legalenvvar(*lenvp)) { 2321 continue; 2322 } 2323 /* 2324 * If this isn't 'xxx=yyy', skip it. XXX 2325 */ 2326 if ((endptr = strchr(*lenvp, '=')) == NULL) { 2327 continue; 2328 } 2329 length = endptr + 1 - *lenvp; 2330 for (j = 0; j < basicenv; j++) { 2331 if (strncmp(envinit[j], *lenvp, length) == 0) { 2332 /* 2333 * Replace previously established value 2334 */ 2335 envinit[j] = *lenvp; 2336 break; 2337 } 2338 } 2339 if (j == basicenv) { 2340 /* 2341 * It's a new definition, so add it at the end. 2342 */ 2343 envinit[basicenv++] = *lenvp; 2344 } 2345 } 2346 } 2347 2348 /* 2349 * Add in all the environment variables picked up from the 2350 * argument list to "login" or from the user response to the 2351 * "login" request, if any. 2352 */ 2353 2354 if (envp == NULL) 2355 goto switch_env; /* done */ 2356 2357 for (j = 0, k = 0, l_index = 0; 2358 *envp != NULL && j < (MAXARGS-1); 2359 j++, envp++) { 2360 2361 /* 2362 * Scan each string provided. If it doesn't have the 2363 * format xxx=yyy, then add the string "Ln=" to the beginning. 2364 */ 2365 if ((endptr = strchr(*envp, '=')) == NULL) { 2366 /* 2367 * This much to be malloc'd: 2368 * strlen(*envp) + 1 char for 'L' + 2369 * MAXARGSWIDTH + 1 char for '=' + 1 for null char; 2370 * 2371 * total = strlen(*envp) + MAXARGSWIDTH + 3 2372 */ 2373 int total = strlen(*envp) + MAXARGSWIDTH + 3; 2374 envinit[basicenv+k] = malloc(total); 2375 if (envinit[basicenv+k] == NULL) { 2376 (void) printf("%s: malloc failed\n", PROG_NAME); 2377 login_exit(1); 2378 } 2379 (void) snprintf(envinit[basicenv+k], total, "L%d=%s", 2380 l_index, *envp); 2381 2382 k++; 2383 l_index++; 2384 } else { 2385 if (!legalenvvar(*envp)) { /* this env var permited? */ 2386 continue; 2387 } else { 2388 2389 /* 2390 * Check to see whether this string replaces 2391 * any previously defined string 2392 */ 2393 for (i = 0, length = endptr + 1 - *envp; 2394 i < basicenv + k; i++) { 2395 if (strncmp(*envp, envinit[i], length) 2396 == 0) { 2397 envinit[i] = *envp; 2398 break; 2399 } 2400 } 2401 2402 /* 2403 * If it doesn't, place it at the end of 2404 * environment array. 2405 */ 2406 if (i == basicenv+k) { 2407 envinit[basicenv+k] = *envp; 2408 k++; 2409 } 2410 } 2411 } 2412 } /* for (j = 0 ... ) */ 2413 2414 switch_env: 2415 /* 2416 * Switch to the new environment. 2417 */ 2418 environ = envinit; 2419 } 2420 2421 /* 2422 * print_banner - Print the banner at start up 2423 * Do not turn on DOBANNER ifdef. This is not 2424 * relevant to SunOS. 2425 */ 2426 2427 static void 2428 print_banner(void) 2429 { 2430 #ifdef DOBANNER 2431 uname(&un); 2432 #if i386 2433 (void) printf("UNIX System V/386 Release %s\n%s\n" 2434 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n" 2435 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n", 2436 un.release, un.nodename); 2437 #elif sun 2438 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n" 2439 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n" 2440 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n" 2441 "All Rights Reserved\n", 2442 un.release, un.machine, un.nodename); 2443 #else 2444 (void) printf("UNIX System V Release %s AT&T %s\n%s\n" 2445 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n", 2446 un.release, un.machine, un.nodename); 2447 #endif /* i386 */ 2448 #endif /* DOBANNER */ 2449 } 2450 2451 /* 2452 * display_last_login_time - Advise the user the time and date 2453 * that this login-id was last used. 2454 */ 2455 2456 static void 2457 display_last_login_time(void) 2458 { 2459 if (lastlogok) { 2460 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time)); 2461 2462 if (*ll.ll_host != '\0') 2463 (void) printf("from %.*s\n", sizeof (ll.ll_host), 2464 ll.ll_host); 2465 else 2466 (void) printf("on %.*s\n", sizeof (ll.ll_line), 2467 ll.ll_line); 2468 } 2469 } 2470 2471 /* 2472 * exec_the_shell - invoke the specified shell or start up program 2473 */ 2474 2475 static void 2476 exec_the_shell(void) 2477 { 2478 char *endptr; 2479 int i; 2480 2481 (void) strlcat(minusnam, basename(pwd->pw_shell), 2482 sizeof (minusnam)); 2483 2484 /* 2485 * Exec the shell 2486 */ 2487 (void) execl(pwd->pw_shell, minusnam, (char *)0); 2488 2489 /* 2490 * pwd->pw_shell was not an executable object file, maybe it 2491 * is a shell proceedure or a command line with arguments. 2492 * If so, turn off the SHELL= environment variable. 2493 */ 2494 for (i = 0; envinit[i] != NULL; ++i) { 2495 if ((envinit[i] == shell) && 2496 ((endptr = strchr(shell, '=')) != NULL)) 2497 (*++endptr) = '\0'; 2498 } 2499 2500 if (access(pwd->pw_shell, R_OK|X_OK) == 0) { 2501 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0); 2502 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0); 2503 } 2504 2505 (void) printf("No shell\n"); 2506 } 2507 2508 /* 2509 * login_exit - Call exit() and terminate. 2510 * This function is here for PAM so cleanup can 2511 * be done before the process exits. 2512 */ 2513 static void 2514 login_exit(int exit_code) 2515 { 2516 if (pamh) 2517 (void) pam_end(pamh, PAM_ABORT); 2518 2519 if (audit_error) 2520 audit_failure(get_audit_id(), audit_error, 2521 pwd, remote_host, ttyn, zone_name); 2522 2523 exit(exit_code); 2524 /*NOTREACHED*/ 2525 } 2526 2527 /* 2528 * Check if lenv and penv matches or not. 2529 */ 2530 static int 2531 locale_envmatch(char *lenv, char *penv) 2532 { 2533 while ((*lenv == *penv) && *lenv && *penv != '=') { 2534 lenv++; 2535 penv++; 2536 } 2537 2538 /* 2539 * '/' is eliminated for security reason. 2540 */ 2541 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/') 2542 return (1); 2543 return (0); 2544 } 2545 2546 static int 2547 is_number(char *ptr) 2548 { 2549 while (*ptr != '\0') { 2550 if (!isdigit(*ptr)) 2551 return (0); 2552 ptr++; 2553 } 2554 return (1); 2555 } 2556