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 41static 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 75extern 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 93struct termios tmode, omode; 94 95int crmod, digit_or_punc, lower, upper; 96 97char hostname[MAXHOSTNAMELEN + 1]; 98struct utsname kerninfo; 99char name[LOGIN_NAME_MAX]; 100char dev[] = _PATH_DEV; 101char ttyn[32]; 102uid_t ttyowner = 0; 103char *rawttyn; 104 105#define OBUFSIZ 128 106#define TABBUFSIZ 512 107 108char defent[TABBUFSIZ]; 109char tabent[TABBUFSIZ]; 110 111char *env[128]; 112 113const 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 136static void clearscreen(void); 137 138sigjmp_buf timeout; 139 140__dead static void 141/*ARGSUSED*/ 142dingdong(int signo) 143{ 144 145 (void)alarm(0); 146 (void)signal(SIGALRM, SIG_DFL); 147 siglongjmp(timeout, 1); 148} 149 150sigjmp_buf intrupt; 151 152__dead static void 153/*ARGSUSED*/ 154interrupt(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*/ 166timeoverrun(int signo) 167{ 168 169 syslog(LOG_ERR, "getty exiting due to excessive running time"); 170 exit(1); 171} 172 173static int getname(void); 174static void oflush(void); 175static void prompt(void); 176static int putchr(int); 177static void putf(const char *); 178static void xputs(const char *); 179 180#define putpad(s) tputs(s, 1, putchr) 181 182int 183main(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 402static int 403getname(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 526static void 527xputs(const char *s) 528{ 529 while (*s) 530 putchr(*s++); 531} 532 533char outbuf[OBUFSIZ]; 534size_t obufcnt = 0; 535 536static int 537putchr(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 556static void 557oflush(void) 558{ 559 if (obufcnt) 560 (void)write(STDOUT_FILENO, outbuf, obufcnt); 561 obufcnt = 0; 562} 563 564static void 565prompt(void) 566{ 567 568 putf(LM); 569 if (CO) 570 putchr('\n'); 571} 572 573static void 574putf(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 631static void 632clearscreen(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