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