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