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