xref: /original-bsd/usr.bin/login/login.c.1 (revision 54e6d6c7)
1/*
2 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley.  The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18#ifndef lint
19char copyright[] =
20"@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\
21 All rights reserved.\n";
22#endif /* not lint */
23
24#ifndef lint
25static char sccsid[] = "@(#)login.c.1	5.34 (Berkeley) 02/23/89";
26#endif /* not lint */
27
28/*
29 * login [ name ]
30 * login -h hostname	(for telnetd, etc.)
31 * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
32 */
33
34#include <sys/param.h>
35#include <sys/quota.h>
36#include <sys/stat.h>
37#include <sys/time.h>
38#include <sys/resource.h>
39#include <sys/file.h>
40#include <sys/ioctl.h>
41
42#include <utmp.h>
43#include <signal.h>
44#include <lastlog.h>
45#include <errno.h>
46#include <ttyent.h>
47#include <syslog.h>
48#include <grp.h>
49#include <pwd.h>
50#include <setjmp.h>
51#include <stdio.h>
52#include <strings.h>
53
54#ifdef	KERBEROS
55#include <kerberos/krb.h>
56#include <sys/termios.h>
57char	realm[REALM_SZ];
58int	kerror = KSUCCESS, notickets = 1;
59#endif
60
61#define	TTYGRPNAME	"tty"		/* name of group to own ttys */
62
63#define	MOTDFILE	"/etc/motd"
64#define	MAILDIR		"/usr/spool/mail"
65#define	NOLOGIN		"/etc/nologin"
66#define	HUSHLOGIN	".hushlogin"
67#define	LASTLOG		"/usr/adm/lastlog"
68#define	BSHELL		"/bin/sh"
69
70/*
71 * This bounds the time given to login.  Not a define so it can
72 * be patched on machines where it's too small.
73 */
74int	timeout = 300;
75
76struct	passwd *pwd;
77int	failures;
78char	term[64], *hostname, *username, *tty;
79
80struct	sgttyb sgttyb;
81struct	tchars tc = {
82	CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
83};
84struct	ltchars ltc = {
85	CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
86};
87
88char *months[] =
89	{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
90	  "Sep", "Oct", "Nov", "Dec" };
91
92main(argc, argv)
93	int argc;
94	char **argv;
95{
96	extern int errno, optind;
97	extern char *optarg, **environ;
98	struct timeval tp;
99	struct tm *ttp;
100	struct group *gr;
101	register int ch;
102	register char *p;
103	int ask, fflag, hflag, pflag, cnt;
104	int quietlog, passwd_req, ioctlval, timedout();
105	char *domain, *salt, *envinit[1], *ttyn, *pp;
106	char tbuf[MAXPATHLEN + 2];
107	char *ctime(), *ttyname(), *stypeof(), *crypt(), *getpass();
108	time_t time();
109	off_t lseek();
110
111	(void)signal(SIGALRM, timedout);
112	(void)alarm((u_int)timeout);
113	(void)signal(SIGQUIT, SIG_IGN);
114	(void)signal(SIGINT, SIG_IGN);
115	(void)setpriority(PRIO_PROCESS, 0, 0);
116	(void)quota(Q_SETUID, 0, 0, 0);
117
118	/*
119	 * -p is used by getty to tell login not to destroy the environment
120 	 * -f is used to skip a second login authentication
121	 * -h is used by other servers to pass the name of the remote
122	 *    host to login so that it may be placed in utmp and wtmp
123	 */
124	(void)gethostname(tbuf, sizeof(tbuf));
125	domain = index(tbuf, '.');
126
127	fflag = hflag = pflag = 0;
128	passwd_req = 1;
129	while ((ch = getopt(argc, argv, "fh:p")) != EOF)
130		switch (ch) {
131		case 'f':
132			fflag = 1;
133			break;
134		case 'h':
135			if (getuid()) {
136				fprintf(stderr,
137				    "login: -h for super-user only.\n");
138				exit(1);
139			}
140			hflag = 1;
141			if (domain && (p = index(optarg, '.')) &&
142			    strcasecmp(p, domain) == 0)
143				*p = 0;
144			hostname = optarg;
145			break;
146		case 'p':
147			pflag = 1;
148			break;
149		case '?':
150		default:
151			fprintf(stderr, "usage: login [-fp] [username]\n");
152			exit(1);
153		}
154	argc -= optind;
155	argv += optind;
156	if (*argv) {
157		username = *argv;
158		ask = 0;
159	} else
160		ask = 1;
161
162	ioctlval = 0;
163	(void)ioctl(0, TIOCLSET, &ioctlval);
164	(void)ioctl(0, TIOCNXCL, 0);
165	(void)fcntl(0, F_SETFL, ioctlval);
166	(void)ioctl(0, TIOCGETP, &sgttyb);
167	sgttyb.sg_erase = CERASE;
168	sgttyb.sg_kill = CKILL;
169	(void)ioctl(0, TIOCSLTC, &ltc);
170	(void)ioctl(0, TIOCSETC, &tc);
171	(void)ioctl(0, TIOCSETP, &sgttyb);
172
173	for (cnt = getdtablesize(); cnt > 2; cnt--)
174		close(cnt);
175
176	ttyn = ttyname(0);
177	if (ttyn == NULL || *ttyn == '\0')
178		ttyn = "/dev/tty??";
179	if (tty = rindex(ttyn, '/'))
180		++tty;
181	else
182		tty = ttyn;
183
184	openlog("login", LOG_ODELAY, LOG_AUTH);
185
186	for (cnt = 0;; ask = 1) {
187		ioctlval = 0;
188		(void)ioctl(0, TIOCSETD, &ioctlval);
189
190		if (ask) {
191			fflag = 0;
192			getloginname();
193		}
194		/*
195		 * Note if trying multiple user names;
196		 * log failures for previous user name,
197		 * but don't bother logging one failure
198		 * for nonexistent name (mistyped username).
199		 */
200		if (failures && strcmp(tbuf, username)) {
201			if (failures > (pwd ? 0 : 1))
202				badlogin(tbuf);
203			failures = 0;
204		}
205		(void)strcpy(tbuf, username);
206		if (pwd = getpwnam(username))
207			salt = pwd->pw_passwd;
208		else
209			salt = "xx";
210
211		/* if user not super-user, check for disabled logins */
212		if (pwd == NULL || pwd->pw_uid)
213			checknologin();
214
215		/*
216		 * Disallow automatic login to root; if not invoked by
217		 * root, disallow if the uid's differ.
218		 */
219		if (fflag && pwd) {
220			int uid = getuid();
221
222			passwd_req = pwd->pw_uid == 0 ||
223			    (uid && uid != pwd->pw_uid);
224		}
225
226		/*
227		 * If no pre-authentication and a password exists
228		 * for this user, prompt for one and verify it.
229		 */
230		if (!passwd_req || (pwd && !*pwd->pw_passwd))
231			break;
232
233		setpriority(PRIO_PROCESS, 0, -4);
234		pp = getpass("Password:");
235		p = crypt(pp, salt);
236		setpriority(PRIO_PROCESS, 0, 0);
237
238#ifdef	KERBEROS
239
240		/*
241		 * If not present in pw file, act as we normally would.
242		 * If we aren't Kerberos-authenticated, try the normal
243		 * pw file for a password.  If that's ok, log the user
244		 * in without issueing any tickets.
245		 */
246
247		if (pwd && !krb_get_lrealm(realm,1)) {
248			/* get TGT for local realm
249			 * be careful about uid's here for ticket
250			 * file ownership
251			 */
252			(void) setreuid(geteuid(),pwd->pw_uid);
253			kerror = krb_get_pw_in_tkt(
254				pwd->pw_name, "", realm,
255				"krbtgt", realm, DEFAULT_TKT_LIFE, pp);
256			(void) setuid(0);
257			if (kerror == INTK_OK) {
258				bzero(pp, strlen(pp));
259				notickets = 0;	/* user got ticket */
260				break;
261			}
262		}
263#endif
264		(void) bzero(pp, strlen(pp));
265		if (pwd && !strcmp(p, pwd->pw_passwd))
266			break;
267
268		printf("Login incorrect\n");
269		failures++;
270		/* we allow 10 tries, but after 3 we start backing off */
271		if (++cnt > 3) {
272			if (cnt >= 10) {
273				badlogin(username);
274				(void)ioctl(0, TIOCHPCL, (struct sgttyb *)NULL);
275				sleepexit(1);
276			}
277			sleep((u_int)((cnt - 3) * 5));
278		}
279	}
280
281	/* committed to login -- turn off timeout */
282	(void)alarm((u_int)0);
283
284	/*
285	 * If valid so far and root is logging in, see if root logins on
286	 * this terminal are permitted.
287	 */
288	if (pwd->pw_uid == 0 && !rootterm(tty)) {
289		if (hostname)
290			syslog(LOG_NOTICE, "ROOT LOGIN REFUSED FROM %s",
291			    hostname);
292		else
293			syslog(LOG_NOTICE, "ROOT LOGIN REFUSED ON %s", tty);
294		printf("Login incorrect\n");
295		sleepexit(1);
296	}
297
298	if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
299		switch(errno) {
300		case EUSERS:
301			fprintf(stderr,
302		"Too many users logged on already.\nTry again later.\n");
303			break;
304		case EPROCLIM:
305			fprintf(stderr,
306			    "You have too many processes running.\n");
307			break;
308		default:
309			perror("quota (Q_SETUID)");
310		}
311		sleepexit(0);
312	}
313
314	if (chdir(pwd->pw_dir) < 0) {
315		printf("No directory %s!\n", pwd->pw_dir);
316		if (chdir("/"))
317			exit(0);
318		pwd->pw_dir = "/";
319		printf("Logging in with home = \"/\".\n");
320	}
321
322#ifdef KERBEROS
323	if (notickets)
324		printf("Warning: no Kerberos tickets issued\n");
325#endif
326
327#define	TWOWEEKS	(14*24*60*60)
328	if (pwd->pw_change || pwd->pw_expire)
329		(void)gettimeofday(&tp, (struct timezone *)NULL);
330	if (pwd->pw_change)
331		if (tp.tv_sec >= pwd->pw_change) {
332			printf("Sorry -- your password has expired.\n");
333			sleepexit(1);
334		}
335		else if (tp.tv_sec - pwd->pw_change < TWOWEEKS) {
336			ttp = localtime(&pwd->pw_change);
337			printf("Warning: your password expires on %s %d, 19%d\n",
338			    months[ttp->tm_mon], ttp->tm_mday, ttp->tm_year);
339		}
340	if (pwd->pw_expire)
341		if (tp.tv_sec >= pwd->pw_expire) {
342			printf("Sorry -- your account has expired.\n");
343			sleepexit(1);
344		}
345		else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS) {
346			ttp = localtime(&pwd->pw_expire);
347			printf("Warning: your account expires on %s %d, 19%d\n",
348			    months[ttp->tm_mon], ttp->tm_mday, ttp->tm_year);
349		}
350
351	/* nothing else left to fail -- really log in */
352	{
353		struct utmp utmp;
354
355		bzero((char *)&utmp, sizeof(utmp));
356		(void)time(&utmp.ut_time);
357		strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
358		if (hostname)
359			strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
360		strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
361		login(&utmp);
362	}
363
364	quietlog = access(HUSHLOGIN, F_OK) == 0;
365	dolastlog(quietlog);
366
367	if (!hflag) {					/* XXX */
368		static struct winsize win = { 0, 0, 0, 0 };
369
370		(void)ioctl(0, TIOCSWINSZ, &win);
371	}
372
373	(void)chown(ttyn, pwd->pw_uid,
374	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
375	(void)chmod(ttyn, 0620);
376	(void)setgid(pwd->pw_gid);
377
378	initgroups(username, pwd->pw_gid);
379
380	quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
381	(void)setuid(pwd->pw_uid);
382
383	if (*pwd->pw_shell == '\0')
384		pwd->pw_shell = BSHELL;
385	/* turn on new line discipline for the csh */
386	else if (!strcmp(pwd->pw_shell, "/bin/csh")) {
387		ioctlval = NTTYDISC;
388		(void)ioctl(0, TIOCSETD, &ioctlval);
389	}
390
391	/* destroy environment unless user has requested preservation */
392	if (!pflag)
393		environ = envinit;
394	(void)setenv("HOME", pwd->pw_dir, 1);
395	(void)setenv("SHELL", pwd->pw_shell, 1);
396	if (term[0] == '\0')
397		strncpy(term, stypeof(tty), sizeof(term));
398	(void)setenv("TERM", term, 0);
399	(void)setenv("USER", pwd->pw_name, 1);
400	(void)setenv("PATH", "/usr/ucb:/bin:/usr/bin:", 0);
401
402	if (tty[sizeof("tty")-1] == 'd')
403		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
404	if (pwd->pw_uid == 0)
405		if (hostname)
406			syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s",
407			    tty, hostname);
408		else
409			syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty);
410
411	if (!quietlog) {
412		struct stat st;
413
414		motd();
415		(void)sprintf(tbuf, "%s/%s", MAILDIR, pwd->pw_name);
416		if (stat(tbuf, &st) == 0 && st.st_size != 0)
417			printf("You have %smail.\n",
418			    (st.st_mtime > st.st_atime) ? "new " : "");
419	}
420
421	(void)signal(SIGALRM, SIG_DFL);
422	(void)signal(SIGQUIT, SIG_DFL);
423	(void)signal(SIGINT, SIG_DFL);
424	(void)signal(SIGTSTP, SIG_IGN);
425
426	tbuf[0] = '-';
427	strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ?
428	    p + 1 : pwd->pw_shell);
429	execlp(pwd->pw_shell, tbuf, 0);
430	fprintf(stderr, "login: no shell: ");
431	perror(pwd->pw_shell);
432	exit(0);
433}
434
435getloginname()
436{
437	register int ch;
438	register char *p;
439	static char nbuf[UT_NAMESIZE + 1];
440
441	for (;;) {
442		printf("login: ");
443		for (p = nbuf; (ch = getchar()) != '\n'; ) {
444			if (ch == EOF) {
445				badlogin(username);
446				exit(0);
447			}
448			if (p < nbuf + UT_NAMESIZE)
449				*p++ = ch;
450		}
451		if (p > nbuf)
452			if (nbuf[0] == '-')
453				fprintf(stderr,
454				    "login names may not start with '-'.\n");
455			else {
456				*p = '\0';
457				username = nbuf;
458				break;
459			}
460	}
461}
462
463timedout()
464{
465	fprintf(stderr, "Login timed out after %d seconds\n", timeout);
466	exit(0);
467}
468
469rootterm(ttyn)
470	char *ttyn;
471{
472	struct ttyent *t;
473
474	return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE);
475}
476
477jmp_buf motdinterrupt;
478
479motd()
480{
481	register int fd, nchars;
482	int (*oldint)(), sigint();
483	char tbuf[8192];
484
485	if ((fd = open(MOTDFILE, O_RDONLY, 0)) < 0)
486		return;
487	oldint = signal(SIGINT, sigint);
488	if (setjmp(motdinterrupt) == 0)
489		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
490			(void)write(fileno(stdout), tbuf, nchars);
491	(void)signal(SIGINT, oldint);
492	(void)close(fd);
493}
494
495sigint()
496{
497	longjmp(motdinterrupt, 1);
498}
499
500checknologin()
501{
502	register int fd, nchars;
503	char tbuf[8192];
504
505	if ((fd = open(NOLOGIN, O_RDONLY, 0)) >= 0) {
506		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
507			(void)write(fileno(stdout), tbuf, nchars);
508		sleepexit(0);
509	}
510}
511
512dolastlog(quiet)
513	int quiet;
514{
515	struct lastlog ll;
516	int fd;
517	char *ctime();
518
519	if ((fd = open(LASTLOG, O_RDWR, 0)) >= 0) {
520		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
521		if (!quiet) {
522			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
523			    ll.ll_time != 0) {
524				printf("Last login: %.*s ",
525				    24-5, (char *)ctime(&ll.ll_time));
526				if (*ll.ll_host != '\0')
527					printf("from %.*s\n",
528					    sizeof(ll.ll_host), ll.ll_host);
529				else
530					printf("on %.*s\n",
531					    sizeof(ll.ll_line), ll.ll_line);
532			}
533			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
534		}
535		bzero((char *)&ll, sizeof(ll));
536		(void)time(&ll.ll_time);
537		strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
538		if (hostname)
539			strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
540		(void)write(fd, (char *)&ll, sizeof(ll));
541		(void)close(fd);
542	}
543}
544
545badlogin(name)
546	char *name;
547{
548	if (failures == 0)
549		return;
550	if (hostname)
551		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s",
552		    failures, failures > 1 ? "S" : "", hostname, name);
553	else
554		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s",
555		    failures, failures > 1 ? "S" : "", tty, name);
556}
557
558#undef	UNKNOWN
559#define	UNKNOWN	"su"
560
561char *
562stypeof(ttyid)
563	char *ttyid;
564{
565	struct ttyent *t;
566
567	return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
568}
569
570getstr(buf, cnt, err)
571	char *buf, *err;
572	int cnt;
573{
574	char ch;
575
576	do {
577		if (read(0, &ch, sizeof(ch)) != sizeof(ch))
578			exit(1);
579		if (--cnt < 0) {
580			fprintf(stderr, "%s too long\r\n", err);
581			sleepexit(1);
582		}
583		*buf++ = ch;
584	} while (ch);
585}
586
587sleepexit(eval)
588	int eval;
589{
590	sleep((u_int)5);
591	exit(eval);
592}
593