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