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