xref: /netbsd-src/libexec/getty/main.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: main.c,v 1.66 2017/01/10 21:08:15 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 
34 #ifndef lint
35 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
36  The Regents of the University of California.  All rights reserved.");
37 #endif /* not lint */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";
42 #else
43 __RCSID("$NetBSD: main.c,v 1.66 2017/01/10 21:08:15 christos Exp $");
44 #endif
45 #endif /* not lint */
46 
47 #include <sys/param.h>
48 #include <sys/ioctl.h>
49 #include <sys/resource.h>
50 #include <sys/stat.h>
51 #include <sys/utsname.h>
52 
53 #include <ctype.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <limits.h>
57 #include <pwd.h>
58 #include <setjmp.h>
59 #include <signal.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <syslog.h>
64 #include <term.h>
65 #include <termios.h>
66 #include <time.h>
67 #include <ttyent.h>
68 #include <unistd.h>
69 #include <util.h>
70 
71 #include "gettytab.h"
72 #include "pathnames.h"
73 #include "extern.h"
74 
75 extern char editedhost[];
76 
77 /*
78  * Set the amount of running time that getty should accumulate
79  * before deciding that something is wrong and exit.
80  */
81 #define GETTY_TIMEOUT	60 /* seconds */
82 
83 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
84 
85 #define PPP_FRAME           0x7e  /* PPP Framing character */
86 #define PPP_STATION         0xff  /* "All Station" character */
87 #define PPP_ESCAPE          0x7d  /* Escape Character */
88 #define PPP_CONTROL         0x03  /* PPP Control Field */
89 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
90 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
91 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
92 
93 struct termios tmode, omode;
94 
95 int crmod, digit_or_punc, lower, upper;
96 
97 char	hostname[MAXHOSTNAMELEN + 1];
98 struct	utsname kerninfo;
99 char	name[LOGIN_NAME_MAX];
100 char	dev[] = _PATH_DEV;
101 char	ttyn[32];
102 char	lockfile[512];
103 uid_t	ttyowner;
104 char	*rawttyn;
105 
106 #define	OBUFSIZ		128
107 #define	TABBUFSIZ	512
108 
109 char	defent[TABBUFSIZ];
110 char	tabent[TABBUFSIZ];
111 
112 char	*env[128];
113 
114 const unsigned char partab[] = {
115 	0001,0201,0201,0001,0201,0001,0001,0201,
116 	0202,0004,0003,0205,0005,0206,0201,0001,
117 	0201,0001,0001,0201,0001,0201,0201,0001,
118 	0001,0201,0201,0001,0201,0001,0001,0201,
119 	0200,0000,0000,0200,0000,0200,0200,0000,
120 	0000,0200,0200,0000,0200,0000,0000,0200,
121 	0000,0200,0200,0000,0200,0000,0000,0200,
122 	0200,0000,0000,0200,0000,0200,0200,0000,
123 	0200,0000,0000,0200,0000,0200,0200,0000,
124 	0000,0200,0200,0000,0200,0000,0000,0200,
125 	0000,0200,0200,0000,0200,0000,0000,0200,
126 	0200,0000,0000,0200,0000,0200,0200,0000,
127 	0000,0200,0200,0000,0200,0000,0000,0200,
128 	0200,0000,0000,0200,0000,0200,0200,0000,
129 	0200,0000,0000,0200,0000,0200,0200,0000,
130 	0000,0200,0200,0000,0200,0000,0000,0201
131 };
132 
133 #define	ERASE	tmode.c_cc[VERASE]
134 #define	KILL	tmode.c_cc[VKILL]
135 #define	EOT	tmode.c_cc[VEOF]
136 
137 static void	clearscreen(void);
138 
139 sigjmp_buf timeout;
140 
141 __dead static void
142 /*ARGSUSED*/
143 dingdong(int signo)
144 {
145 
146 	(void)alarm(0);
147 	(void)signal(SIGALRM, SIG_DFL);
148 	siglongjmp(timeout, 1);
149 }
150 
151 sigjmp_buf intrupt;
152 
153 __dead static void
154 /*ARGSUSED*/
155 interrupt(int signo)
156 {
157 
158 	(void)signal(SIGINT, interrupt);
159 	siglongjmp(intrupt, 1);
160 }
161 
162 /*
163  * Action to take when getty is running too long.
164  */
165 __dead static void
166 /*ARGSUSED*/
167 timeoverrun(int signo)
168 {
169 
170 	syslog(LOG_ERR, "getty exiting due to excessive running time");
171 	exit(1);
172 }
173 
174 static int	getname(void);
175 static void	oflush(void);
176 static void	prompt(void);
177 static int	putchr(int);
178 static void	putf(const char *);
179 static void	xputs(const char *);
180 
181 #define putpad(s) tputs(s, 1, putchr)
182 
183 int
184 main(int argc, char *argv[], char *envp[])
185 {
186 	const char *progname;
187 	int repcnt = 0, failopenlogged = 0;
188 	volatile int first_time = 1;
189 	struct rlimit limit;
190 	struct passwd *pw;
191 	int rval;
192 	/* this is used past the siglongjmp, so make sure it is not cached
193 	   in registers that might become invalid. */
194 	volatile int uugetty = 0;
195 	const char * volatile tname = "default";
196 
197 	(void)signal(SIGINT, SIG_IGN);
198 	openlog("getty", LOG_PID, LOG_AUTH);
199 	(void)gethostname(hostname, sizeof(hostname));
200 	hostname[sizeof(hostname) - 1] = '\0';
201 	if (hostname[0] == '\0')
202 		(void)strlcpy(hostname, "Amnesiac", sizeof(hostname));
203 	(void)uname(&kerninfo);
204 
205 	progname = getprogname();
206 	if (progname[0] == 'u' && progname[1] == 'u')
207 		uugetty = 1;
208 
209 	/*
210 	 * Find id of uucp login (if present) so we can chown tty properly.
211 	 */
212 	if (uugetty && (pw = getpwnam("uucp")))
213 		ttyowner = pw->pw_uid;
214 	else
215 		ttyowner = 0;
216 
217 	/*
218 	 * Limit running time to deal with broken or dead lines.
219 	 */
220 	(void)signal(SIGXCPU, timeoverrun);
221 	limit.rlim_max = RLIM_INFINITY;
222 	limit.rlim_cur = GETTY_TIMEOUT;
223 	(void)setrlimit(RLIMIT_CPU, &limit);
224 
225 	/*
226 	 * The following is a work around for vhangup interactions
227 	 * which cause great problems getting window systems started.
228 	 * If the tty line is "-", we do the old style getty presuming
229 	 * that the file descriptors are already set up for us.
230 	 * J. Gettys - MIT Project Athena.
231 	 */
232 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
233 		(void)strlcpy(ttyn, ttyname(0), sizeof(ttyn));
234 	}
235 	else {
236 		int i;
237 
238 		rawttyn = argv[2];
239 		(void)strlcpy(ttyn, dev, sizeof(ttyn));
240 		(void)strlcat(ttyn, argv[2], sizeof(ttyn));
241 		if (uugetty)  {
242 			(void)chown(ttyn, ttyowner, 0);
243 			(void)strlcpy(lockfile, _PATH_LOCK,
244 				sizeof(lockfile));
245 			(void)strlcat(lockfile, argv[2],
246 				sizeof(lockfile));
247 			/*
248 			 * wait for lockfiles to go away before we try
249 			 * to open
250 			 */
251 			if (pidlock(lockfile, 0, 0, 0) != 0)  {
252 				syslog(LOG_ERR,
253 					"%s: can't create lockfile", ttyn);
254 				exit(1);
255 			}
256 			(void)unlink(lockfile);
257 		}
258 		if (strcmp(argv[0], "+") != 0) {
259 			(void)chown(ttyn, ttyowner, 0);
260 			(void)chmod(ttyn, 0600);
261 			(void)revoke(ttyn);
262 			if (ttyaction(ttyn, "getty", "root"))
263 				syslog(LOG_WARNING, "%s: ttyaction failed",
264 					ttyn);
265 			/*
266 			 * Delay the open so DTR stays down long enough
267 			 * to be detected.
268 			 */
269 			(void)sleep(2);
270 			while ((i = open(ttyn, O_RDWR)) == -1) {
271 				if ((repcnt % 10 == 0) &&
272 				    (errno != ENXIO || !failopenlogged)) {
273 					syslog(LOG_WARNING, "%s: %m", ttyn);
274 					closelog();
275 					failopenlogged = 1;
276 				}
277 				repcnt++;
278 				(void)sleep(60);
279 			}
280 			if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
281 				syslog(LOG_ERR, "%s: can't create lockfile",
282 					ttyn);
283 				exit(1);
284 			}
285 			if (uugetty)
286 				(void)chown(lockfile, ttyowner, 0);
287 			(void)login_tty(i);
288 		}
289 	}
290 
291 	/* Start with default tty settings */
292 	if (tcgetattr(0, &tmode) < 0) {
293 		syslog(LOG_ERR, "%s: %m", ttyn);
294 		exit(1);
295 	}
296 	omode = tmode;
297 
298 	gettable("default", defent);
299 	gendefaults();
300 	if (argc > 1)
301 		tname = argv[1];
302 	for (;;) {
303 		int off;
304 
305 		rval = 0;
306 		gettable(tname, tabent);
307 		if (OPset || EPset || APset)
308 			APset++, OPset++, EPset++;
309 		setdefaults();
310 		off = 0;
311 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
312 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
313 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
314 
315 		if (IS)
316 			(void)cfsetispeed(&tmode, (speed_t)IS);
317 		else if (SP)
318 			(void)cfsetispeed(&tmode, (speed_t)SP);
319 		if (OS)
320 			(void)cfsetospeed(&tmode, (speed_t)OS);
321 		else if (SP)
322 			(void)cfsetospeed(&tmode, (speed_t)SP);
323 		setflags(0);
324 		setchars();
325 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
326 			syslog(LOG_ERR, "%s: %m", ttyn);
327 			exit(1);
328 		}
329 		if (AB) {
330 			tname = autobaud();
331 			continue;
332 		}
333 		if (PS) {
334 			tname = portselector();
335 			continue;
336 		}
337 		if (CS)
338 			clearscreen();
339 		if (CL && *CL)
340 			putpad(CL);
341 		edithost(HE);
342 
343 		/*
344 		 * If this is the first time through this, and an
345 		 * issue file has been given, then send it.
346 		 */
347 		if (first_time != 0 && IF != NULL) {
348 			char buf[_POSIX2_LINE_MAX];
349 			FILE *fp;
350 
351 			if ((fp = fopen(IF, "r")) != NULL) {
352 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
353 					putf(buf);
354 				(void)fclose(fp);
355 			}
356 		}
357 		first_time = 0;
358 
359 		if (IM && *IM)
360 			putf(IM);
361 		oflush();
362 		if (sigsetjmp(timeout, 1)) {
363 			tmode.c_ispeed = tmode.c_ospeed = 0;
364 			(void)tcsetattr(0, TCSANOW, &tmode);
365 			exit(1);
366 		}
367 		if (TO) {
368 			(void)signal(SIGALRM, dingdong);
369 			(void)alarm((unsigned int)TO);
370 		}
371 		if (NN) {
372 			name[0] = '\0';
373 			lower = 1;
374 			upper = digit_or_punc = 0;
375 		} else if (AL) {
376 			const char *p = AL;
377 			char *q = name;
378 
379 			while (*p && q < &name[sizeof name - 1]) {
380 				if (isupper((unsigned char)*p))
381 					upper = 1;
382 				else if (islower((unsigned char)*p))
383 					lower = 1;
384 				else if (isdigit((unsigned char)*p))
385 					digit_or_punc = 1;
386 				*q++ = *p++;
387 			}
388 		} else if ((rval = getname()) == 2) {
389 			setflags(2);
390 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
391 			syslog(LOG_ERR, "%s: %m", PP);
392 			exit(1);
393 		}
394 
395 		if (rval || AL || NN) {
396 			int i;
397 
398 			oflush();
399 			(void)alarm(0);
400 			(void)signal(SIGALRM, SIG_DFL);
401 			if (name[0] == '-') {
402 				xputs("user names may not start with '-'.");
403 				continue;
404 			}
405 			if (!(upper || lower || digit_or_punc))
406 				continue;
407 			setflags(2);
408 			if (crmod) {
409 				tmode.c_iflag |= ICRNL;
410 				tmode.c_oflag |= ONLCR;
411 			}
412 #if XXX
413 			if (upper || UC)
414 				tmode.sg_flags |= LCASE;
415 			if (lower || LC)
416 				tmode.sg_flags &= ~LCASE;
417 #endif
418 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
419 				syslog(LOG_ERR, "%s: %m", ttyn);
420 				exit(1);
421 			}
422 			(void)signal(SIGINT, SIG_DFL);
423 			for (i = 0; envp[i] != NULL; i++)
424 				env[i] = envp[i];
425 			makeenv(&env[i]);
426 
427 			limit.rlim_max = RLIM_INFINITY;
428 			limit.rlim_cur = RLIM_INFINITY;
429 			(void)setrlimit(RLIMIT_CPU, &limit);
430 			if (NN)
431 				(void)execle(LO, "login", AL ? "-fp" : "-p",
432 				    NULL, env);
433 			else
434 				(void)execle(LO, "login", AL ? "-fp" : "-p",
435 				    "--", name, NULL, env);
436 			syslog(LOG_ERR, "%s: %m", LO);
437 			exit(1);
438 		}
439 		(void)alarm(0);
440 		(void)signal(SIGALRM, SIG_DFL);
441 		(void)signal(SIGINT, SIG_IGN);
442 		if (NX && *NX)
443 			tname = NX;
444 		if (uugetty)
445 			(void)unlink(lockfile);
446 	}
447 }
448 
449 static int
450 getname(void)
451 {
452 	int c;
453 	char *np;
454 	unsigned char cs;
455 	int ppp_state, ppp_connection;
456 
457 	/*
458 	 * Interrupt may happen if we use CBREAK mode
459 	 */
460 	if (sigsetjmp(intrupt, 1)) {
461 		(void)signal(SIGINT, SIG_IGN);
462 		return (0);
463 	}
464 	(void)signal(SIGINT, interrupt);
465 	setflags(1);
466 	prompt();
467 	if (PF > 0) {
468 		oflush();
469 		(void)sleep((unsigned int)PF);
470 		PF = 0;
471 	}
472 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
473 		syslog(LOG_ERR, "%s: %m", ttyn);
474 		exit(1);
475 	}
476 	crmod = digit_or_punc = lower = upper = 0;
477 	ppp_state = ppp_connection = 0;
478 	np = name;
479 	for (;;) {
480 		oflush();
481 		if (read(STDIN_FILENO, &cs, 1) <= 0)
482 			exit(0);
483 		if ((c = cs&0177) == 0)
484 			return (0);
485 
486 		/*
487 		 * PPP detection state machine..
488 		 * Look for sequences:
489 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
490 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
491 		 * See RFC1662.
492 		 * Derived from code from Michael Hancock <michaelh@cet.co.jp>
493 		 * and Erik 'PPP' Olson <eriko@wrq.com>
494 		 */
495 		if (PP && cs == PPP_FRAME) {
496 			ppp_state = 1;
497 		} else if (ppp_state == 1 && cs == PPP_STATION) {
498 			ppp_state = 2;
499 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
500 			ppp_state = 3;
501 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
502 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
503 			ppp_state = 4;
504 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
505 			ppp_state = 5;
506 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
507 			ppp_connection = 1;
508 			break;
509 		} else {
510 			ppp_state = 0;
511 		}
512 
513 		if (c == EOT)
514 			exit(1);
515 		if (c == '\r' || c == '\n' ||
516 		    np >= &name[LOGIN_NAME_MAX - 1]) {
517 			*np = '\0';
518 			putf("\r\n");
519 			break;
520 		}
521 		if (islower(c))
522 			lower = 1;
523 		else if (isupper(c))
524 			upper = 1;
525 		else if (c == ERASE || c == '#' || c == '\b') {
526 			if (np > name) {
527 				np--;
528 				if (cfgetospeed(&tmode) >= 1200)
529 					xputs("\b \b");
530 				else
531 					putchr(cs);
532 			}
533 			continue;
534 		} else if (c == KILL || c == '@') {
535 			putchr(cs);
536 			putchr('\r');
537 			if (cfgetospeed(&tmode) < 1200)
538 				putchr('\n');
539 			/* this is the way they do it down under ... */
540 			else if (np > name)
541 				xputs(
542 				    "                                     \r");
543 			prompt();
544 			np = name;
545 			continue;
546 		} else if (isdigit(c) || c == '_')
547 			digit_or_punc = 1;
548 		if (IG && (c <= ' ' || c > 0176))
549 			continue;
550 		*np++ = c;
551 		putchr(cs);
552 
553 		/*
554 		 * An MS-Windows direct connect PPP "client" won't send its
555 		 * first PPP packet until we respond to its "CLIENT" poll
556 		 * with a CRLF sequence.  We cater to yet another broken
557 		 * implementation of a previously-standard protocol...
558 		 */
559 		*np = '\0';
560 		if (strstr(name, "CLIENT"))
561 			putf("\r\n");
562 	}
563 	(void)signal(SIGINT, SIG_IGN);
564 	*np = 0;
565 	if (c == '\r')
566 		crmod = 1;
567 	if ((upper && !lower && !LC) || UC)
568 		for (np = name; *np; np++)
569 			*np = tolower((unsigned char)*np);
570 	return (1 + ppp_connection);
571 }
572 
573 static void
574 xputs(const char *s)
575 {
576 	while (*s)
577 		putchr(*s++);
578 }
579 
580 char	outbuf[OBUFSIZ];
581 size_t	obufcnt = 0;
582 
583 static int
584 putchr(int cc)
585 {
586 	unsigned char c;
587 
588 	c = cc;
589 	if (!NP) {
590 		c |= partab[c&0177] & 0200;
591 		if (OP)
592 			c ^= 0200;
593 	}
594 	if (!UB) {
595 		outbuf[obufcnt++] = c;
596 		if (obufcnt >= OBUFSIZ)
597 			oflush();
598 		return 1;
599 	}
600 	return write(STDOUT_FILENO, &c, 1);
601 }
602 
603 static void
604 oflush(void)
605 {
606 	if (obufcnt)
607 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
608 	obufcnt = 0;
609 }
610 
611 static void
612 prompt(void)
613 {
614 
615 	putf(LM);
616 	if (CO)
617 		putchr('\n');
618 }
619 
620 static void
621 putf(const char *cp)
622 {
623 	time_t t;
624 	char *slash, db[100];
625 
626 	while (*cp) {
627 		if (*cp != '%') {
628 			putchr(*cp++);
629 			continue;
630 		}
631 		switch (*++cp) {
632 
633 		case 't':
634 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
635 				slash = strrchr(ttyn, '/');
636 			if (slash == NULL)
637 				xputs(ttyn);
638 			else
639 				xputs(&slash[1]);
640 			break;
641 
642 		case 'h':
643 			xputs(editedhost);
644 			break;
645 
646 		case 'd':
647 			(void)time(&t);
648 			(void)strftime(db, sizeof(db),
649 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
650 			xputs(db);
651 			break;
652 
653 		case 's':
654 			xputs(kerninfo.sysname);
655 			break;
656 
657 		case 'm':
658 			xputs(kerninfo.machine);
659 			break;
660 
661 		case 'r':
662 			xputs(kerninfo.release);
663 			break;
664 
665 		case 'v':
666 			xputs(kerninfo.version);
667 			break;
668 
669 		case '%':
670 			putchr('%');
671 			break;
672 		}
673 		if (*cp)
674 			cp++;
675 	}
676 }
677 
678 static void
679 clearscreen(void)
680 {
681 	struct ttyent *typ;
682 	int err;
683 
684 	if (rawttyn == NULL)
685 		return;
686 
687 	typ = getttynam(rawttyn);
688 
689 	if ((typ == NULL) || (typ->ty_type == NULL) ||
690 	    (typ->ty_type[0] == 0))
691 		return;
692 
693 	if (setupterm(typ->ty_type, 0, &err) == ERR)
694 		return;
695 
696 	if (clear_screen)
697 		putpad(clear_screen);
698 
699 	del_curterm(cur_term);
700 	cur_term = NULL;
701 }
702