xref: /netbsd-src/libexec/getty/main.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: main.c,v 1.67 2020/02/26 15:44:57 riastradh 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.67 2020/02/26 15:44:57 riastradh 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 uid_t	ttyowner = 0;
103 char	*rawttyn;
104 
105 #define	OBUFSIZ		128
106 #define	TABBUFSIZ	512
107 
108 char	defent[TABBUFSIZ];
109 char	tabent[TABBUFSIZ];
110 
111 char	*env[128];
112 
113 const unsigned char partab[] = {
114 	0001,0201,0201,0001,0201,0001,0001,0201,
115 	0202,0004,0003,0205,0005,0206,0201,0001,
116 	0201,0001,0001,0201,0001,0201,0201,0001,
117 	0001,0201,0201,0001,0201,0001,0001,0201,
118 	0200,0000,0000,0200,0000,0200,0200,0000,
119 	0000,0200,0200,0000,0200,0000,0000,0200,
120 	0000,0200,0200,0000,0200,0000,0000,0200,
121 	0200,0000,0000,0200,0000,0200,0200,0000,
122 	0200,0000,0000,0200,0000,0200,0200,0000,
123 	0000,0200,0200,0000,0200,0000,0000,0200,
124 	0000,0200,0200,0000,0200,0000,0000,0200,
125 	0200,0000,0000,0200,0000,0200,0200,0000,
126 	0000,0200,0200,0000,0200,0000,0000,0200,
127 	0200,0000,0000,0200,0000,0200,0200,0000,
128 	0200,0000,0000,0200,0000,0200,0200,0000,
129 	0000,0200,0200,0000,0200,0000,0000,0201
130 };
131 
132 #define	ERASE	tmode.c_cc[VERASE]
133 #define	KILL	tmode.c_cc[VKILL]
134 #define	EOT	tmode.c_cc[VEOF]
135 
136 static void	clearscreen(void);
137 
138 sigjmp_buf timeout;
139 
140 __dead static void
141 /*ARGSUSED*/
142 dingdong(int signo)
143 {
144 
145 	(void)alarm(0);
146 	(void)signal(SIGALRM, SIG_DFL);
147 	siglongjmp(timeout, 1);
148 }
149 
150 sigjmp_buf intrupt;
151 
152 __dead static void
153 /*ARGSUSED*/
154 interrupt(int signo)
155 {
156 
157 	(void)signal(SIGINT, interrupt);
158 	siglongjmp(intrupt, 1);
159 }
160 
161 /*
162  * Action to take when getty is running too long.
163  */
164 __dead static void
165 /*ARGSUSED*/
166 timeoverrun(int signo)
167 {
168 
169 	syslog(LOG_ERR, "getty exiting due to excessive running time");
170 	exit(1);
171 }
172 
173 static int	getname(void);
174 static void	oflush(void);
175 static void	prompt(void);
176 static int	putchr(int);
177 static void	putf(const char *);
178 static void	xputs(const char *);
179 
180 #define putpad(s) tputs(s, 1, putchr)
181 
182 int
183 main(int argc, char *argv[], char *envp[])
184 {
185 	int repcnt = 0, failopenlogged = 0;
186 	volatile int first_time = 1;
187 	struct rlimit limit;
188 	int rval;
189 	/* this is used past the siglongjmp, so make sure it is not cached
190 	   in registers that might become invalid. */
191 	const char * volatile tname = "default";
192 
193 	(void)signal(SIGINT, SIG_IGN);
194 	openlog("getty", LOG_PID, LOG_AUTH);
195 	(void)gethostname(hostname, sizeof(hostname));
196 	hostname[sizeof(hostname) - 1] = '\0';
197 	if (hostname[0] == '\0')
198 		(void)strlcpy(hostname, "Amnesiac", sizeof(hostname));
199 	(void)uname(&kerninfo);
200 
201 	/*
202 	 * Limit running time to deal with broken or dead lines.
203 	 */
204 	(void)signal(SIGXCPU, timeoverrun);
205 	limit.rlim_max = RLIM_INFINITY;
206 	limit.rlim_cur = GETTY_TIMEOUT;
207 	(void)setrlimit(RLIMIT_CPU, &limit);
208 
209 	/*
210 	 * The following is a work around for vhangup interactions
211 	 * which cause great problems getting window systems started.
212 	 * If the tty line is "-", we do the old style getty presuming
213 	 * that the file descriptors are already set up for us.
214 	 * J. Gettys - MIT Project Athena.
215 	 */
216 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
217 		(void)strlcpy(ttyn, ttyname(0), sizeof(ttyn));
218 	}
219 	else {
220 		int i;
221 
222 		rawttyn = argv[2];
223 		(void)strlcpy(ttyn, dev, sizeof(ttyn));
224 		(void)strlcat(ttyn, argv[2], sizeof(ttyn));
225 		if (strcmp(argv[0], "+") != 0) {
226 			(void)chown(ttyn, ttyowner, 0);
227 			(void)chmod(ttyn, 0600);
228 			(void)revoke(ttyn);
229 			if (ttyaction(ttyn, "getty", "root"))
230 				syslog(LOG_WARNING, "%s: ttyaction failed",
231 					ttyn);
232 			/*
233 			 * Delay the open so DTR stays down long enough
234 			 * to be detected.
235 			 */
236 			(void)sleep(2);
237 			while ((i = open(ttyn, O_RDWR)) == -1) {
238 				if ((repcnt % 10 == 0) &&
239 				    (errno != ENXIO || !failopenlogged)) {
240 					syslog(LOG_WARNING, "%s: %m", ttyn);
241 					closelog();
242 					failopenlogged = 1;
243 				}
244 				repcnt++;
245 				(void)sleep(60);
246 			}
247 			(void)login_tty(i);
248 		}
249 	}
250 
251 	/* Start with default tty settings */
252 	if (tcgetattr(0, &tmode) < 0) {
253 		syslog(LOG_ERR, "%s: %m", ttyn);
254 		exit(1);
255 	}
256 	omode = tmode;
257 
258 	gettable("default", defent);
259 	gendefaults();
260 	if (argc > 1)
261 		tname = argv[1];
262 	for (;;) {
263 		int off;
264 
265 		rval = 0;
266 		gettable(tname, tabent);
267 		if (OPset || EPset || APset)
268 			APset++, OPset++, EPset++;
269 		setdefaults();
270 		off = 0;
271 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
272 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
273 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
274 
275 		if (IS)
276 			(void)cfsetispeed(&tmode, (speed_t)IS);
277 		else if (SP)
278 			(void)cfsetispeed(&tmode, (speed_t)SP);
279 		if (OS)
280 			(void)cfsetospeed(&tmode, (speed_t)OS);
281 		else if (SP)
282 			(void)cfsetospeed(&tmode, (speed_t)SP);
283 		setflags(0);
284 		setchars();
285 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
286 			syslog(LOG_ERR, "%s: %m", ttyn);
287 			exit(1);
288 		}
289 		if (AB) {
290 			tname = autobaud();
291 			continue;
292 		}
293 		if (PS) {
294 			tname = portselector();
295 			continue;
296 		}
297 		if (CS)
298 			clearscreen();
299 		if (CL && *CL)
300 			putpad(CL);
301 		edithost(HE);
302 
303 		/*
304 		 * If this is the first time through this, and an
305 		 * issue file has been given, then send it.
306 		 */
307 		if (first_time != 0 && IF != NULL) {
308 			char buf[_POSIX2_LINE_MAX];
309 			FILE *fp;
310 
311 			if ((fp = fopen(IF, "r")) != NULL) {
312 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
313 					putf(buf);
314 				(void)fclose(fp);
315 			}
316 		}
317 		first_time = 0;
318 
319 		if (IM && *IM)
320 			putf(IM);
321 		oflush();
322 		if (sigsetjmp(timeout, 1)) {
323 			tmode.c_ispeed = tmode.c_ospeed = 0;
324 			(void)tcsetattr(0, TCSANOW, &tmode);
325 			exit(1);
326 		}
327 		if (TO) {
328 			(void)signal(SIGALRM, dingdong);
329 			(void)alarm((unsigned int)TO);
330 		}
331 		if (NN) {
332 			name[0] = '\0';
333 			lower = 1;
334 			upper = digit_or_punc = 0;
335 		} else if (AL) {
336 			const char *p = AL;
337 			char *q = name;
338 
339 			while (*p && q < &name[sizeof name - 1]) {
340 				if (isupper((unsigned char)*p))
341 					upper = 1;
342 				else if (islower((unsigned char)*p))
343 					lower = 1;
344 				else if (isdigit((unsigned char)*p))
345 					digit_or_punc = 1;
346 				*q++ = *p++;
347 			}
348 		} else if ((rval = getname()) == 2) {
349 			setflags(2);
350 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
351 			syslog(LOG_ERR, "%s: %m", PP);
352 			exit(1);
353 		}
354 
355 		if (rval || AL || NN) {
356 			int i;
357 
358 			oflush();
359 			(void)alarm(0);
360 			(void)signal(SIGALRM, SIG_DFL);
361 			if (name[0] == '-') {
362 				xputs("user names may not start with '-'.");
363 				continue;
364 			}
365 			if (!(upper || lower || digit_or_punc))
366 				continue;
367 			setflags(2);
368 			if (crmod) {
369 				tmode.c_iflag |= ICRNL;
370 				tmode.c_oflag |= ONLCR;
371 			}
372 #if XXX
373 			if (upper || UC)
374 				tmode.sg_flags |= LCASE;
375 			if (lower || LC)
376 				tmode.sg_flags &= ~LCASE;
377 #endif
378 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
379 				syslog(LOG_ERR, "%s: %m", ttyn);
380 				exit(1);
381 			}
382 			(void)signal(SIGINT, SIG_DFL);
383 			for (i = 0; envp[i] != NULL; i++)
384 				env[i] = envp[i];
385 			makeenv(&env[i]);
386 
387 			limit.rlim_max = RLIM_INFINITY;
388 			limit.rlim_cur = RLIM_INFINITY;
389 			(void)setrlimit(RLIMIT_CPU, &limit);
390 			if (NN)
391 				(void)execle(LO, "login", AL ? "-fp" : "-p",
392 				    NULL, env);
393 			else
394 				(void)execle(LO, "login", AL ? "-fp" : "-p",
395 				    "--", name, NULL, env);
396 			syslog(LOG_ERR, "%s: %m", LO);
397 			exit(1);
398 		}
399 		(void)alarm(0);
400 		(void)signal(SIGALRM, SIG_DFL);
401 		(void)signal(SIGINT, SIG_IGN);
402 		if (NX && *NX)
403 			tname = NX;
404 	}
405 }
406 
407 static int
408 getname(void)
409 {
410 	int c;
411 	char *np;
412 	unsigned char cs;
413 	int ppp_state, ppp_connection;
414 
415 	/*
416 	 * Interrupt may happen if we use CBREAK mode
417 	 */
418 	if (sigsetjmp(intrupt, 1)) {
419 		(void)signal(SIGINT, SIG_IGN);
420 		return (0);
421 	}
422 	(void)signal(SIGINT, interrupt);
423 	setflags(1);
424 	prompt();
425 	if (PF > 0) {
426 		oflush();
427 		(void)sleep((unsigned int)PF);
428 		PF = 0;
429 	}
430 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
431 		syslog(LOG_ERR, "%s: %m", ttyn);
432 		exit(1);
433 	}
434 	crmod = digit_or_punc = lower = upper = 0;
435 	ppp_state = ppp_connection = 0;
436 	np = name;
437 	for (;;) {
438 		oflush();
439 		if (read(STDIN_FILENO, &cs, 1) <= 0)
440 			exit(0);
441 		if ((c = cs&0177) == 0)
442 			return (0);
443 
444 		/*
445 		 * PPP detection state machine..
446 		 * Look for sequences:
447 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
448 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
449 		 * See RFC1662.
450 		 * Derived from code from Michael Hancock <michaelh@cet.co.jp>
451 		 * and Erik 'PPP' Olson <eriko@wrq.com>
452 		 */
453 		if (PP && cs == PPP_FRAME) {
454 			ppp_state = 1;
455 		} else if (ppp_state == 1 && cs == PPP_STATION) {
456 			ppp_state = 2;
457 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
458 			ppp_state = 3;
459 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
460 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
461 			ppp_state = 4;
462 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
463 			ppp_state = 5;
464 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
465 			ppp_connection = 1;
466 			break;
467 		} else {
468 			ppp_state = 0;
469 		}
470 
471 		if (c == EOT)
472 			exit(1);
473 		if (c == '\r' || c == '\n' ||
474 		    np >= &name[LOGIN_NAME_MAX - 1]) {
475 			*np = '\0';
476 			putf("\r\n");
477 			break;
478 		}
479 		if (islower(c))
480 			lower = 1;
481 		else if (isupper(c))
482 			upper = 1;
483 		else if (c == ERASE || c == '#' || c == '\b') {
484 			if (np > name) {
485 				np--;
486 				if (cfgetospeed(&tmode) >= 1200)
487 					xputs("\b \b");
488 				else
489 					putchr(cs);
490 			}
491 			continue;
492 		} else if (c == KILL || c == '@') {
493 			putchr(cs);
494 			putchr('\r');
495 			if (cfgetospeed(&tmode) < 1200)
496 				putchr('\n');
497 			/* this is the way they do it down under ... */
498 			else if (np > name)
499 				xputs(
500 				    "                                     \r");
501 			prompt();
502 			np = name;
503 			continue;
504 		} else if (isdigit(c) || c == '_')
505 			digit_or_punc = 1;
506 		if (IG && (c <= ' ' || c > 0176))
507 			continue;
508 		*np++ = c;
509 		putchr(cs);
510 
511 		/*
512 		 * An MS-Windows direct connect PPP "client" won't send its
513 		 * first PPP packet until we respond to its "CLIENT" poll
514 		 * with a CRLF sequence.  We cater to yet another broken
515 		 * implementation of a previously-standard protocol...
516 		 */
517 		*np = '\0';
518 		if (strstr(name, "CLIENT"))
519 			putf("\r\n");
520 	}
521 	(void)signal(SIGINT, SIG_IGN);
522 	*np = 0;
523 	if (c == '\r')
524 		crmod = 1;
525 	if ((upper && !lower && !LC) || UC)
526 		for (np = name; *np; np++)
527 			*np = tolower((unsigned char)*np);
528 	return (1 + ppp_connection);
529 }
530 
531 static void
532 xputs(const char *s)
533 {
534 	while (*s)
535 		putchr(*s++);
536 }
537 
538 char	outbuf[OBUFSIZ];
539 size_t	obufcnt = 0;
540 
541 static int
542 putchr(int cc)
543 {
544 	unsigned char c;
545 
546 	c = cc;
547 	if (!NP) {
548 		c |= partab[c&0177] & 0200;
549 		if (OP)
550 			c ^= 0200;
551 	}
552 	if (!UB) {
553 		outbuf[obufcnt++] = c;
554 		if (obufcnt >= OBUFSIZ)
555 			oflush();
556 		return 1;
557 	}
558 	return write(STDOUT_FILENO, &c, 1);
559 }
560 
561 static void
562 oflush(void)
563 {
564 	if (obufcnt)
565 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
566 	obufcnt = 0;
567 }
568 
569 static void
570 prompt(void)
571 {
572 
573 	putf(LM);
574 	if (CO)
575 		putchr('\n');
576 }
577 
578 static void
579 putf(const char *cp)
580 {
581 	time_t t;
582 	char *slash, db[100];
583 
584 	while (*cp) {
585 		if (*cp != '%') {
586 			putchr(*cp++);
587 			continue;
588 		}
589 		switch (*++cp) {
590 
591 		case 't':
592 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
593 				slash = strrchr(ttyn, '/');
594 			if (slash == NULL)
595 				xputs(ttyn);
596 			else
597 				xputs(&slash[1]);
598 			break;
599 
600 		case 'h':
601 			xputs(editedhost);
602 			break;
603 
604 		case 'd':
605 			(void)time(&t);
606 			(void)strftime(db, sizeof(db),
607 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
608 			xputs(db);
609 			break;
610 
611 		case 's':
612 			xputs(kerninfo.sysname);
613 			break;
614 
615 		case 'm':
616 			xputs(kerninfo.machine);
617 			break;
618 
619 		case 'r':
620 			xputs(kerninfo.release);
621 			break;
622 
623 		case 'v':
624 			xputs(kerninfo.version);
625 			break;
626 
627 		case '%':
628 			putchr('%');
629 			break;
630 		}
631 		if (*cp)
632 			cp++;
633 	}
634 }
635 
636 static void
637 clearscreen(void)
638 {
639 	struct ttyent *typ;
640 	int err;
641 
642 	if (rawttyn == NULL)
643 		return;
644 
645 	typ = getttynam(rawttyn);
646 
647 	if ((typ == NULL) || (typ->ty_type == NULL) ||
648 	    (typ->ty_type[0] == 0))
649 		return;
650 
651 	if (setupterm(typ->ty_type, 0, &err) == ERR)
652 		return;
653 
654 	if (clear_screen)
655 		putpad(clear_screen);
656 
657 	del_curterm(cur_term);
658 	cur_term = NULL;
659 }
660