xref: /openbsd-src/usr.bin/login/login.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
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 
101 void	 badlogin(char *);
102 void	 dolastlog(int);
103 void	 getloginname(void);
104 void	 motd(void);
105 void	 quickexit(int);
106 int	 rootterm(char *);
107 void	 sigint(int);
108 void	 sighup(int);
109 void	 sleepexit(int);
110 char	*stypeof(char *);
111 void	 timedout(int);
112 int	 main(int, char **);
113 
114 extern int check_failedlogin(uid_t);
115 extern 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  */
125 u_int		timeout = 300;
126 
127 struct passwd	*pwd;
128 login_cap_t	*lc = NULL;
129 auth_session_t	*as = NULL;
130 int		failures;
131 int		needbanner = 1;
132 char		term[64], *hostname, *tty;
133 char		*style;
134 char		*username = NULL, *rusername = NULL;
135 
136 extern char **environ;
137 
138 int
main(int argc,char * argv[])139 main(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 
515 failed:
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 
756 void
getloginname(void)757 getloginname(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 
785 int
rootterm(char * ttyn)786 rootterm(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 
794 void
motd(void)795 motd(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 
821 void
sigint(int signo)822 sigint(int signo)
823 {
824 	return;			/* just interrupt syscall */
825 }
826 
827 void
timedout(int signo)828 timedout(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 
837 void
dolastlog(int quiet)838 dolastlog(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 
871 void
badlogin(char * name)872 badlogin(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 
901 char *
stypeof(char * ttyid)902 stypeof(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 
910 void
sleepexit(int eval)911 sleepexit(int eval)
912 {
913 	auth_close(as);
914 	(void)sleep(5);
915 	exit(eval);
916 }
917 
918 void
quickexit(int eval)919 quickexit(int eval)
920 {
921 	if (as)
922 		auth_close(as);
923 	exit(eval);
924 }
925 
926 
927 void
sighup(int signum)928 sighup(int signum)
929 {
930 	if (username)
931 		badlogin(username);
932 	_exit(0);
933 }
934