xref: /openbsd/usr.bin/login/login.c (revision 133306f0)
1 /*	$OpenBSD: login.c,v 1.35 2000/10/14 20:33:13 miod 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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static char copyright[] =
39 "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)login.c	8.4 (Berkeley) 4/2/94";
46 #endif
47 static char rcsid[] = "$OpenBSD: login.c,v 1.35 2000/10/14 20:33:13 miod Exp $";
48 #endif /* not lint */
49 
50 /*
51  * login [ name ]
52  * login -h hostname	(for telnetd, etc.)
53  * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
54  */
55 
56 #include <sys/param.h>
57 #include <sys/stat.h>
58 #include <sys/time.h>
59 #include <sys/resource.h>
60 #include <sys/wait.h>
61 
62 #include <err.h>
63 #include <errno.h>
64 #include <fcntl.h>
65 #include <grp.h>
66 #include <login_cap.h>
67 #include <pwd.h>
68 #include <setjmp.h>
69 #include <signal.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <syslog.h>
74 #include <ttyent.h>
75 #include <tzfile.h>
76 #include <unistd.h>
77 #include <utmp.h>
78 #include <util.h>
79 #include <skey.h>
80 
81 #include "pathnames.h"
82 
83 void	 badlogin __P((char *));
84 void	 checknologin __P((void));
85 void	 dolastlog __P((int));
86 void	 getloginname __P((void));
87 void	 motd __P((void));
88 int	 rootterm __P((char *));
89 void	 sigint __P((int));
90 void	 sighup __P((int));
91 void	 sleepexit __P((int));
92 char	*stypeof __P((char *));
93 void	 timedout __P((int));
94 int	 pwcheck __P((char *, char *, char *, char *));
95 #if defined(KERBEROS) || defined(KERBEROS5)
96 int	 klogin __P((struct passwd *, char *, char *, char *));
97 void	 kdestroy __P((void));
98 void	 dofork __P((void));
99 void	 kgettokens __P((char *));
100 #endif
101 
102 extern int check_failedlogin __P((uid_t));
103 extern void log_failedlogin __P((uid_t, char *, char *, char *));
104 
105 #define	TTYGRPNAME	"tty"		/* name of group to own ttys */
106 
107 /*
108  * This bounds the time given to login.  Not a define so it can
109  * be patched on machines where it's too small.
110  * XXX - should be a login.conf variable!
111  */
112 u_int		timeout = 300;
113 
114 #if defined(KERBEROS) || defined(KERBEROS5)
115 int		notickets = 1;
116 char		*instance;
117 char		*krbtkfile_env;
118 int		authok;
119 #endif
120 
121 struct	passwd	*pwd;
122 login_cap_t	*lc = NULL;
123 int		failures;
124 char		term[64], *hostname, *tty;
125 char		*username = NULL, *rusername = NULL;
126 
127 int
128 main(argc, argv)
129 	int argc;
130 	char *argv[];
131 {
132 	extern char **environ;
133 	struct group *gr;
134 	struct stat st;
135 	struct timeval tp;
136 	struct utmp utmp;
137 	int ask, ch, cnt, fflag, hflag, pflag, uflag, quietlog, rootlogin, rval;
138 	uid_t uid;
139 	char *domain, *p, *salt, *ttyn, *shell;
140 	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
141 	char localhost[MAXHOSTNAMELEN];
142 
143 	(void)signal(SIGALRM, timedout);
144 	(void)alarm(timeout);
145 	(void)signal(SIGQUIT, SIG_IGN);
146 	(void)signal(SIGINT, SIG_IGN);
147 	(void)signal(SIGHUP, sighup);
148 	(void)setpriority(PRIO_PROCESS, 0, 0);
149 
150 	openlog("login", LOG_ODELAY, LOG_AUTH);
151 
152 	/*
153 	 * -p is used by getty to tell login not to destroy the environment
154 	 * -f is used to skip a second login authentication
155 	 * -h is used by other servers to pass the name of the remote
156 	 *    host to login so that it may be placed in utmp and wtmp
157 	 */
158 	domain = NULL;
159 	if (gethostname(localhost, sizeof(localhost)) < 0)
160 		syslog(LOG_ERR, "couldn't get local hostname: %m");
161 	else
162 		domain = strchr(localhost, '.');
163 	if (domain) {
164 		domain++;
165 		if (*domain && strchr(domain, '.') == NULL)
166 			domain = localhost;
167 	}
168 
169 	fflag = hflag = pflag = 0;
170 	uid = getuid();
171 	while ((ch = getopt(argc, argv, "fh:u:p")) != -1)
172 		switch (ch) {
173 		case 'f':
174 			fflag = 1;
175 			break;
176 		case 'h':
177 			if (uid)
178 				errx(1, "-h option: %s", strerror(EPERM));
179 			hflag = 1;
180 			if (domain && (p = strchr(optarg, '.')) &&
181 			    strcasecmp(p+1, domain) == 0)
182 				*p = 0;
183 			hostname = optarg;
184 			break;
185 		case 'p':
186 			pflag = 1;
187 			break;
188 		case 'u':
189 			if (uid)
190 				errx(1, "-u option: %s", strerror(EPERM));
191 			uflag = 1;
192 			rusername = optarg;
193 			break;
194 		case '?':
195 		default:
196 			if (!uid)
197 				syslog(LOG_ERR, "invalid flag %c", ch);
198 			(void)fprintf(stderr,
199 			    "usage: login [-fp] [-h hostname] [username]\n");
200 			exit(1);
201 		}
202 	argc -= optind;
203 	argv += optind;
204 
205 	if (*argv) {
206 		username = *argv;
207 		ask = 0;
208 	} else
209 		ask = 1;
210 
211 	for (cnt = getdtablesize(); cnt > 2; cnt--)
212 		(void)close(cnt);
213 
214 	ttyn = ttyname(STDIN_FILENO);
215 	if (ttyn == NULL || *ttyn == '\0') {
216 		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
217 		ttyn = tname;
218 	}
219 	if ((tty = strrchr(ttyn, '/')))
220 		++tty;
221 	else
222 		tty = ttyn;
223 
224 	for (cnt = 0;; ask = 1) {
225 #if defined(KERBEROS) || defined(KERBEROS5)
226 	        kdestroy();
227 		authok = 0;
228 #endif
229 		if (ask) {
230 			fflag = 0;
231 			getloginname();
232 		}
233 		rootlogin = 0;
234 
235 #if defined(KERBEROS) || defined(KERBEROS5)
236 		/*
237 		 * Why should anyone with a root instance be able
238 		 * to be root here?
239 		 */
240 		instance = "";
241 #endif
242 #ifdef	KERBEROS
243 		if ((instance = strchr(username, '.')) != NULL) {
244 			if (strncmp(instance, ".root", 5) == 0)
245 				rootlogin = 1;
246 			*instance++ = '\0';
247 		} else
248 			instance = "";
249 #endif
250 #ifdef KERBEROS5
251 		if ((instance = strchr(username, '/')) != NULL) {
252 			if (strncmp(instance, "/root", 5) == 0)
253 				rootlogin = 1;
254 			*instance++ = '\0';
255 		} else
256 			instance = "";
257 #endif
258 		if (strlen(username) > UT_NAMESIZE)
259 			username[UT_NAMESIZE] = '\0';
260 
261 		/*
262 		 * Note if trying multiple user names; log failures for
263 		 * previous user name, but don't bother logging one failure
264 		 * for nonexistent name (mistyped username).
265 		 */
266 		if (failures && strcmp(tbuf, username)) {
267 			if (failures > (pwd ? 0 : 1))
268 				badlogin(tbuf);
269 			failures = 0;
270 		}
271 		(void)strlcpy(tbuf, username, sizeof tbuf);
272 
273 		if ((pwd = getpwnam(username)))
274 			salt = pwd->pw_passwd;
275 		else
276 			salt = "xx";
277 		lc = login_getclass(pwd ? pwd->pw_class : LOGIN_DEFCLASS);
278 		if (!lc)
279 		    err(1, "unable to get login class");
280 
281 		/*
282 		 * If we have a valid account name, and it doesn't have a
283 		 * password, or the -f option was specified and the caller
284 		 * is root or the caller isn't changing their uid, don't
285 		 * authenticate.
286 		 */
287 		if (pwd) {
288 			if (pwd->pw_uid == 0)
289 				rootlogin = 1;
290 
291 			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
292 				/* already authenticated */
293 				break;
294 			} else if (pwd->pw_passwd[0] == '\0') {
295 				/* pretend password okay */
296 				rval = 0;
297 #if defined(KERBEROS) || defined(KERBEROS5)
298 				authok = 1;
299 #endif
300 				goto ttycheck;
301 			}
302 		}
303 
304 		fflag = 0;
305 
306 		(void)setpriority(PRIO_PROCESS, 0, -4);
307 
308 		p = getpass("Password:");
309 
310 		if (pwd) {
311 #if defined(KERBEROS) || defined(KERBEROS5)
312 			rval = klogin(pwd, instance, localhost, p);
313 			if (rval != 0 && rootlogin && pwd->pw_uid != 0)
314 				rootlogin = 0;
315 			if (rval == 1) {
316 				/* Fall back on password file. */
317 				if (pwd->pw_uid != 0)
318 					rootlogin = 0;
319 				rval = pwcheck(username, p, salt, pwd->pw_passwd);
320 			}
321 			if (rval == 0)
322 				authok = 1;
323 #else
324 			rval = pwcheck(username, p, salt, pwd->pw_passwd);
325 #endif
326 		} else {
327 #ifdef SKEY
328 			if (strcasecmp(p, "s/key") == 0)
329 				(void)skey_authenticate(username);
330 			else
331 #endif
332 			{
333 				useconds_t us;
334 
335 				/*
336 				 * Sleep between 1 and 3 seconds
337 				 * to emulate a crypt.
338 				 */
339 				us = arc4random() % 3000000;
340 				usleep(us);
341 			}
342 			rval = 1;
343 		}
344 		memset(p, 0, strlen(p));
345 
346 		(void)setpriority(PRIO_PROCESS, 0, 0);
347 
348 	ttycheck:
349 		/*
350 		 * If trying to log in as root without Kerberos,
351 		 * but with insecure terminal, refuse the login attempt.
352 		 */
353 #if defined(KERBEROS) || defined(KERBEROS5)
354 		if (authok == 1)
355 #endif
356 		/* if logging in as root, user must be on a secure tty */
357 		if (pwd && rval == 0 && (!rootlogin || rootterm(tty)))
358 			break;
359 
360 		/*
361 		 * We don't want to give out info to an attacker trying
362 		 * to guess root's password so we always say "login refused"
363 		 * in that case, not "Login incorrect".
364 		 */
365 		if (rootlogin && !rootterm(tty)) {
366 			(void)fprintf(stderr,
367 			    "%s login refused on this terminal.\n",
368 			    pwd ? pwd->pw_name : "root");
369 			if (hostname)
370 				syslog(LOG_NOTICE,
371 				    "LOGIN %s REFUSED FROM %s%s%s ON TTY %s",
372 				    pwd ? pwd->pw_name : "root",
373 				    rusername ? rusername : "",
374 				    rusername ? "@" : "", hostname, tty);
375 			else
376 				syslog(LOG_NOTICE,
377 				    "LOGIN %s REFUSED ON TTY %s",
378 				     pwd ? pwd->pw_name : "root", tty);
379 		} else
380 			(void)printf("Login incorrect\n");
381 		failures++;
382 		if (pwd)
383 			log_failedlogin(pwd->pw_uid, hostname, rusername, tty);
384 		/* we allow 10 tries, but after 3 we start backing off */
385 		if (++cnt > 3) {
386 			if (cnt >= 10) {
387 				badlogin(username);
388 				sleepexit(1);
389 			}
390 			sleep((u_int)((cnt - 3) * 5));
391 		}
392 	}
393 
394 	/* committed to login -- turn off timeout */
395 	(void)alarm((u_int)0);
396 
397 	endpwent();
398 
399 	/* if user not super-user, check for disabled logins */
400 	if (!rootlogin)
401 		checknologin();
402 
403 	setegid(pwd->pw_gid);
404 	seteuid(pwd->pw_uid);
405 
406 	if (chdir(pwd->pw_dir) < 0) {
407 		(void)printf("No home directory %s!\n", pwd->pw_dir);
408 		if (login_getcapbool(lc, "requirehome", 0))
409 			exit(1);
410 		if (chdir("/"))
411 			exit(0);
412 		pwd->pw_dir = "/";
413 		(void)printf("Logging in with home = \"/\".\n");
414 	}
415 
416 	shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
417 	if (*shell == '\0')
418 		shell = _PATH_BSHELL;
419 	else if (strlen(shell) >= MAXPATHLEN) {
420 		syslog(LOG_ERR, "shell path too long: %s", shell);
421 		warnx("invalid shell");
422 		sleepexit(1);
423 	}
424 
425 	quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) ||
426 	    login_getcapbool(lc, "hushlogin", 0) ||
427 	    (access(_PATH_HUSHLOGIN, F_OK) == 0));
428 
429 	seteuid(0);
430 	setegid(0);	/* XXX use a saved gid instead? */
431 
432 	if (pwd->pw_change || pwd->pw_expire)
433 		(void)gettimeofday(&tp, (struct timezone *)NULL);
434 	if (pwd->pw_expire) {
435 		if (tp.tv_sec >= pwd->pw_expire) {
436 			(void)printf("Sorry -- your account has expired.\n");
437 			sleepexit(1);
438 		} else if (!quietlog &&pwd->pw_expire - tp.tv_sec <
439 		    login_getcaptime(lc, "expire-warn",
440 		    2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY))
441 			(void)printf("Warning: your account expires on %s",
442 			    ctime(&pwd->pw_expire));
443 	}
444 	if (pwd->pw_change) {
445 		if (tp.tv_sec >= pwd->pw_change) {
446 			(void)printf("Sorry -- your password has expired.\n");
447 			sleepexit(1);
448 		} else if (!quietlog && pwd->pw_change - tp.tv_sec <
449 		    login_getcaptime(lc, "password-warn",
450 		    2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY))
451 			(void)printf("Warning: your password expires on %s",
452 			    ctime(&pwd->pw_change));
453 	}
454 
455 	/* Nothing else left to fail -- really log in. */
456 	(void)signal(SIGHUP, SIG_DFL);
457 	memset((void *)&utmp, 0, sizeof(utmp));
458 	(void)time(&utmp.ut_time);
459 	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
460 	if (hostname)
461 		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
462 	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
463 	login(&utmp);
464 
465 	if (!quietlog)
466 		(void)check_failedlogin(pwd->pw_uid);
467 	dolastlog(quietlog);
468 
469 	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
470 
471 	(void)chown(ttyn, pwd->pw_uid,
472 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
473 #if defined(KERBEROS) || defined(KERBEROS5)
474 	/* Fork so that we can call kdestroy */
475 	if (krbtkfile_env)
476 	    dofork();
477 #endif
478 
479 	/* Destroy environment unless user has requested its preservation. */
480 	if (!pflag) {
481 		if ((environ = calloc(1, sizeof (char *))) == NULL)
482 			err(1, "calloc");
483 	} else {
484 		char **cpp, **cpp2;
485 
486 		for (cpp2 = cpp = environ; *cpp; cpp++) {
487 			if (strncmp(*cpp, "LD_", 3) &&
488 			    strncmp(*cpp, "ENV=", 4) &&
489 			    strncmp(*cpp, "BASH_ENV=", 9) &&
490 			    strncmp(*cpp, "IFS=", 4))
491 				*cpp2++ = *cpp;
492 		}
493 		*cpp2 = 0;
494 	}
495 	/* Note: setusercontext(3) will set PATH */
496 	if (setenv("HOME", pwd->pw_dir, 1) == -1 ||
497 	    setenv("SHELL", shell, 1) == -1) {
498 		warn("unable to setenv()");
499 		exit(1);
500 	}
501 	if (term[0] == '\0')
502 		(void)strlcpy(term, stypeof(tty), sizeof(term));
503 	if (setenv("TERM", term, 0) == -1 ||
504 	    setenv("LOGNAME", pwd->pw_name, 1) == -1 ||
505 	    setenv("USER", pwd->pw_name, 1) == -1) {
506 		warn("unable to setenv()");
507 		exit(1);
508 	}
509 	if (hostname) {
510 		if (setenv("REMOTEHOST", hostname, 1) == -1) {
511 			warn("unable to setenv()");
512 			exit(1);
513 		}
514 	}
515 	if (rusername) {
516 		if (setenv("REMOTEUSER", rusername, 1) == -1) {
517 			warn("unable to setenv()");
518 			exit(1);
519 		}
520 	}
521 #ifdef KERBEROS
522 	if (krbtkfile_env) {
523 		if (setenv("KRBTKFILE", krbtkfile_env, 1) == -1) {
524 			warn("unable to setenv()");
525 			exit(1);
526 		}
527 	}
528 #endif
529 #ifdef KERBEROS5
530 	if (krbtkfile_env) {
531 		if (setenv("KRB5CCNAME", krbtkfile_env, 1) == -1) {
532 			warn("unable to setenv()");
533 			exit(1);
534 		}
535 	}
536 #endif
537 	/* If fflag is on, assume caller/authenticator has logged root login. */
538 	if (rootlogin && fflag == 0) {
539 		if (hostname)
540 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s%s%s",
541 			    username, tty, rusername ? rusername : "",
542 			    rusername ? "@" : "", hostname);
543 		else
544 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
545 	}
546 
547 #if defined(KERBEROS) || defined(KERBEROS5)
548 	if (!quietlog && notickets == 1)
549 		(void)printf("Warning: no Kerberos tickets issued.\n");
550 #endif
551 
552 	if (!quietlog) {
553 #if 0
554 		(void)printf("%s\n\t%s  %s\n\n",
555 	    "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
556 		    "The Regents of the University of California. ",
557 		    "All rights reserved.");
558 #endif
559 		motd();
560 		(void)snprintf(tbuf,
561 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
562 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
563 			(void)printf("You have %smail.\n",
564 			    (st.st_mtime > st.st_atime) ? "new " : "");
565 	}
566 
567 	(void)signal(SIGALRM, SIG_DFL);
568 	(void)signal(SIGQUIT, SIG_DFL);
569 	(void)signal(SIGINT, SIG_DFL);
570 	(void)signal(SIGTSTP, SIG_IGN);
571 
572 	tbuf[0] = '-';
573 	(void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ?
574 	    p + 1 : shell, sizeof tbuf - 1);
575 
576 	/* Discard permissions last so can't get killed and drop core. */
577 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL)) {
578 		warn("unable to set user context");
579 		exit(1);
580 	}
581 
582 #ifdef KERBEROS
583 	kgettokens(pwd->pw_dir);
584 #endif
585 
586 	execlp(shell, tbuf, 0);
587 	err(1, "%s", shell);
588 }
589 
590 int
591 pwcheck(user, p, salt, passwd)
592 	char *user, *p, *salt, *passwd;
593 {
594 #ifdef SKEY
595 	if (strcasecmp(p, "s/key") == 0)
596 		return skey_authenticate(user);
597 #endif
598 	return strcmp(crypt(p, salt), passwd);
599 }
600 
601 #if defined(KERBEROS) || defined(KERBEROS5)
602 #define	NBUFSIZ		(UT_NAMESIZE + 1 + 5)	/* .root suffix */
603 #else
604 #define	NBUFSIZ		(UT_NAMESIZE + 1)
605 #endif
606 
607 #if defined(KERBEROS) || defined(KERBEROS5)
608 /*
609  * This routine handles cleanup stuff, and the like.
610  * It exists only in the child process.
611  */
612 #include <sys/wait.h>
613 void
614 dofork()
615 {
616     int child;
617 
618     if (!(child = fork()))
619 	    return; /* Child process */
620 
621     /* Setup stuff?  This would be things we could do in parallel with login */
622     (void) chdir("/");	/* Let's not keep the fs busy... */
623 
624     /* If we're the parent, watch the child until it dies */
625     while (wait(0) != child)
626 	    ;
627 
628     /* Cleanup stuff */
629     /* Run kdestroy to destroy tickets */
630     kdestroy();
631 
632     /* Leave */
633     exit(0);
634 }
635 #endif
636 
637 void
638 getloginname()
639 {
640 	int ch;
641 	char *p;
642 	static char nbuf[NBUFSIZ];
643 
644 	for (;;) {
645 		(void)printf("login: ");
646 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
647 			if (ch == EOF) {
648 				badlogin(username);
649 				exit(0);
650 			}
651 			if (p < nbuf + (NBUFSIZ - 1))
652 				*p++ = ch;
653 		}
654 		if (p > nbuf) {
655 			if (nbuf[0] == '-')
656 				(void)fprintf(stderr,
657 				    "login names may not start with '-'.\n");
658 			else {
659 				*p = '\0';
660 				username = nbuf;
661 				break;
662 			}
663 		}
664 	}
665 }
666 
667 int
668 rootterm(ttyn)
669 	char *ttyn;
670 {
671 	struct ttyent *t;
672 
673 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
674 }
675 
676 jmp_buf motdinterrupt;
677 
678 void
679 motd()
680 {
681 	int fd, nchars;
682 	sig_t oldint;
683 	char tbuf[8192];
684 	char *motd;
685 
686 	motd = login_getcapstr(lc, "welcome", _PATH_MOTDFILE, _PATH_MOTDFILE);
687 
688 	if ((fd = open(motd, O_RDONLY, 0)) < 0)
689 		return;
690 	oldint = signal(SIGINT, sigint);
691 	if (setjmp(motdinterrupt) == 0)
692 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
693 			(void)write(fileno(stdout), tbuf, nchars);
694 	(void)signal(SIGINT, oldint);
695 	(void)close(fd);
696 }
697 
698 /* ARGSUSED */
699 void
700 sigint(signo)
701 	int signo;
702 {
703 	longjmp(motdinterrupt, 1);
704 }
705 
706 /* ARGSUSED */
707 void
708 timedout(signo)
709 	int signo;
710 {
711 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
712 	exit(0);
713 }
714 
715 void
716 checknologin()
717 {
718 	int fd, nchars;
719 	char *nologin;
720 	char tbuf[8192];
721 
722 	if (!login_getcapbool(lc, "ignorenologin", 0)) {
723 		nologin = login_getcapstr(lc, "nologin", _PATH_NOLOGIN,
724 		    _PATH_NOLOGIN);
725 		if ((fd = open(nologin, O_RDONLY, 0)) >= 0) {
726 			while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
727 				(void)write(fileno(stdout), tbuf, nchars);
728 			sleepexit(0);
729 		}
730 	}
731 }
732 
733 void
734 dolastlog(quiet)
735 	int quiet;
736 {
737 	struct lastlog ll;
738 	int fd;
739 
740 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
741 		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
742 		if (!quiet) {
743 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
744 			    ll.ll_time != 0) {
745 				(void)printf("Last login: %.*s ",
746 				    24-5, (char *)ctime(&ll.ll_time));
747 				(void)printf("on %.*s",
748 				    (int)sizeof(ll.ll_line),
749 				    ll.ll_line);
750 				if (*ll.ll_host != '\0')
751 					(void)printf(" from %.*s",
752 					    (int)sizeof(ll.ll_host),
753 					    ll.ll_host);
754 				(void)putchar('\n');
755 			}
756 			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll),
757 			    SEEK_SET);
758 		}
759 		memset((void *)&ll, 0, sizeof(ll));
760 		(void)time(&ll.ll_time);
761 		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
762 		if (hostname)
763 			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
764 		(void)write(fd, (char *)&ll, sizeof(ll));
765 		(void)close(fd);
766 	}
767 }
768 
769 void
770 badlogin(name)
771 	char *name;
772 {
773 	if (failures == 0)
774 		return;
775 	if (hostname) {
776 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s%s%s",
777 		    failures, failures > 1 ? "S" : "",
778 		    rusername ? rusername : "", rusername ? "@" : "", hostname);
779 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
780 		    "%d LOGIN FAILURE%s FROM %s%s%s, %s",
781 		    failures, failures > 1 ? "S" : "",
782 		    rusername ? rusername : "", rusername ? "@" : "",
783 		    hostname, name);
784 	} else {
785 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
786 		    failures, failures > 1 ? "S" : "", tty);
787 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
788 		    "%d LOGIN FAILURE%s ON %s, %s",
789 		    failures, failures > 1 ? "S" : "", tty, name);
790 	}
791 }
792 
793 #undef	UNKNOWN
794 #define	UNKNOWN	"su"
795 
796 char *
797 stypeof(ttyid)
798 	char *ttyid;
799 {
800 	struct ttyent *t;
801 
802 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type :
803 	    login_getcapstr(lc, "term", UNKNOWN, UNKNOWN));
804 }
805 
806 void
807 sleepexit(eval)
808 	int eval;
809 {
810 	(void)sleep(5);
811 	exit(eval);
812 }
813 
814 void
815 sighup(signum)
816 	int signum;
817 {
818 	if (username)
819 		badlogin(username);
820 	exit(0);
821 }
822