xref: /original-bsd/usr.bin/login/login.c (revision f1dcf51e)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)login.c	5.1 (Berkeley) 04/30/85";
15 #endif not lint
16 
17 /*
18  * login [ name ]
19  * login -r hostname (for rlogind)
20  * login -h hostname (for telnetd, etc.)
21  */
22 
23 #include <sys/param.h>
24 #include <sys/quota.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <sys/resource.h>
28 #include <sys/file.h>
29 
30 #include <sgtty.h>
31 #include <utmp.h>
32 #include <signal.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <lastlog.h>
36 #include <errno.h>
37 #include <ttyent.h>
38 #include <syslog.h>
39 
40 #define	SCMPN(a, b)	strncmp(a, b, sizeof(a))
41 #define	SCPYN(a, b)	strncpy(a, b, sizeof(a))
42 
43 #define NMAX	sizeof(utmp.ut_name)
44 
45 #define	FALSE	0
46 #define	TRUE	-1
47 
48 char	nolog[] =	"/etc/nologin";
49 char	qlog[]  =	".hushlogin";
50 char	maildir[30] =	"/usr/spool/mail/";
51 char	lastlog[] =	"/usr/adm/lastlog";
52 struct	passwd nouser = {"", "nope", -1, -1, -1, "", "", "", "" };
53 struct	sgttyb ttyb;
54 struct	utmp utmp;
55 char	minusnam[16] = "-";
56 char	*envinit[] = { 0 };		/* now set by setenv calls */
57 /*
58  * This bounds the time given to login.  We initialize it here
59  * so it can be patched on machines where it's too small.
60  */
61 int	timeout = 60;
62 
63 char	term[64];
64 
65 struct	passwd *pwd;
66 char	*strcat(), *rindex(), *index(), *malloc(), *realloc();
67 int	timedout();
68 char	*ttyname();
69 char	*crypt();
70 char	*getpass();
71 char	*stypeof();
72 extern	char **environ;
73 extern	int errno;
74 
75 struct	tchars tc = {
76 	CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
77 };
78 struct	ltchars ltc = {
79 	CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
80 };
81 
82 struct winsize win = { 0, 0, 0, 0 };
83 
84 int	rflag;
85 char	rusername[NMAX+1], lusername[NMAX+1];
86 char	rpassword[NMAX+1];
87 char	name[NMAX+1];
88 char	*rhost;
89 
90 main(argc, argv)
91 	char *argv[];
92 {
93 	register char *namep;
94 	int pflag = 0, hflag = 0, t, f, c;
95 	int invalid, quietlog;
96 	FILE *nlfd;
97 	char *ttyn, *tty;
98 	int ldisc = 0, zero = 0, i;
99 	char **envnew;
100 
101 	signal(SIGALRM, timedout);
102 	alarm(timeout);
103 	signal(SIGQUIT, SIG_IGN);
104 	signal(SIGINT, SIG_IGN);
105 	setpriority(PRIO_PROCESS, 0, 0);
106 	quota(Q_SETUID, 0, 0, 0);
107 	/*
108 	 * -p is used by getty to tell login not to destroy the environment
109 	 * -r is used by rlogind to cause the autologin protocol;
110 	 * -h is used by other servers to pass the name of the
111 	 * remote host to login so that it may be placed in utmp and wtmp
112 	 */
113 	if (argc > 1) {
114 		if (strcmp(argv[1], "-r") == 0) {
115 			rflag = doremotelogin(argv[2]);
116 			SCPYN(utmp.ut_host, argv[2]);
117 			argc = 0;
118 		}
119 		if (strcmp(argv[1], "-h") == 0 && getuid() == 0) {
120 			hflag = 1;
121 			SCPYN(utmp.ut_host, argv[2]);
122 			argc = 0;
123 		}
124 		if (strcmp(argv[1], "-p") == 0) {
125 			argc--;
126 			argv++;
127 			pflag = 1;
128 		}
129 	}
130 	ioctl(0, TIOCLSET, &zero);
131 	ioctl(0, TIOCNXCL, 0);
132 	ioctl(0, FIONBIO, &zero);
133 	ioctl(0, FIOASYNC, &zero);
134 	ioctl(0, TIOCGETP, &ttyb);
135 	/*
136 	 * If talking to an rlogin process,
137 	 * propagate the terminal type and
138 	 * baud rate across the network.
139 	 */
140 	if (rflag)
141 		doremoteterm(term, &ttyb);
142 	ioctl(0, TIOCSLTC, &ltc);
143 	ioctl(0, TIOCSETC, &tc);
144 	ioctl(0, TIOCSETP, &ttyb);
145 	for (t = getdtablesize(); t > 3; t--)
146 		close(t);
147 	ttyn = ttyname(0);
148 	if (ttyn == (char *)0)
149 		ttyn = "/dev/tty??";
150 	tty = rindex(ttyn, '/');
151 	if (tty == NULL)
152 		tty = ttyn;
153 	else
154 		tty++;
155 	openlog("login", LOG_ODELAY, 0);
156 	t = 0;
157 	do {
158 		ldisc = 0;
159 		ioctl(0, TIOCSETD, &ldisc);
160 		invalid = FALSE;
161 		SCPYN(utmp.ut_name, "");
162 		/*
163 		 * Name specified, take it.
164 		 */
165 		if (argc > 1) {
166 			SCPYN(utmp.ut_name, argv[1]);
167 			argc = 0;
168 		}
169 		/*
170 		 * If remote login take given name,
171 		 * otherwise prompt user for something.
172 		 */
173 		if (rflag) {
174 			SCPYN(utmp.ut_name, lusername);
175 			/* autologin failed, prompt for passwd */
176 			if (rflag == -1)
177 				rflag = 0;
178 		} else
179 			getloginname(&utmp);
180 		if (!strcmp(pwd->pw_shell, "/bin/csh")) {
181 			ldisc = NTTYDISC;
182 			ioctl(0, TIOCSETD, &ldisc);
183 		}
184 		/*
185 		 * If no remote login authentication and
186 		 * a password exists for this user, prompt
187 		 * for one and verify it.
188 		 */
189 		if (!rflag && *pwd->pw_passwd != '\0') {
190 			char *pp;
191 
192 			setpriority(PRIO_PROCESS, 0, -4);
193 			pp = getpass("Password:");
194 			namep = crypt(pp, pwd->pw_passwd);
195 			setpriority(PRIO_PROCESS, 0, 0);
196 			if (strcmp(namep, pwd->pw_passwd))
197 				invalid = TRUE;
198 		}
199 		/*
200 		 * If user not super-user, check for logins disabled.
201 		 */
202 		if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) > 0) {
203 			while ((c = getc(nlfd)) != EOF)
204 				putchar(c);
205 			fflush(stdout);
206 			sleep(5);
207 			exit(0);
208 		}
209 		/*
210 		 * If valid so far and root is logging in,
211 		 * see if root logins on this terminal are permitted.
212 		 */
213 		if (!invalid && pwd->pw_uid == 0 && !rootterm(tty)) {
214 			syslog(LOG_SECURITY, "ROOT LOGIN REFUSED %s", tty);
215 			invalid = TRUE;
216 		}
217 		if (invalid) {
218 			printf("Login incorrect\n");
219 			if (++t >= 5) {
220 				syslog(LOG_SECURITY,
221 				    "REPEATED LOGIN FAILURES %s, %s",
222 					tty, utmp.ut_name);
223 				ioctl(0, TIOCHPCL, (struct sgttyb *) 0);
224 				close(0), close(1), close(2);
225 				sleep(10);
226 				exit(1);
227 			}
228 		}
229 		if (*pwd->pw_shell == '\0')
230 			pwd->pw_shell = "/bin/sh";
231 		if (chdir(pwd->pw_dir) < 0 && !invalid ) {
232 			if (chdir("/") < 0) {
233 				printf("No directory!\n");
234 				invalid = TRUE;
235 			} else {
236 				printf("No directory! %s\n",
237 				   "Logging in with home=/");
238 				pwd->pw_dir = "/";
239 			}
240 		}
241 		/*
242 		 * Remote login invalid must have been because
243 		 * of a restriction of some sort, no extra chances.
244 		 */
245 		if (rflag && invalid)
246 			exit(1);
247 	} while (invalid);
248 /* committed to login turn off timeout */
249 	alarm(0);
250 
251 	if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0) {
252 		if (errno == EUSERS)
253 			printf("%s.\n%s.\n",
254 			   "Too many users logged on already",
255 			   "Try again later");
256 		else if (errno == EPROCLIM)
257 			printf("You have too many processes running.\n");
258 		else
259 			perror("quota (Q_SETUID)");
260 		sleep(5);
261 		exit(0);
262 	}
263 	time(&utmp.ut_time);
264 	t = ttyslot();
265 	if (t > 0 && (f = open("/etc/utmp", O_WRONLY)) >= 0) {
266 		lseek(f, (long)(t*sizeof(utmp)), 0);
267 		SCPYN(utmp.ut_line, tty);
268 		write(f, (char *)&utmp, sizeof(utmp));
269 		close(f);
270 	}
271 	if ((f = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) {
272 		write(f, (char *)&utmp, sizeof(utmp));
273 		close(f);
274 	}
275 	quietlog = access(qlog, F_OK) == 0;
276 	if ((f = open(lastlog, O_RDWR)) >= 0) {
277 		struct lastlog ll;
278 
279 		lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
280 		if (read(f, (char *) &ll, sizeof ll) == sizeof ll &&
281 		    ll.ll_time != 0 && !quietlog) {
282 			printf("Last login: %.*s ",
283 			    24-5, (char *)ctime(&ll.ll_time));
284 			if (*ll.ll_host != '\0')
285 				printf("from %.*s\n",
286 				    sizeof (ll.ll_host), ll.ll_host);
287 			else
288 				printf("on %.*s\n",
289 				    sizeof (ll.ll_line), ll.ll_line);
290 		}
291 		lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
292 		time(&ll.ll_time);
293 		SCPYN(ll.ll_line, tty);
294 		SCPYN(ll.ll_host, utmp.ut_host);
295 		write(f, (char *) &ll, sizeof ll);
296 		close(f);
297 	}
298 	chown(ttyn, pwd->pw_uid, pwd->pw_gid);
299 	if (!hflag)					/* XXX */
300 		ioctl(0, TIOCSWINSZ, &win);
301 	chmod(ttyn, 0622);
302 	setgid(pwd->pw_gid);
303 	strncpy(name, utmp.ut_name, NMAX);
304 	name[NMAX] = '\0';
305 	initgroups(name, pwd->pw_gid);
306 	quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
307 	setuid(pwd->pw_uid);
308 	/* destroy environment unless user has asked to preserve it */
309 	if (!pflag)
310 		environ = envinit;
311 
312 	/* set up environment, this time without destruction */
313 	/* copy the environment before setenving */
314 	i = 0;
315 	while (environ[i] != NULL)
316 		i++;
317 	envnew = (char **) malloc(sizeof (char *) * (i + 1));
318 	for (; i >= 0; i--)
319 		envnew[i] = environ[i];
320 	environ = envnew;
321 
322 	setenv("HOME=", pwd->pw_dir);
323 	setenv("SHELL=", pwd->pw_shell);
324 	if (term[0] == '\0')
325 		strncpy(term, stypeof(tty), sizeof(term));
326 	setenv("TERM=", term);
327 	setenv("USER=", pwd->pw_name);
328 	setenv("PATH=", ":/usr/ucb:/bin:/usr/bin");
329 
330 	if ((namep = rindex(pwd->pw_shell, '/')) == NULL)
331 		namep = pwd->pw_shell;
332 	else
333 		namep++;
334 	strcat(minusnam, namep);
335 	if (tty[sizeof("tty")-1] == 'd')
336 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
337 	if (pwd->pw_uid == 0)
338 		syslog(LOG_SECURITY, "ROOT LOGIN %s", tty);
339 	if (!quietlog) {
340 		struct stat st;
341 
342 		showmotd();
343 		strcat(maildir, pwd->pw_name);
344 		if (stat(maildir, &st) == 0 && st.st_size != 0)
345 			printf("You have %smail.\n",
346 				(st.st_mtime > st.st_atime) ? "new " : "");
347 	}
348 	signal(SIGALRM, SIG_DFL);
349 	signal(SIGQUIT, SIG_DFL);
350 	signal(SIGINT, SIG_DFL);
351 	signal(SIGTSTP, SIG_IGN);
352 	execlp(pwd->pw_shell, minusnam, 0);
353 	perror(pwd->pw_shell);
354 	printf("No shell\n");
355 	exit(0);
356 }
357 
358 getloginname(up)
359 	register struct utmp *up;
360 {
361 	register char *namep;
362 	char c;
363 
364 	while (up->ut_name[0] == '\0') {
365 		namep = up->ut_name;
366 		printf("login: ");
367 		while ((c = getchar()) != '\n') {
368 			if (c == ' ')
369 				c = '_';
370 			if (c == EOF)
371 				exit(0);
372 			if (namep < up->ut_name+NMAX)
373 				*namep++ = c;
374 		}
375 	}
376 	strncpy(lusername, up->ut_name, NMAX);
377 	lusername[NMAX] = 0;
378 	if ((pwd = getpwnam(lusername)) == NULL)
379 		pwd = &nouser;
380 }
381 
382 timedout()
383 {
384 
385 	printf("Login timed out after %d seconds\n", timeout);
386 	exit(0);
387 }
388 
389 int	stopmotd;
390 catch()
391 {
392 
393 	signal(SIGINT, SIG_IGN);
394 	stopmotd++;
395 }
396 
397 rootterm(tty)
398 	char *tty;
399 {
400 	register struct ttyent *t;
401 
402 	if ((t = getttynam(tty)) != NULL) {
403 		if (t->ty_status & TTY_SECURE)
404 			return (1);
405 	}
406 	return (0);
407 }
408 
409 showmotd()
410 {
411 	FILE *mf;
412 	register c;
413 
414 	signal(SIGINT, catch);
415 	if ((mf = fopen("/etc/motd", "r")) != NULL) {
416 		while ((c = getc(mf)) != EOF && stopmotd == 0)
417 			putchar(c);
418 		fclose(mf);
419 	}
420 	signal(SIGINT, SIG_IGN);
421 }
422 
423 #undef	UNKNOWN
424 #define UNKNOWN "su"
425 
426 char *
427 stypeof(ttyid)
428 	char *ttyid;
429 {
430 	register struct ttyent *t;
431 
432 	if (ttyid == NULL || (t = getttynam(ttyid)) == NULL)
433 		return (UNKNOWN);
434 	return (t->ty_type);
435 }
436 
437 doremotelogin(host)
438 	char *host;
439 {
440 	FILE *hostf;
441 	int first = 1;
442 
443 	getstr(rusername, sizeof (rusername), "remuser");
444 	getstr(lusername, sizeof (lusername), "locuser");
445 	getstr(term, sizeof(term), "Terminal type");
446 	if (getuid()) {
447 		pwd = &nouser;
448 		goto bad;
449 	}
450 	pwd = getpwnam(lusername);
451 	if (pwd == NULL) {
452 		pwd = &nouser;
453 		goto bad;
454 	}
455 	hostf = pwd->pw_uid ? fopen("/etc/hosts.equiv", "r") : 0;
456 again:
457 	if (hostf) {
458 		char ahost[32];
459 
460 		while (fgets(ahost, sizeof (ahost), hostf)) {
461 			register char *p;
462 			char *user;
463 
464 			p = ahost;
465 			while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0')
466 				p++;
467 			if (*p == ' ' || *p == '\t') {
468 				*p++ = '\0';
469 				while (*p == ' ' || *p == '\t')
470 					p++;
471 				user = p;
472 				while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0')
473 					p++;
474 			} else
475 				user = p;
476 			*p = '\0';
477 			if (!strcmp(host, ahost) &&
478 			    !strcmp(rusername, *user ? user : lusername)) {
479 				fclose(hostf);
480 				return (1);
481 			}
482 		}
483 		fclose(hostf);
484 	}
485 	if (first == 1) {
486 		char *rhosts = ".rhosts";
487 		struct stat sbuf;
488 
489 		first = 0;
490 		if (chdir(pwd->pw_dir) < 0)
491 			goto again;
492 		if (lstat(rhosts, &sbuf) < 0)
493 			goto again;
494 		if ((sbuf.st_mode & S_IFMT) == S_IFLNK) {
495 			printf("login: .rhosts is a soft link.\r\n");
496 			goto bad;
497 		}
498 		hostf = fopen(rhosts, "r");
499 		fstat(fileno(hostf), &sbuf);
500 		if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) {
501 			printf("login: Bad .rhosts ownership.\r\n");
502 			fclose(hostf);
503 			goto bad;
504 		}
505 		goto again;
506 	}
507 bad:
508 	return (-1);
509 }
510 
511 getstr(buf, cnt, err)
512 	char *buf;
513 	int cnt;
514 	char *err;
515 {
516 	char c;
517 
518 	do {
519 		if (read(0, &c, 1) != 1)
520 			exit(1);
521 		if (--cnt < 0) {
522 			printf("%s too long\r\n", err);
523 			exit(1);
524 		}
525 		*buf++ = c;
526 	} while (c != 0);
527 }
528 
529 char	*speeds[] =
530     { "0", "50", "75", "110", "134", "150", "200", "300",
531       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
532 #define	NSPEEDS	(sizeof (speeds) / sizeof (speeds[0]))
533 
534 doremoteterm(term, tp)
535 	char *term;
536 	struct sgttyb *tp;
537 {
538 	register char *cp = index(term, '/'), **cpp;
539 	char *speed;
540 	struct winsize ws;
541 
542 	if (cp) {
543 		*cp++ = '\0';
544 		speed = cp;
545 		cp = index(speed, '/');
546 		if (cp)
547 			*cp++ = '\0';
548 		for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
549 			if (strcmp(*cpp, speed) == 0) {
550 				tp->sg_ispeed = tp->sg_ospeed = cpp-speeds;
551 				break;
552 			}
553 		ws.ws_row = ws.ws_col = -1;
554 		ws.ws_xpixel = ws.ws_ypixel = -1;
555 		if (cp) {
556 			ws.ws_row = atoi(cp);
557 			cp = index(cp, ',');
558 			if (cp == 0)
559 				goto done;
560 			ws.ws_col = atoi(++cp);
561 			cp = index(cp, ',');
562 			if (cp == 0)
563 				goto done;
564 			ws.ws_xpixel = atoi(++cp);
565 			cp = index(cp, ',');
566 			if (cp == 0)
567 				goto done;
568 			ws.ws_ypixel = atoi(++cp);
569 		}
570 done:
571 		if (ws.ws_row != -1 && ws.ws_col != -1 &&
572 		    ws.ws_xpixel != -1 && ws.ws_ypixel != -1)
573 			win = ws;
574 	}
575 	tp->sg_flags = ECHO|CRMOD|ANYP|XTABS;
576 }
577 
578 /*
579  * Set the value of var to be arg in the Unix 4.2 BSD environment env.
580  * Var should end with '='.
581  * (bindings are of the form "var=value")
582  * This procedure assumes the memory for the first level of environ
583  * was allocated using malloc.
584  */
585 setenv(var, value)
586 	char *var, *value;
587 {
588 	extern char **environ;
589 	int index = 0;
590 	int varlen = strlen(var);
591 	int vallen = strlen(value);
592 
593 	for (index = 0; environ[index] != NULL; index++) {
594 		if (strncmp(environ[index], var, varlen) == 0) {
595 			/* found it */
596 			environ[index] = malloc(varlen + vallen + 1);
597 			strcpy(environ[index], var);
598 			strcat(environ[index], value);
599 			return;
600 		}
601 	}
602 	environ = (char **) realloc(environ, sizeof (char *) * (index + 2));
603 	if (environ == NULL) {
604 		fprintf(stderr, "login: malloc out of memory\n");
605 		exit(1);
606 	}
607 	environ[index] = malloc(varlen + vallen + 1);
608 	strcpy(environ[index], var);
609 	strcat(environ[index], value);
610 	environ[++index] = NULL;
611 }
612