1/*	$OpenBSD: main.c,v 1.55 2024/04/28 16:42:53 florian 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/stat.h>
33#include <termios.h>
34#include <sys/ioctl.h>
35#include <sys/resource.h>
36#include <sys/utsname.h>
37#include <errno.h>
38#include <signal.h>
39#include <fcntl.h>
40#include <time.h>
41#include <ctype.h>
42#include <stdlib.h>
43#include <string.h>
44#include <syslog.h>
45#include <stdio.h>
46#include <unistd.h>
47#include <limits.h>
48#include <util.h>
49
50#include "gettytab.h"
51#include "pathnames.h"
52#include "extern.h"
53
54/*
55 * Set the amount of running time that getty should accumulate
56 * before deciding that something is wrong and exit.
57 */
58#define GETTY_TIMEOUT	60 /* seconds */
59
60struct termios tmode, omode;
61
62int crmod, digit, lower, upper;
63
64char	hostname[HOST_NAME_MAX+1];
65char	globalhostname[HOST_NAME_MAX+1];
66struct	utsname kerninfo;
67char	name[LOGIN_NAME_MAX];
68char	ttyn[32];
69char	*portselector(void);
70
71#define	OBUFSIZ		128
72#define	TABBUFSIZ	512
73
74char	defent[TABBUFSIZ];
75char	tabent[TABBUFSIZ];
76char	saveLO[FILENAME_MAX];
77
78char	*env[128];
79
80char partab[] = {
81	0001,0201,0201,0001,0201,0001,0001,0201,
82	0202,0004,0003,0205,0005,0206,0201,0001,
83	0201,0001,0001,0201,0001,0201,0201,0001,
84	0001,0201,0201,0001,0201,0001,0001,0201,
85	0200,0000,0000,0200,0000,0200,0200,0000,
86	0000,0200,0200,0000,0200,0000,0000,0200,
87	0000,0200,0200,0000,0200,0000,0000,0200,
88	0200,0000,0000,0200,0000,0200,0200,0000,
89	0200,0000,0000,0200,0000,0200,0200,0000,
90	0000,0200,0200,0000,0200,0000,0000,0200,
91	0000,0200,0200,0000,0200,0000,0000,0200,
92	0200,0000,0000,0200,0000,0200,0200,0000,
93	0000,0200,0200,0000,0200,0000,0000,0200,
94	0200,0000,0000,0200,0000,0200,0200,0000,
95	0200,0000,0000,0200,0000,0200,0200,0000,
96	0000,0200,0200,0000,0200,0000,0000,0201
97};
98
99#define	ERASE	tmode.c_cc[VERASE]
100#define	KILL	tmode.c_cc[VKILL]
101#define	EOT	tmode.c_cc[VEOF]
102
103static void
104dingdong(int signo)
105{
106	tmode.c_ispeed = tmode.c_ospeed = 0;
107	(void)tcsetattr(0, TCSANOW, &tmode);
108	_exit(1);
109}
110
111volatile sig_atomic_t interrupt_flag;
112
113static void
114interrupt(int signo)
115{
116	int save_errno = errno;
117
118	interrupt_flag = 1;
119	signal(SIGINT, interrupt);
120	errno = save_errno;
121}
122
123/*
124 * Action to take when getty is running too long.
125 */
126static void
127timeoverrun(int signo)
128{
129	struct syslog_data sdata = SYSLOG_DATA_INIT;
130
131	syslog_r(LOG_ERR, &sdata,
132	    "getty exiting due to excessive running time");
133	_exit(1);
134}
135
136static int	getname(void);
137static void	oflush(void);
138static void	prompt(void);
139static void	putchr(int);
140static void	putf(char *);
141static void	putpad(char *);
142static void	xputs(char *);
143
144int
145main(int argc, char *argv[])
146{
147	extern char **environ;
148	char *tname;
149	int repcnt = 0, failopenlogged = 0;
150	struct rlimit limit;
151	int off = 0;
152
153	signal(SIGINT, SIG_IGN);
154/*
155	signal(SIGQUIT, SIG_DFL);
156*/
157	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
158	gethostname(hostname, sizeof(hostname));
159	if (hostname[0] == '\0')
160		strlcpy(hostname, "Amnesiac", sizeof hostname);
161	uname(&kerninfo);
162
163	/*
164	 * Limit running time to deal with broken or dead lines.
165	 */
166	(void)signal(SIGXCPU, timeoverrun);
167	limit.rlim_max = RLIM_INFINITY;
168	limit.rlim_cur = GETTY_TIMEOUT;
169	(void)setrlimit(RLIMIT_CPU, &limit);
170
171	ioctl(0, FIOASYNC, &off);	/* turn off async mode */
172
173	tname = "default";
174
175	if (unveil(_PATH_GETTYTAB, "r") == -1) {
176		syslog(LOG_ERR, "%s: %m", tname);
177		exit(1);
178	}
179	if (unveil("/dev", "rw") == -1) {
180		syslog(LOG_ERR, "%s: %m", tname);
181		exit(1);
182	}
183	if (unveil(_PATH_GETTY, "x") == -1) {
184		syslog(LOG_ERR, "%s: %m", tname);
185		exit(1);
186	}
187
188	gettable("default", defent);
189	gendefaults();
190	if (argc > 1)
191		tname = argv[1];
192	gettable(tname, tabent);
193	if (LO == NULL)
194		LO = _PATH_LOGIN;
195	if (unveil(LO, "x") == -1) {
196		syslog(LOG_ERR, "%s: %m", tname);
197		exit(1);
198	}
199	if (unveil(NULL, NULL) == -1) {
200		syslog(LOG_ERR, "%s: %m", tname);
201		exit(1);
202	}
203	strlcpy(saveLO, LO, sizeof saveLO);
204
205	/*
206	 * The following is a work around for vhangup interactions
207	 * which cause great problems getting window systems started.
208	 * If the tty line is "-", we do the old style getty presuming
209	 * that the file descriptors are already set up for us.
210	 * J. Gettys - MIT Project Athena.
211	 */
212	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
213		if (pledge("stdio rpath proc exec tty", NULL) == -1) {
214			syslog(LOG_ERR, "pledge: %m");
215			exit(1);
216		}
217
218		if ((tname = ttyname(0)) == NULL) {
219			syslog(LOG_ERR, "stdin: %m");
220			exit(1);
221		}
222		if (strlcpy(ttyn, tname, sizeof(ttyn)) >= sizeof(ttyn)) {
223			errno = ENAMETOOLONG;
224			syslog(LOG_ERR, "%s: %m", tname);
225			exit(1);
226		}
227	} else {
228		int i;
229
230		snprintf(ttyn, sizeof ttyn, "%s%s", _PATH_DEV, argv[2]);
231		if (strcmp(argv[0], "+") != 0) {
232			chown(ttyn, 0, 0);
233			chmod(ttyn, 0600);
234			revoke(ttyn);
235			/*
236			 * Delay the open so DTR stays down long enough to be detected.
237			 */
238			sleep(2);
239			while ((i = open(ttyn, O_RDWR)) == -1) {
240				if ((repcnt % 10 == 0) &&
241				    (errno != ENXIO || !failopenlogged)) {
242					syslog(LOG_ERR, "%s: %m", ttyn);
243					closelog();
244					failopenlogged = 1;
245				}
246				repcnt++;
247				sleep(60);
248			}
249			login_tty(i);
250		}
251	}
252
253	if (pledge("stdio rpath proc exec tty", NULL) == -1) {
254		syslog(LOG_ERR, "pledge: %m");
255		exit(1);
256	}
257
258	/* Start with default tty settings */
259	if (tcgetattr(0, &tmode) == -1) {
260		syslog(LOG_ERR, "%s: %m", ttyn);
261		exit(1);
262	}
263	omode = tmode;
264
265	for (;;) {
266		gettable(tname, tabent);
267		if (strcmp(LO, saveLO) != 0) {
268			/* re-exec to apply new unveil */
269			closefrom(0);
270			execv(_PATH_GETTY, argv);
271			exit(0);
272		}
273		if (OPset || EPset || APset)
274			APset++, OPset++, EPset++;
275		setdefaults();
276		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
277		ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
278
279		if (IS)
280			cfsetispeed(&tmode, IS);
281		else if (SP)
282			cfsetispeed(&tmode, SP);
283		if (OS)
284			cfsetospeed(&tmode, OS);
285		else if (SP)
286			cfsetospeed(&tmode, SP);
287		setflags(0);
288		setchars();
289		if (tcsetattr(0, TCSANOW, &tmode) == -1) {
290			syslog(LOG_ERR, "%s: %m", ttyn);
291			exit(1);
292		}
293		if (AB) {
294			tname = autobaud();
295			continue;
296		}
297		if (PS) {
298			tname = portselector();
299			continue;
300		}
301		if (CL && *CL)
302			putpad(CL);
303		strlcpy(globalhostname, HN, sizeof(globalhostname));
304		if (IM && *IM)
305			putf(IM);
306		if (TO) {
307			signal(SIGALRM, dingdong);
308			alarm(TO);
309		}
310		if (getname()) {
311			int i;
312
313			oflush();
314			alarm(0);
315			signal(SIGALRM, SIG_DFL);
316			if (name[0] == '-') {
317				xputs("user names may not start with '-'.");
318				continue;
319			}
320			if (!(upper || lower || digit))
321				continue;
322			setflags(2);
323			if (crmod) {
324				tmode.c_iflag |= ICRNL;
325				tmode.c_oflag |= ONLCR;
326			}
327			if (UC) {
328				tmode.c_iflag |= IUCLC;
329				tmode.c_oflag |= OLCUC;
330				tmode.c_lflag |= XCASE;
331			}
332			if (lower || LC) {
333				tmode.c_iflag &= ~IUCLC;
334				tmode.c_oflag &= ~OLCUC;
335				tmode.c_lflag &= ~XCASE;
336			}
337			if (tcsetattr(0, TCSANOW, &tmode) == -1) {
338				syslog(LOG_ERR, "%s: %m", ttyn);
339				exit(1);
340			}
341			signal(SIGINT, SIG_DFL);
342			for (i = 0; environ[i] != NULL; i++)
343				env[i] = environ[i];
344			makeenv(&env[i]);
345
346			limit.rlim_max = RLIM_INFINITY;
347			limit.rlim_cur = RLIM_INFINITY;
348			(void)setrlimit(RLIMIT_CPU, &limit);
349			execle(LO, "login", "-p", "--", name, NULL, env);
350			syslog(LOG_ERR, "%s: %m", LO);
351			exit(1);
352		}
353		alarm(0);
354		signal(SIGALRM, SIG_DFL);
355		signal(SIGINT, SIG_IGN);
356		if (NX && *NX)
357			tname = NX;
358	}
359}
360
361static int
362getname(void)
363{
364	unsigned char cs;
365	int c, r;
366	char *np;
367
368	/*
369	 * Interrupt may happen if we use CBREAK mode
370	 */
371	signal(SIGINT, interrupt);
372	setflags(1);
373	prompt();
374	if (PF > 0) {
375		oflush();
376		sleep(PF);
377		PF = 0;
378	}
379	if (tcsetattr(0, TCSANOW, &tmode) == -1) {
380		syslog(LOG_ERR, "%s: %m", ttyn);
381		exit(1);
382	}
383	crmod = digit = lower = upper = 0;
384	np = name;
385	for (;;) {
386		oflush();
387		r = read(STDIN_FILENO, &cs, 1);
388		if (r <= 0) {
389			if (r == -1 && errno == EINTR && interrupt_flag) {
390				interrupt_flag = 0;
391				return (0);
392			}
393			exit(0);
394		}
395		/* Handle 'printables' we cannot erase */
396		if (cs == CTRL('L') || cs == CTRL('K'))
397			continue;
398		if (cs == '\t')
399			cs = ' ';
400		if ((c = cs&0177) == 0)
401			return (0);
402
403		if (c == EOT)
404			exit(1);
405		if (c == '\r' || c == '\n' || np >= name + sizeof name -1) {
406			putf("\r\n");
407			break;
408		}
409		if (islower(c))
410			lower = 1;
411		else if (isupper(c))
412			upper = 1;
413		else if (c == ERASE || c == '\b') {
414			if (np > name) {
415				if (*--np == '\033')
416					xputs("\b\b  \b\b");
417				else if (isprint(*np))
418					xputs("\b \b");
419			}
420			continue;
421		} else if (c == KILL) {
422			putchr('\r');
423			putf(LM);
424			while (np > name) {
425				if (*--np == '\033')
426					xputs("  ");
427				else if (isprint(*np))
428					putchr(' ');
429			}
430			putchr('\r');
431			prompt();
432			np = name;
433			continue;
434		} else if (isdigit(c))
435			digit++;
436		if (IG && (c <= ' ' || c > 0176))
437			continue;
438		*np++ = c;
439		if (c == '\033') {
440			putchr('^');
441			putchr('[');
442		} else
443			putchr(cs);
444	}
445	signal(SIGINT, SIG_IGN);
446	if (interrupt_flag) {
447		interrupt_flag = 0;
448		return (0);
449	}
450	*np = 0;
451	if (c == '\r')
452		crmod = 1;
453	return (1);
454}
455
456static void
457putpad(char *s)
458{
459	int pad = 0;
460	speed_t ospeed = cfgetospeed(&tmode);
461
462	if (isdigit((unsigned char)*s)) {
463		while (isdigit((unsigned char)*s)) {
464			pad *= 10;
465			pad += *s++ - '0';
466		}
467		pad *= 10;
468		if (*s == '.' && isdigit((unsigned char)s[1])) {
469			pad += s[1] - '0';
470			s += 2;
471		}
472	}
473
474	xputs(s);
475	/*
476	 * If no delay needed, or output speed is
477	 * not comprehensible, then don't try to delay.
478	 */
479	if (pad == 0 || ospeed <= 0)
480		return;
481
482	/*
483	 * Round up by a half a character frame, and then do the delay.
484	 * Too bad there are no user program accessible programmed delays.
485	 * Transmitting pad characters slows many terminals down and also
486	 * loads the system.
487	 */
488	pad = (pad * ospeed + 50000) / 100000;
489	while (pad--)
490		putchr(*PC);
491}
492
493static void
494xputs(char *s)
495{
496	while (*s)
497		putchr(*s++);
498}
499
500char	outbuf[OBUFSIZ];
501int	obufcnt = 0;
502
503static void
504putchr(int cc)
505{
506	char c;
507
508	c = cc;
509	if (!NP) {
510		c |= partab[c&0177] & 0200;
511		if (OP)
512			c ^= 0200;
513	}
514	if (!UB) {
515		outbuf[obufcnt++] = c;
516		if (obufcnt >= OBUFSIZ)
517			oflush();
518	} else
519		write(STDOUT_FILENO, &c, 1);
520}
521
522static void
523oflush(void)
524{
525	if (obufcnt)
526		write(STDOUT_FILENO, outbuf, obufcnt);
527	obufcnt = 0;
528}
529
530static void
531prompt(void)
532{
533
534	putf(LM);
535	if (CO)
536		putchr('\n');
537}
538
539static void
540putf(char *cp)
541{
542	char *slash, db[100];
543	time_t t;
544
545	while (*cp) {
546		if (*cp != '%') {
547			putchr(*cp++);
548			continue;
549		}
550		switch (*++cp) {
551
552		case 't':
553			slash = strrchr(ttyn, '/');
554			if (slash == (char *) 0)
555				xputs(ttyn);
556			else
557				xputs(&slash[1]);
558			break;
559
560		case 'h':
561			xputs(globalhostname);
562			break;
563
564		case 'd': {
565			struct tm *tm;
566			time(&t);
567			if ((tm = localtime(&t)) != NULL)
568				if (strftime(db, sizeof(db),
569				    "%l:%M%p on %A, %d %B %Y", tm) != 0)
570					xputs(db);
571			break;
572		}
573
574		case 's':
575			xputs(kerninfo.sysname);
576			break;
577
578		case 'm':
579			xputs(kerninfo.machine);
580			break;
581
582		case 'r':
583			xputs(kerninfo.release);
584			break;
585
586		case 'v':
587			xputs(kerninfo.version);
588			break;
589
590		case '%':
591			putchr('%');
592			break;
593		}
594		cp++;
595	}
596}
597