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