if (setusercontext(lc, pwd, pwd->pw_uid,
if (setusercontext(lc, pwd, rootlogin ? 0 : pwd->pw_uid,
main(argc, argv)	int argc;	char *argv[];{	extern char **environ;	struct addrinfo *ai, hints;	struct group *gr;	struct rlimit cds, scds;	struct stat st;	struct utmp utmp;	quad_t expire, warning;	uid_t uid;	int ask, ch, cnt, fflag, pflag, quietlog, rootlogin, lastchance;	int error, homeless, needto, authok, tries, backoff;	char *domain, *p, *ttyn, *shell, *fullname, *instance;	char *lipaddr, *script, *ripaddr, *style, *type, *fqdn, *copyright;	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];	char localhost[MAXHOSTNAMELEN];	(void)signal(SIGALRM, timedout);	if (argc > 1) {		needto = 0;		(void)alarm(timeout);	} else		needto = 1;	(void)signal(SIGQUIT, SIG_IGN);	(void)signal(SIGINT, SIG_IGN);	(void)signal(SIGHUP, SIG_IGN);	(void)setpriority(PRIO_PROCESS, 0, 0);	openlog("login", LOG_ODELAY, LOG_AUTH);	fqdn = lipaddr = ripaddr = fullname = type = NULL;	authok = 0;	tries = 10;	backoff = 3;	/*	 * Since login deals with sensitive information, turn off coredumps.	 */	if (getrlimit(RLIMIT_CORE, &scds) < 0) {		syslog(LOG_ERR, "couldn't get core dump size: %m");		scds.rlim_cur = scds.rlim_max = QUAD_MIN;	}	cds.rlim_cur = cds.rlim_max = 0;	if (setrlimit(RLIMIT_CORE, &cds) < 0) {		syslog(LOG_ERR, "couldn't set core dump size to 0: %m");		scds.rlim_cur = scds.rlim_max = QUAD_MIN;	}	/*	 * -p is used by getty to tell login not to destroy the environment	 * -f is used to skip a second login authentication	 * -h is used by other servers to pass the name of the remote	 *    host to login so that it may be placed in utmp and wtmp	 */	domain = NULL;	if (gethostname(localhost, sizeof(localhost)) < 0)		syslog(LOG_ERR, "couldn't get local hostname: %m");	else		domain = strchr(localhost, '.');	if (domain) {		domain++;		if (*domain && strchr(domain, '.') == NULL)			domain = localhost;	}	if ((as = auth_open()) == NULL) {		syslog(LOG_ERR, "%m");		err(1, NULL);	}	fflag = pflag = 0;	uid = getuid();	while ((ch = getopt(argc, argv, "fh:pu:L:R:")) != -1)		switch (ch) {		case 'f':			fflag = 1;			break;		case 'h':			if (uid) {				warn("-h option: %s", strerror(EPERM));				quickexit(1);			}			if ((fqdn = strdup(optarg)) == NULL) {				warn(NULL);				quickexit(1);			}			auth_setoption(as, "fqdn", fqdn);			if (domain && (p = strchr(optarg, '.')) &&			    strcasecmp(p+1, domain) == 0)				*p = 0;			hostname = optarg;			auth_setoption(as, "hostname", hostname);			break;		case 'L':			if (uid) {				warnx("-L option: %s", strerror(EPERM));				quickexit(1);			}			if (lipaddr) {				warnx("duplicate -L option");				quickexit(1);			}			lipaddr = optarg;			memset(&hints, 0, sizeof(hints));			hints.ai_family = PF_UNSPEC;			hints.ai_flags = AI_CANONNAME;			error = getaddrinfo(lipaddr, NULL, &hints, &ai);			if (!error) {				strlcpy(localhost, ai->ai_canonname,				    sizeof(localhost));				freeaddrinfo(ai);			} else				strlcpy(localhost, lipaddr, sizeof(localhost));			auth_setoption(as, "local_addr", lipaddr);			break;		case 'p':			pflag = 1;			break;		case 'R':			if (uid) {				warnx("-R option: %s", strerror(EPERM));				quickexit(1);			}			if (ripaddr) {				warnx("duplicate -R option");				quickexit(1);			}			ripaddr = optarg;			auth_setoption(as, "remote_addr", ripaddr);			break;		case 'u':			if (uid) {				warnx("-u option: %s", strerror(EPERM));				quickexit(1);			}			rusername = optarg;			break;		default:			if (!uid)				syslog(LOG_ERR, "invalid flag %c", ch);			(void)fprintf(stderr,			    "usage: login [-fp] [-h hostname] [-L lipaddr] [-R ripaddr] [username]\n");			quickexit(1);		}	argc -= optind;	argv += optind;	if (*argv) {		username = *argv;		ask = 0;	} else		ask = 1;	for (cnt = getdtablesize(); cnt > 2; cnt--)		(void)close(cnt);	ttyn = ttyname(STDIN_FILENO);	if (ttyn == NULL || *ttyn == '\0') {		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);		ttyn = tname;	}	if ((tty = strrchr(ttyn, '/')))		++tty;	else		tty = ttyn;#ifdef notyet	/* XXX - we don't (yet) support per-tty auth stuff */	/* BSDi uses a ttys.conf file but we could just overload /etc/ttys */	/*	 * Classify the attempt.	 * By default we use the value in the ttys file.	 * If there is a classify script we run that as	 *	 *	classify [-f] [username]	 */	if (type = getttyauth(tty))		auth_setoption(as, "auth_type", type);#endif	/* get the default login class */	if ((lc = login_getclass(0)) == NULL) { /* get the default class */ 		warnx("Failure to retrieve default class");		quickexit(1);	}	timeout = (u_int)login_getcapnum(lc, "login-timeout", 300, 300);	if ((script = login_getcapstr(lc, "classify", NULL, NULL)) != NULL) {		unsetenv("AUTH_TYPE");		unsetenv("REMOTE_NAME");		if (script[0] != '/') {			syslog(LOG_ERR, "Invalid classify script: %s", script);			warnx("Classification failure");			quickexit(1);		}		shell = strrchr(script, '/') + 1;		auth_setstate(as, AUTH_OKAY);		auth_call(as, script, shell, 		    fflag ? "-f" : username, fflag ? username : 0, 0);		if (!(auth_getstate(as) & AUTH_ALLOW))			quickexit(1);		auth_setenv(as);		if ((p = getenv("AUTH_TYPE")) != NULL &&		    strncmp(p, "auth-", 5) == 0)			type = p;		if ((p = getenv("REMOTE_NAME")) != NULL)			hostname = p;		/*		 * we may have changed some values, reset them		 */		auth_clroptions(as);		if (type)			auth_setoption(as, "auth_type", type);		if (fqdn)			auth_setoption(as, "fqdn", fqdn);		if (hostname)			auth_setoption(as, "hostname", hostname);		if (lipaddr)			auth_setoption(as, "local_addr", lipaddr);		if (ripaddr)			auth_setoption(as, "remote_addr", ripaddr);	}	/*	 * Request the things like the approval script print things	 * to stdout (in particular, the nologins files)	 */	auth_setitem(as, AUTHV_INTERACTIVE, "True");	for (cnt = 0;; ask = 1) {		/*		 * Clean up our current authentication session.		 * Options are not cleared so we need to clear any		 * we might set below.		 */		auth_clean(as);		auth_clroption(as, "style");		auth_clroption(as, "lastchance");		lastchance = 0;		if (ask) {			fflag = 0;			getloginname();		}		if (needto) {			needto = 0;			alarm(timeout);		}    	    	if ((style = strchr(username, ':')) != NULL)			*style++ = '\0';		if (fullname)			free(fullname);		if (auth_setitem(as, AUTHV_NAME, username) < 0 ||		    (fullname = strdup(username)) == NULL) {			syslog(LOG_ERR, "%m");			warn(NULL);			quickexit(1);		}		rootlogin = 0;		/* XXX - kerb5 uses a '/' not a '.' ??? */		if ((instance = strchr(username, '.')) != NULL) {			if (strncmp(instance, ".root", 5) == 0)				rootlogin = 1;			*instance++ = '\0';		} else			instance = "";		if (strlen(username) > UT_NAMESIZE)			username[UT_NAMESIZE] = '\0';		/*		 * Note if trying multiple user names; log failures for		 * previous user name, but don't bother logging one failure		 * for nonexistent name (mistyped username).		 */		if (failures && strcmp(tbuf, username)) {			if (failures > (pwd ? 0 : 1))				badlogin(tbuf);			failures = 0;		}		(void)strlcpy(tbuf, username, sizeof(tbuf));		if ((pwd = getpwnam(username)) != NULL &&		    auth_setpwd(as, pwd) < 0) {			syslog(LOG_ERR, "%m");			warn(NULL);			quickexit(1);		}		lc = login_getclass(pwd ? pwd->pw_class : NULL);		if (!lc)			goto failed;		style = login_getstyle(lc, style, type);		if (!style)			goto failed;		/*		 * We allow "login-tries" attempts to login but start		 * slowing down after "login-backoff" attempts.		 */		tries = (int)login_getcapnum(lc, "login-tries", 10, 10);		backoff = (int)login_getcapnum(lc, "login-backoff", 3, 3);		/*		 * Turn off the fflag if we have an an invalid user		 * or we are not root and we are trying to change uids.		 */		if (!pwd || (uid && uid != pwd->pw_uid))			fflag = 0;		if (pwd && pwd->pw_uid == 0)			rootlogin = 1;		/*		 * If we do not have the force flag authenticate the user		 */		if (fflag)			authok = AUTH_SECURE;		else {			lastchance =			    login_getcaptime(lc, "password-dead", 0, 0) != 0;			if (lastchance)				auth_setoption(as, "lastchance", "yes");			/*			 * Once we start asking for a password			 *  we want to log a failure on a hup.			 */			signal(SIGHUP, sighup);			auth_verify(as, style, NULL, lc->lc_class, NULL);			authok = auth_getstate(as);			/*			 * If their password expired and it has not been			 * too long since then, give the user one last			 * chance to change their password			 */			if ((authok & AUTH_PWEXPIRED) && lastchance) {				authok = AUTH_OKAY;			} else				lastchance = 0;			if ((authok & AUTH_ALLOW) == 0)				goto failed;			if (auth_setoption(as, "style", style) < 0) {				syslog(LOG_ERR, "%m");				warn(NULL);				quickexit(1);			}		}		/*		 * explicitly reject users without password file entries		 */		if (pwd == 0)			goto failed;		authok &= AUTH_SECURE;		/*		 * If trying to log in as root on an insecure terminal,		 * refuse the login attempt unless the authentication		 * style explicitly says a root login is okay.		 */		if (authok == 0 && pwd && rootlogin && !rootterm(tty))			goto failed;		if (fflag) {			type = 0;			style = "forced";		}		break;failed:		if (authok & AUTH_SILENT)			quickexit(0);		if (rootlogin && !rootterm(tty)) {			warnx("%s login refused on this terminal.",			    fullname);			if (hostname)				syslog(LOG_NOTICE,				    "LOGIN %s REFUSED FROM %s%s%s ON TTY %s",				    fullname, rusername ? rusername : "",				    rusername ? "@" : "", hostname, tty);			else				syslog(LOG_NOTICE,				    "LOGIN %s REFUSED ON TTY %s",				     fullname, tty);		} else {			if (!as || (p = auth_getvalue(as, "errormsg")) == NULL)				p = "Login incorrect";			(void)printf("%s\n", p);		}		failures++;		if (pwd)			log_failedlogin(pwd->pw_uid, hostname, rusername, tty);		/*		 * By default, we allow 10 tries, but after 3 we start		 * backing off to slow down password guessers.		 */		if (++cnt > backoff) {			if (cnt >= tries) {				badlogin(username);				sleepexit(1);			}			sleep((u_int)((cnt - backoff) * tries / 2));		}	}	/* committed to login -- turn off timeout */	(void)alarm(0);	endpwent();	shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);	if (*shell == '\0')		shell = _PATH_BSHELL;	else if (strlen(shell) >= MAXPATHLEN) {		syslog(LOG_ERR, "shell path too long: %s", shell);		warnx("invalid shell");		quickexit(1);	}	/* Destroy environment unless user has requested its preservation. */	if (!pflag) {		if ((environ = calloc(1, sizeof (char *))) == NULL)			err(1, "calloc");	} else {		char **cpp, **cpp2;		for (cpp2 = cpp = environ; *cpp; cpp++) {			if (strncmp(*cpp, "LD_", 3) &&			    strncmp(*cpp, "ENV=", 4) &&			    strncmp(*cpp, "BASH_ENV=", 9) &&			    strncmp(*cpp, "IFS=", 4))				*cpp2++ = *cpp;		}		*cpp2 = 0;	}	/* Note: setusercontext(3) will set PATH */	if (setenv("HOME", pwd->pw_dir, 1) == -1 ||	    setenv("SHELL", shell, 1) == -1) {		warn("unable to setenv()");		quickexit(1);	}	if (term[0] == '\0')		(void)strlcpy(term, stypeof(tty), sizeof(term));	if (setenv("TERM", term, 0) == -1 ||	    setenv("LOGNAME", pwd->pw_name, 1) == -1 ||	    setenv("USER", pwd->pw_name, 1) == -1) {		warn("unable to setenv()");		quickexit(1);	}	if (hostname) {		if (setenv("REMOTEHOST", hostname, 1) == -1) {			warn("unable to setenv()");			quickexit(1);		}	}	if (rusername) {		if (setenv("REMOTEUSER", rusername, 1) == -1) {			warn("unable to setenv()");			quickexit(1);		}	}	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) {		warn("unable to set user context");		quickexit(1);	}	auth_setenv(as);	/* if user not super-user, check for disabled logins */	if (!rootlogin)		auth_checknologin(lc);	setegid(pwd->pw_gid);	seteuid(pwd->pw_uid);	homeless = chdir(pwd->pw_dir);	if (homeless) {		if (login_getcapbool(lc, "requirehome", 0)) {			(void)printf("No home directory %s!\n", pwd->pw_dir);			quickexit(1);		}		if (chdir("/"))			quickexit(0);	}	quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) ||	    login_getcapbool(lc, "hushlogin", 0) ||	    (access(_PATH_HUSHLOGIN, F_OK) == 0));	seteuid(0);	setegid(0);	/* XXX use a saved gid instead? */	if ((p = auth_getvalue(as, "warnmsg")) != NULL)		(void)printf("WARNING: %s\n\n", p);	expire = auth_check_expire(as);	if (expire < 0) {		(void)printf("Sorry -- your account has expired.\n");		quickexit(1);	} else if (expire > 0 && !quietlog) {		warning = login_getcaptime(lc, "expire-warn",		    2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY);		if (expire < warning)			(void)printf("Warning: your account expires on %s",  			    ctime(&pwd->pw_expire));	}	/* Nothing else left to fail -- really log in. */	(void)signal(SIGHUP, SIG_DFL);	memset(&utmp, 0, sizeof(utmp));	(void)time(&utmp.ut_time);	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));	if (hostname)		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));	login(&utmp);	if (!quietlog)		(void)check_failedlogin(pwd->pw_uid);	dolastlog(quietlog);	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);	(void)chown(ttyn, pwd->pw_uid,	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);	/* If fflag is on, assume caller/authenticator has logged root login. */	if (rootlogin && fflag == 0) {		if (hostname)			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s%s%s",			    username, tty, rusername ? rusername : "",			    rusername ? "@" : "", hostname);		else			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);	}	if (!quietlog) {#if 0		(void)printf("%s\n\t%s  %s\n\n",	    "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",		    "The Regents of the University of California. ",		    "All rights reserved.");#endif		if ((copyright =		    login_getcapstr(lc, "copyright", NULL, NULL)) != NULL)			auth_cat(copyright);		motd();		(void)snprintf(tbuf,		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);		if (stat(tbuf, &st) == 0 && st.st_size != 0)			(void)printf("You have %smail.\n",			    (st.st_mtime > st.st_atime) ? "new " : "");	}	(void)signal(SIGALRM, SIG_DFL);	(void)signal(SIGQUIT, SIG_DFL);	(void)signal(SIGHUP, SIG_DFL);	(void)signal(SIGINT, SIG_DFL);	(void)signal(SIGTSTP, SIG_IGN);	tbuf[0] = '-';	(void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ?	    p + 1 : shell, sizeof(tbuf) - 1);	if ((scds.rlim_cur != QUAD_MIN || scds.rlim_max != QUAD_MIN) &&	    setrlimit(RLIMIT_CORE, &scds) < 0)		syslog(LOG_ERR, "couldn't reset core dump size: %m");	if (lastchance)		(void)printf("WARNING: Your password has expired.  You must change your password, now!\n");	if (setusercontext(lc, pwd, pwd->pw_uid,	    LOGIN_SETALL & ~LOGIN_SETPATH) < 0) {		warn("unable to set user context");		quickexit(1);	}	if (homeless) {		(void)printf("No home directory %s!\n", pwd->pw_dir);		(void)printf("Logging in with home = \"/\".\n");		(void)setenv("HOME", "/", 1);	}	if (auth_approval(as, lc, NULL, "login") == 0) {		if (auth_getstate(as) & AUTH_EXPIRED)			(void)printf("Sorry -- your account has expired.\n");		else			(void)printf("approval failure\n");		quickexit(1);	}	/*	 * The last thing we do is discard all of the open file descriptors.	 * Last because the C library may have some open.	 *	 * XXX	 * Assume that stdin, stdout and stderr are 0, 1 and 2, and that	 * STDERR_FILENO is 2.	 */	for (cnt = getdtablesize(); cnt > STDERR_FILENO; cnt--)		(void)close(cnt);	/*	 * Close the authentication session, make sure it is marked	 * as okay so no files are removed.	 */	auth_setstate(as, AUTH_OKAY);	auth_close(as);#ifdef KERBEROS        kgettokens(pwd->pw_dir);#endif	execlp(shell, tbuf, 0);	err(1, "%s", shell);}