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