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