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