xref: /netbsd-src/libexec/getty/main.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: main.c,v 1.64 2013/08/12 13:54:33 joerg 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.64 2013/08/12 13:54:33 joerg 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, first_time = 1;
187 	struct rlimit limit;
188 	struct passwd *pw;
189 	int rval;
190 	/* this is used past the siglongjmp, so make sure it is not cached
191 	   in registers that might become invalid. */
192 	volatile int uugetty = 0;
193 	const char * volatile tname = "default";
194 
195 	(void)signal(SIGINT, SIG_IGN);
196 	openlog("getty", LOG_PID, LOG_AUTH);
197 	(void)gethostname(hostname, sizeof(hostname));
198 	hostname[sizeof(hostname) - 1] = '\0';
199 	if (hostname[0] == '\0')
200 		(void)strlcpy(hostname, "Amnesiac", sizeof(hostname));
201 	(void)uname(&kerninfo);
202 
203 	progname = getprogname();
204 	if (progname[0] == 'u' && progname[1] == 'u')
205 		uugetty = 1;
206 
207 	/*
208 	 * Find id of uucp login (if present) so we can chown tty properly.
209 	 */
210 	if (uugetty && (pw = getpwnam("uucp")))
211 		ttyowner = pw->pw_uid;
212 	else
213 		ttyowner = 0;
214 
215 	/*
216 	 * Limit running time to deal with broken or dead lines.
217 	 */
218 	(void)signal(SIGXCPU, timeoverrun);
219 	limit.rlim_max = RLIM_INFINITY;
220 	limit.rlim_cur = GETTY_TIMEOUT;
221 	(void)setrlimit(RLIMIT_CPU, &limit);
222 
223 	/*
224 	 * The following is a work around for vhangup interactions
225 	 * which cause great problems getting window systems started.
226 	 * If the tty line is "-", we do the old style getty presuming
227 	 * that the file descriptors are already set up for us.
228 	 * J. Gettys - MIT Project Athena.
229 	 */
230 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
231 		(void)strlcpy(ttyn, ttyname(0), sizeof(ttyn));
232 	}
233 	else {
234 		int i;
235 
236 		rawttyn = argv[2];
237 		(void)strlcpy(ttyn, dev, sizeof(ttyn));
238 		(void)strlcat(ttyn, argv[2], sizeof(ttyn));
239 		if (uugetty)  {
240 			(void)chown(ttyn, ttyowner, 0);
241 			(void)strlcpy(lockfile, _PATH_LOCK,
242 				sizeof(lockfile));
243 			(void)strlcat(lockfile, argv[2],
244 				sizeof(lockfile));
245 			/*
246 			 * wait for lockfiles to go away before we try
247 			 * to open
248 			 */
249 			if (pidlock(lockfile, 0, 0, 0) != 0)  {
250 				syslog(LOG_ERR,
251 					"%s: can't create lockfile", ttyn);
252 				exit(1);
253 			}
254 			(void)unlink(lockfile);
255 		}
256 		if (strcmp(argv[0], "+") != 0) {
257 			(void)chown(ttyn, ttyowner, 0);
258 			(void)chmod(ttyn, 0600);
259 			(void)revoke(ttyn);
260 			if (ttyaction(ttyn, "getty", "root"))
261 				syslog(LOG_WARNING, "%s: ttyaction failed",
262 					ttyn);
263 			/*
264 			 * Delay the open so DTR stays down long enough
265 			 * to be detected.
266 			 */
267 			(void)sleep(2);
268 			while ((i = open(ttyn, O_RDWR)) == -1) {
269 				if ((repcnt % 10 == 0) &&
270 				    (errno != ENXIO || !failopenlogged)) {
271 					syslog(LOG_WARNING, "%s: %m", ttyn);
272 					closelog();
273 					failopenlogged = 1;
274 				}
275 				repcnt++;
276 				(void)sleep(60);
277 			}
278 			if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
279 				syslog(LOG_ERR, "%s: can't create lockfile",
280 					ttyn);
281 				exit(1);
282 			}
283 			if (uugetty)
284 				(void)chown(lockfile, ttyowner, 0);
285 			(void)login_tty(i);
286 		}
287 	}
288 
289 	/* Start with default tty settings */
290 	if (tcgetattr(0, &tmode) < 0) {
291 		syslog(LOG_ERR, "%s: %m", ttyn);
292 		exit(1);
293 	}
294 	omode = tmode;
295 
296 	gettable("default", defent);
297 	gendefaults();
298 	if (argc > 1)
299 		tname = argv[1];
300 	for (;;) {
301 		int off;
302 
303 		rval = 0;
304 		gettable(tname, tabent);
305 		if (OPset || EPset || APset)
306 			APset++, OPset++, EPset++;
307 		setdefaults();
308 		off = 0;
309 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
310 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
311 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
312 
313 		if (IS)
314 			(void)cfsetispeed(&tmode, (speed_t)IS);
315 		else if (SP)
316 			(void)cfsetispeed(&tmode, (speed_t)SP);
317 		if (OS)
318 			(void)cfsetospeed(&tmode, (speed_t)OS);
319 		else if (SP)
320 			(void)cfsetospeed(&tmode, (speed_t)SP);
321 		setflags(0);
322 		setchars();
323 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
324 			syslog(LOG_ERR, "%s: %m", ttyn);
325 			exit(1);
326 		}
327 		if (AB) {
328 			tname = autobaud();
329 			continue;
330 		}
331 		if (PS) {
332 			tname = portselector();
333 			continue;
334 		}
335 		if (CS)
336 			clearscreen();
337 		if (CL && *CL)
338 			putpad(CL);
339 		edithost(HE);
340 
341 		/*
342 		 * If this is the first time through this, and an
343 		 * issue file has been given, then send it.
344 		 */
345 		if (first_time != 0 && IF != NULL) {
346 			char buf[_POSIX2_LINE_MAX];
347 			FILE *fp;
348 
349 			if ((fp = fopen(IF, "r")) != NULL) {
350 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
351 					putf(buf);
352 				(void)fclose(fp);
353 			}
354 		}
355 		first_time = 0;
356 
357 		if (IM && *IM)
358 			putf(IM);
359 		oflush();
360 		if (sigsetjmp(timeout, 1)) {
361 			tmode.c_ispeed = tmode.c_ospeed = 0;
362 			(void)tcsetattr(0, TCSANOW, &tmode);
363 			exit(1);
364 		}
365 		if (TO) {
366 			(void)signal(SIGALRM, dingdong);
367 			(void)alarm((unsigned int)TO);
368 		}
369 		if (NN) {
370 			name[0] = '\0';
371 			lower = 1;
372 			upper = digit_or_punc = 0;
373 		} else if (AL) {
374 			const char *p = AL;
375 			char *q = name;
376 
377 			while (*p && q < &name[sizeof name - 1]) {
378 				if (isupper((unsigned char)*p))
379 					upper = 1;
380 				else if (islower((unsigned char)*p))
381 					lower = 1;
382 				else if (isdigit((unsigned char)*p))
383 					digit_or_punc = 1;
384 				*q++ = *p++;
385 			}
386 		} else if ((rval = getname()) == 2) {
387 			setflags(2);
388 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
389 			syslog(LOG_ERR, "%s: %m", PP);
390 			exit(1);
391 		}
392 
393 		if (rval || AL || NN) {
394 			int i;
395 
396 			oflush();
397 			(void)alarm(0);
398 			(void)signal(SIGALRM, SIG_DFL);
399 			if (name[0] == '-') {
400 				xputs("user names may not start with '-'.");
401 				continue;
402 			}
403 			if (!(upper || lower || digit_or_punc))
404 				continue;
405 			setflags(2);
406 			if (crmod) {
407 				tmode.c_iflag |= ICRNL;
408 				tmode.c_oflag |= ONLCR;
409 			}
410 #if XXX
411 			if (upper || UC)
412 				tmode.sg_flags |= LCASE;
413 			if (lower || LC)
414 				tmode.sg_flags &= ~LCASE;
415 #endif
416 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
417 				syslog(LOG_ERR, "%s: %m", ttyn);
418 				exit(1);
419 			}
420 			(void)signal(SIGINT, SIG_DFL);
421 			for (i = 0; envp[i] != NULL; i++)
422 				env[i] = envp[i];
423 			makeenv(&env[i]);
424 
425 			limit.rlim_max = RLIM_INFINITY;
426 			limit.rlim_cur = RLIM_INFINITY;
427 			(void)setrlimit(RLIMIT_CPU, &limit);
428 			if (NN)
429 				(void)execle(LO, "login", AL ? "-fp" : "-p",
430 				    NULL, env);
431 			else
432 				(void)execle(LO, "login", AL ? "-fp" : "-p",
433 				    "--", name, NULL, env);
434 			syslog(LOG_ERR, "%s: %m", LO);
435 			exit(1);
436 		}
437 		(void)alarm(0);
438 		(void)signal(SIGALRM, SIG_DFL);
439 		(void)signal(SIGINT, SIG_IGN);
440 		if (NX && *NX)
441 			tname = NX;
442 		if (uugetty)
443 			(void)unlink(lockfile);
444 	}
445 }
446 
447 static int
448 getname(void)
449 {
450 	int c;
451 	char *np;
452 	unsigned char cs;
453 	int ppp_state, ppp_connection;
454 
455 	/*
456 	 * Interrupt may happen if we use CBREAK mode
457 	 */
458 	if (sigsetjmp(intrupt, 1)) {
459 		(void)signal(SIGINT, SIG_IGN);
460 		return (0);
461 	}
462 	(void)signal(SIGINT, interrupt);
463 	setflags(1);
464 	prompt();
465 	if (PF > 0) {
466 		oflush();
467 		(void)sleep((unsigned int)PF);
468 		PF = 0;
469 	}
470 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
471 		syslog(LOG_ERR, "%s: %m", ttyn);
472 		exit(1);
473 	}
474 	crmod = digit_or_punc = lower = upper = 0;
475 	ppp_state = ppp_connection = 0;
476 	np = name;
477 	for (;;) {
478 		oflush();
479 		if (read(STDIN_FILENO, &cs, 1) <= 0)
480 			exit(0);
481 		if ((c = cs&0177) == 0)
482 			return (0);
483 
484 		/*
485 		 * PPP detection state machine..
486 		 * Look for sequences:
487 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
488 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
489 		 * See RFC1662.
490 		 * Derived from code from Michael Hancock <michaelh@cet.co.jp>
491 		 * and Erik 'PPP' Olson <eriko@wrq.com>
492 		 */
493 		if (PP && cs == PPP_FRAME) {
494 			ppp_state = 1;
495 		} else if (ppp_state == 1 && cs == PPP_STATION) {
496 			ppp_state = 2;
497 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
498 			ppp_state = 3;
499 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
500 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
501 			ppp_state = 4;
502 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
503 			ppp_state = 5;
504 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
505 			ppp_connection = 1;
506 			break;
507 		} else {
508 			ppp_state = 0;
509 		}
510 
511 		if (c == EOT)
512 			exit(1);
513 		if (c == '\r' || c == '\n' ||
514 		    np >= &name[LOGIN_NAME_MAX - 1]) {
515 			*np = '\0';
516 			putf("\r\n");
517 			break;
518 		}
519 		if (islower(c))
520 			lower = 1;
521 		else if (isupper(c))
522 			upper = 1;
523 		else if (c == ERASE || c == '#' || c == '\b') {
524 			if (np > name) {
525 				np--;
526 				if (cfgetospeed(&tmode) >= 1200)
527 					xputs("\b \b");
528 				else
529 					putchr(cs);
530 			}
531 			continue;
532 		} else if (c == KILL || c == '@') {
533 			putchr(cs);
534 			putchr('\r');
535 			if (cfgetospeed(&tmode) < 1200)
536 				putchr('\n');
537 			/* this is the way they do it down under ... */
538 			else if (np > name)
539 				xputs(
540 				    "                                     \r");
541 			prompt();
542 			np = name;
543 			continue;
544 		} else if (isdigit(c) || c == '_')
545 			digit_or_punc = 1;
546 		if (IG && (c <= ' ' || c > 0176))
547 			continue;
548 		*np++ = c;
549 		putchr(cs);
550 
551 		/*
552 		 * An MS-Windows direct connect PPP "client" won't send its
553 		 * first PPP packet until we respond to its "CLIENT" poll
554 		 * with a CRLF sequence.  We cater to yet another broken
555 		 * implementation of a previously-standard protocol...
556 		 */
557 		*np = '\0';
558 		if (strstr(name, "CLIENT"))
559 			putf("\r\n");
560 	}
561 	(void)signal(SIGINT, SIG_IGN);
562 	*np = 0;
563 	if (c == '\r')
564 		crmod = 1;
565 	if ((upper && !lower && !LC) || UC)
566 		for (np = name; *np; np++)
567 			*np = tolower((unsigned char)*np);
568 	return (1 + ppp_connection);
569 }
570 
571 static void
572 xputs(const char *s)
573 {
574 	while (*s)
575 		putchr(*s++);
576 }
577 
578 char	outbuf[OBUFSIZ];
579 size_t	obufcnt = 0;
580 
581 static int
582 putchr(int cc)
583 {
584 	unsigned char c;
585 
586 	c = cc;
587 	if (!NP) {
588 		c |= partab[c&0177] & 0200;
589 		if (OP)
590 			c ^= 0200;
591 	}
592 	if (!UB) {
593 		outbuf[obufcnt++] = c;
594 		if (obufcnt >= OBUFSIZ)
595 			oflush();
596 		return 1;
597 	}
598 	return write(STDOUT_FILENO, &c, 1);
599 }
600 
601 static void
602 oflush(void)
603 {
604 	if (obufcnt)
605 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
606 	obufcnt = 0;
607 }
608 
609 static void
610 prompt(void)
611 {
612 
613 	putf(LM);
614 	if (CO)
615 		putchr('\n');
616 }
617 
618 static void
619 putf(const char *cp)
620 {
621 	time_t t;
622 	char *slash, db[100];
623 
624 	while (*cp) {
625 		if (*cp != '%') {
626 			putchr(*cp++);
627 			continue;
628 		}
629 		switch (*++cp) {
630 
631 		case 't':
632 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
633 				slash = strrchr(ttyn, '/');
634 			if (slash == NULL)
635 				xputs(ttyn);
636 			else
637 				xputs(&slash[1]);
638 			break;
639 
640 		case 'h':
641 			xputs(editedhost);
642 			break;
643 
644 		case 'd':
645 			(void)time(&t);
646 			(void)strftime(db, sizeof(db),
647 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
648 			xputs(db);
649 			break;
650 
651 		case 's':
652 			xputs(kerninfo.sysname);
653 			break;
654 
655 		case 'm':
656 			xputs(kerninfo.machine);
657 			break;
658 
659 		case 'r':
660 			xputs(kerninfo.release);
661 			break;
662 
663 		case 'v':
664 			xputs(kerninfo.version);
665 			break;
666 
667 		case '%':
668 			putchr('%');
669 			break;
670 		}
671 		if (*cp)
672 			cp++;
673 	}
674 }
675 
676 static void
677 clearscreen(void)
678 {
679 	struct ttyent *typ;
680 	int err;
681 
682 	if (rawttyn == NULL)
683 		return;
684 
685 	typ = getttynam(rawttyn);
686 
687 	if ((typ == NULL) || (typ->ty_type == NULL) ||
688 	    (typ->ty_type[0] == 0))
689 		return;
690 
691 	if (setupterm(typ->ty_type, 0, &err) == ERR)
692 		return;
693 
694 	if (clear_screen)
695 		putpad(clear_screen);
696 
697 	del_curterm(cur_term);
698 	cur_term = NULL;
699 }
700