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