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