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