xref: /netbsd-src/libexec/getty/main.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /*	$NetBSD: main.c,v 1.65 2016/03/17 00:21:04 christos 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/cdefs.h>
33 
34 #ifndef lint
35 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
36  The Regents of the University of California.  All rights reserved.");
37 #endif /* not lint */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";
42 #else
43 __RCSID("$NetBSD: main.c,v 1.65 2016/03/17 00:21:04 christos Exp $");
44 #endif
45 #endif /* not lint */
46 
47 #include <sys/param.h>
48 #include <sys/ioctl.h>
49 #include <sys/resource.h>
50 #include <sys/utsname.h>
51 
52 #include <ctype.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <limits.h>
56 #include <pwd.h>
57 #include <setjmp.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <syslog.h>
63 #include <term.h>
64 #include <termios.h>
65 #include <time.h>
66 #include <ttyent.h>
67 #include <unistd.h>
68 #include <util.h>
69 
70 #include "gettytab.h"
71 #include "pathnames.h"
72 #include "extern.h"
73 
74 extern char editedhost[];
75 
76 /*
77  * Set the amount of running time that getty should accumulate
78  * before deciding that something is wrong and exit.
79  */
80 #define GETTY_TIMEOUT	60 /* seconds */
81 
82 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
83 
84 #define PPP_FRAME           0x7e  /* PPP Framing character */
85 #define PPP_STATION         0xff  /* "All Station" character */
86 #define PPP_ESCAPE          0x7d  /* Escape Character */
87 #define PPP_CONTROL         0x03  /* PPP Control Field */
88 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
89 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
90 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
91 
92 struct termios tmode, omode;
93 
94 int crmod, digit_or_punc, lower, upper;
95 
96 char	hostname[MAXHOSTNAMELEN + 1];
97 struct	utsname kerninfo;
98 char	name[LOGIN_NAME_MAX];
99 char	dev[] = _PATH_DEV;
100 char	ttyn[32];
101 char	lockfile[512];
102 uid_t	ttyowner;
103 char	*rawttyn;
104 
105 #define	OBUFSIZ		128
106 #define	TABBUFSIZ	512
107 
108 char	defent[TABBUFSIZ];
109 char	tabent[TABBUFSIZ];
110 
111 char	*env[128];
112 
113 const unsigned char partab[] = {
114 	0001,0201,0201,0001,0201,0001,0001,0201,
115 	0202,0004,0003,0205,0005,0206,0201,0001,
116 	0201,0001,0001,0201,0001,0201,0201,0001,
117 	0001,0201,0201,0001,0201,0001,0001,0201,
118 	0200,0000,0000,0200,0000,0200,0200,0000,
119 	0000,0200,0200,0000,0200,0000,0000,0200,
120 	0000,0200,0200,0000,0200,0000,0000,0200,
121 	0200,0000,0000,0200,0000,0200,0200,0000,
122 	0200,0000,0000,0200,0000,0200,0200,0000,
123 	0000,0200,0200,0000,0200,0000,0000,0200,
124 	0000,0200,0200,0000,0200,0000,0000,0200,
125 	0200,0000,0000,0200,0000,0200,0200,0000,
126 	0000,0200,0200,0000,0200,0000,0000,0200,
127 	0200,0000,0000,0200,0000,0200,0200,0000,
128 	0200,0000,0000,0200,0000,0200,0200,0000,
129 	0000,0200,0200,0000,0200,0000,0000,0201
130 };
131 
132 #define	ERASE	tmode.c_cc[VERASE]
133 #define	KILL	tmode.c_cc[VKILL]
134 #define	EOT	tmode.c_cc[VEOF]
135 
136 static void	clearscreen(void);
137 
138 sigjmp_buf timeout;
139 
140 __dead static void
141 /*ARGSUSED*/
142 dingdong(int signo)
143 {
144 
145 	(void)alarm(0);
146 	(void)signal(SIGALRM, SIG_DFL);
147 	siglongjmp(timeout, 1);
148 }
149 
150 sigjmp_buf intrupt;
151 
152 __dead static void
153 /*ARGSUSED*/
154 interrupt(int signo)
155 {
156 
157 	(void)signal(SIGINT, interrupt);
158 	siglongjmp(intrupt, 1);
159 }
160 
161 /*
162  * Action to take when getty is running too long.
163  */
164 __dead static void
165 /*ARGSUSED*/
166 timeoverrun(int signo)
167 {
168 
169 	syslog(LOG_ERR, "getty exiting due to excessive running time");
170 	exit(1);
171 }
172 
173 static int	getname(void);
174 static void	oflush(void);
175 static void	prompt(void);
176 static int	putchr(int);
177 static void	putf(const char *);
178 static void	xputs(const char *);
179 
180 #define putpad(s) tputs(s, 1, putchr)
181 
182 int
183 main(int argc, char *argv[], char *envp[])
184 {
185 	const char *progname;
186 	int repcnt = 0, failopenlogged = 0;
187 	volatile int first_time = 1;
188 	struct rlimit limit;
189 	struct passwd *pw;
190 	int rval;
191 	/* this is used past the siglongjmp, so make sure it is not cached
192 	   in registers that might become invalid. */
193 	volatile int uugetty = 0;
194 	const char * volatile tname = "default";
195 
196 	(void)signal(SIGINT, SIG_IGN);
197 	openlog("getty", LOG_PID, LOG_AUTH);
198 	(void)gethostname(hostname, sizeof(hostname));
199 	hostname[sizeof(hostname) - 1] = '\0';
200 	if (hostname[0] == '\0')
201 		(void)strlcpy(hostname, "Amnesiac", sizeof(hostname));
202 	(void)uname(&kerninfo);
203 
204 	progname = getprogname();
205 	if (progname[0] == 'u' && progname[1] == 'u')
206 		uugetty = 1;
207 
208 	/*
209 	 * Find id of uucp login (if present) so we can chown tty properly.
210 	 */
211 	if (uugetty && (pw = getpwnam("uucp")))
212 		ttyowner = pw->pw_uid;
213 	else
214 		ttyowner = 0;
215 
216 	/*
217 	 * Limit running time to deal with broken or dead lines.
218 	 */
219 	(void)signal(SIGXCPU, timeoverrun);
220 	limit.rlim_max = RLIM_INFINITY;
221 	limit.rlim_cur = GETTY_TIMEOUT;
222 	(void)setrlimit(RLIMIT_CPU, &limit);
223 
224 	/*
225 	 * The following is a work around for vhangup interactions
226 	 * which cause great problems getting window systems started.
227 	 * If the tty line is "-", we do the old style getty presuming
228 	 * that the file descriptors are already set up for us.
229 	 * J. Gettys - MIT Project Athena.
230 	 */
231 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
232 		(void)strlcpy(ttyn, ttyname(0), sizeof(ttyn));
233 	}
234 	else {
235 		int i;
236 
237 		rawttyn = argv[2];
238 		(void)strlcpy(ttyn, dev, sizeof(ttyn));
239 		(void)strlcat(ttyn, argv[2], sizeof(ttyn));
240 		if (uugetty)  {
241 			(void)chown(ttyn, ttyowner, 0);
242 			(void)strlcpy(lockfile, _PATH_LOCK,
243 				sizeof(lockfile));
244 			(void)strlcat(lockfile, argv[2],
245 				sizeof(lockfile));
246 			/*
247 			 * wait for lockfiles to go away before we try
248 			 * to open
249 			 */
250 			if (pidlock(lockfile, 0, 0, 0) != 0)  {
251 				syslog(LOG_ERR,
252 					"%s: can't create lockfile", ttyn);
253 				exit(1);
254 			}
255 			(void)unlink(lockfile);
256 		}
257 		if (strcmp(argv[0], "+") != 0) {
258 			(void)chown(ttyn, ttyowner, 0);
259 			(void)chmod(ttyn, 0600);
260 			(void)revoke(ttyn);
261 			if (ttyaction(ttyn, "getty", "root"))
262 				syslog(LOG_WARNING, "%s: ttyaction failed",
263 					ttyn);
264 			/*
265 			 * Delay the open so DTR stays down long enough
266 			 * to be detected.
267 			 */
268 			(void)sleep(2);
269 			while ((i = open(ttyn, O_RDWR)) == -1) {
270 				if ((repcnt % 10 == 0) &&
271 				    (errno != ENXIO || !failopenlogged)) {
272 					syslog(LOG_WARNING, "%s: %m", ttyn);
273 					closelog();
274 					failopenlogged = 1;
275 				}
276 				repcnt++;
277 				(void)sleep(60);
278 			}
279 			if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
280 				syslog(LOG_ERR, "%s: can't create lockfile",
281 					ttyn);
282 				exit(1);
283 			}
284 			if (uugetty)
285 				(void)chown(lockfile, ttyowner, 0);
286 			(void)login_tty(i);
287 		}
288 	}
289 
290 	/* Start with default tty settings */
291 	if (tcgetattr(0, &tmode) < 0) {
292 		syslog(LOG_ERR, "%s: %m", ttyn);
293 		exit(1);
294 	}
295 	omode = tmode;
296 
297 	gettable("default", defent);
298 	gendefaults();
299 	if (argc > 1)
300 		tname = argv[1];
301 	for (;;) {
302 		int off;
303 
304 		rval = 0;
305 		gettable(tname, tabent);
306 		if (OPset || EPset || APset)
307 			APset++, OPset++, EPset++;
308 		setdefaults();
309 		off = 0;
310 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
311 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
312 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
313 
314 		if (IS)
315 			(void)cfsetispeed(&tmode, (speed_t)IS);
316 		else if (SP)
317 			(void)cfsetispeed(&tmode, (speed_t)SP);
318 		if (OS)
319 			(void)cfsetospeed(&tmode, (speed_t)OS);
320 		else if (SP)
321 			(void)cfsetospeed(&tmode, (speed_t)SP);
322 		setflags(0);
323 		setchars();
324 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
325 			syslog(LOG_ERR, "%s: %m", ttyn);
326 			exit(1);
327 		}
328 		if (AB) {
329 			tname = autobaud();
330 			continue;
331 		}
332 		if (PS) {
333 			tname = portselector();
334 			continue;
335 		}
336 		if (CS)
337 			clearscreen();
338 		if (CL && *CL)
339 			putpad(CL);
340 		edithost(HE);
341 
342 		/*
343 		 * If this is the first time through this, and an
344 		 * issue file has been given, then send it.
345 		 */
346 		if (first_time != 0 && IF != NULL) {
347 			char buf[_POSIX2_LINE_MAX];
348 			FILE *fp;
349 
350 			if ((fp = fopen(IF, "r")) != NULL) {
351 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
352 					putf(buf);
353 				(void)fclose(fp);
354 			}
355 		}
356 		first_time = 0;
357 
358 		if (IM && *IM)
359 			putf(IM);
360 		oflush();
361 		if (sigsetjmp(timeout, 1)) {
362 			tmode.c_ispeed = tmode.c_ospeed = 0;
363 			(void)tcsetattr(0, TCSANOW, &tmode);
364 			exit(1);
365 		}
366 		if (TO) {
367 			(void)signal(SIGALRM, dingdong);
368 			(void)alarm((unsigned int)TO);
369 		}
370 		if (NN) {
371 			name[0] = '\0';
372 			lower = 1;
373 			upper = digit_or_punc = 0;
374 		} else if (AL) {
375 			const char *p = AL;
376 			char *q = name;
377 
378 			while (*p && q < &name[sizeof name - 1]) {
379 				if (isupper((unsigned char)*p))
380 					upper = 1;
381 				else if (islower((unsigned char)*p))
382 					lower = 1;
383 				else if (isdigit((unsigned char)*p))
384 					digit_or_punc = 1;
385 				*q++ = *p++;
386 			}
387 		} else if ((rval = getname()) == 2) {
388 			setflags(2);
389 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
390 			syslog(LOG_ERR, "%s: %m", PP);
391 			exit(1);
392 		}
393 
394 		if (rval || AL || NN) {
395 			int i;
396 
397 			oflush();
398 			(void)alarm(0);
399 			(void)signal(SIGALRM, SIG_DFL);
400 			if (name[0] == '-') {
401 				xputs("user names may not start with '-'.");
402 				continue;
403 			}
404 			if (!(upper || lower || digit_or_punc))
405 				continue;
406 			setflags(2);
407 			if (crmod) {
408 				tmode.c_iflag |= ICRNL;
409 				tmode.c_oflag |= ONLCR;
410 			}
411 #if XXX
412 			if (upper || UC)
413 				tmode.sg_flags |= LCASE;
414 			if (lower || LC)
415 				tmode.sg_flags &= ~LCASE;
416 #endif
417 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
418 				syslog(LOG_ERR, "%s: %m", ttyn);
419 				exit(1);
420 			}
421 			(void)signal(SIGINT, SIG_DFL);
422 			for (i = 0; envp[i] != NULL; i++)
423 				env[i] = envp[i];
424 			makeenv(&env[i]);
425 
426 			limit.rlim_max = RLIM_INFINITY;
427 			limit.rlim_cur = RLIM_INFINITY;
428 			(void)setrlimit(RLIMIT_CPU, &limit);
429 			if (NN)
430 				(void)execle(LO, "login", AL ? "-fp" : "-p",
431 				    NULL, env);
432 			else
433 				(void)execle(LO, "login", AL ? "-fp" : "-p",
434 				    "--", name, NULL, env);
435 			syslog(LOG_ERR, "%s: %m", LO);
436 			exit(1);
437 		}
438 		(void)alarm(0);
439 		(void)signal(SIGALRM, SIG_DFL);
440 		(void)signal(SIGINT, SIG_IGN);
441 		if (NX && *NX)
442 			tname = NX;
443 		if (uugetty)
444 			(void)unlink(lockfile);
445 	}
446 }
447 
448 static int
449 getname(void)
450 {
451 	int c;
452 	char *np;
453 	unsigned char cs;
454 	int ppp_state, ppp_connection;
455 
456 	/*
457 	 * Interrupt may happen if we use CBREAK mode
458 	 */
459 	if (sigsetjmp(intrupt, 1)) {
460 		(void)signal(SIGINT, SIG_IGN);
461 		return (0);
462 	}
463 	(void)signal(SIGINT, interrupt);
464 	setflags(1);
465 	prompt();
466 	if (PF > 0) {
467 		oflush();
468 		(void)sleep((unsigned int)PF);
469 		PF = 0;
470 	}
471 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
472 		syslog(LOG_ERR, "%s: %m", ttyn);
473 		exit(1);
474 	}
475 	crmod = digit_or_punc = lower = upper = 0;
476 	ppp_state = ppp_connection = 0;
477 	np = name;
478 	for (;;) {
479 		oflush();
480 		if (read(STDIN_FILENO, &cs, 1) <= 0)
481 			exit(0);
482 		if ((c = cs&0177) == 0)
483 			return (0);
484 
485 		/*
486 		 * PPP detection state machine..
487 		 * Look for sequences:
488 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
489 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
490 		 * See RFC1662.
491 		 * Derived from code from Michael Hancock <michaelh@cet.co.jp>
492 		 * and Erik 'PPP' Olson <eriko@wrq.com>
493 		 */
494 		if (PP && cs == PPP_FRAME) {
495 			ppp_state = 1;
496 		} else if (ppp_state == 1 && cs == PPP_STATION) {
497 			ppp_state = 2;
498 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
499 			ppp_state = 3;
500 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
501 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
502 			ppp_state = 4;
503 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
504 			ppp_state = 5;
505 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
506 			ppp_connection = 1;
507 			break;
508 		} else {
509 			ppp_state = 0;
510 		}
511 
512 		if (c == EOT)
513 			exit(1);
514 		if (c == '\r' || c == '\n' ||
515 		    np >= &name[LOGIN_NAME_MAX - 1]) {
516 			*np = '\0';
517 			putf("\r\n");
518 			break;
519 		}
520 		if (islower(c))
521 			lower = 1;
522 		else if (isupper(c))
523 			upper = 1;
524 		else if (c == ERASE || c == '#' || c == '\b') {
525 			if (np > name) {
526 				np--;
527 				if (cfgetospeed(&tmode) >= 1200)
528 					xputs("\b \b");
529 				else
530 					putchr(cs);
531 			}
532 			continue;
533 		} else if (c == KILL || c == '@') {
534 			putchr(cs);
535 			putchr('\r');
536 			if (cfgetospeed(&tmode) < 1200)
537 				putchr('\n');
538 			/* this is the way they do it down under ... */
539 			else if (np > name)
540 				xputs(
541 				    "                                     \r");
542 			prompt();
543 			np = name;
544 			continue;
545 		} else if (isdigit(c) || c == '_')
546 			digit_or_punc = 1;
547 		if (IG && (c <= ' ' || c > 0176))
548 			continue;
549 		*np++ = c;
550 		putchr(cs);
551 
552 		/*
553 		 * An MS-Windows direct connect PPP "client" won't send its
554 		 * first PPP packet until we respond to its "CLIENT" poll
555 		 * with a CRLF sequence.  We cater to yet another broken
556 		 * implementation of a previously-standard protocol...
557 		 */
558 		*np = '\0';
559 		if (strstr(name, "CLIENT"))
560 			putf("\r\n");
561 	}
562 	(void)signal(SIGINT, SIG_IGN);
563 	*np = 0;
564 	if (c == '\r')
565 		crmod = 1;
566 	if ((upper && !lower && !LC) || UC)
567 		for (np = name; *np; np++)
568 			*np = tolower((unsigned char)*np);
569 	return (1 + ppp_connection);
570 }
571 
572 static void
573 xputs(const char *s)
574 {
575 	while (*s)
576 		putchr(*s++);
577 }
578 
579 char	outbuf[OBUFSIZ];
580 size_t	obufcnt = 0;
581 
582 static int
583 putchr(int cc)
584 {
585 	unsigned char c;
586 
587 	c = cc;
588 	if (!NP) {
589 		c |= partab[c&0177] & 0200;
590 		if (OP)
591 			c ^= 0200;
592 	}
593 	if (!UB) {
594 		outbuf[obufcnt++] = c;
595 		if (obufcnt >= OBUFSIZ)
596 			oflush();
597 		return 1;
598 	}
599 	return write(STDOUT_FILENO, &c, 1);
600 }
601 
602 static void
603 oflush(void)
604 {
605 	if (obufcnt)
606 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
607 	obufcnt = 0;
608 }
609 
610 static void
611 prompt(void)
612 {
613 
614 	putf(LM);
615 	if (CO)
616 		putchr('\n');
617 }
618 
619 static void
620 putf(const char *cp)
621 {
622 	time_t t;
623 	char *slash, db[100];
624 
625 	while (*cp) {
626 		if (*cp != '%') {
627 			putchr(*cp++);
628 			continue;
629 		}
630 		switch (*++cp) {
631 
632 		case 't':
633 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
634 				slash = strrchr(ttyn, '/');
635 			if (slash == NULL)
636 				xputs(ttyn);
637 			else
638 				xputs(&slash[1]);
639 			break;
640 
641 		case 'h':
642 			xputs(editedhost);
643 			break;
644 
645 		case 'd':
646 			(void)time(&t);
647 			(void)strftime(db, sizeof(db),
648 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
649 			xputs(db);
650 			break;
651 
652 		case 's':
653 			xputs(kerninfo.sysname);
654 			break;
655 
656 		case 'm':
657 			xputs(kerninfo.machine);
658 			break;
659 
660 		case 'r':
661 			xputs(kerninfo.release);
662 			break;
663 
664 		case 'v':
665 			xputs(kerninfo.version);
666 			break;
667 
668 		case '%':
669 			putchr('%');
670 			break;
671 		}
672 		if (*cp)
673 			cp++;
674 	}
675 }
676 
677 static void
678 clearscreen(void)
679 {
680 	struct ttyent *typ;
681 	int err;
682 
683 	if (rawttyn == NULL)
684 		return;
685 
686 	typ = getttynam(rawttyn);
687 
688 	if ((typ == NULL) || (typ->ty_type == NULL) ||
689 	    (typ->ty_type[0] == 0))
690 		return;
691 
692 	if (setupterm(typ->ty_type, 0, &err) == ERR)
693 		return;
694 
695 	if (clear_screen)
696 		putpad(clear_screen);
697 
698 	del_curterm(cur_term);
699 	cur_term = NULL;
700 }
701