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