xref: /original-bsd/usr.bin/login/login.c (revision 7e5c8007)
1 /*-
2  * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)login.c	8.4 (Berkeley) 04/02/94";
16 #endif /* not lint */
17 
18 /*
19  * login [ name ]
20  * login -h hostname	(for telnetd, etc.)
21  * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
22  */
23 
24 #include <sys/param.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <sys/resource.h>
28 #include <sys/file.h>
29 
30 #include <err.h>
31 #include <errno.h>
32 #include <grp.h>
33 #include <pwd.h>
34 #include <setjmp.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <syslog.h>
40 #include <ttyent.h>
41 #include <tzfile.h>
42 #include <unistd.h>
43 #include <utmp.h>
44 
45 #include "pathnames.h"
46 
47 void	 badlogin __P((char *));
48 void	 checknologin __P((void));
49 void	 dolastlog __P((int));
50 void	 getloginname __P((void));
51 void	 motd __P((void));
52 int	 rootterm __P((char *));
53 void	 sigint __P((int));
54 void	 sleepexit __P((int));
55 char	*stypeof __P((char *));
56 void	 timedout __P((int));
57 #ifdef KERBEROS
58 int	 klogin __P((struct passwd *, char *, char *, char *));
59 #endif
60 
61 extern void login __P((struct utmp *));
62 
63 #define	TTYGRPNAME	"tty"		/* name of group to own ttys */
64 
65 /*
66  * This bounds the time given to login.  Not a define so it can
67  * be patched on machines where it's too small.
68  */
69 u_int	timeout = 300;
70 
71 #ifdef KERBEROS
72 int	notickets = 1;
73 char	*instance;
74 char	*krbtkfile_env;
75 int	authok;
76 #endif
77 
78 struct	passwd *pwd;
79 int	failures;
80 char	term[64], *envinit[1], *hostname, *username, *tty;
81 
82 int
83 main(argc, argv)
84 	int argc;
85 	char *argv[];
86 {
87 	extern char **environ;
88 	struct group *gr;
89 	struct stat st;
90 	struct timeval tp;
91 	struct utmp utmp;
92 	int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
93 	uid_t uid;
94 	char *domain, *p, *salt, *ttyn;
95 	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
96 	char localhost[MAXHOSTNAMELEN];
97 
98 	(void)signal(SIGALRM, timedout);
99 	(void)alarm(timeout);
100 	(void)signal(SIGQUIT, SIG_IGN);
101 	(void)signal(SIGINT, SIG_IGN);
102 	(void)setpriority(PRIO_PROCESS, 0, 0);
103 
104 	openlog("login", LOG_ODELAY, LOG_AUTH);
105 
106 	/*
107 	 * -p is used by getty to tell login not to destroy the environment
108 	 * -f is used to skip a second login authentication
109 	 * -h is used by other servers to pass the name of the remote
110 	 *    host to login so that it may be placed in utmp and wtmp
111 	 */
112 	domain = NULL;
113 	if (gethostname(localhost, sizeof(localhost)) < 0)
114 		syslog(LOG_ERR, "couldn't get local hostname: %m");
115 	else
116 		domain = strchr(localhost, '.');
117 
118 	fflag = hflag = pflag = 0;
119 	uid = getuid();
120 	while ((ch = getopt(argc, argv, "fh:p")) != EOF)
121 		switch (ch) {
122 		case 'f':
123 			fflag = 1;
124 			break;
125 		case 'h':
126 			if (uid)
127 				errx(1, "-h option: %s", strerror(EPERM));
128 			hflag = 1;
129 			if (domain && (p = strchr(optarg, '.')) &&
130 			    strcasecmp(p, domain) == 0)
131 				*p = 0;
132 			hostname = optarg;
133 			break;
134 		case 'p':
135 			pflag = 1;
136 			break;
137 		case '?':
138 		default:
139 			if (!uid)
140 				syslog(LOG_ERR, "invalid flag %c", ch);
141 			(void)fprintf(stderr,
142 			    "usage: login [-fp] [-h hostname] [username]\n");
143 			exit(1);
144 		}
145 	argc -= optind;
146 	argv += optind;
147 
148 	if (*argv) {
149 		username = *argv;
150 		ask = 0;
151 	} else
152 		ask = 1;
153 
154 	for (cnt = getdtablesize(); cnt > 2; cnt--)
155 		(void)close(cnt);
156 
157 	ttyn = ttyname(STDIN_FILENO);
158 	if (ttyn == NULL || *ttyn == '\0') {
159 		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
160 		ttyn = tname;
161 	}
162 	if (tty = strrchr(ttyn, '/'))
163 		++tty;
164 	else
165 		tty = ttyn;
166 
167 	for (cnt = 0;; ask = 1) {
168 		if (ask) {
169 			fflag = 0;
170 			getloginname();
171 		}
172 		rootlogin = 0;
173 #ifdef	KERBEROS
174 		if ((instance = strchr(username, '.')) != NULL) {
175 			if (strncmp(instance, ".root", 5) == 0)
176 				rootlogin = 1;
177 			*instance++ = '\0';
178 		} else
179 			instance = "";
180 #endif
181 		if (strlen(username) > UT_NAMESIZE)
182 			username[UT_NAMESIZE] = '\0';
183 
184 		/*
185 		 * Note if trying multiple user names; log failures for
186 		 * previous user name, but don't bother logging one failure
187 		 * for nonexistent name (mistyped username).
188 		 */
189 		if (failures && strcmp(tbuf, username)) {
190 			if (failures > (pwd ? 0 : 1))
191 				badlogin(tbuf);
192 			failures = 0;
193 		}
194 		(void)strcpy(tbuf, username);
195 
196 		if (pwd = getpwnam(username))
197 			salt = pwd->pw_passwd;
198 		else
199 			salt = "xx";
200 
201 		/*
202 		 * if we have a valid account name, and it doesn't have a
203 		 * password, or the -f option was specified and the caller
204 		 * is root or the caller isn't changing their uid, don't
205 		 * authenticate.
206 		 */
207 		if (pwd && (*pwd->pw_passwd == '\0' ||
208 		    fflag && (uid == 0 || uid == pwd->pw_uid)))
209 			break;
210 		fflag = 0;
211 		if (pwd && pwd->pw_uid == 0)
212 			rootlogin = 1;
213 
214 		(void)setpriority(PRIO_PROCESS, 0, -4);
215 
216 		p = getpass("Password:");
217 
218 		if (pwd) {
219 #ifdef KERBEROS
220 			rval = klogin(pwd, instance, localhost, p);
221 			if (rval != 0 && rootlogin && pwd->pw_uid != 0)
222 				rootlogin = 0;
223 			if (rval == 0)
224 				authok = 1;
225 			else if (rval == 1)
226 				rval = strcmp(crypt(p, salt), pwd->pw_passwd);
227 #else
228 			rval = strcmp(crypt(p, salt), pwd->pw_passwd);
229 #endif
230 		}
231 		memset(p, 0, strlen(p));
232 
233 		(void)setpriority(PRIO_PROCESS, 0, 0);
234 
235 		/*
236 		 * If trying to log in as root without Kerberos,
237 		 * but with insecure terminal, refuse the login attempt.
238 		 */
239 #ifdef KERBEROS
240 		if (authok == 0)
241 #endif
242 		if (pwd && rootlogin && !rootterm(tty)) {
243 			(void)fprintf(stderr,
244 			    "%s login refused on this terminal.\n",
245 			    pwd->pw_name);
246 			if (hostname)
247 				syslog(LOG_NOTICE,
248 				    "LOGIN %s REFUSED FROM %s ON TTY %s",
249 				    pwd->pw_name, hostname, tty);
250 			else
251 				syslog(LOG_NOTICE,
252 				    "LOGIN %s REFUSED ON TTY %s",
253 				     pwd->pw_name, tty);
254 			continue;
255 		}
256 
257 		if (pwd && !rval)
258 			break;
259 
260 		(void)printf("Login incorrect\n");
261 		failures++;
262 		/* we allow 10 tries, but after 3 we start backing off */
263 		if (++cnt > 3) {
264 			if (cnt >= 10) {
265 				badlogin(username);
266 				sleepexit(1);
267 			}
268 			sleep((u_int)((cnt - 3) * 5));
269 		}
270 	}
271 
272 	/* committed to login -- turn off timeout */
273 	(void)alarm((u_int)0);
274 
275 	endpwent();
276 
277 	/* if user not super-user, check for disabled logins */
278 	if (!rootlogin)
279 		checknologin();
280 
281 	if (chdir(pwd->pw_dir) < 0) {
282 		(void)printf("No home directory %s!\n", pwd->pw_dir);
283 		if (chdir("/"))
284 			exit(0);
285 		pwd->pw_dir = "/";
286 		(void)printf("Logging in with home = \"/\".\n");
287 	}
288 
289 	quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
290 
291 	if (pwd->pw_change || pwd->pw_expire)
292 		(void)gettimeofday(&tp, (struct timezone *)NULL);
293 	if (pwd->pw_change)
294 		if (tp.tv_sec >= pwd->pw_change) {
295 			(void)printf("Sorry -- your password has expired.\n");
296 			sleepexit(1);
297 		} else if (pwd->pw_change - tp.tv_sec <
298 		    2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
299 			(void)printf("Warning: your password expires on %s",
300 			    ctime(&pwd->pw_change));
301 	if (pwd->pw_expire)
302 		if (tp.tv_sec >= pwd->pw_expire) {
303 			(void)printf("Sorry -- your account has expired.\n");
304 			sleepexit(1);
305 		} else if (pwd->pw_expire - tp.tv_sec <
306 		    2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
307 			(void)printf("Warning: your account expires on %s",
308 			    ctime(&pwd->pw_expire));
309 
310 	/* Nothing else left to fail -- really log in. */
311 	memset((void *)&utmp, 0, sizeof(utmp));
312 	(void)time(&utmp.ut_time);
313 	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
314 	if (hostname)
315 		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
316 	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
317 	login(&utmp);
318 
319 	dolastlog(quietlog);
320 
321 	(void)chown(ttyn, pwd->pw_uid,
322 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
323 	(void)setgid(pwd->pw_gid);
324 
325 	initgroups(username, pwd->pw_gid);
326 
327 	if (*pwd->pw_shell == '\0')
328 		pwd->pw_shell = _PATH_BSHELL;
329 
330 	/* Destroy environment unless user has requested its preservation. */
331 	if (!pflag)
332 		environ = envinit;
333 	(void)setenv("HOME", pwd->pw_dir, 1);
334 	(void)setenv("SHELL", pwd->pw_shell, 1);
335 	if (term[0] == '\0')
336 		(void)strncpy(term, stypeof(tty), sizeof(term));
337 	(void)setenv("TERM", term, 0);
338 	(void)setenv("LOGNAME", pwd->pw_name, 1);
339 	(void)setenv("USER", pwd->pw_name, 1);
340 	(void)setenv("PATH", _PATH_DEFPATH, 0);
341 #ifdef KERBEROS
342 	if (krbtkfile_env)
343 		(void)setenv("KRBTKFILE", krbtkfile_env, 1);
344 #endif
345 
346 	if (tty[sizeof("tty")-1] == 'd')
347 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
348 
349 	/* If fflag is on, assume caller/authenticator has logged root login. */
350 	if (rootlogin && fflag == 0)
351 		if (hostname)
352 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
353 			    username, tty, hostname);
354 		else
355 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
356 
357 #ifdef KERBEROS
358 	if (!quietlog && notickets == 1)
359 		(void)printf("Warning: no Kerberos tickets issued.\n");
360 #endif
361 
362 	if (!quietlog) {
363 		(void)printf("%s\n\t%s  %s\n\n",
364 	    "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
365 		    "The Regents of the University of California. ",
366 		    "All rights reserved.");
367 		motd();
368 		(void)snprintf(tbuf,
369 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
370 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
371 			(void)printf("You have %smail.\n",
372 			    (st.st_mtime > st.st_atime) ? "new " : "");
373 	}
374 
375 	(void)signal(SIGALRM, SIG_DFL);
376 	(void)signal(SIGQUIT, SIG_DFL);
377 	(void)signal(SIGINT, SIG_DFL);
378 	(void)signal(SIGTSTP, SIG_IGN);
379 
380 	tbuf[0] = '-';
381 	(void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
382 	    p + 1 : pwd->pw_shell);
383 
384 	if (setlogin(pwd->pw_name) < 0)
385 		syslog(LOG_ERR, "setlogin() failure: %m");
386 
387 	/* Discard permissions last so can't get killed and drop core. */
388 	if (rootlogin)
389 		(void) setuid(0);
390 	else
391 		(void) setuid(pwd->pw_uid);
392 
393 	execlp(pwd->pw_shell, tbuf, 0);
394 	err(1, "%s", pwd->pw_shell);
395 }
396 
397 #ifdef	KERBEROS
398 #define	NBUFSIZ		(UT_NAMESIZE + 1 + 5)	/* .root suffix */
399 #else
400 #define	NBUFSIZ		(UT_NAMESIZE + 1)
401 #endif
402 
403 void
404 getloginname()
405 {
406 	int ch;
407 	char *p;
408 	static char nbuf[NBUFSIZ];
409 
410 	for (;;) {
411 		(void)printf("login: ");
412 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
413 			if (ch == EOF) {
414 				badlogin(username);
415 				exit(0);
416 			}
417 			if (p < nbuf + (NBUFSIZ - 1))
418 				*p++ = ch;
419 		}
420 		if (p > nbuf)
421 			if (nbuf[0] == '-')
422 				(void)fprintf(stderr,
423 				    "login names may not start with '-'.\n");
424 			else {
425 				*p = '\0';
426 				username = nbuf;
427 				break;
428 			}
429 	}
430 }
431 
432 int
433 rootterm(ttyn)
434 	char *ttyn;
435 {
436 	struct ttyent *t;
437 
438 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
439 }
440 
441 jmp_buf motdinterrupt;
442 
443 void
444 motd()
445 {
446 	int fd, nchars;
447 	sig_t oldint;
448 	char tbuf[8192];
449 
450 	if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
451 		return;
452 	oldint = signal(SIGINT, sigint);
453 	if (setjmp(motdinterrupt) == 0)
454 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
455 			(void)write(fileno(stdout), tbuf, nchars);
456 	(void)signal(SIGINT, oldint);
457 	(void)close(fd);
458 }
459 
460 /* ARGSUSED */
461 void
462 sigint(signo)
463 	int signo;
464 {
465 
466 	longjmp(motdinterrupt, 1);
467 }
468 
469 /* ARGSUSED */
470 void
471 timedout(signo)
472 	int signo;
473 {
474 
475 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
476 	exit(0);
477 }
478 
479 void
480 checknologin()
481 {
482 	int fd, nchars;
483 	char tbuf[8192];
484 
485 	if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
486 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
487 			(void)write(fileno(stdout), tbuf, nchars);
488 		sleepexit(0);
489 	}
490 }
491 
492 void
493 dolastlog(quiet)
494 	int quiet;
495 {
496 	struct lastlog ll;
497 	int fd;
498 
499 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
500 		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
501 		if (!quiet) {
502 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
503 			    ll.ll_time != 0) {
504 				(void)printf("Last login: %.*s ",
505 				    24-5, (char *)ctime(&ll.ll_time));
506 				if (*ll.ll_host != '\0')
507 					(void)printf("from %.*s\n",
508 					    (int)sizeof(ll.ll_host),
509 					    ll.ll_host);
510 				else
511 					(void)printf("on %.*s\n",
512 					    (int)sizeof(ll.ll_line),
513 					    ll.ll_line);
514 			}
515 			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
516 		}
517 		memset((void *)&ll, 0, sizeof(ll));
518 		(void)time(&ll.ll_time);
519 		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
520 		if (hostname)
521 			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
522 		(void)write(fd, (char *)&ll, sizeof(ll));
523 		(void)close(fd);
524 	}
525 }
526 
527 void
528 badlogin(name)
529 	char *name;
530 {
531 
532 	if (failures == 0)
533 		return;
534 	if (hostname) {
535 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
536 		    failures, failures > 1 ? "S" : "", hostname);
537 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
538 		    "%d LOGIN FAILURE%s FROM %s, %s",
539 		    failures, failures > 1 ? "S" : "", hostname, name);
540 	} else {
541 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
542 		    failures, failures > 1 ? "S" : "", tty);
543 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
544 		    "%d LOGIN FAILURE%s ON %s, %s",
545 		    failures, failures > 1 ? "S" : "", tty, name);
546 	}
547 }
548 
549 #undef	UNKNOWN
550 #define	UNKNOWN	"su"
551 
552 char *
553 stypeof(ttyid)
554 	char *ttyid;
555 {
556 	struct ttyent *t;
557 
558 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
559 }
560 
561 void
562 sleepexit(eval)
563 	int eval;
564 {
565 
566 	(void)sleep(5);
567 	exit(eval);
568 }
569