login.c revision 59621
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
3423246Swosch#if 0
351590Srgrimesstatic char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3823246Swosch#endif
391590Srgrimes
401590Srgrimes#ifndef lint
4127605Scharnier#if 0
421590Srgrimesstatic char sccsid[] = "@(#)login.c	8.4 (Berkeley) 4/2/94";
4327605Scharnier#endif
4427605Scharnierstatic const char rcsid[] =
4550477Speter  "$FreeBSD: head/usr.bin/login/login.c 59621 2000-04-25 08:59:51Z sheldonh $";
461590Srgrimes#endif /* not lint */
471590Srgrimes
481590Srgrimes/*
491590Srgrimes * login [ name ]
501590Srgrimes * login -h hostname	(for telnetd, etc.)
511590Srgrimes * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
521590Srgrimes */
531590Srgrimes
5423246Swosch#include <sys/copyright.h>
551590Srgrimes#include <sys/param.h>
561590Srgrimes#include <sys/stat.h>
5757339Sshin#include <sys/socket.h>
581590Srgrimes#include <sys/time.h>
591590Srgrimes#include <sys/resource.h>
601590Srgrimes#include <sys/file.h>
6116423Sache#include <netinet/in.h>
6216423Sache#include <arpa/inet.h>
631590Srgrimes
641590Srgrimes#include <err.h>
651590Srgrimes#include <errno.h>
661590Srgrimes#include <grp.h>
6740102Smarkm#include <libutil.h>
6841079Sjdp#include <login_cap.h>
6916423Sache#include <netdb.h>
701590Srgrimes#include <pwd.h>
711590Srgrimes#include <setjmp.h>
721590Srgrimes#include <signal.h>
731590Srgrimes#include <stdio.h>
741590Srgrimes#include <stdlib.h>
751590Srgrimes#include <string.h>
761590Srgrimes#include <syslog.h>
771590Srgrimes#include <ttyent.h>
781590Srgrimes#include <unistd.h>
791590Srgrimes#include <utmp.h>
801590Srgrimes
8142850Sabial#ifndef NO_PAM
8241279Sjdp#include <security/pam_appl.h>
8341279Sjdp#include <security/pam_misc.h>
8442850Sabial#endif
853702Spst
861590Srgrimes#include "pathnames.h"
871590Srgrimes
8857339Sshin/* wrapper for KAME-special getnameinfo() */
8957339Sshin#ifndef NI_WITHSCOPEID
9057339Sshin#define	NI_WITHSCOPEID	0
9157339Sshin#endif
9257339Sshin
931590Srgrimesvoid	 badlogin __P((char *));
941590Srgrimesvoid	 checknologin __P((void));
951590Srgrimesvoid	 dolastlog __P((int));
961590Srgrimesvoid	 getloginname __P((void));
9721528Sdavidnvoid	 motd __P((char *));
981590Srgrimesint	 rootterm __P((char *));
991590Srgrimesvoid	 sigint __P((int));
1001590Srgrimesvoid	 sleepexit __P((int));
10123985Sdavidnvoid	 refused __P((char *,char *,int));
1021590Srgrimeschar	*stypeof __P((char *));
1031590Srgrimesvoid	 timedout __P((int));
10429922Smarkmint	 login_access __P((char *, char *));
1052224Sguidovoid     login_fbtab __P((char *, uid_t, gid_t));
1061590Srgrimes
10742850Sabial#ifndef NO_PAM
10841279Sjdpstatic int auth_pam __P((void));
10942850Sabial#endif
11041279Sjdpstatic int auth_traditional __P((void));
1111590Srgrimesextern void login __P((struct utmp *));
11227605Scharnierstatic void usage __P((void));
1131590Srgrimes
1141590Srgrimes#define	TTYGRPNAME	"tty"		/* name of group to own ttys */
11523985Sdavidn#define	DEFAULT_BACKOFF	3
11623985Sdavidn#define	DEFAULT_RETRIES	10
1171590Srgrimes
1181590Srgrimes/*
1191590Srgrimes * This bounds the time given to login.  Not a define so it can
1201590Srgrimes * be patched on machines where it's too small.
1211590Srgrimes */
1221590Srgrimesu_int	timeout = 300;
1231590Srgrimes
12442272Seivind/* Buffer for signal handling of timeout */
12542272Seivindjmp_buf timeout_buf;
12642272Seivind
1271590Srgrimesstruct	passwd *pwd;
1281590Srgrimesint	failures;
12923985Sdavidnchar	*term, *envinit[1], *hostname, *username, *tty;
13016423Sachechar    full_hostname[MAXHOSTNAMELEN];
1311590Srgrimes
1321590Srgrimesint
1331590Srgrimesmain(argc, argv)
1341590Srgrimes	int argc;
1351590Srgrimes	char *argv[];
1361590Srgrimes{
1371590Srgrimes	extern char **environ;
1381590Srgrimes	struct group *gr;
1391590Srgrimes	struct stat st;
1401590Srgrimes	struct timeval tp;
1411590Srgrimes	struct utmp utmp;
14223985Sdavidn	int rootok, retries, backoff;
1431590Srgrimes	int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
1444878Sugen	int changepass;
14523985Sdavidn	time_t warntime;
14635559Speter	uid_t uid, euid;
14746007Sache	gid_t egid;
14845431Sbrian	char *p, *ttyn;
14942272Seivind	char tbuf[MAXPATHLEN + 2];
15042272Seivind	char tname[sizeof(_PATH_TTY) + 10];
15123985Sdavidn	char *shell = NULL;
15221528Sdavidn	login_cap_t *lc = NULL;
1531590Srgrimes
15442272Seivind	(void)signal(SIGQUIT, SIG_IGN);
15542272Seivind	(void)signal(SIGINT, SIG_IGN);
15642272Seivind	if (setjmp(timeout_buf)) {
15742272Seivind		if (failures)
15842272Seivind			badlogin(tbuf);
15942272Seivind		(void)fprintf(stderr,
16042272Seivind			      "Login timed out after %d seconds\n", timeout);
16142272Seivind		exit(0);
16242272Seivind	}
1631590Srgrimes	(void)signal(SIGALRM, timedout);
1641590Srgrimes	(void)alarm(timeout);
1651590Srgrimes	(void)setpriority(PRIO_PROCESS, 0, 0);
1661590Srgrimes
1671590Srgrimes	openlog("login", LOG_ODELAY, LOG_AUTH);
1681590Srgrimes
1691590Srgrimes	/*
1701590Srgrimes	 * -p is used by getty to tell login not to destroy the environment
1711590Srgrimes	 * -f is used to skip a second login authentication
1721590Srgrimes	 * -h is used by other servers to pass the name of the remote
1731590Srgrimes	 *    host to login so that it may be placed in utmp and wtmp
1741590Srgrimes	 */
1753205Spst	*full_hostname = '\0';
17623985Sdavidn	term = NULL;
1771590Srgrimes
1781590Srgrimes	fflag = hflag = pflag = 0;
1791590Srgrimes	uid = getuid();
18035557Speter	euid = geteuid();
18146007Sache	egid = getegid();
18224360Simp	while ((ch = getopt(argc, argv, "fh:p")) != -1)
1831590Srgrimes		switch (ch) {
1841590Srgrimes		case 'f':
1851590Srgrimes			fflag = 1;
1861590Srgrimes			break;
1871590Srgrimes		case 'h':
1881590Srgrimes			if (uid)
1891590Srgrimes				errx(1, "-h option: %s", strerror(EPERM));
1901590Srgrimes			hflag = 1;
1913205Spst			strncpy(full_hostname, optarg, sizeof(full_hostname)-1);
19236559Samurai
19345431Sbrian			trimdomain(optarg, UT_HOSTSIZE);
19436559Samurai
19516423Sache			if (strlen(optarg) > UT_HOSTSIZE) {
19657339Sshin				struct addrinfo hints, *res;
19757339Sshin				int ga_err;
19857339Sshin
19957339Sshin				memset(&hints, 0, sizeof(hints));
20057339Sshin				hints.ai_family = AF_UNSPEC;
20157339Sshin				ga_err = getaddrinfo(optarg, NULL, &hints,
20257339Sshin						    &res);
20357339Sshin				if (ga_err == 0) {
20457339Sshin					char hostbuf[MAXHOSTNAMELEN];
20516423Sache
20657339Sshin					getnameinfo(res->ai_addr,
20757339Sshin						    res->ai_addrlen,
20857339Sshin						    hostbuf,
20957339Sshin						    sizeof(hostbuf), NULL, 0,
21057339Sshin						    NI_NUMERICHOST|
21157339Sshin						    NI_WITHSCOPEID);
21257339Sshin					optarg = strdup(hostbuf);
21316423Sache				} else
21416423Sache					optarg = "invalid hostname";
21557339Sshin				if (res != NULL)
21657339Sshin					freeaddrinfo(res);
21716423Sache			}
2181590Srgrimes			hostname = optarg;
2191590Srgrimes			break;
2201590Srgrimes		case 'p':
2211590Srgrimes			pflag = 1;
2221590Srgrimes			break;
2231590Srgrimes		case '?':
2241590Srgrimes		default:
2251590Srgrimes			if (!uid)
2261590Srgrimes				syslog(LOG_ERR, "invalid flag %c", ch);
22727605Scharnier			usage();
2281590Srgrimes		}
2291590Srgrimes	argc -= optind;
2301590Srgrimes	argv += optind;
2311590Srgrimes
2321590Srgrimes	if (*argv) {
2331590Srgrimes		username = *argv;
2341590Srgrimes		ask = 0;
2351590Srgrimes	} else
2361590Srgrimes		ask = 1;
2371590Srgrimes
2381590Srgrimes	for (cnt = getdtablesize(); cnt > 2; cnt--)
2391590Srgrimes		(void)close(cnt);
2401590Srgrimes
2411590Srgrimes	ttyn = ttyname(STDIN_FILENO);
2421590Srgrimes	if (ttyn == NULL || *ttyn == '\0') {
2431590Srgrimes		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
2441590Srgrimes		ttyn = tname;
2451590Srgrimes	}
24623985Sdavidn	if ((tty = strrchr(ttyn, '/')) != NULL)
2471590Srgrimes		++tty;
2481590Srgrimes	else
2491590Srgrimes		tty = ttyn;
2501590Srgrimes
25123985Sdavidn	/*
25223985Sdavidn	 * Get "login-retries" & "login-backoff" from default class
25323985Sdavidn	 */
25423985Sdavidn	lc = login_getclass(NULL);
25523985Sdavidn	retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES);
25623985Sdavidn	backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF);
25723985Sdavidn	login_close(lc);
25823985Sdavidn	lc = NULL;
25921528Sdavidn
2601590Srgrimes	for (cnt = 0;; ask = 1) {
2611590Srgrimes		if (ask) {
2621590Srgrimes			fflag = 0;
2631590Srgrimes			getloginname();
2641590Srgrimes		}
2651590Srgrimes		rootlogin = 0;
26621528Sdavidn		rootok = rootterm(tty); /* Default (auth may change) */
26721528Sdavidn
2681590Srgrimes		if (strlen(username) > UT_NAMESIZE)
2691590Srgrimes			username[UT_NAMESIZE] = '\0';
2701590Srgrimes
2711590Srgrimes		/*
2721590Srgrimes		 * Note if trying multiple user names; log failures for
2731590Srgrimes		 * previous user name, but don't bother logging one failure
2741590Srgrimes		 * for nonexistent name (mistyped username).
2751590Srgrimes		 */
2761590Srgrimes		if (failures && strcmp(tbuf, username)) {
2771590Srgrimes			if (failures > (pwd ? 0 : 1))
2781590Srgrimes				badlogin(tbuf);
2791590Srgrimes		}
28027605Scharnier		(void)strncpy(tbuf, username, sizeof tbuf-1);
28127605Scharnier		tbuf[sizeof tbuf-1] = '\0';
2821590Srgrimes
28341279Sjdp		pwd = getpwnam(username);
28423985Sdavidn
28523985Sdavidn		/*
2861590Srgrimes		 * if we have a valid account name, and it doesn't have a
2871590Srgrimes		 * password, or the -f option was specified and the caller
2881590Srgrimes		 * is root or the caller isn't changing their uid, don't
2891590Srgrimes		 * authenticate.
2901590Srgrimes		 */
29121528Sdavidn		if (pwd != NULL) {
2923205Spst			if (pwd->pw_uid == 0)
2933205Spst				rootlogin = 1;
2943205Spst
29523985Sdavidn			if (fflag && (uid == (uid_t)0 ||
29623985Sdavidn				      uid == (uid_t)pwd->pw_uid)) {
2973205Spst				/* already authenticated */
2983205Spst				break;
2993205Spst			} else if (pwd->pw_passwd[0] == '\0') {
30024321Sdavidn				if (!rootlogin || rootok) {
30124251Sdavidn					/* pretend password okay */
30224251Sdavidn					rval = 0;
30324251Sdavidn					goto ttycheck;
30424251Sdavidn				}
3053205Spst			}
3063205Spst		}
3073205Spst
3081590Srgrimes		fflag = 0;
3091590Srgrimes
3101590Srgrimes		(void)setpriority(PRIO_PROCESS, 0, -4);
3111590Srgrimes
31242850Sabial#ifndef NO_PAM
31341279Sjdp		/*
31441279Sjdp		 * Try to authenticate using PAM.  If a PAM system error
31541279Sjdp		 * occurs, perhaps because of a botched configuration,
31641279Sjdp		 * then fall back to using traditional Unix authentication.
31741279Sjdp		 */
31841279Sjdp		if ((rval = auth_pam()) == -1)
31942850Sabial#endif /* NO_PAM */
32041279Sjdp			rval = auth_traditional();
32123985Sdavidn
32241279Sjdp		(void)setpriority(PRIO_PROCESS, 0, 0);
32323985Sdavidn
32442850Sabial#ifndef NO_PAM
32541279Sjdp		/*
32641279Sjdp		 * PAM authentication may have changed "pwd" to the
32741279Sjdp		 * entry for the template user.  Check again to see if
32841279Sjdp		 * this is a root login after all.
32941279Sjdp		 */
33041279Sjdp		if (pwd != NULL && pwd->pw_uid == 0)
33141279Sjdp			rootlogin = 1;
33242850Sabial#endif /* NO_PAM */
3331590Srgrimes
3343205Spst	ttycheck:
3351590Srgrimes		/*
3361590Srgrimes		 * If trying to log in as root without Kerberos,
3371590Srgrimes		 * but with insecure terminal, refuse the login attempt.
3381590Srgrimes		 */
33924222Sdavidn		if (pwd && !rval) {
34024222Sdavidn			if (rootlogin && !rootok)
34124222Sdavidn				refused(NULL, "NOROOT", 0);
34224222Sdavidn			else	/* valid password & authenticated */
34324222Sdavidn				break;
3441590Srgrimes		}
3451590Srgrimes
3461590Srgrimes		(void)printf("Login incorrect\n");
3471590Srgrimes		failures++;
34823985Sdavidn
34923985Sdavidn		/*
35023985Sdavidn		 * we allow up to 'retry' (10) tries,
35123985Sdavidn		 * but after 'backoff' (3) we start backing off
35223985Sdavidn		 */
35323985Sdavidn		if (++cnt > backoff) {
35423985Sdavidn			if (cnt >= retries) {
3551590Srgrimes				badlogin(username);
3561590Srgrimes				sleepexit(1);
3571590Srgrimes			}
35838374Sjkoshy			sleep((u_int)((cnt - backoff) * 5));
3591590Srgrimes		}
3601590Srgrimes	}
3611590Srgrimes
3621590Srgrimes	/* committed to login -- turn off timeout */
3631590Srgrimes	(void)alarm((u_int)0);
3641590Srgrimes
3651590Srgrimes	endpwent();
3661590Srgrimes
36741279Sjdp	/*
36841279Sjdp	 * Establish the login class.
36941279Sjdp	 */
37041279Sjdp	lc = login_getpwclass(pwd);
37141279Sjdp
3721590Srgrimes	/* if user not super-user, check for disabled logins */
3731590Srgrimes	if (!rootlogin)
37421528Sdavidn		auth_checknologin(lc);
3751590Srgrimes
37621528Sdavidn	quietlog = login_getcapbool(lc, "hushlogin", 0);
37746007Sache	/* Switching needed for NFS with root access disabled */
37846007Sache	(void)setegid(pwd->pw_gid);
37935557Speter	(void)seteuid(rootlogin ? 0 : pwd->pw_uid);
38021528Sdavidn	if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
38126021Spst		if (login_getcapbool(lc, "requirehome", 0))
38223985Sdavidn			refused("Home directory not available", "HOMEDIR", 1);
38324485Sdavidn		if (chdir("/") < 0)
38423985Sdavidn			refused("Cannot find root directory", "ROOTDIR", 1);
38523985Sdavidn		if (!quietlog || *pwd->pw_dir)
38623985Sdavidn			printf("No home directory.\nLogging in with home = \"/\".\n");
38757546Sache		pwd->pw_dir = "/";
3881590Srgrimes	}
38935557Speter	(void)seteuid(euid);
39046007Sache	(void)setegid(egid);
39121528Sdavidn	if (!quietlog)
39221528Sdavidn		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
3931590Srgrimes
3941590Srgrimes	if (pwd->pw_change || pwd->pw_expire)
3951590Srgrimes		(void)gettimeofday(&tp, (struct timezone *)NULL);
3962532Sjkh
39730564Sjoerg#define DEFAULT_WARN  (2L * 7L * 86400L)  /* Two weeks */
39821528Sdavidn
39923985Sdavidn	warntime = login_getcaptime(lc, "warnpassword",
40023985Sdavidn				    DEFAULT_WARN, DEFAULT_WARN);
40123985Sdavidn
4024878Sugen	changepass=0;
40321528Sdavidn	if (pwd->pw_change) {
4041590Srgrimes		if (tp.tv_sec >= pwd->pw_change) {
4051590Srgrimes			(void)printf("Sorry -- your password has expired.\n");
4064878Sugen			changepass=1;
40723985Sdavidn			syslog(LOG_INFO,
40823985Sdavidn			       "%s Password expired - forcing change",
40923985Sdavidn			       pwd->pw_name);
41023985Sdavidn		} else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog)
41123985Sdavidn		    (void)printf("Warning: your password expires on %s",
41223985Sdavidn				 ctime(&pwd->pw_change));
41323985Sdavidn	}
41423985Sdavidn
41523985Sdavidn	warntime = login_getcaptime(lc, "warnexpire",
41623985Sdavidn				    DEFAULT_WARN, DEFAULT_WARN);
41723985Sdavidn
41821528Sdavidn	if (pwd->pw_expire) {
4191590Srgrimes		if (tp.tv_sec >= pwd->pw_expire) {
42023985Sdavidn			refused("Sorry -- your account has expired",
42123985Sdavidn				"EXPIRED", 1);
42223985Sdavidn		} else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
42323985Sdavidn		    (void)printf("Warning: your account expires on %s",
42423985Sdavidn				 ctime(&pwd->pw_expire));
42521528Sdavidn	}
4261590Srgrimes
42721528Sdavidn	if (lc != NULL) {
42821528Sdavidn		if (hostname) {
42957339Sshin			struct addrinfo hints, *res;
43057339Sshin			int ga_err;
43121528Sdavidn
43257339Sshin			memset(&hints, 0, sizeof(hints));
43357339Sshin			hints.ai_family = AF_UNSPEC;
43457339Sshin			ga_err = getaddrinfo(full_hostname, NULL, &hints,
43557339Sshin					     &res);
43657339Sshin			if (ga_err == 0) {
43757339Sshin				char hostbuf[MAXHOSTNAMELEN];
43857339Sshin
43957339Sshin				getnameinfo(res->ai_addr, res->ai_addrlen,
44057339Sshin					    hostbuf, sizeof(hostbuf), NULL, 0,
44157339Sshin					    NI_NUMERICHOST|NI_WITHSCOPEID);
44257339Sshin				optarg = strdup(hostbuf);
44357339Sshin			} else
44421528Sdavidn				optarg = NULL;
44557339Sshin			if (res != NULL)
44657339Sshin				freeaddrinfo(res);
44723985Sdavidn			if (!auth_hostok(lc, full_hostname, optarg))
44823985Sdavidn				refused("Permission denied", "HOST", 1);
44921528Sdavidn		}
45021528Sdavidn
45123985Sdavidn		if (!auth_ttyok(lc, tty))
45223985Sdavidn			refused("Permission denied", "TTY", 1);
45321528Sdavidn
45423985Sdavidn		if (!auth_timeok(lc, time(NULL)))
45523985Sdavidn			refused("Logins not available right now", "TIME", 1);
45621528Sdavidn	}
45723985Sdavidn        shell=login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
45823985Sdavidn	if (*pwd->pw_shell == '\0')
45923985Sdavidn		pwd->pw_shell = _PATH_BSHELL;
46023985Sdavidn	if (*shell == '\0')   /* Not overridden */
46123985Sdavidn		shell = pwd->pw_shell;
46223985Sdavidn	if ((shell = strdup(shell)) == NULL) {
46323985Sdavidn		syslog(LOG_NOTICE, "memory allocation error");
46423985Sdavidn		sleepexit(1);
46523985Sdavidn	}
46621528Sdavidn
46721528Sdavidn#ifdef LOGIN_ACCESS
46823985Sdavidn	if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
46923985Sdavidn		refused("Permission denied", "ACCESS", 1);
47021528Sdavidn#endif /* LOGIN_ACCESS */
47121528Sdavidn
4721590Srgrimes	/* Nothing else left to fail -- really log in. */
4731590Srgrimes	memset((void *)&utmp, 0, sizeof(utmp));
4741590Srgrimes	(void)time(&utmp.ut_time);
4751590Srgrimes	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
4761590Srgrimes	if (hostname)
4771590Srgrimes		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
4781590Srgrimes	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
4791590Srgrimes	login(&utmp);
4801590Srgrimes
4811590Srgrimes	dolastlog(quietlog);
4821590Srgrimes
4832224Sguido	/*
4842224Sguido	 * Set device protections, depending on what terminal the
4852224Sguido	 * user is logged in. This feature is used on Suns to give
4862224Sguido	 * console users better privacy.
4872224Sguido	 */
4882224Sguido	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
4892224Sguido
49050124Simp	/*
49150124Simp	 * Clear flags of the tty.  None should be set, and when the
49250124Simp	 * user sets them otherwise, this can cause the chown to fail.
49350124Simp	 * Since it isn't clear that flags are useful on character
49450124Simp	 * devices, we just clear them.
49550124Simp	 */
49659621Ssheldonh	if (chflags(ttyn, 0) && errno != EOPNOTSUPP)
49750124Simp		syslog(LOG_ERR, "chmod(%s): %m", ttyn);
49850124Simp	if (chown(ttyn, pwd->pw_uid,
49950124Simp	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
50050124Simp		syslog(LOG_ERR, "chmod(%s): %m", ttyn);
5011590Srgrimes
50250124Simp
50323985Sdavidn	/*
50423985Sdavidn	 * Preserve TERM if it happens to be already set.
50523985Sdavidn	 */
50624222Sdavidn	if ((term = getenv("TERM")) != NULL)
50724222Sdavidn		term = strdup(term);
5081590Srgrimes
50923985Sdavidn	/*
51023985Sdavidn	 * Exclude cons/vt/ptys only, assume dialup otherwise
51123985Sdavidn	 * TODO: Make dialup tty determination a library call
51223985Sdavidn	 * for consistency (finger etc.)
51323985Sdavidn	 */
51424894Sdavidn	if (hostname==NULL && isdialuptty(tty))
5151590Srgrimes		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
5161590Srgrimes
5173205Spst#ifdef LOGALL
5183205Spst	/*
5193205Spst	 * Syslog each successful login, so we don't have to watch hundreds
5203205Spst	 * of wtmp or lastlogin files.
5213205Spst	 */
52223985Sdavidn	if (hostname)
52323985Sdavidn		syslog(LOG_INFO, "login from %s on %s as %s",
52423985Sdavidn		       full_hostname, tty, pwd->pw_name);
52523985Sdavidn	else
52623985Sdavidn		syslog(LOG_INFO, "login on %s as %s",
52723985Sdavidn		       tty, pwd->pw_name);
52821528Sdavidn#endif
52921528Sdavidn
53023985Sdavidn	/*
53123985Sdavidn	 * If fflag is on, assume caller/authenticator has logged root login.
53223985Sdavidn	 */
53323985Sdavidn	if (rootlogin && fflag == 0)
53423985Sdavidn	{
53523985Sdavidn		if (hostname)
53623985Sdavidn			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
53723985Sdavidn			       username, tty, full_hostname);
53823985Sdavidn		else
53923985Sdavidn			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
54023985Sdavidn			       username, tty);
54123985Sdavidn	}
54223985Sdavidn
54323985Sdavidn	/*
54423985Sdavidn	 * Destroy environment unless user has requested its preservation.
54523985Sdavidn	 * We need to do this before setusercontext() because that may
54623985Sdavidn	 * set or reset some environment variables.
54723985Sdavidn	 */
54821528Sdavidn	if (!pflag)
54921528Sdavidn		environ = envinit;
55021528Sdavidn
55123985Sdavidn	/*
55223985Sdavidn	 * We don't need to be root anymore, so
55321528Sdavidn	 * set the user and session context
55421528Sdavidn	 */
55541279Sjdp	if (setlogin(username) != 0) {
55641279Sjdp                syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
55741279Sjdp		exit(1);
55841279Sjdp	}
55941279Sjdp	if (setusercontext(lc, pwd, pwd->pw_uid,
56041279Sjdp	    LOGIN_SETALL & ~LOGIN_SETLOGIN) != 0) {
56121528Sdavidn                syslog(LOG_ERR, "setusercontext() failed - exiting");
56221528Sdavidn		exit(1);
5633205Spst	}
56421528Sdavidn
56523148Sache	(void)setenv("SHELL", pwd->pw_shell, 1);
56621528Sdavidn	(void)setenv("HOME", pwd->pw_dir, 1);
56723985Sdavidn	if (term != NULL && *term != '\0')
56821528Sdavidn		(void)setenv("TERM", term, 1);	/* Preset overrides */
56921528Sdavidn	else {
57023985Sdavidn		(void)setenv("TERM", stypeof(tty), 0);	/* Fallback doesn't */
57121528Sdavidn	}
57241279Sjdp	(void)setenv("LOGNAME", username, 1);
57341279Sjdp	(void)setenv("USER", username, 1);
57421528Sdavidn	(void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
57521528Sdavidn
5761590Srgrimes	if (!quietlog) {
57723985Sdavidn		char	*cw;
57823985Sdavidn
57923985Sdavidn		cw = login_getcapstr(lc, "copyright", NULL, NULL);
58021528Sdavidn		if (cw != NULL && access(cw, F_OK) == 0)
58121528Sdavidn			motd(cw);
58221528Sdavidn		else
58323985Sdavidn		    (void)printf("%s\n\t%s %s\n",
58423985Sdavidn	"Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
58523985Sdavidn	"The Regents of the University of California. ",
58623985Sdavidn	"All rights reserved.");
58723985Sdavidn
58823985Sdavidn		(void)printf("\n");
58923985Sdavidn
59021528Sdavidn		cw = login_getcapstr(lc, "welcome", NULL, NULL);
59121528Sdavidn		if (cw == NULL || access(cw, F_OK) != 0)
59221528Sdavidn			cw = _PATH_MOTDFILE;
59321528Sdavidn		motd(cw);
59423985Sdavidn
59521528Sdavidn		cw = getenv("MAIL");	/* $MAIL may have been set by class */
59621528Sdavidn		if (cw != NULL) {
59721528Sdavidn			strncpy(tbuf, cw, sizeof(tbuf));
59821528Sdavidn			tbuf[sizeof(tbuf)-1] = '\0';
59921528Sdavidn		} else
60023985Sdavidn			snprintf(tbuf, sizeof(tbuf), "%s/%s",
60123985Sdavidn				 _PATH_MAILDIR, pwd->pw_name);
6021590Srgrimes		if (stat(tbuf, &st) == 0 && st.st_size != 0)
60323985Sdavidn			(void)printf("You have %smail.\n",
60423985Sdavidn				     (st.st_mtime > st.st_atime) ? "new " : "");
6051590Srgrimes	}
6061590Srgrimes
60721528Sdavidn	login_close(lc);
6083205Spst
6091590Srgrimes	(void)signal(SIGALRM, SIG_DFL);
6101590Srgrimes	(void)signal(SIGQUIT, SIG_DFL);
6111590Srgrimes	(void)signal(SIGINT, SIG_DFL);
6121590Srgrimes	(void)signal(SIGTSTP, SIG_IGN);
6131590Srgrimes
6144878Sugen	if (changepass) {
61521528Sdavidn		if (system(_PATH_CHPASS) != 0)
6164878Sugen			sleepexit(1);
6174878Sugen	}
6184878Sugen
61923985Sdavidn	/*
62023985Sdavidn	 * Login shells have a leading '-' in front of argv[0]
62123985Sdavidn	 */
62223985Sdavidn	tbuf[0] = '-';
62323985Sdavidn	(void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell);
62423985Sdavidn
62521528Sdavidn	execlp(shell, tbuf, 0);
62621528Sdavidn	err(1, "%s", shell);
6271590Srgrimes}
6281590Srgrimes
62941279Sjdpstatic int
63041279Sjdpauth_traditional()
63141279Sjdp{
63241279Sjdp	int rval;
63341279Sjdp	char *p;
63441279Sjdp	char *ep;
63541279Sjdp	char *salt;
63641279Sjdp
63741279Sjdp	rval = 1;
63841279Sjdp	salt = pwd != NULL ? pwd->pw_passwd : "xx";
63941279Sjdp
64041279Sjdp	p = getpass("Password:");
64141279Sjdp	ep = crypt(p, salt);
64241279Sjdp
64341279Sjdp	if (pwd) {
64441279Sjdp		if (!p[0] && pwd->pw_passwd[0])
64541279Sjdp			ep = ":";
64641279Sjdp		if (strcmp(ep, pwd->pw_passwd) == 0)
64741279Sjdp			rval = 0;
64841279Sjdp	}
64941279Sjdp
65041279Sjdp	/* clear entered password */
65141279Sjdp	memset(p, 0, strlen(p));
65241279Sjdp	return rval;
65341279Sjdp}
65441279Sjdp
65542850Sabial#ifndef NO_PAM
65641279Sjdp/*
65741279Sjdp * Attempt to authenticate the user using PAM.  Returns 0 if the user is
65841279Sjdp * authenticated, or 1 if not authenticated.  If some sort of PAM system
65941279Sjdp * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
66041279Sjdp * function returns -1.  This can be used as an indication that we should
66141279Sjdp * fall back to a different authentication mechanism.
66241279Sjdp */
66341279Sjdpstatic int
66441279Sjdpauth_pam()
66541279Sjdp{
66641279Sjdp	pam_handle_t *pamh = NULL;
66741279Sjdp	const char *tmpl_user;
66841279Sjdp	const void *item;
66941279Sjdp	int rval;
67041279Sjdp	int e;
67141279Sjdp	static struct pam_conv conv = { misc_conv, NULL };
67241279Sjdp
67341279Sjdp	if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) {
67441279Sjdp		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
67541279Sjdp		return -1;
67641279Sjdp	}
67741279Sjdp	if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
67841279Sjdp		syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
67941279Sjdp		    pam_strerror(pamh, e));
68041279Sjdp		return -1;
68141279Sjdp	}
68241279Sjdp	if (hostname != NULL &&
68341279Sjdp	    (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
68441279Sjdp		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
68541279Sjdp		    pam_strerror(pamh, e));
68641279Sjdp		return -1;
68741279Sjdp	}
68841279Sjdp	e = pam_authenticate(pamh, 0);
68941279Sjdp	switch (e) {
69041279Sjdp
69141279Sjdp	case PAM_SUCCESS:
69241279Sjdp		/*
69341279Sjdp		 * With PAM we support the concept of a "template"
69441279Sjdp		 * user.  The user enters a login name which is
69541279Sjdp		 * authenticated by PAM, usually via a remote service
69641279Sjdp		 * such as RADIUS or TACACS+.  If authentication
69741279Sjdp		 * succeeds, a different but related "template" name
69841279Sjdp		 * is used for setting the credentials, shell, and
69941279Sjdp		 * home directory.  The name the user enters need only
70041279Sjdp		 * exist on the remote authentication server, but the
70141279Sjdp		 * template name must be present in the local password
70241279Sjdp		 * database.
70341279Sjdp		 *
70441279Sjdp		 * This is supported by two various mechanisms in the
70541279Sjdp		 * individual modules.  However, from the application's
70641279Sjdp		 * point of view, the template user is always passed
70741279Sjdp		 * back as a changed value of the PAM_USER item.
70841279Sjdp		 */
70941279Sjdp		if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
71041279Sjdp		    PAM_SUCCESS) {
71141279Sjdp			tmpl_user = (const char *) item;
71241279Sjdp			if (strcmp(username, tmpl_user) != 0)
71341279Sjdp				pwd = getpwnam(tmpl_user);
71441279Sjdp		} else
71541279Sjdp			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
71641279Sjdp			    pam_strerror(pamh, e));
71741279Sjdp		rval = 0;
71841279Sjdp		break;
71941279Sjdp
72041279Sjdp	case PAM_AUTH_ERR:
72141279Sjdp	case PAM_USER_UNKNOWN:
72241279Sjdp	case PAM_MAXTRIES:
72341279Sjdp		rval = 1;
72441279Sjdp		break;
72541279Sjdp
72641279Sjdp	default:
72741279Sjdp		syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e));
72841279Sjdp		rval = -1;
72941279Sjdp		break;
73041279Sjdp	}
73141279Sjdp	if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
73241279Sjdp		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
73341279Sjdp		rval = -1;
73441279Sjdp	}
73541279Sjdp	return rval;
73641279Sjdp}
73742850Sabial#endif /* NO_PAM */
73841279Sjdp
73927605Scharnierstatic void
74027605Scharnierusage()
74127605Scharnier{
74227605Scharnier	(void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
74327605Scharnier	exit(1);
74427605Scharnier}
7451590Srgrimes
74623985Sdavidn/*
74723985Sdavidn * Allow for authentication style and/or kerberos instance
74823985Sdavidn * */
74921528Sdavidn
75021528Sdavidn#define	NBUFSIZ		UT_NAMESIZE + 64
75121528Sdavidn
7521590Srgrimesvoid
7531590Srgrimesgetloginname()
7541590Srgrimes{
7551590Srgrimes	int ch;
7561590Srgrimes	char *p;
7571590Srgrimes	static char nbuf[NBUFSIZ];
7581590Srgrimes
7591590Srgrimes	for (;;) {
7601590Srgrimes		(void)printf("login: ");
7611590Srgrimes		for (p = nbuf; (ch = getchar()) != '\n'; ) {
7621590Srgrimes			if (ch == EOF) {
7631590Srgrimes				badlogin(username);
7641590Srgrimes				exit(0);
7651590Srgrimes			}
7661590Srgrimes			if (p < nbuf + (NBUFSIZ - 1))
7671590Srgrimes				*p++ = ch;
7681590Srgrimes		}
7691590Srgrimes		if (p > nbuf)
7701590Srgrimes			if (nbuf[0] == '-')
7711590Srgrimes				(void)fprintf(stderr,
7721590Srgrimes				    "login names may not start with '-'.\n");
7731590Srgrimes			else {
7741590Srgrimes				*p = '\0';
7751590Srgrimes				username = nbuf;
7761590Srgrimes				break;
7771590Srgrimes			}
7781590Srgrimes	}
7791590Srgrimes}
7801590Srgrimes
7811590Srgrimesint
7821590Srgrimesrootterm(ttyn)
7831590Srgrimes	char *ttyn;
7841590Srgrimes{
7851590Srgrimes	struct ttyent *t;
78623985Sdavidn
7871590Srgrimes	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
7881590Srgrimes}
7891590Srgrimes
79021528Sdavidnvolatile int motdinterrupt;
7911590Srgrimes
79221528Sdavidn/* ARGSUSED */
7931590Srgrimesvoid
79421528Sdavidnsigint(signo)
79521528Sdavidn	int signo;
7961590Srgrimes{
79721528Sdavidn	motdinterrupt = 1;
79821528Sdavidn}
79921528Sdavidn
80021528Sdavidnvoid
80121528Sdavidnmotd(motdfile)
80221528Sdavidn	char *motdfile;
80321528Sdavidn{
8041590Srgrimes	int fd, nchars;
8051590Srgrimes	sig_t oldint;
80621528Sdavidn	char tbuf[256];
8071590Srgrimes
80821528Sdavidn	if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
8091590Srgrimes		return;
81021528Sdavidn	motdinterrupt = 0;
8111590Srgrimes	oldint = signal(SIGINT, sigint);
81221528Sdavidn	while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
81321528Sdavidn		(void)write(fileno(stdout), tbuf, nchars);
8141590Srgrimes	(void)signal(SIGINT, oldint);
8151590Srgrimes	(void)close(fd);
8161590Srgrimes}
8171590Srgrimes
8181590Srgrimes/* ARGSUSED */
8191590Srgrimesvoid
8201590Srgrimestimedout(signo)
8211590Srgrimes	int signo;
8221590Srgrimes{
82342272Seivind	longjmp(timeout_buf, signo);
8241590Srgrimes}
8251590Srgrimes
8261590Srgrimes
8271590Srgrimesvoid
8281590Srgrimesdolastlog(quiet)
8291590Srgrimes	int quiet;
8301590Srgrimes{
8311590Srgrimes	struct lastlog ll;
8321590Srgrimes	int fd;
8331590Srgrimes
8341590Srgrimes	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
8351590Srgrimes		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
8361590Srgrimes		if (!quiet) {
8371590Srgrimes			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
8381590Srgrimes			    ll.ll_time != 0) {
8391590Srgrimes				(void)printf("Last login: %.*s ",
8401590Srgrimes				    24-5, (char *)ctime(&ll.ll_time));
8411590Srgrimes				if (*ll.ll_host != '\0')
8421590Srgrimes					(void)printf("from %.*s\n",
8431590Srgrimes					    (int)sizeof(ll.ll_host),
8441590Srgrimes					    ll.ll_host);
8451590Srgrimes				else
8461590Srgrimes					(void)printf("on %.*s\n",
8471590Srgrimes					    (int)sizeof(ll.ll_line),
8481590Srgrimes					    ll.ll_line);
8491590Srgrimes			}
8501590Srgrimes			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
8511590Srgrimes		}
8521590Srgrimes		memset((void *)&ll, 0, sizeof(ll));
8531590Srgrimes		(void)time(&ll.ll_time);
8541590Srgrimes		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
8551590Srgrimes		if (hostname)
8561590Srgrimes			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
8571590Srgrimes		(void)write(fd, (char *)&ll, sizeof(ll));
8581590Srgrimes		(void)close(fd);
8591590Srgrimes	}
8601590Srgrimes}
8611590Srgrimes
8621590Srgrimesvoid
8631590Srgrimesbadlogin(name)
8641590Srgrimes	char *name;
8651590Srgrimes{
8661590Srgrimes
8671590Srgrimes	if (failures == 0)
8681590Srgrimes		return;
8691590Srgrimes	if (hostname) {
8701590Srgrimes		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
87116423Sache		    failures, failures > 1 ? "S" : "", full_hostname);
8721590Srgrimes		syslog(LOG_AUTHPRIV|LOG_NOTICE,
8731590Srgrimes		    "%d LOGIN FAILURE%s FROM %s, %s",
87416423Sache		    failures, failures > 1 ? "S" : "", full_hostname, name);
8751590Srgrimes	} else {
8761590Srgrimes		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
8771590Srgrimes		    failures, failures > 1 ? "S" : "", tty);
8781590Srgrimes		syslog(LOG_AUTHPRIV|LOG_NOTICE,
8791590Srgrimes		    "%d LOGIN FAILURE%s ON %s, %s",
8801590Srgrimes		    failures, failures > 1 ? "S" : "", tty, name);
8811590Srgrimes	}
88242272Seivind	failures = 0;
8831590Srgrimes}
8841590Srgrimes
8851590Srgrimes#undef	UNKNOWN
8861590Srgrimes#define	UNKNOWN	"su"
8871590Srgrimes
8881590Srgrimeschar *
8891590Srgrimesstypeof(ttyid)
8901590Srgrimes	char *ttyid;
8911590Srgrimes{
89223985Sdavidn
8931590Srgrimes	struct ttyent *t;
89423985Sdavidn
8951590Srgrimes	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
8961590Srgrimes}
8971590Srgrimes
8981590Srgrimesvoid
89923985Sdavidnrefused(msg, rtype, lout)
90023985Sdavidn	char *msg;
90123985Sdavidn	char *rtype;
90223985Sdavidn	int lout;
90323985Sdavidn{
90423985Sdavidn
90523985Sdavidn	if (msg != NULL)
90623985Sdavidn	    printf("%s.\n", msg);
90723985Sdavidn	if (hostname)
90823985Sdavidn		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
90923985Sdavidn		       pwd->pw_name, rtype, full_hostname, tty);
91023985Sdavidn	else
91123985Sdavidn		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
91223985Sdavidn		       pwd->pw_name, rtype, tty);
91323985Sdavidn	if (lout)
91423985Sdavidn		sleepexit(1);
91523985Sdavidn}
91623985Sdavidn
91723985Sdavidnvoid
9181590Srgrimessleepexit(eval)
9191590Srgrimes	int eval;
9201590Srgrimes{
92123985Sdavidn
9221590Srgrimes	(void)sleep(5);
9231590Srgrimes	exit(eval);
9241590Srgrimes}
925