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
41static 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
75extern 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
93struct termios tmode, omode;
94
95int crmod, digit_or_punc, lower, upper;
96
97char	hostname[MAXHOSTNAMELEN + 1];
98struct	utsname kerninfo;
99char	name[LOGIN_NAME_MAX];
100char	dev[] = _PATH_DEV;
101char	ttyn[32];
102uid_t	ttyowner = 0;
103char	*rawttyn;
104
105#define	OBUFSIZ		128
106#define	TABBUFSIZ	512
107
108char	defent[TABBUFSIZ];
109char	tabent[TABBUFSIZ];
110
111char	*env[128];
112
113const 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
136static void	clearscreen(void);
137
138sigjmp_buf timeout;
139
140__dead static void
141/*ARGSUSED*/
142dingdong(int signo)
143{
144
145	(void)alarm(0);
146	(void)signal(SIGALRM, SIG_DFL);
147	siglongjmp(timeout, 1);
148}
149
150sigjmp_buf intrupt;
151
152__dead static void
153/*ARGSUSED*/
154interrupt(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*/
166timeoverrun(int signo)
167{
168
169	syslog(LOG_ERR, "getty exiting due to excessive running time");
170	exit(1);
171}
172
173static int	getname(void);
174static void	oflush(void);
175static void	prompt(void);
176static int	putchr(int);
177static void	putf(const char *);
178static void	xputs(const char *);
179
180#define putpad(s) tputs(s, 1, putchr)
181
182int
183main(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
402static int
403getname(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
526static void
527xputs(const char *s)
528{
529	while (*s)
530		putchr(*s++);
531}
532
533char	outbuf[OBUFSIZ];
534size_t	obufcnt = 0;
535
536static int
537putchr(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
556static void
557oflush(void)
558{
559	if (obufcnt)
560		(void)write(STDOUT_FILENO, outbuf, obufcnt);
561	obufcnt = 0;
562}
563
564static void
565prompt(void)
566{
567
568	putf(LM);
569	if (CO)
570		putchr('\n');
571}
572
573static void
574putf(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
631static void
632clearscreen(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