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