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