Home | History | Annotate | Line # | Download | only in login
login_pam.c revision 1.9
      1 /*     $NetBSD: login_pam.c,v 1.9 2005/09/21 12:24:11 christos Exp $       */
      2 
      3 /*-
      4  * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __COPYRIGHT(
     35 "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
     36 	The Regents of the University of California.  All rights reserved.\n");
     37 #endif /* not lint */
     38 
     39 #ifndef lint
     40 #if 0
     41 static char sccsid[] = "@(#)login.c	8.4 (Berkeley) 4/2/94";
     42 #endif
     43 __RCSID("$NetBSD: login_pam.c,v 1.9 2005/09/21 12:24:11 christos Exp $");
     44 #endif /* not lint */
     45 
     46 /*
     47  * login [ name ]
     48  * login -h hostname	(for telnetd, etc.)
     49  * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
     50  */
     51 
     52 #include <sys/param.h>
     53 #include <sys/stat.h>
     54 #include <sys/time.h>
     55 #include <sys/resource.h>
     56 #include <sys/file.h>
     57 #include <sys/wait.h>
     58 #include <sys/socket.h>
     59 
     60 #include <err.h>
     61 #include <errno.h>
     62 #include <grp.h>
     63 #include <pwd.h>
     64 #include <setjmp.h>
     65 #include <signal.h>
     66 #include <stdio.h>
     67 #include <stdlib.h>
     68 #include <string.h>
     69 #include <syslog.h>
     70 #include <time.h>
     71 #include <ttyent.h>
     72 #include <tzfile.h>
     73 #include <unistd.h>
     74 #include <util.h>
     75 #include <login_cap.h>
     76 #include <vis.h>
     77 
     78 #include <security/pam_appl.h>
     79 #include <security/openpam.h>
     80 
     81 #include "pathnames.h"
     82 
     83 void	 badlogin (char *);
     84 static void	 update_db (int);
     85 void	 getloginname (void);
     86 int	 main (int, char *[]);
     87 void	 motd (char *);
     88 int	 rootterm (char *);
     89 void	 sigint (int);
     90 void	 sleepexit (int);
     91 const	 char *stypeof (const char *);
     92 void	 timedout (int);
     93 void	 decode_ss (const char *);
     94 void	 usage (void);
     95 
     96 static struct pam_conv pamc = { openpam_ttyconv, NULL };
     97 
     98 #define	TTYGRPNAME	"tty"		/* name of group to own ttys */
     99 
    100 #define DEFAULT_BACKOFF 3
    101 #define DEFAULT_RETRIES 10
    102 
    103 /*
    104  * This bounds the time given to login.  Not a define so it can
    105  * be patched on machines where it's too small.
    106  */
    107 u_int	timeout = 300;
    108 
    109 struct	passwd *pwd, pwres;
    110 char	pwbuf[1024];
    111 struct	group grs, *grp;
    112 char	grbuf[1024];
    113 int	failures, have_ss;
    114 char	term[64], *envinit[1], *hostname, *username, *tty, *nested;
    115 struct timeval now;
    116 struct sockaddr_storage ss;
    117 
    118 extern const char copyrightstr[];
    119 
    120 int
    121 main(int argc, char *argv[])
    122 {
    123 	extern char **environ;
    124 	struct stat st;
    125 	int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin;
    126 	int auth_passed;
    127 	int Fflag;
    128 	uid_t uid, saved_uid;
    129 	gid_t saved_gid, saved_gids[NGROUPS_MAX];
    130 	int nsaved_gids;
    131 	char *domain, *p, *ttyn, *pwprompt;
    132 	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
    133 	char localhost[MAXHOSTNAMELEN + 1];
    134 	int need_chpass, require_chpass;
    135 	int login_retries = DEFAULT_RETRIES,
    136 	    login_backoff = DEFAULT_BACKOFF;
    137 	char *shell = NULL;
    138 	login_cap_t *lc = NULL;
    139 	pam_handle_t *pamh = NULL;
    140 	int pam_err;
    141 	void *oint;
    142 	void *oabrt;
    143 	const void *newuser;
    144 	int pam_silent = PAM_SILENT;
    145 	pid_t xpid, pid;
    146 	int status;
    147 	char *saved_term;
    148 	char **pamenv;
    149 
    150 	tbuf[0] = '\0';
    151 	pwprompt = NULL;
    152 	nested = NULL;
    153 	need_chpass = require_chpass = 0;
    154 
    155 	(void)signal(SIGALRM, timedout);
    156 	(void)alarm(timeout);
    157 	(void)signal(SIGQUIT, SIG_IGN);
    158 	(void)signal(SIGINT, SIG_IGN);
    159 	(void)setpriority(PRIO_PROCESS, 0, 0);
    160 
    161 	openlog("login", 0, LOG_AUTH);
    162 
    163 	/*
    164 	 * -p is used by getty to tell login not to destroy the environment
    165 	 * -f is used to skip a second login authentication
    166 	 * -h is used by other servers to pass the name of the remote host to
    167 	 *    login so that it may be placed in utmp/utmpx and wtmp/wtmpx
    168 	 * -a in addition to -h, a server my supply -a to pass the actual
    169 	 *    server address.
    170 	 * -s is used to force use of S/Key or equivalent.
    171 	 */
    172 	domain = NULL;
    173 	if (gethostname(localhost, sizeof(localhost)) < 0)
    174 		syslog(LOG_ERR, "couldn't get local hostname: %m");
    175 	else
    176 		domain = strchr(localhost, '.');
    177 	localhost[sizeof(localhost) - 1] = '\0';
    178 
    179 	Fflag = fflag = hflag = pflag = sflag = 0;
    180 	have_ss = 0;
    181 	uid = getuid();
    182 	while ((ch = getopt(argc, argv, "a:Ffh:ps")) != -1)
    183 		switch (ch) {
    184 		case 'a':
    185 			if (uid) {
    186 				errno = EPERM;
    187 				err(EXIT_FAILURE, "-a option");
    188 			}
    189 			decode_ss(optarg);
    190 #ifdef notdef
    191 			(void)sockaddr_snprintf(optarg,
    192 			    sizeof(struct sockaddr_storage), "%a", (void *)&ss);
    193 #endif
    194 			break;
    195 		case 'F':
    196 			Fflag = 1;
    197 			/* FALLTHROUGH */
    198 		case 'f':
    199 			fflag = 1;
    200 			break;
    201 		case 'h':
    202 			if (uid) {
    203 				errno = EPERM;
    204 				err(EXIT_FAILURE, "-h option");
    205 			}
    206 			hflag = 1;
    207 			if (domain && (p = strchr(optarg, '.')) != NULL &&
    208 			    strcasecmp(p, domain) == 0)
    209 				*p = '\0';
    210 			hostname = optarg;
    211 			break;
    212 		case 'p':
    213 			pflag = 1;
    214 			break;
    215 		case 's':
    216 			sflag = 1;
    217 			break;
    218 		default:
    219 		case '?':
    220 			usage();
    221 			break;
    222 		}
    223 
    224 	setproctitle(NULL);
    225 	argc -= optind;
    226 	argv += optind;
    227 
    228 	if (*argv) {
    229 		username = *argv;
    230 		ask = 0;
    231 	} else
    232 		ask = 1;
    233 
    234 #ifdef F_CLOSEM
    235 	(void)fcntl(3, F_CLOSEM, 0);
    236 #else
    237 	for (cnt = getdtablesize(); cnt > 2; cnt--)
    238 		(void)close(cnt);
    239 #endif
    240 
    241 	ttyn = ttyname(STDIN_FILENO);
    242 	if (ttyn == NULL || *ttyn == '\0') {
    243 		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
    244 		ttyn = tname;
    245 	}
    246 	if ((tty = strstr(ttyn, "/pts/")) != NULL)
    247 		++tty;
    248 	else if ((tty = strrchr(ttyn, '/')) != NULL)
    249 		++tty;
    250 	else
    251 		tty = ttyn;
    252 
    253 	if (issetugid()) {
    254 		nested = strdup(user_from_uid(getuid(), 0));
    255 		if (nested == NULL) {
    256                 	syslog(LOG_ERR, "strdup: %m");
    257                 	sleepexit(EXIT_FAILURE);
    258 		}
    259 	}
    260 
    261 	/* Get "login-retries" and "login-backoff" from default class */
    262 	if ((lc = login_getclass(NULL)) != NULL) {
    263 		login_retries = (int)login_getcapnum(lc, "login-retries",
    264 		    DEFAULT_RETRIES, DEFAULT_RETRIES);
    265 		login_backoff = (int)login_getcapnum(lc, "login-backoff",
    266 		    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
    267 		login_close(lc);
    268 		lc = NULL;
    269 	}
    270 
    271 
    272 	for (cnt = 0;; ask = 1) {
    273 		if (ask) {
    274 			fflag = 0;
    275 			getloginname();
    276 		}
    277 		rootlogin = 0;
    278 		auth_passed = 0;
    279 		if (strlen(username) > MAXLOGNAME)
    280 			username[MAXLOGNAME] = '\0';
    281 
    282 		/*
    283 		 * Note if trying multiple user names; log failures for
    284 		 * previous user name, but don't bother logging one failure
    285 		 * for nonexistent name (mistyped username).
    286 		 */
    287 		if (failures && strcmp(tbuf, username)) {
    288 			if (failures > (pwd ? 0 : 1))
    289 				badlogin(tbuf);
    290 			failures = 0;
    291 		}
    292 
    293 #define PAM_END(msg) do { 						\
    294 	syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); 	\
    295 	warnx("%s: %s", msg, pam_strerror(pamh, pam_err));		\
    296 	pam_end(pamh, pam_err);						\
    297 	sleepexit(EXIT_FAILURE);					\
    298 } while (/*CONSTCOND*/0)
    299 
    300 		pam_err = pam_start("login", username, &pamc, &pamh);
    301 		if (pam_err != PAM_SUCCESS) {
    302 			if (pamh != NULL)
    303 				PAM_END("pam_start");
    304 			/* Things went really bad... */
    305 			syslog(LOG_ERR, "pam_start failed: %s",
    306 			    pam_strerror(pamh, pam_err));
    307 			errx(EXIT_FAILURE, "pam_start failed");
    308 		}
    309 
    310 #define PAM_SET_ITEM(item, var)	do {					\
    311 	pam_err = pam_set_item(pamh, (item), (var));			\
    312 	if (pam_err != PAM_SUCCESS)					\
    313 		PAM_END("pam_set_item(" # item ")");			\
    314 } while (/*CONSTCOND*/0)
    315 
    316 		/*
    317 		 * Fill hostname tty, and nested user
    318 		 */
    319 		PAM_SET_ITEM(PAM_RHOST, hostname);
    320 		PAM_SET_ITEM(PAM_TTY, tty);
    321 		if (nested)
    322 			PAM_SET_ITEM(PAM_NUSER, nested);
    323 		if (have_ss)
    324 			PAM_SET_ITEM(PAM_SOCKADDR, &ss);
    325 
    326 		/*
    327 		 * Don't check for errors, because we don't want to give
    328 		 * out any information.
    329 		 */
    330 		pwd = NULL;
    331 		(void)getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf), &pwd);
    332 
    333 		/*
    334 		 * Establish the class now, before we might goto
    335 		 * within the next block. pwd can be NULL since it
    336 		 * falls back to the "default" class if it is.
    337 		 */
    338 		lc = login_getclass(pwd ? pwd->pw_class : NULL);
    339 
    340 		/*
    341 		 * if we have a valid account name, and it doesn't have a
    342 		 * password, or the -f option was specified and the caller
    343 		 * is root or the caller isn't changing their uid, don't
    344 		 * authenticate.
    345 		 */
    346 		if (pwd) {
    347 			if (pwd->pw_uid == 0)
    348 				rootlogin = 1;
    349 
    350 			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
    351 				/* already authenticated */
    352 				auth_passed = 1;
    353 				goto skip_auth;
    354 			}
    355 		}
    356 
    357 		(void)setpriority(PRIO_PROCESS, 0, -4);
    358 
    359 		switch(pam_err = pam_authenticate(pamh, pam_silent)) {
    360 		case PAM_SUCCESS:
    361 			/*
    362 			 * PAM can change the user, refresh
    363 			 * username, pwd, and lc.
    364 			 */
    365 			pam_err = pam_get_item(pamh, PAM_USER, &newuser);
    366 			if (pam_err != PAM_SUCCESS)
    367 				PAM_END("pam_get_item(PAM_USER)");
    368 
    369 			username = (char *)newuser;
    370 			/*
    371 			 * Don't check for errors, because we don't want to give
    372 			 * out any information.
    373 			 */
    374 			pwd = NULL;
    375 			(void)getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf),
    376 			    &pwd);
    377 			lc = login_getpwclass(pwd);
    378 			auth_passed = 1;
    379 
    380 			switch (pam_err = pam_acct_mgmt(pamh, pam_silent)) {
    381 			case PAM_SUCCESS:
    382 				break;
    383 
    384 			case PAM_NEW_AUTHTOK_REQD:
    385 				pam_err = pam_chauthtok(pamh,
    386 				    pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
    387 
    388 				if (pam_err != PAM_SUCCESS)
    389 					PAM_END("pam_chauthtok");
    390 				break;
    391 
    392 			default:
    393 				PAM_END("pam_acct_mgmt");
    394 				break;
    395 			}
    396 			break;
    397 
    398 		case PAM_AUTH_ERR:
    399 		case PAM_USER_UNKNOWN:
    400 		case PAM_MAXTRIES:
    401 			auth_passed = 0;
    402 			break;
    403 
    404 		default:
    405 			PAM_END("pam_authenticate");
    406 			break;
    407 		}
    408 
    409 		(void)setpriority(PRIO_PROCESS, 0, 0);
    410 
    411 skip_auth:
    412 		/*
    413 		 * If the user exists and authentication passed,
    414 		 * get out of the loop and login the user.
    415 		 */
    416 		if (pwd && auth_passed)
    417 			break;
    418 
    419 		(void)printf("Login incorrect\n");
    420 		failures++;
    421 		cnt++;
    422 		/* we allow 10 tries, but after 3 we start backing off */
    423 		if (cnt > login_backoff) {
    424 			if (cnt >= login_retries) {
    425 				badlogin(username);
    426 				pam_end(pamh, PAM_SUCCESS);
    427 				sleepexit(EXIT_FAILURE);
    428 			}
    429 			sleep((u_int)((cnt - 3) * 5));
    430 		}
    431 	}
    432 
    433 	/* committed to login -- turn off timeout */
    434 	(void)alarm((u_int)0);
    435 
    436 	endpwent();
    437 
    438         quietlog = login_getcapbool(lc, "hushlogin", 0);
    439 
    440 	/*
    441 	 * Temporarily give up special privileges so we can change
    442 	 * into NFS-mounted homes that are exported for non-root
    443 	 * access and have mode 7x0
    444 	 */
    445 	saved_uid = geteuid();
    446 	saved_gid = getegid();
    447 	nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
    448 
    449 	(void)setegid(pwd->pw_gid);
    450 	initgroups(username, pwd->pw_gid);
    451 	(void)seteuid(pwd->pw_uid);
    452 
    453 	if (chdir(pwd->pw_dir) != 0) {
    454                 if (login_getcapbool(lc, "requirehome", 0)) {
    455 			(void)printf("Home directory %s required\n",
    456 			    pwd->pw_dir);
    457 			pam_end(pamh, PAM_SUCCESS);
    458                         exit(EXIT_FAILURE);
    459 		}
    460 
    461 		(void)printf("No home directory %s!\n", pwd->pw_dir);
    462 		if (chdir("/")) {
    463 			pam_end(pamh, PAM_SUCCESS);
    464 			exit(EXIT_SUCCESS);
    465 		}
    466 		pwd->pw_dir = "/";
    467 		(void)printf("Logging in with home = \"/\".\n");
    468 	}
    469 
    470 	if (!quietlog) {
    471 		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
    472 		pam_silent = 0;
    473 	}
    474 
    475 	/* regain special privileges */
    476 	setegid(saved_gid);
    477 	setgroups(nsaved_gids, saved_gids);
    478 	seteuid(saved_uid);
    479 
    480 	(void)getgrnam_r(TTYGRPNAME, &grs, grbuf, sizeof(grbuf), &grp);
    481 	(void)chown(ttyn, pwd->pw_uid,
    482 	    (grp != NULL) ? grp->gr_gid : pwd->pw_gid);
    483 
    484 	if (ttyaction(ttyn, "login", pwd->pw_name))
    485 		(void)printf("Warning: ttyaction failed.\n");
    486 
    487 	/* Nothing else left to fail -- really log in. */
    488         update_db(quietlog);
    489 
    490 	if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
    491 	    LOGIN_SETLOGIN) != 0) {
    492 		syslog(LOG_ERR, "setusercontext failed");
    493 		pam_end(pamh, PAM_SUCCESS);
    494 		exit(EXIT_FAILURE);
    495 	}
    496 
    497 	if (tty[sizeof("tty")-1] == 'd')
    498 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
    499 
    500 	/* If fflag is on, assume caller/authenticator has logged root login. */
    501 	if (rootlogin && fflag == 0) {
    502 		if (hostname)
    503 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
    504 			    username, tty, hostname);
    505 		else
    506 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
    507 			    username, tty);
    508 	}
    509 
    510 	/*
    511 	 * Establish groups
    512 	 */
    513 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
    514 		syslog(LOG_ERR, "setusercontext failed");
    515 		pam_end(pamh, PAM_SUCCESS);
    516 		exit(EXIT_FAILURE);
    517 	}
    518 
    519 	pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
    520 	if (pam_err != PAM_SUCCESS)
    521 		PAM_END("pam_setcred");
    522 
    523 	pam_err = pam_open_session(pamh, pam_silent);
    524 	if (pam_err != PAM_SUCCESS)
    525 		PAM_END("pam_open_session");
    526 
    527 	/*
    528 	 * Fork because we need to call pam_closesession as root.
    529 	 * Make sure signals cannot kill the parent.
    530 	 * This is copied from crontab(8), which has to
    531          * cope with a similar situation.
    532 	 */
    533 	oint = signal(SIGINT, SIG_IGN);
    534 	oabrt = signal(SIGABRT, SIG_IGN);
    535 
    536 	switch(pid = fork()) {
    537 	case -1:
    538 		pam_err = pam_close_session(pamh, 0);
    539 		if (pam_err != PAM_SUCCESS) {
    540 			syslog(LOG_ERR, "pam_close_session: %s",
    541 			    pam_strerror(pamh, pam_err));
    542 			warnx("pam_close_session: %s",
    543 			    pam_strerror(pamh, pam_err));
    544 		}
    545 		syslog(LOG_ERR, "fork failed: %m");
    546 		warn("fork failed");
    547 		pam_end(pamh, pam_err);
    548 		exit(EXIT_FAILURE);
    549 		break;
    550 
    551 	case 0: /* Child */
    552 		break;
    553 
    554 	default:
    555 		/*
    556 		 * Parent: wait for the child to terminate
    557 		 * and call pam_close_session.
    558 		 */
    559 		if ((xpid = waitpid(pid, &status, 0)) != pid) {
    560 			pam_err = pam_close_session(pamh, 0);
    561 			if (pam_err != PAM_SUCCESS) {
    562 				syslog(LOG_ERR,
    563 				    "pam_close_session: %s",
    564 				    pam_strerror(pamh, pam_err));
    565 				warnx("pam_close_session: %s",
    566 				    pam_strerror(pamh, pam_err));
    567 			}
    568 			pam_end(pamh, pam_err);
    569 			if (xpid != -1)
    570 				warnx("wrong PID: %d != %d", pid, xpid);
    571 			else
    572 				warn("wait for pid %d failed", pid);
    573 			exit(EXIT_FAILURE);
    574 		}
    575 
    576 		(void)signal(SIGINT, oint);
    577 		(void)signal(SIGABRT, oabrt);
    578 		if ((pam_err = pam_close_session(pamh, 0)) != PAM_SUCCESS) {
    579 			syslog(LOG_ERR, "pam_close_session: %s",
    580 			    pam_strerror(pamh, pam_err));
    581 			warnx("pam_close_session: %s",
    582 			    pam_strerror(pamh, pam_err));
    583 		}
    584 		pam_end(pamh, PAM_SUCCESS);
    585 		exit(EXIT_SUCCESS);
    586 		break;
    587 	}
    588 
    589 	/*
    590 	 * The child: starting here, we don't have to care about
    591 	 * handling PAM issues if we exit, the parent will do the
    592 	 * job when we exit.
    593          *
    594 	 * Destroy environment unless user has requested its preservation.
    595 	 * Try to preserve TERM anyway.
    596 	 */
    597 	saved_term = getenv("TERM");
    598 	if (!pflag) {
    599 		environ = envinit;
    600 		if (saved_term)
    601 			setenv("TERM", saved_term, 0);
    602 	}
    603 
    604 	if (*pwd->pw_shell == '\0')
    605 		pwd->pw_shell = _PATH_BSHELL;
    606 
    607 	shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
    608 	if (*shell == '\0')
    609 		shell = pwd->pw_shell;
    610 
    611 	if ((pwd->pw_shell = strdup(shell)) == NULL) {
    612 		syslog(LOG_ERR, "Cannot alloc mem");
    613 		exit(EXIT_FAILURE);
    614 	}
    615 
    616 	(void)setenv("HOME", pwd->pw_dir, 1);
    617 	(void)setenv("SHELL", pwd->pw_shell, 1);
    618 	if (term[0] == '\0') {
    619 		char *tt = (char *)stypeof(tty);
    620 
    621 		if (tt == NULL)
    622 			tt = login_getcapstr(lc, "term", NULL, NULL);
    623 
    624 		/* unknown term -> "su" */
    625 		(void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term));
    626 	}
    627 	(void)setenv("TERM", term, 0);
    628 	(void)setenv("LOGNAME", pwd->pw_name, 1);
    629 	(void)setenv("USER", pwd->pw_name, 1);
    630 
    631 	/*
    632 	 * Add PAM environement
    633 	 */
    634 	if ((pamenv = pam_getenvlist(pamh)) != NULL) {
    635 		char **envitem;
    636 
    637 		for (envitem = pamenv; *envitem; envitem++) {
    638 			putenv(*envitem);
    639 			free(*envitem);
    640 		}
    641 
    642 		free(pamenv);
    643 	}
    644 
    645 	/* This drops root privs */
    646 	if (setusercontext(lc, pwd, pwd->pw_uid,
    647 	    (LOGIN_SETALL & ~LOGIN_SETLOGIN)) != 0) {
    648 		syslog(LOG_ERR, "setusercontext failed");
    649 		exit(EXIT_FAILURE);
    650 	}
    651 
    652 	if (!quietlog) {
    653 		char *fname;
    654 
    655 		fname = login_getcapstr(lc, "copyright", NULL, NULL);
    656 		if (fname != NULL && access(fname, F_OK) == 0)
    657 			motd(fname);
    658 		else
    659 			(void)printf("%s", copyrightstr);
    660 
    661                 fname = login_getcapstr(lc, "welcome", NULL, NULL);
    662                 if (fname == NULL || access(fname, F_OK) != 0)
    663                         fname = _PATH_MOTDFILE;
    664                 motd(fname);
    665 
    666 		(void)snprintf(tbuf,
    667 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
    668 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
    669 			(void)printf("You have %smail.\n",
    670 			    (st.st_mtime > st.st_atime) ? "new " : "");
    671 	}
    672 
    673 	login_close(lc);
    674 
    675 	(void)signal(SIGALRM, SIG_DFL);
    676 	(void)signal(SIGQUIT, SIG_DFL);
    677 	(void)signal(SIGINT, SIG_DFL);
    678 	(void)signal(SIGTSTP, SIG_IGN);
    679 
    680 	tbuf[0] = '-';
    681 	(void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
    682 	    p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
    683 
    684 	execlp(pwd->pw_shell, tbuf, NULL);
    685 	err(EXIT_FAILURE, "%s", pwd->pw_shell);
    686 }
    687 
    688 #define	NBUFSIZ		(MAXLOGNAME + 1)
    689 
    690 
    691 void
    692 getloginname(void)
    693 {
    694 	int ch;
    695 	char *p;
    696 	static char nbuf[NBUFSIZ];
    697 
    698 	for (;;) {
    699 		(void)printf("login: ");
    700 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
    701 			if (ch == EOF) {
    702 				badlogin(username);
    703 				exit(EXIT_SUCCESS);
    704 			}
    705 			if (p < nbuf + (NBUFSIZ - 1))
    706 				*p++ = ch;
    707 		}
    708 		if (p > nbuf) {
    709 			if (nbuf[0] == '-')
    710 				(void)fprintf(stderr,
    711 				    "login names may not start with '-'.\n");
    712 			else {
    713 				*p = '\0';
    714 				username = nbuf;
    715 				break;
    716 			}
    717 		}
    718 	}
    719 }
    720 
    721 int
    722 rootterm(char *ttyn)
    723 {
    724 	struct ttyent *t;
    725 
    726 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
    727 }
    728 
    729 jmp_buf motdinterrupt;
    730 
    731 void
    732 motd(char *fname)
    733 {
    734 	int fd, nchars;
    735 	sig_t oldint;
    736 	char tbuf[8192];
    737 
    738 	if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
    739 		return;
    740 	oldint = signal(SIGINT, sigint);
    741 	if (setjmp(motdinterrupt) == 0)
    742 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    743 			(void)write(fileno(stdout), tbuf, nchars);
    744 	(void)signal(SIGINT, oldint);
    745 	(void)close(fd);
    746 }
    747 
    748 /* ARGSUSED */
    749 void
    750 sigint(int signo)
    751 {
    752 
    753 	longjmp(motdinterrupt, 1);
    754 }
    755 
    756 /* ARGSUSED */
    757 void
    758 timedout(int signo)
    759 {
    760 
    761 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
    762 	exit(EXIT_SUCCESS);
    763 }
    764 
    765 static void
    766 update_db(int quietlog)
    767 {
    768 	if (nested != NULL) {
    769 		if (hostname != NULL)
    770 			syslog(LOG_NOTICE, "%s to %s on tty %s from %s",
    771 			    nested, pwd->pw_name, tty, hostname);
    772 		else
    773 			syslog(LOG_NOTICE, "%s to %s on tty %s", nested,
    774 			    pwd->pw_name, tty);
    775 
    776 	}
    777 	if (hostname != NULL && have_ss == 0) {
    778 		socklen_t len = sizeof(ss);
    779 		(void)getpeername(STDIN_FILENO, (struct sockaddr *)&ss, &len);
    780 	}
    781 	(void)gettimeofday(&now, NULL);
    782 }
    783 
    784 void
    785 badlogin(char *name)
    786 {
    787 
    788 	if (failures == 0)
    789 		return;
    790 	if (hostname) {
    791 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
    792 		    failures, failures > 1 ? "S" : "", hostname);
    793 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    794 		    "%d LOGIN FAILURE%s FROM %s, %s",
    795 		    failures, failures > 1 ? "S" : "", hostname, name);
    796 	} else {
    797 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
    798 		    failures, failures > 1 ? "S" : "", tty);
    799 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    800 		    "%d LOGIN FAILURE%s ON %s, %s",
    801 		    failures, failures > 1 ? "S" : "", tty, name);
    802 	}
    803 }
    804 
    805 const char *
    806 stypeof(const char *ttyid)
    807 {
    808 	struct ttyent *t;
    809 
    810 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
    811 }
    812 
    813 void
    814 sleepexit(int eval)
    815 {
    816 
    817 	(void)sleep(5);
    818 	exit(eval);
    819 }
    820 
    821 void
    822 decode_ss(const char *arg)
    823 {
    824 	struct sockaddr_storage *ssp;
    825 	size_t len = strlen(arg);
    826 
    827 	if (len > sizeof(*ssp) * 4 + 1 || len < sizeof(*ssp))
    828 		errx(EXIT_FAILURE, "Bad argument");
    829 
    830 	if ((ssp = malloc(len)) == NULL)
    831 		err(EXIT_FAILURE, NULL);
    832 
    833 	if (strunvis((char *)ssp, arg) != sizeof(*ssp))
    834 		errx(EXIT_FAILURE, "Decoding error");
    835 
    836 	(void)memcpy(&ss, ssp, sizeof(ss));
    837 	have_ss = 1;
    838 }
    839 
    840 void
    841 usage(void)
    842 {
    843 	(void)fprintf(stderr,
    844 	    "Usage: %s [-Ffps] [-a address] [-h hostname] [username]\n",
    845 	    getprogname());
    846 	exit(EXIT_FAILURE);
    847 }
    848