1/*	$OpenBSD: login.c,v 1.74 2023/03/08 04:43:11 guenther Exp $	*/
2/*	$NetBSD: login.c,v 1.13 1996/05/15 23:50:16 jtc Exp $	*/
3
4/*-
5 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32/*-
33 * Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 *    must display the following acknowledgement:
45 *      This product includes software developed by Berkeley Software Design,
46 *      Inc.
47 * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
48 *    or promote products derived from this software without specific prior
49 *    written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 *
63 *	BSDI $From: login.c,v 2.28 1999/09/08 22:35:36 prb Exp $
64 */
65
66/*
67 * login [ name ]
68 * login -h hostname	(for telnetd, etc.)
69 * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
70 * login -p		(preserve existing environment; for getty)
71 */
72
73#include <sys/socket.h>
74#include <sys/stat.h>
75#include <sys/time.h>
76#include <sys/resource.h>
77#include <sys/wait.h>
78
79#include <err.h>
80#include <errno.h>
81#include <fcntl.h>
82#include <grp.h>
83#include <login_cap.h>
84#include <netdb.h>
85#include <pwd.h>
86#include <signal.h>
87#include <stdarg.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <syslog.h>
92#include <ttyent.h>
93#include <unistd.h>
94#include <limits.h>
95#include <utmp.h>
96#include <util.h>
97#include <bsd_auth.h>
98
99#include "pathnames.h"
100
101void	 badlogin(char *);
102void	 dolastlog(int);
103void	 getloginname(void);
104void	 motd(void);
105void	 quickexit(int);
106int	 rootterm(char *);
107void	 sigint(int);
108void	 sighup(int);
109void	 sleepexit(int);
110char	*stypeof(char *);
111void	 timedout(int);
112int	 main(int, char **);
113
114extern int check_failedlogin(uid_t);
115extern void log_failedlogin(uid_t, char *, char *, char *);
116
117#define	TTYGRPNAME	"tty"		/* name of group to own ttys */
118
119#define	SECSPERDAY	(24 * 60 * 60)
120#define	TWOWEEKS	(2 * 7 * SECSPERDAY)
121
122/*
123 * This bounds the time given to login; may be overridden by /etc/login.conf.
124 */
125u_int		timeout = 300;
126
127struct passwd	*pwd;
128login_cap_t	*lc = NULL;
129auth_session_t	*as = NULL;
130int		failures;
131int		needbanner = 1;
132char		term[64], *hostname, *tty;
133char		*style;
134char		*username = NULL, *rusername = NULL;
135
136extern char **environ;
137
138int
139main(int argc, char *argv[])
140{
141	char *domain, *p, *ttyn, *shell, *fullname, *instance;
142	char *lipaddr, *script, *ripaddr, *style, *type, *fqdn;
143	char tbuf[PATH_MAX + 2], tname[sizeof(_PATH_TTY) + 10];
144	char localhost[HOST_NAME_MAX+1], *copyright;
145	char mail[sizeof(_PATH_MAILDIR) + 1 + NAME_MAX];
146	int ask, ch, cnt, fflag, pflag, quietlog, rootlogin, lastchance;
147	int error, homeless, needto, authok, tries, backoff;
148	struct addrinfo *ai, hints;
149	struct rlimit cds, scds;
150	quad_t expire, warning;
151	struct utmp utmp;
152	struct group *gr;
153	struct stat st;
154	uid_t uid;
155
156	openlog("login", LOG_ODELAY, LOG_AUTH);
157
158	fqdn = lipaddr = ripaddr = fullname = type = NULL;
159	authok = 0;
160	tries = 10;
161	backoff = 3;
162
163	domain = NULL;
164	if (gethostname(localhost, sizeof(localhost)) == -1) {
165		syslog(LOG_ERR, "couldn't get local hostname: %m");
166		strlcpy(localhost, "localhost", sizeof(localhost));
167	} else if ((domain = strchr(localhost, '.'))) {
168		domain++;
169		if (*domain && strchr(domain, '.') == NULL)
170			domain = localhost;
171	}
172
173	if ((as = auth_open()) == NULL) {
174		syslog(LOG_ERR, "auth_open: %m");
175		err(1, "unable to initialize BSD authentication");
176	}
177	auth_setoption(as, "login", "yes");
178
179	/*
180	 * -p is used by getty to tell login not to destroy the environment
181	 * -f is used to skip a second login authentication
182	 * -h is used by other servers to pass the name of the remote
183	 *    host to login so that it may be placed in utmp and wtmp
184	 */
185	fflag = pflag = 0;
186	uid = getuid();
187	while ((ch = getopt(argc, argv, "fh:pu:L:R:")) != -1)
188		switch (ch) {
189		case 'f':
190			fflag = 1;
191			break;
192		case 'h':
193			if (uid) {
194				warnc(EPERM, "-h option");
195				quickexit(1);
196			}
197			free(fqdn);
198			if ((fqdn = strdup(optarg)) == NULL) {
199				warn(NULL);
200				quickexit(1);
201			}
202			auth_setoption(as, "fqdn", fqdn);
203			if (domain && (p = strchr(optarg, '.')) &&
204			    strcasecmp(p+1, domain) == 0)
205				*p = 0;
206			hostname = optarg;
207			auth_setoption(as, "hostname", hostname);
208			break;
209		case 'L':
210			if (uid) {
211				warnc(EPERM, "-L option");
212				quickexit(1);
213			}
214			if (lipaddr) {
215				warnx("duplicate -L option");
216				quickexit(1);
217			}
218			lipaddr = optarg;
219			memset(&hints, 0, sizeof(hints));
220			hints.ai_family = PF_UNSPEC;
221			hints.ai_flags = AI_CANONNAME;
222			error = getaddrinfo(lipaddr, NULL, &hints, &ai);
223			if (!error) {
224				strlcpy(localhost, ai->ai_canonname,
225				    sizeof(localhost));
226				freeaddrinfo(ai);
227			} else
228				strlcpy(localhost, lipaddr, sizeof(localhost));
229			auth_setoption(as, "local_addr", lipaddr);
230			break;
231		case 'p':
232			pflag = 1;
233			break;
234		case 'R':
235			if (uid) {
236				warnc(EPERM, "-R option");
237				quickexit(1);
238			}
239			if (ripaddr) {
240				warnx("duplicate -R option");
241				quickexit(1);
242			}
243			ripaddr = optarg;
244			auth_setoption(as, "remote_addr", ripaddr);
245			break;
246		case 'u':
247			if (uid) {
248				warnc(EPERM, "-u option");
249				quickexit(1);
250			}
251			rusername = optarg;
252			break;
253		default:
254			if (!uid)
255				syslog(LOG_ERR, "invalid flag %c", ch);
256			(void)fprintf(stderr,
257			    "usage: login [-fp] [-h hostname] [-L local-addr] "
258			    "[-R remote-addr] [-u username]\n\t[user]\n");
259			quickexit(1);
260		}
261	argc -= optind;
262	argv += optind;
263
264	if (*argv) {
265		username = *argv;
266		ask = 0;
267	} else
268		ask = 1;
269
270	/*
271	 * If effective user is not root, just run su(1) to emulate login(1).
272	 */
273	if (geteuid() != 0) {
274		char *av[5], **ap;
275
276		auth_close(as);
277		closelog();
278		closefrom(STDERR_FILENO + 1);
279
280		ap = av;
281		*ap++ = _PATH_SU;
282		*ap++ = "-L";
283		if (!pflag)
284			*ap++ = "-l";
285		if (!ask)
286			*ap++ = username;
287		*ap = NULL;
288		execv(_PATH_SU, av);
289		warn("unable to exec %s", _PATH_SU);
290		_exit(1);
291	}
292
293	ttyn = ttyname(STDIN_FILENO);
294	if (ttyn == NULL || *ttyn == '\0') {
295		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
296		ttyn = tname;
297	}
298	if ((tty = strrchr(ttyn, '/')))
299		++tty;
300	else
301		tty = ttyn;
302
303	/*
304	 * Since login deals with sensitive information, turn off coredumps.
305	 */
306	if (getrlimit(RLIMIT_CORE, &scds) == -1) {
307		syslog(LOG_ERR, "couldn't get core dump size: %m");
308		scds.rlim_cur = scds.rlim_max = QUAD_MIN;
309	}
310	cds.rlim_cur = cds.rlim_max = 0;
311	if (setrlimit(RLIMIT_CORE, &cds) == -1) {
312		syslog(LOG_ERR, "couldn't set core dump size to 0: %m");
313		scds.rlim_cur = scds.rlim_max = QUAD_MIN;
314	}
315
316	(void)signal(SIGALRM, timedout);
317	if (argc > 1) {
318		needto = 0;
319		(void)alarm(timeout);
320	} else
321		needto = 1;
322	(void)signal(SIGQUIT, SIG_IGN);
323	(void)signal(SIGINT, SIG_IGN);
324	(void)signal(SIGHUP, SIG_IGN);
325	(void)setpriority(PRIO_PROCESS, 0, 0);
326
327	/* get the default login class */
328	if ((lc = login_getclass(0)) == NULL) { /* get the default class */
329		warnx("Failure to retrieve default class");
330		quickexit(1);
331	}
332	timeout = (u_int)login_getcapnum(lc, "login-timeout", 300, 300);
333	if ((script = login_getcapstr(lc, "classify", NULL, NULL)) != NULL) {
334		unsetenv("AUTH_TYPE");
335		unsetenv("REMOTE_NAME");
336		if (script[0] != '/') {
337			syslog(LOG_ERR, "Invalid classify script: %s", script);
338			warnx("Classification failure");
339			quickexit(1);
340		}
341		shell = strrchr(script, '/') + 1;
342		auth_setstate(as, AUTH_OKAY);
343		if (fflag) {
344			auth_call(as, script, shell, "-f", "--", username,
345			    (char *)NULL);
346		} else {
347			auth_call(as, script, shell, "--", username,
348			    (char *)NULL);
349		}
350		if (!(auth_getstate(as) & AUTH_ALLOW))
351			quickexit(1);
352		auth_setenv(as);
353		if ((p = getenv("AUTH_TYPE")) != NULL &&
354		    strncmp(p, "auth-", 5) == 0)
355			type = p;
356		if ((p = getenv("REMOTE_NAME")) != NULL)
357			hostname = p;
358		/*
359		 * we may have changed some values, reset them
360		 */
361		auth_clroptions(as);
362		if (type)
363			auth_setoption(as, "auth_type", type);
364		if (fqdn)
365			auth_setoption(as, "fqdn", fqdn);
366		if (hostname)
367			auth_setoption(as, "hostname", hostname);
368		if (lipaddr)
369			auth_setoption(as, "local_addr", lipaddr);
370		if (ripaddr)
371			auth_setoption(as, "remote_addr", ripaddr);
372	}
373
374	/*
375	 * Request that things like the approval script print things
376	 * to stdout (in particular, the nologins files)
377	 */
378	auth_setitem(as, AUTHV_INTERACTIVE, "True");
379
380	for (cnt = 0;; ask = 1) {
381		/*
382		 * Clean up our current authentication session.
383		 * Options are not cleared so we need to clear any
384		 * we might set below.
385		 */
386		auth_clean(as);
387		auth_clroption(as, "style");
388		auth_clroption(as, "lastchance");
389
390		lastchance = 0;
391
392		if (ask) {
393			fflag = 0;
394			getloginname();
395		}
396		if (needto) {
397			needto = 0;
398			alarm(timeout);
399		}
400		if ((style = strchr(username, ':')) != NULL)
401			*style++ = '\0';
402		free(fullname);
403		if (auth_setitem(as, AUTHV_NAME, username) < 0 ||
404		    (fullname = strdup(username)) == NULL) {
405			syslog(LOG_ERR, "%m");
406			warn(NULL);
407			quickexit(1);
408		}
409		rootlogin = 0;
410		if ((instance = strchr(username, '/')) != NULL) {
411			if (strncmp(instance + 1, "root", 4) == 0)
412				rootlogin = 1;
413			*instance = '\0';
414		}
415
416		if (strlen(username) > UT_NAMESIZE)
417			username[UT_NAMESIZE] = '\0';
418
419		/*
420		 * Note if trying multiple user names; log failures for
421		 * previous user name, but don't bother logging one failure
422		 * for nonexistent name (mistyped username).
423		 */
424		if (failures && strcmp(tbuf, username)) {
425			if (failures > (pwd ? 0 : 1))
426				badlogin(tbuf);
427			failures = 0;
428		}
429		(void)strlcpy(tbuf, username, sizeof(tbuf));
430
431		if ((pwd = getpwnam(username)) != NULL &&
432		    auth_setpwd(as, pwd) < 0) {
433			syslog(LOG_ERR, "%m");
434			warn(NULL);
435			quickexit(1);
436		}
437
438		lc = login_getclass(pwd ? pwd->pw_class : NULL);
439		if (!lc)
440			goto failed;
441
442		style = login_getstyle(lc, style, type);
443		if (!style)
444			goto failed;
445
446		/*
447		 * We allow "login-tries" attempts to login but start
448		 * slowing down after "login-backoff" attempts.
449		 */
450		tries = (int)login_getcapnum(lc, "login-tries", 10, 10);
451		backoff = (int)login_getcapnum(lc, "login-backoff", 3, 3);
452
453		/*
454		 * Turn off the fflag if we have an invalid user
455		 * or we are not root and we are trying to change uids.
456		 */
457		if (!pwd || (uid && uid != pwd->pw_uid))
458			fflag = 0;
459
460		if (pwd && pwd->pw_uid == 0)
461			rootlogin = 1;
462
463		/*
464		 * If we do not have the force flag authenticate the user
465		 */
466		if (!fflag) {
467			lastchance =
468			    login_getcaptime(lc, "password-dead", 0, 0) != 0;
469			if (lastchance)
470				auth_setoption(as, "lastchance", "yes");
471			/*
472			 * Once we start asking for a password
473			 *  we want to log a failure on a hup.
474			 */
475			signal(SIGHUP, sighup);
476			auth_verify(as, style, NULL, lc->lc_class, NULL);
477			authok = auth_getstate(as);
478			/*
479			 * If their password expired and it has not been
480			 * too long since then, give the user one last
481			 * chance to change their password
482			 */
483			if ((authok & AUTH_PWEXPIRED) && lastchance) {
484				authok = AUTH_OKAY;
485			} else
486				lastchance = 0;
487			if ((authok & AUTH_ALLOW) == 0)
488				goto failed;
489			if (auth_setoption(as, "style", style) < 0) {
490				syslog(LOG_ERR, "%m");
491				warn(NULL);
492				quickexit(1);
493			}
494		}
495		/*
496		 * explicitly reject users without password file entries
497		 */
498		if (pwd == NULL)
499			goto failed;
500
501		/*
502		 * If trying to log in as root on an insecure terminal,
503		 * refuse the login attempt unless the authentication
504		 * style explicitly says a root login is okay.
505		 */
506		if (pwd && rootlogin && !rootterm(tty))
507			goto failed;
508
509		if (fflag) {
510			type = 0;
511			style = "forced";
512		}
513		break;
514
515failed:
516		if (authok & AUTH_SILENT)
517			quickexit(0);
518		if (rootlogin && !rootterm(tty)) {
519			warnx("%s login refused on this terminal.",
520			    fullname);
521			if (hostname)
522				syslog(LOG_NOTICE,
523				    "LOGIN %s REFUSED FROM %s%s%s ON TTY %s",
524				    fullname, rusername ? rusername : "",
525				    rusername ? "@" : "", hostname, tty);
526			else
527				syslog(LOG_NOTICE,
528				    "LOGIN %s REFUSED ON TTY %s",
529				    fullname, tty);
530		} else {
531			if (!as || (p = auth_getvalue(as, "errormsg")) == NULL)
532				p = "Login incorrect";
533			(void)printf("%s\n", p);
534		}
535		failures++;
536		if (pwd)
537			log_failedlogin(pwd->pw_uid, hostname, rusername, tty);
538		/*
539		 * By default, we allow 10 tries, but after 3 we start
540		 * backing off to slow down password guessers.
541		 */
542		if (++cnt > backoff) {
543			if (cnt >= tries) {
544				badlogin(username);
545				sleepexit(1);
546			}
547			sleep(1);
548		}
549	}
550
551	/* committed to login -- turn off timeout */
552	(void)alarm(0);
553
554	endpwent();
555
556	shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
557	if (*shell == '\0')
558		shell = _PATH_BSHELL;
559	else if (strlen(shell) >= PATH_MAX) {
560		syslog(LOG_ERR, "shell path too long: %s", shell);
561		warnx("invalid shell");
562		quickexit(1);
563	}
564
565	/* Destroy environment unless user has requested its preservation. */
566	if (!pflag) {
567		if ((environ = calloc(1, sizeof (char *))) == NULL)
568			err(1, "calloc");
569	} else {
570		char **cpp, **cpp2;
571
572		for (cpp2 = cpp = environ; *cpp; cpp++) {
573			if (strncmp(*cpp, "LD_", 3) &&
574			    strncmp(*cpp, "ENV=", 4) &&
575			    strncmp(*cpp, "BASH_ENV=", 9) &&
576			    strncmp(*cpp, "IFS=", 4))
577				*cpp2++ = *cpp;
578		}
579		*cpp2 = 0;
580	}
581	/* Note: setusercontext(3) will set PATH */
582	if (setenv("HOME", pwd->pw_dir, 1) == -1 ||
583	    setenv("SHELL", pwd->pw_shell, 1) == -1) {
584		warn("unable to setenv()");
585		quickexit(1);
586	}
587	if (term[0] == '\0')
588		(void)strlcpy(term, stypeof(tty), sizeof(term));
589	(void)snprintf(mail, sizeof(mail), "%s/%s", _PATH_MAILDIR,
590		pwd->pw_name);
591	if (setenv("TERM", term, 0) == -1 ||
592	    setenv("LOGNAME", pwd->pw_name, 1) == -1 ||
593	    setenv("USER", pwd->pw_name, 1) == -1 ||
594	    setenv("MAIL", mail, 1) == -1) {
595		warn("unable to setenv()");
596		quickexit(1);
597	}
598	if (hostname) {
599		if (setenv("REMOTEHOST", hostname, 1) == -1) {
600			warn("unable to setenv()");
601			quickexit(1);
602		}
603	}
604	if (rusername) {
605		if (setenv("REMOTEUSER", rusername, 1) == -1) {
606			warn("unable to setenv()");
607			quickexit(1);
608		}
609	}
610
611	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) {
612		warn("unable to set user context");
613		quickexit(1);
614	}
615	auth_setenv(as);
616
617	/* if user not super-user, check for disabled logins */
618	if (!rootlogin)
619		auth_checknologin(lc);
620
621	setegid(pwd->pw_gid);
622	seteuid(pwd->pw_uid);
623
624	homeless = chdir(pwd->pw_dir);
625	if (homeless) {
626		if (login_getcapbool(lc, "requirehome", 0)) {
627			(void)printf("No home directory %s!\n", pwd->pw_dir);
628			quickexit(1);
629		}
630		if (chdir("/"))
631			quickexit(0);
632	}
633
634	quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) ||
635	    login_getcapbool(lc, "hushlogin", 0) ||
636	    (access(_PATH_HUSHLOGIN, F_OK) == 0));
637
638	seteuid(0);
639	setegid(0);	/* XXX use a saved gid instead? */
640
641	if ((p = auth_getvalue(as, "warnmsg")) != NULL)
642		(void)printf("WARNING: %s\n\n", p);
643
644	expire = auth_check_expire(as);
645	if (expire < 0) {
646		(void)printf("Sorry -- your account has expired.\n");
647		quickexit(1);
648	} else if (expire > 0 && !quietlog) {
649		warning = login_getcaptime(lc, "expire-warn",
650		    TWOWEEKS, TWOWEEKS);
651		if (expire < warning)
652			(void)printf("Warning: your account expires on %s",
653			    ctime(&pwd->pw_expire));
654	}
655
656	/* Nothing else left to fail -- really log in. */
657	(void)signal(SIGHUP, SIG_DFL);
658	memset(&utmp, 0, sizeof(utmp));
659	(void)time(&utmp.ut_time);
660	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
661	if (hostname)
662		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
663	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
664	login(&utmp);
665
666	if (!quietlog)
667		(void)check_failedlogin(pwd->pw_uid);
668	dolastlog(quietlog);
669
670	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
671
672	(void)chown(ttyn, pwd->pw_uid,
673	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
674
675	/* If fflag is on, assume caller/authenticator has logged root login. */
676	if (rootlogin && fflag == 0) {
677		if (hostname)
678			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s%s%s",
679			    username, tty, rusername ? rusername : "",
680			    rusername ? "@" : "", hostname);
681		else
682			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
683	}
684
685	if (!quietlog) {
686		if ((copyright =
687		    login_getcapstr(lc, "copyright", NULL, NULL)) != NULL)
688			auth_cat(copyright);
689		motd();
690		if (stat(mail, &st) == 0 && st.st_size != 0)
691			(void)printf("You have %smail.\n",
692			    (st.st_mtime > st.st_atime) ? "new " : "");
693	}
694
695	(void)signal(SIGALRM, SIG_DFL);
696	(void)signal(SIGQUIT, SIG_DFL);
697	(void)signal(SIGHUP, SIG_DFL);
698	(void)signal(SIGINT, SIG_DFL);
699	(void)signal(SIGTSTP, SIG_IGN);
700
701	tbuf[0] = '-';
702	(void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ?
703	    p + 1 : shell, sizeof(tbuf) - 1);
704
705	if ((scds.rlim_cur != QUAD_MIN || scds.rlim_max != QUAD_MIN) &&
706	    setrlimit(RLIMIT_CORE, &scds) == -1)
707		syslog(LOG_ERR, "couldn't reset core dump size: %m");
708
709	if (lastchance)
710		(void)printf("WARNING: Your password has expired."
711		    "  You must change your password, now!\n");
712
713	if (setusercontext(lc, pwd, rootlogin ? 0 : pwd->pw_uid,
714	    LOGIN_SETALL & ~LOGIN_SETPATH) < 0) {
715		warn("unable to set user context");
716		quickexit(1);
717	}
718
719	if (homeless) {
720		(void)printf("No home directory %s!\n", pwd->pw_dir);
721		(void)printf("Logging in with home = \"/\".\n");
722		(void)setenv("HOME", "/", 1);
723	}
724
725	if (auth_approval(as, lc, NULL, "login") == 0) {
726		if (auth_getstate(as) & AUTH_EXPIRED)
727			(void)printf("Sorry -- your account has expired.\n");
728		else
729			(void)printf("approval failure\n");
730		quickexit(1);
731	}
732
733	/*
734	 * The last thing we do is discard all of the open file descriptors.
735	 * Last because the C library may have some open.
736	 */
737	closefrom(STDERR_FILENO + 1);
738
739	/*
740	 * Close the authentication session, make sure it is marked
741	 * as okay so no files are removed.
742	 */
743	auth_setstate(as, AUTH_OKAY);
744	auth_close(as);
745
746	execlp(shell, tbuf, (char *)NULL);
747	err(1, "%s", shell);
748}
749
750/*
751 * Allow for a '.' and 16 characters for any instance as well as
752 * space for a ':' and 16 characters defining the authentication type.
753 */
754#define NBUFSIZ		(UT_NAMESIZE + 1 + 16 + 1 + 16)
755
756void
757getloginname(void)
758{
759	static char nbuf[NBUFSIZ], *p;
760	int ch;
761
762	for (;;) {
763		(void)printf("login: ");
764		for (p = nbuf; (ch = getchar()) != '\n'; ) {
765			if (ch == EOF) {
766				badlogin(username);
767				quickexit(0);
768			}
769			if (p < nbuf + (NBUFSIZ - 1))
770				*p++ = ch;
771		}
772		if (p > nbuf) {
773			if (nbuf[0] == '-')
774				(void)fprintf(stderr,
775				    "login names may not start with '-'.\n");
776			else {
777				*p = '\0';
778				username = nbuf;
779				break;
780			}
781		}
782	}
783}
784
785int
786rootterm(char *ttyn)
787{
788	struct ttyent *t;
789
790	/* XXX - stash output of getttynam() elsewhere */
791	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
792}
793
794void
795motd(void)
796{
797	char tbuf[8192], *motd;
798	int fd, nchars;
799	struct sigaction sa, osa;
800
801	motd = login_getcapstr(lc, "welcome", _PATH_MOTDFILE, _PATH_MOTDFILE);
802
803	if ((fd = open(motd, O_RDONLY)) == -1)
804		return;
805
806	memset(&sa, 0, sizeof(sa));
807	sa.sa_handler = sigint;
808	sigemptyset(&sa.sa_mask);
809	sa.sa_flags = 0;		/* don't set SA_RESTART */
810	(void)sigaction(SIGINT, &sa, &osa);
811
812	/* read and spew motd until EOF, error, or SIGINT */
813	while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 &&
814	    write(STDOUT_FILENO, tbuf, nchars) == nchars)
815		;
816
817	(void)sigaction(SIGINT, &osa, NULL);
818	(void)close(fd);
819}
820
821void
822sigint(int signo)
823{
824	return;			/* just interrupt syscall */
825}
826
827void
828timedout(int signo)
829{
830	dprintf(STDERR_FILENO,
831	    "Login timed out after %d seconds\n", timeout);
832	if (username)
833		badlogin(username);
834	_exit(0);
835}
836
837void
838dolastlog(int quiet)
839{
840	struct lastlog ll;
841	off_t pos;
842	int fd;
843
844	if ((fd = open(_PATH_LASTLOG, O_RDWR)) >= 0) {
845		pos = (off_t)pwd->pw_uid * sizeof(ll);
846		if (!quiet) {
847			if (pread(fd, &ll, sizeof(ll), pos) == sizeof(ll) &&
848			    ll.ll_time != 0) {
849				(void)printf("Last login: %.*s ",
850				    24-5, (char *)ctime(&ll.ll_time));
851				(void)printf("on %.*s",
852				    (int)sizeof(ll.ll_line),
853				    ll.ll_line);
854				if (*ll.ll_host != '\0')
855					(void)printf(" from %.*s",
856					    (int)sizeof(ll.ll_host),
857					    ll.ll_host);
858				(void)putchar('\n');
859			}
860		}
861		memset(&ll, 0, sizeof(ll));
862		(void)time(&ll.ll_time);
863		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
864		if (hostname)
865			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
866		(void)pwrite(fd, &ll, sizeof(ll), pos);
867		(void)close(fd);
868	}
869}
870
871void
872badlogin(char *name)
873{
874	struct syslog_data sdata = SYSLOG_DATA_INIT;
875
876	if (failures == 0)
877		return;
878	if (hostname) {
879		syslog_r(LOG_NOTICE, &sdata,
880		    "%d LOGIN FAILURE%s FROM %s%s%s",
881		    failures, failures > 1 ? "S" : "",
882		    rusername ? rusername : "", rusername ? "@" : "", hostname);
883		syslog_r(LOG_AUTHPRIV|LOG_NOTICE, &sdata,
884		    "%d LOGIN FAILURE%s FROM %s%s%s, %s",
885		    failures, failures > 1 ? "S" : "",
886		    rusername ? rusername : "", rusername ? "@" : "",
887		    hostname, name);
888	} else {
889		syslog_r(LOG_NOTICE, &sdata,
890		    "%d LOGIN FAILURE%s ON %s",
891		    failures, failures > 1 ? "S" : "", tty);
892		syslog_r(LOG_AUTHPRIV|LOG_NOTICE, &sdata,
893		    "%d LOGIN FAILURE%s ON %s, %s",
894		    failures, failures > 1 ? "S" : "", tty, name);
895	}
896}
897
898#undef	UNKNOWN
899#define	UNKNOWN	"su"
900
901char *
902stypeof(char *ttyid)
903{
904	struct ttyent *t;
905
906	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type :
907	    login_getcapstr(lc, "term", UNKNOWN, UNKNOWN));
908}
909
910void
911sleepexit(int eval)
912{
913	auth_close(as);
914	(void)sleep(5);
915	exit(eval);
916}
917
918void
919quickexit(int eval)
920{
921	if (as)
922		auth_close(as);
923	exit(eval);
924}
925
926
927void
928sighup(int signum)
929{
930	if (username)
931		badlogin(username);
932	_exit(0);
933}
934