xref: /netbsd-src/libexec/getty/main.c (revision caba86e5d2c05a270b81d3bcd9151d74997e8104)
1 /*	$NetBSD: main.c,v 1.68 2021/10/12 23:40:38 jmcneill 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.68 2021/10/12 23:40:38 jmcneill 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*/
dingdong(int signo)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*/
interrupt(int signo)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*/
timeoverrun(int signo)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
main(int argc,char * argv[],char * envp[])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 			while ((i = open(ttyn, O_RDWR)) == -1) {
233 				if ((repcnt % 10 == 0) &&
234 				    (errno != ENXIO || !failopenlogged)) {
235 					syslog(LOG_WARNING, "%s: %m", ttyn);
236 					closelog();
237 					failopenlogged = 1;
238 				}
239 				repcnt++;
240 				(void)sleep(60);
241 			}
242 			(void)login_tty(i);
243 		}
244 	}
245 
246 	/* Start with default tty settings */
247 	if (tcgetattr(0, &tmode) < 0) {
248 		syslog(LOG_ERR, "%s: %m", ttyn);
249 		exit(1);
250 	}
251 	omode = tmode;
252 
253 	gettable("default", defent);
254 	gendefaults();
255 	if (argc > 1)
256 		tname = argv[1];
257 	for (;;) {
258 		int off;
259 
260 		rval = 0;
261 		gettable(tname, tabent);
262 		if (OPset || EPset || APset)
263 			APset++, OPset++, EPset++;
264 		setdefaults();
265 		off = 0;
266 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
267 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
268 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
269 
270 		if (IS)
271 			(void)cfsetispeed(&tmode, (speed_t)IS);
272 		else if (SP)
273 			(void)cfsetispeed(&tmode, (speed_t)SP);
274 		if (OS)
275 			(void)cfsetospeed(&tmode, (speed_t)OS);
276 		else if (SP)
277 			(void)cfsetospeed(&tmode, (speed_t)SP);
278 		setflags(0);
279 		setchars();
280 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
281 			syslog(LOG_ERR, "%s: %m", ttyn);
282 			exit(1);
283 		}
284 		if (AB) {
285 			tname = autobaud();
286 			continue;
287 		}
288 		if (PS) {
289 			tname = portselector();
290 			continue;
291 		}
292 		if (CS)
293 			clearscreen();
294 		if (CL && *CL)
295 			putpad(CL);
296 		edithost(HE);
297 
298 		/*
299 		 * If this is the first time through this, and an
300 		 * issue file has been given, then send it.
301 		 */
302 		if (first_time != 0 && IF != NULL) {
303 			char buf[_POSIX2_LINE_MAX];
304 			FILE *fp;
305 
306 			if ((fp = fopen(IF, "r")) != NULL) {
307 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
308 					putf(buf);
309 				(void)fclose(fp);
310 			}
311 		}
312 		first_time = 0;
313 
314 		if (IM && *IM)
315 			putf(IM);
316 		oflush();
317 		if (sigsetjmp(timeout, 1)) {
318 			tmode.c_ispeed = tmode.c_ospeed = 0;
319 			(void)tcsetattr(0, TCSANOW, &tmode);
320 			exit(1);
321 		}
322 		if (TO) {
323 			(void)signal(SIGALRM, dingdong);
324 			(void)alarm((unsigned int)TO);
325 		}
326 		if (NN) {
327 			name[0] = '\0';
328 			lower = 1;
329 			upper = digit_or_punc = 0;
330 		} else if (AL) {
331 			const char *p = AL;
332 			char *q = name;
333 
334 			while (*p && q < &name[sizeof name - 1]) {
335 				if (isupper((unsigned char)*p))
336 					upper = 1;
337 				else if (islower((unsigned char)*p))
338 					lower = 1;
339 				else if (isdigit((unsigned char)*p))
340 					digit_or_punc = 1;
341 				*q++ = *p++;
342 			}
343 		} else if ((rval = getname()) == 2) {
344 			setflags(2);
345 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
346 			syslog(LOG_ERR, "%s: %m", PP);
347 			exit(1);
348 		}
349 
350 		if (rval || AL || NN) {
351 			int i;
352 
353 			oflush();
354 			(void)alarm(0);
355 			(void)signal(SIGALRM, SIG_DFL);
356 			if (name[0] == '-') {
357 				xputs("user names may not start with '-'.");
358 				continue;
359 			}
360 			if (!(upper || lower || digit_or_punc))
361 				continue;
362 			setflags(2);
363 			if (crmod) {
364 				tmode.c_iflag |= ICRNL;
365 				tmode.c_oflag |= ONLCR;
366 			}
367 #if XXX
368 			if (upper || UC)
369 				tmode.sg_flags |= LCASE;
370 			if (lower || LC)
371 				tmode.sg_flags &= ~LCASE;
372 #endif
373 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
374 				syslog(LOG_ERR, "%s: %m", ttyn);
375 				exit(1);
376 			}
377 			(void)signal(SIGINT, SIG_DFL);
378 			for (i = 0; envp[i] != NULL; i++)
379 				env[i] = envp[i];
380 			makeenv(&env[i]);
381 
382 			limit.rlim_max = RLIM_INFINITY;
383 			limit.rlim_cur = RLIM_INFINITY;
384 			(void)setrlimit(RLIMIT_CPU, &limit);
385 			if (NN)
386 				(void)execle(LO, "login", AL ? "-fp" : "-p",
387 				    NULL, env);
388 			else
389 				(void)execle(LO, "login", AL ? "-fp" : "-p",
390 				    "--", name, NULL, env);
391 			syslog(LOG_ERR, "%s: %m", LO);
392 			exit(1);
393 		}
394 		(void)alarm(0);
395 		(void)signal(SIGALRM, SIG_DFL);
396 		(void)signal(SIGINT, SIG_IGN);
397 		if (NX && *NX)
398 			tname = NX;
399 	}
400 }
401 
402 static int
getname(void)403 getname(void)
404 {
405 	int c;
406 	char *np;
407 	unsigned char cs;
408 	int ppp_state, ppp_connection;
409 
410 	/*
411 	 * Interrupt may happen if we use CBREAK mode
412 	 */
413 	if (sigsetjmp(intrupt, 1)) {
414 		(void)signal(SIGINT, SIG_IGN);
415 		return (0);
416 	}
417 	(void)signal(SIGINT, interrupt);
418 	setflags(1);
419 	prompt();
420 	if (PF > 0) {
421 		oflush();
422 		(void)sleep((unsigned int)PF);
423 		PF = 0;
424 	}
425 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
426 		syslog(LOG_ERR, "%s: %m", ttyn);
427 		exit(1);
428 	}
429 	crmod = digit_or_punc = lower = upper = 0;
430 	ppp_state = ppp_connection = 0;
431 	np = name;
432 	for (;;) {
433 		oflush();
434 		if (read(STDIN_FILENO, &cs, 1) <= 0)
435 			exit(0);
436 		if ((c = cs&0177) == 0)
437 			return (0);
438 
439 		/*
440 		 * PPP detection state machine..
441 		 * Look for sequences:
442 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
443 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
444 		 * See RFC1662.
445 		 * Derived from code from Michael Hancock <michaelh@cet.co.jp>
446 		 * and Erik 'PPP' Olson <eriko@wrq.com>
447 		 */
448 		if (PP && cs == PPP_FRAME) {
449 			ppp_state = 1;
450 		} else if (ppp_state == 1 && cs == PPP_STATION) {
451 			ppp_state = 2;
452 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
453 			ppp_state = 3;
454 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
455 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
456 			ppp_state = 4;
457 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
458 			ppp_state = 5;
459 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
460 			ppp_connection = 1;
461 			break;
462 		} else {
463 			ppp_state = 0;
464 		}
465 
466 		if (c == EOT)
467 			exit(1);
468 		if (c == '\r' || c == '\n' ||
469 		    np >= &name[LOGIN_NAME_MAX - 1]) {
470 			*np = '\0';
471 			putf("\r\n");
472 			break;
473 		}
474 		if (islower(c))
475 			lower = 1;
476 		else if (isupper(c))
477 			upper = 1;
478 		else if (c == ERASE || c == '#' || c == '\b') {
479 			if (np > name) {
480 				np--;
481 				if (cfgetospeed(&tmode) >= 1200)
482 					xputs("\b \b");
483 				else
484 					putchr(cs);
485 			}
486 			continue;
487 		} else if (c == KILL || c == '@') {
488 			putchr(cs);
489 			putchr('\r');
490 			if (cfgetospeed(&tmode) < 1200)
491 				putchr('\n');
492 			/* this is the way they do it down under ... */
493 			else if (np > name)
494 				xputs(
495 				    "                                     \r");
496 			prompt();
497 			np = name;
498 			continue;
499 		} else if (isdigit(c) || c == '_')
500 			digit_or_punc = 1;
501 		if (IG && (c <= ' ' || c > 0176))
502 			continue;
503 		*np++ = c;
504 		putchr(cs);
505 
506 		/*
507 		 * An MS-Windows direct connect PPP "client" won't send its
508 		 * first PPP packet until we respond to its "CLIENT" poll
509 		 * with a CRLF sequence.  We cater to yet another broken
510 		 * implementation of a previously-standard protocol...
511 		 */
512 		*np = '\0';
513 		if (strstr(name, "CLIENT"))
514 			putf("\r\n");
515 	}
516 	(void)signal(SIGINT, SIG_IGN);
517 	*np = 0;
518 	if (c == '\r')
519 		crmod = 1;
520 	if ((upper && !lower && !LC) || UC)
521 		for (np = name; *np; np++)
522 			*np = tolower((unsigned char)*np);
523 	return (1 + ppp_connection);
524 }
525 
526 static void
xputs(const char * s)527 xputs(const char *s)
528 {
529 	while (*s)
530 		putchr(*s++);
531 }
532 
533 char	outbuf[OBUFSIZ];
534 size_t	obufcnt = 0;
535 
536 static int
putchr(int cc)537 putchr(int cc)
538 {
539 	unsigned char c;
540 
541 	c = cc;
542 	if (!NP) {
543 		c |= partab[c&0177] & 0200;
544 		if (OP)
545 			c ^= 0200;
546 	}
547 	if (!UB) {
548 		outbuf[obufcnt++] = c;
549 		if (obufcnt >= OBUFSIZ)
550 			oflush();
551 		return 1;
552 	}
553 	return write(STDOUT_FILENO, &c, 1);
554 }
555 
556 static void
oflush(void)557 oflush(void)
558 {
559 	if (obufcnt)
560 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
561 	obufcnt = 0;
562 }
563 
564 static void
prompt(void)565 prompt(void)
566 {
567 
568 	putf(LM);
569 	if (CO)
570 		putchr('\n');
571 }
572 
573 static void
putf(const char * cp)574 putf(const char *cp)
575 {
576 	time_t t;
577 	char *slash, db[100];
578 
579 	while (*cp) {
580 		if (*cp != '%') {
581 			putchr(*cp++);
582 			continue;
583 		}
584 		switch (*++cp) {
585 
586 		case 't':
587 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
588 				slash = strrchr(ttyn, '/');
589 			if (slash == NULL)
590 				xputs(ttyn);
591 			else
592 				xputs(&slash[1]);
593 			break;
594 
595 		case 'h':
596 			xputs(editedhost);
597 			break;
598 
599 		case 'd':
600 			(void)time(&t);
601 			(void)strftime(db, sizeof(db),
602 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
603 			xputs(db);
604 			break;
605 
606 		case 's':
607 			xputs(kerninfo.sysname);
608 			break;
609 
610 		case 'm':
611 			xputs(kerninfo.machine);
612 			break;
613 
614 		case 'r':
615 			xputs(kerninfo.release);
616 			break;
617 
618 		case 'v':
619 			xputs(kerninfo.version);
620 			break;
621 
622 		case '%':
623 			putchr('%');
624 			break;
625 		}
626 		if (*cp)
627 			cp++;
628 	}
629 }
630 
631 static void
clearscreen(void)632 clearscreen(void)
633 {
634 	struct ttyent *typ;
635 	int err;
636 
637 	if (rawttyn == NULL)
638 		return;
639 
640 	typ = getttynam(rawttyn);
641 
642 	if ((typ == NULL) || (typ->ty_type == NULL) ||
643 	    (typ->ty_type[0] == 0))
644 		return;
645 
646 	if (setupterm(typ->ty_type, 0, &err) == ERR)
647 		return;
648 
649 	if (clear_screen)
650 		putpad(clear_screen);
651 
652 	del_curterm(cur_term);
653 	cur_term = NULL;
654 }
655