xref: /openbsd/libexec/getty/main.c (revision 771fbea0)
1 /*	$OpenBSD: main.c,v 1.54 2019/06/28 13:32:53 deraadt 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 	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 
361 static int
362 getname(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 
456 static void
457 putpad(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 
493 static void
494 xputs(char *s)
495 {
496 	while (*s)
497 		putchr(*s++);
498 }
499 
500 char	outbuf[OBUFSIZ];
501 int	obufcnt = 0;
502 
503 static void
504 putchr(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 
522 static void
523 oflush(void)
524 {
525 	if (obufcnt)
526 		write(STDOUT_FILENO, outbuf, obufcnt);
527 	obufcnt = 0;
528 }
529 
530 static void
531 prompt(void)
532 {
533 
534 	putf(LM);
535 	if (CO)
536 		putchr('\n');
537 }
538 
539 static void
540 putf(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 			(void)time(&t);
566 			(void)strftime(db, sizeof(db),
567 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
568 			xputs(db);
569 			break;
570 		}
571 
572 		case 's':
573 			xputs(kerninfo.sysname);
574 			break;
575 
576 		case 'm':
577 			xputs(kerninfo.machine);
578 			break;
579 
580 		case 'r':
581 			xputs(kerninfo.release);
582 			break;
583 
584 		case 'v':
585 			xputs(kerninfo.version);
586 			break;
587 
588 		case '%':
589 			putchr('%');
590 			break;
591 		}
592 		cp++;
593 	}
594 }
595