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