]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/sulogin.c
sulogin: map NL to CR-NL on output in initial termios settings
[thirdparty/util-linux.git] / login-utils / sulogin.c
1 /*
2 * sulogin
3 *
4 * This program gives Linux machines a reasonable secure way to boot single
5 * user. It forces the user to supply the root password before a shell is
6 * started. If there is a shadow password file and the encrypted root password
7 * is "x" the shadow password will be used.
8 *
9 * Copyright (C) 1998-2003 Miquel van Smoorenburg.
10 * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
11 * Copyright (C) 2012 Werner Fink <werner@suse.de>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 */
27 #include <sys/mman.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <pwd.h>
38 #include <shadow.h>
39 #include <termios.h>
40 #include <errno.h>
41 #include <getopt.h>
42 #include <sys/ioctl.h>
43 #ifdef HAVE_CRYPT_H
44 # include <crypt.h>
45 #endif
46
47 #ifdef HAVE_LIBSELINUX
48 # include <selinux/selinux.h>
49 # include <selinux/get_context_list.h>
50 #endif
51
52 #include "c.h"
53 #include "closestream.h"
54 #include "nls.h"
55 #include "pathnames.h"
56 #include "strutils.h"
57 #include "ttyutils.h"
58 #include "consoles.h"
59 #define CONMAX 16
60
61 #define BS CTRL('h')
62 #define NL CTRL('j')
63 #define CR CTRL('m')
64
65 static unsigned int timeout;
66 static int profile;
67 static volatile uint32_t openfd; /* Remember higher file descriptors */
68 static volatile uint32_t *usemask;
69
70 struct sigaction saved_sigint;
71 struct sigaction saved_sigtstp;
72 struct sigaction saved_sigquit;
73 struct sigaction saved_sighup;
74 struct sigaction saved_sigchld;
75
76 static volatile sig_atomic_t alarm_rised;
77 static volatile sig_atomic_t sigchild;
78
79 #ifndef IUCLC
80 # define IUCLC 0
81 #endif
82
83 /*
84 * Fix the tty modes and set reasonable defaults.
85 */
86 static void tcinit(struct console *con)
87 {
88 int mode = 0, flags = 0;
89 struct termios *tio = &con->tio;
90 int fd = con->fd;
91
92 errno = 0;
93
94 if (tcgetattr(fd, tio) < 0) {
95 warn(_("tcgetattr failed"));
96 con->flags |= CON_NOTTY;
97 return;
98 }
99
100 /* Handle serial lines here */
101 if (ioctl(fd, TIOCMGET, (char *) &mode) == 0) {
102 speed_t ispeed, ospeed;
103 struct winsize ws;
104
105 /* this is a modem line */
106 con->flags |= CON_SERIAL;
107
108 /* Flush input and output queues on modem lines */
109 tcflush(fd, TCIOFLUSH);
110
111 ispeed = cfgetispeed(tio);
112 ospeed = cfgetospeed(tio);
113
114 if (!ispeed) ispeed = TTYDEF_SPEED;
115 if (!ospeed) ospeed = TTYDEF_SPEED;
116
117 tio->c_cflag = CREAD | CS8 | HUPCL | (tio->c_cflag & CLOCAL);
118 tio->c_iflag = 0;
119 tio->c_lflag = 0;
120 tio->c_oflag &= OPOST | ONLCR;
121
122 cfsetispeed(tio, ispeed);
123 cfsetospeed(tio, ospeed);
124
125 tio->c_line = 0;
126 tio->c_cc[VTIME] = 0;
127 tio->c_cc[VMIN] = 1;
128
129 if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
130 int set = 0;
131 if (ws.ws_row == 0) {
132 ws.ws_row = 24;
133 set++;
134 }
135 if (ws.ws_col == 0) {
136 ws.ws_col = 80;
137 set++;
138 }
139 if (set)
140 ignore_result( ioctl(fd, TIOCSWINSZ, &ws) );
141 }
142
143 setlocale(LC_CTYPE, "POSIX");
144 goto setattr;
145 }
146 #if defined(IUTF8) && defined(KDGKBMODE)
147 /* Detect mode of current keyboard setup, e.g. for UTF-8 */
148 if (ioctl(fd, KDGKBMODE, &mode) < 0)
149 mode = K_RAW;
150 switch(mode) {
151 case K_UNICODE:
152 setlocale(LC_CTYPE, "C.UTF-8");
153 flags |= UL_TTY_UTF8;
154 break;
155 case K_RAW:
156 case K_MEDIUMRAW:
157 case K_XLATE:
158 default:
159 setlocale(LC_CTYPE, "POSIX");
160 break;
161 }
162 #else
163 setlocale(LC_CTYPE, "POSIX");
164 #endif
165 reset_virtual_console(tio, flags);
166 setattr:
167 if (tcsetattr(fd, TCSANOW, tio))
168 warn(_("tcsetattr failed"));
169
170 /* Enable blocking mode for read and write */
171 if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
172 ignore_result( fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) );
173 }
174
175 /*
176 * Finalize the tty modes on modem lines.
177 */
178 static void tcfinal(struct console *con)
179 {
180 struct termios *tio;
181 int fd;
182
183 if ((con->flags & CON_SERIAL) == 0) {
184 setenv("TERM", "linux", 1);
185 return;
186 }
187 if (con->flags & CON_NOTTY)
188 return;
189
190 setenv("TERM", "vt102", 1);
191 tio = &con->tio;
192 fd = con->fd;
193
194 tio->c_iflag |= (IXON | IXOFF);
195 tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE);
196 tio->c_oflag |= OPOST;
197
198 tio->c_cc[VINTR] = CINTR;
199 tio->c_cc[VQUIT] = CQUIT;
200 tio->c_cc[VERASE] = con->cp.erase;
201 tio->c_cc[VKILL] = con->cp.kill;
202 tio->c_cc[VEOF] = CEOF;
203 #ifdef VSWTC
204 tio->c_cc[VSWTC] = _POSIX_VDISABLE;
205 #else
206 tio->c_cc[VSWTCH] = _POSIX_VDISABLE;
207 #endif
208 tio->c_cc[VSTART] = CSTART;
209 tio->c_cc[VSTOP] = CSTOP;
210 tio->c_cc[VSUSP] = CSUSP;
211 tio->c_cc[VEOL] = _POSIX_VDISABLE;
212
213 if (con->cp.eol == CR) {
214 tio->c_iflag |= ICRNL;
215 tio->c_iflag &= ~(INLCR|IGNCR);
216 tio->c_oflag |= ONLCR;
217 tio->c_oflag &= ~(OCRNL|ONLRET);
218 }
219
220 switch (con->cp.parity) {
221 default:
222 case 0:
223 tio->c_cflag &= ~(PARODD | PARENB);
224 tio->c_iflag &= ~(INPCK | ISTRIP);
225 break;
226 case 1: /* odd parity */
227 tio->c_cflag |= PARODD;
228 /* fall through */
229 case 2: /* even parity */
230 tio->c_cflag |= PARENB;
231 tio->c_iflag |= (INPCK | ISTRIP);
232 /* fall through */
233 case (1 | 2): /* no parity bit */
234 tio->c_cflag &= ~CSIZE;
235 tio->c_cflag |= CS7;
236 break;
237 }
238
239 /* Set line attributes */
240 tcsetattr(fd, TCSANOW, tio);
241 }
242
243 /*
244 * Called at timeout.
245 */
246 static void alrm_handler(int sig __attribute__((unused)))
247 {
248 /* Timeout expired */
249 alarm_rised++;
250 }
251
252 static void chld_handler(int sig __attribute__((unused)))
253 {
254 sigchild++;
255 }
256
257 static void mask_signal(int signal, void (*handler)(int),
258 struct sigaction *origaction)
259 {
260 struct sigaction newaction;
261
262 newaction.sa_handler = handler;
263 sigemptyset(&newaction.sa_mask);
264 newaction.sa_flags = 0;
265
266 sigaction(signal, &newaction, origaction);
267 }
268
269 static void unmask_signal(int signal, struct sigaction *sa)
270 {
271 sigaction(signal, sa, NULL);
272 }
273
274 /*
275 * See if an encrypted password is valid. The encrypted password is checked for
276 * traditional-style DES and FreeBSD-style MD5 encryption.
277 */
278 static int valid(const char *pass)
279 {
280 const char *s;
281 char id[5];
282 size_t len;
283 off_t off;
284
285 if (pass[0] == 0)
286 return 1;
287 if (pass[0] != '$')
288 goto check_des;
289
290 /*
291 * up to 4 bytes for the signature e.g. $1$
292 */
293 for (s = pass+1; *s && *s != '$'; s++);
294
295 if (*s++ != '$')
296 return 0;
297
298 if ((off = (off_t)(s-pass)) > 4 || off < 3)
299 return 0;
300
301 memset(id, '\0', sizeof(id));
302 strncpy(id, pass, off);
303
304 /*
305 * up to 16 bytes for the salt
306 */
307 for (; *s && *s != '$'; s++);
308
309 if (*s++ != '$')
310 return 0;
311
312 if ((off_t)(s-pass) > 16)
313 return 0;
314
315 len = strlen(s);
316
317 /*
318 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
319 */
320 if ((strcmp(id, "$1$") == 0) && (len < 22 || len > 24))
321 return 0;
322
323 /*
324 * the SHA-256 hash 43 bytes
325 */
326 if ((strcmp(id, "$5$") == 0) && (len < 42 || len > 44))
327 return 0;
328
329 /*
330 * the SHA-512 hash 86 bytes
331 */
332 if ((strcmp(id, "$6$") == 0) && (len < 85 || len > 87))
333 return 0;
334
335 /*
336 * e.g. Blowfish hash
337 */
338 return 1;
339 check_des:
340 if (strlen(pass) != 13)
341 return 0;
342
343 for (s = pass; *s; s++) {
344 if ((*s < '0' || *s > '9') &&
345 (*s < 'a' || *s > 'z') &&
346 (*s < 'A' || *s > 'Z') &&
347 *s != '.' && *s != '/')
348 return 0;
349 }
350 return 1;
351 }
352
353 /*
354 * Set a variable if the value is not NULL.
355 */
356 static inline void set(char **var, char *val)
357 {
358 if (val)
359 *var = val;
360 }
361
362 /*
363 * Get the root password entry.
364 */
365 static struct passwd *getrootpwent(int try_manually)
366 {
367 static struct passwd pwd;
368 struct passwd *pw;
369 struct spwd *spw;
370 FILE *fp;
371 static char line[256];
372 static char sline[256];
373 char *p;
374
375 /*
376 * First, we try to get the password the standard way using normal
377 * library calls.
378 */
379 if ((pw = getpwnam("root")) &&
380 !strcmp(pw->pw_passwd, "x") &&
381 (spw = getspnam("root")))
382 pw->pw_passwd = spw->sp_pwdp;
383
384 if (pw || !try_manually)
385 return pw;
386
387 /*
388 * If we come here, we could not retrieve the root password through
389 * library calls and we try to read the password and shadow files
390 * manually.
391 */
392 pwd.pw_name = "root";
393 pwd.pw_passwd = "";
394 pwd.pw_gecos = "Super User";
395 pwd.pw_dir = "/";
396 pwd.pw_shell = "";
397 pwd.pw_uid = 0;
398 pwd.pw_gid = 0;
399
400 if ((fp = fopen(_PATH_PASSWD, "r")) == NULL) {
401 warn(_("cannot open %s"), _PATH_PASSWD);
402 return &pwd;
403 }
404
405 /*
406 * Find root in the password file.
407 */
408 while ((p = fgets(line, 256, fp)) != NULL) {
409 if (strncmp(line, "root:", 5) != 0)
410 continue;
411 p += 5;
412 set(&pwd.pw_passwd, strsep(&p, ":"));
413 strsep(&p, ":");
414 strsep(&p, ":");
415 set(&pwd.pw_gecos, strsep(&p, ":"));
416 set(&pwd.pw_dir, strsep(&p, ":"));
417 set(&pwd.pw_shell, strsep(&p, "\n"));
418 p = line;
419 break;
420 }
421
422 fclose(fp);
423
424 /*
425 * If the encrypted password is valid or not found, return.
426 */
427 if (p == NULL) {
428 warnx(_("%s: no entry for root\n"), _PATH_PASSWD);
429 return &pwd;
430 }
431 if (valid(pwd.pw_passwd))
432 return &pwd;
433
434 /*
435 * The password is invalid. If there is a shadow password, try it.
436 */
437 strcpy(pwd.pw_passwd, "");
438 if ((fp = fopen(_PATH_SHADOW_PASSWD, "r")) == NULL) {
439 warn(_("cannot open %s"), _PATH_PASSWD);
440 return &pwd;
441 }
442 while ((p = fgets(sline, 256, fp)) != NULL) {
443 if (strncmp(sline, "root:", 5) != 0)
444 continue;
445 p += 5;
446 set(&pwd.pw_passwd, strsep(&p, ":"));
447 break;
448 }
449 fclose(fp);
450
451 /*
452 * If the password is still invalid, NULL it, and return.
453 */
454 if (p == NULL) {
455 warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD);
456 strcpy(pwd.pw_passwd, "");
457 }
458 if (!valid(pwd.pw_passwd)) {
459 warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD);
460 strcpy(pwd.pw_passwd, "");
461 }
462 return &pwd;
463 }
464
465 /*
466 * Ask by prompt for the password.
467 */
468 static void doprompt(const char *crypted, struct console *con)
469 {
470 struct termios tty;
471
472 if (con->flags & CON_SERIAL) {
473 tty = con->tio;
474 /*
475 * For prompting: map NL in output to CR-NL
476 * otherwise we may see stairs in the output.
477 */
478 tty.c_oflag |= (ONLCR | OPOST);
479 tcsetattr(con->fd, TCSADRAIN, &tty);
480 }
481 if (con->file == (FILE*)0) {
482 if ((con->file = fdopen(con->fd, "r+")) == (FILE*)0)
483 goto err;
484 }
485 #if defined(USE_ONELINE)
486 if (crypted[0])
487 fprintf(con->file, _("Give root password for login: "));
488 else
489 fprintf(con->file, _("Press enter for login: "));
490 #else
491 if (crypted[0])
492 fprintf(con->file, _("Give root password for maintenance\n"));
493 else
494 fprintf(con->file, _("Press enter for maintenance"));
495 fprintf(con->file, _("(or type Control-D to continue): "));
496 #endif
497 fflush(con->file);
498 err:
499 if (con->flags & CON_SERIAL)
500 tcsetattr(con->fd, TCSADRAIN, &con->tio);
501 }
502
503 /*
504 * Make sure to have an own session and controlling terminal
505 */
506 static void setup(struct console *con)
507 {
508 pid_t pid, pgrp, ppgrp, ttypgrp;
509 int fd;
510
511 if (con->flags & CON_NOTTY)
512 return;
513 fd = con->fd;
514
515 /*
516 * Only go through this trouble if the new
517 * tty doesn't fall in this process group.
518 */
519 pid = getpid();
520 pgrp = getpgid(0);
521 ppgrp = getpgid(getppid());
522 ttypgrp = tcgetpgrp(fd);
523
524 if (pgrp != ttypgrp && ppgrp != ttypgrp) {
525 if (pid != getsid(0)) {
526 if (pid == getpgid(0))
527 setpgid(0, getpgid(getppid()));
528 setsid();
529 }
530
531 mask_signal(SIGHUP, SIG_IGN, &saved_sighup);
532 if (ttypgrp > 0)
533 ioctl(STDIN_FILENO, TIOCNOTTY, (char *)1);
534 unmask_signal(SIGHUP, &saved_sighup);
535 if (fd > STDIN_FILENO) close(STDIN_FILENO);
536 if (fd > STDOUT_FILENO) close(STDOUT_FILENO);
537 if (fd > STDERR_FILENO) close(STDERR_FILENO);
538
539 ioctl(fd, TIOCSCTTY, (char *)1);
540 tcsetpgrp(fd, ppgrp);
541 }
542 dup2(fd, STDIN_FILENO);
543 dup2(fd, STDOUT_FILENO);
544 dup2(fd, STDERR_FILENO);
545 con->fd = STDIN_FILENO;
546
547 for (fd = STDERR_FILENO+1; fd < 32; fd++) {
548 if (openfd & (1<<fd)) {
549 close(fd);
550 openfd &= ~(1<<fd);
551 }
552 }
553 }
554
555 /*
556 * Ask for the password. Note that there is no default timeout as we normally
557 * skip this during boot.
558 */
559 static char *getpasswd(struct console *con)
560 {
561 struct sigaction sa;
562 struct termios tty;
563 static char pass[128], *ptr;
564 struct chardata *cp;
565 char *ret = pass;
566 unsigned char tc;
567 char c, ascval;
568 int eightbit;
569 int fd;
570
571 if (con->flags & CON_NOTTY)
572 goto out;
573 fd = con->fd;
574 cp = &con->cp;
575 tty = con->tio;
576
577 tty.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
578 tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG);
579 tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0);
580
581 sa.sa_handler = alrm_handler;
582 sa.sa_flags = 0;
583 sigaction(SIGALRM, &sa, NULL);
584
585 if (timeout)
586 alarm(timeout);
587
588 ptr = &pass[0];
589 cp->eol = *ptr = '\0';
590
591 eightbit = ((con->flags & CON_SERIAL) == 0 || (tty.c_cflag & (PARODD|PARENB)) == 0);
592 while (cp->eol == '\0') {
593 if (read(fd, &c, 1) < 1) {
594 if (errno == EINTR || errno == EAGAIN) {
595 usleep(1000);
596 continue;
597 }
598 ret = (char*)0;
599 switch (errno) {
600 case 0:
601 case EIO:
602 case ESRCH:
603 case EINVAL:
604 case ENOENT:
605 break;
606 default:
607 fprintf(stderr, "sulogin: read(%s): %m\n\r", con->tty);
608 break;
609 }
610 goto quit;
611 }
612
613 if (eightbit)
614 ascval = c;
615 else if (c != (ascval = (c & 0177))) {
616 uint32_t bits, mask;
617 for (bits = 1, mask = 1; mask & 0177; mask <<= 1) {
618 if (mask & ascval)
619 bits++;
620 }
621 cp->parity |= ((bits & 1) ? 1 : 2);
622 }
623
624 switch (ascval) {
625 case 0:
626 *ptr = '\0';
627 goto quit;
628 case CR:
629 case NL:
630 *ptr = '\0';
631 cp->eol = ascval;
632 break;
633 case BS:
634 case CERASE:
635 cp->erase = ascval;
636 if (ptr > &pass[0])
637 ptr--;
638 break;
639 case CKILL:
640 cp->kill = ascval;
641 while (ptr > &pass[0])
642 ptr--;
643 break;
644 case CEOF:
645 goto quit;
646 default:
647 if ((size_t)(ptr - &pass[0]) >= (sizeof(pass) -1 )) {
648 fprintf(stderr, "sulogin: input overrun at %s\n\r", con->tty);
649 ret = (char*)0;
650 goto quit;
651 }
652 *ptr++ = ascval;
653 break;
654 }
655 }
656 quit:
657 alarm(0);
658 if (tc)
659 tcsetattr(fd, TCSAFLUSH, &con->tio);
660 if (ret && *ret != '\0')
661 tcfinal(con);
662 printf("\r\n");
663 out:
664 return ret;
665 }
666
667 /*
668 * Password was OK, execute a shell.
669 */
670 static void sushell(struct passwd *pwd)
671 {
672 char shell[PATH_MAX];
673 char home[PATH_MAX];
674 char *p;
675 char *su_shell;
676
677 /*
678 * Set directory and shell.
679 */
680 if (chdir(pwd->pw_dir) != 0) {
681 warn(_("%s: change directory failed"), pwd->pw_dir);
682 printf(_("Logging in with home = \"/\".\n"));
683
684 if (chdir("/") != 0)
685 warn(_("change directory to system root failed"));
686 }
687
688 if ((p = getenv("SUSHELL")) != NULL)
689 su_shell = p;
690 else if ((p = getenv("sushell")) != NULL)
691 su_shell = p;
692 else {
693 if (pwd->pw_shell[0])
694 su_shell = pwd->pw_shell;
695 else
696 su_shell = "/bin/sh";
697 }
698 if ((p = strrchr(su_shell, '/')) == NULL)
699 p = su_shell;
700 else
701 p++;
702
703 snprintf(shell, sizeof(shell), profile ? "-%s" : "%s", p);
704
705 /*
706 * Set some important environment variables.
707 */
708 if (getcwd(home, sizeof(home)) == NULL)
709 strcpy(home, "/");
710
711 setenv("HOME", home, 1);
712 setenv("LOGNAME", "root", 1);
713 setenv("USER", "root", 1);
714 if (!profile)
715 setenv("SHLVL","0",1);
716
717 /*
718 * Try to execute a shell.
719 */
720 setenv("SHELL", su_shell, 1);
721 unmask_signal(SIGINT, &saved_sigint);
722 unmask_signal(SIGTSTP, &saved_sigtstp);
723 unmask_signal(SIGQUIT, &saved_sigquit);
724 mask_signal(SIGHUP, SIG_DFL, NULL);
725
726 #ifdef HAVE_LIBSELINUX
727 if (is_selinux_enabled() > 0) {
728 security_context_t scon=NULL;
729 char *seuser=NULL;
730 char *level=NULL;
731 if (getseuserbyname("root", &seuser, &level) == 0) {
732 if (get_default_context_with_level(seuser, level, 0, &scon) == 0) {
733 if (setexeccon(scon) != 0)
734 warnx(_("setexeccon failed"));
735 freecon(scon);
736 }
737 }
738 free(seuser);
739 free(level);
740 }
741 #endif
742 execl(su_shell, shell, NULL);
743 warn(_("%s: exec failed"), su_shell);
744
745 setenv("SHELL", "/bin/sh", 1);
746 execl("/bin/sh", profile ? "-sh" : "sh", NULL);
747 warn(_("%s: exec failed"), "/bin/sh");
748 }
749
750 static void usage(FILE *out)
751 {
752 fputs(USAGE_HEADER, out);
753 fprintf(out, _(
754 " %s [options] [tty device]\n"), program_invocation_short_name);
755
756 fputs(USAGE_OPTIONS, out);
757 fputs(_(" -p, --login-shell start a login shell\n"
758 " -t, --timeout <seconds> max time to wait for a password (default: no limit)\n"
759 " -e, --force examine password files directly if getpwnam(3) fails\n"),
760 out);
761
762 fputs(USAGE_SEPARATOR, out);
763 fputs(USAGE_HELP, out);
764 fputs(USAGE_VERSION, out);
765 fprintf(out, USAGE_MAN_TAIL("sulogin(8)"));
766 }
767
768 int main(int argc, char **argv)
769 {
770 LIST_HEAD(consoles);
771 struct list_head *ptr;
772 struct console *con;
773 char *tty = NULL;
774 struct passwd *pwd;
775 int c, status = 0;
776 int reconnect = 0;
777 int opt_e = 0;
778 pid_t pid;
779
780 static const struct option longopts[] = {
781 { "login-shell", 0, 0, 'p' },
782 { "timeout", 1, 0, 't' },
783 { "force", 0, 0, 'e' },
784 { "help", 0, 0, 'h' },
785 { "version", 0, 0, 'V' },
786 { NULL, 0, 0, 0 }
787 };
788
789 /*
790 * If we are init we need to set up a own session.
791 */
792 if ((pid = getpid()) == 1) {
793 setsid();
794 ignore_result( ioctl(STDIN_FILENO, TIOCSCTTY, (char *) 1) );
795 }
796
797 setlocale(LC_ALL, "");
798 bindtextdomain(PACKAGE, LOCALEDIR);
799 textdomain(PACKAGE);
800 atexit(close_stdout); /* XXX */
801
802 /*
803 * See if we have a timeout flag.
804 */
805 while ((c = getopt_long(argc, argv, "ehpt:V", longopts, NULL)) != -1) {
806 switch(c) {
807 case 't':
808 timeout = strtou32_or_err(optarg, _("invalid timeout argument"));
809 break;
810 case 'p':
811 profile = 1;
812 break;
813 case 'e':
814 opt_e = 1;
815 break;
816 case 'V':
817 printf(UTIL_LINUX_VERSION);
818 return EXIT_SUCCESS;
819 case 'h':
820 usage(stdout);
821 return EXIT_SUCCESS;
822 default:
823 usage(stderr);
824 /* Do not exit! */
825 break;
826 }
827 }
828
829 if (geteuid() != 0)
830 errx(EXIT_FAILURE, _("only root can run this program."));
831
832 mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
833 mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
834 mask_signal(SIGINT, SIG_IGN, &saved_sigint);
835 mask_signal(SIGHUP, SIG_IGN, &saved_sighup);
836
837 /*
838 * See if we need to open an other tty device.
839 */
840 if (optind < argc)
841 tty = argv[optind];
842
843 if (!tty || *tty == '\0')
844 tty = getenv("CONSOLE");
845
846 /*
847 * Detect possible consoles, use stdin as fallback.
848 * If an optional tty is given, reconnect it to stdin.
849 */
850 reconnect = detect_consoles(tty, STDIN_FILENO, &consoles);
851
852 /*
853 * If previous stdin was not the speified tty and therefore reconnected
854 * to the specified tty also reconnect stdout and stderr.
855 */
856 if (reconnect) {
857 if (isatty(STDOUT_FILENO) == 0)
858 dup2(STDOUT_FILENO, STDIN_FILENO);
859 if (isatty(STDERR_FILENO) == 0)
860 dup2(STDOUT_FILENO, STDERR_FILENO);
861 }
862
863 /*
864 * Should not happen
865 */
866 if (list_empty(&consoles)) {
867 if (!errno)
868 errno = ENOENT;
869 errx(EXIT_FAILURE, _("cannot open console: %m\n"));
870 }
871
872 /*
873 * Get the root password.
874 */
875 if ((pwd = getrootpwent(opt_e)) == NULL) {
876 warnx(_("cannot open password database."));
877 sleep(2);
878 }
879
880 /*
881 * Ask for the password on the consoles.
882 */
883 list_for_each(ptr, &consoles) {
884 con = list_entry(ptr, struct console, entry);
885 if (con->id >= CONMAX)
886 break;
887 if (con->fd >= 0) {
888 openfd |= (1 << con->fd);
889 tcinit(con);
890 continue;
891 }
892 if ((con->fd = open(con->tty, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
893 continue;
894 openfd |= (1 << con->fd);
895 tcinit(con);
896 }
897 ptr = (&consoles)->next;
898 usemask = (uint32_t*) mmap(NULL, sizeof(uint32_t),
899 PROT_READ|PROT_WRITE,
900 MAP_ANONYMOUS|MAP_SHARED, -1, 0);
901
902 if (ptr->next == &consoles) {
903 con = list_entry(ptr, struct console, entry);
904 goto nofork;
905 }
906
907 mask_signal(SIGCHLD, chld_handler, &saved_sigchld);
908 do {
909 con = list_entry(ptr, struct console, entry);
910 if (con->id >= CONMAX)
911 break;
912
913 switch ((con->pid = fork())) {
914 case 0:
915 mask_signal(SIGCHLD, SIG_DFL, NULL);
916 /* fall through */
917 nofork:
918 setup(con);
919 while (1) {
920 const char *passwd = pwd->pw_passwd;
921 const char *answer;
922 int failed = 0, doshell = 0;
923
924 doprompt(passwd, con);
925 if ((answer = getpasswd(con)) == NULL)
926 break;
927
928 if (passwd[0] == '\0')
929 doshell++;
930 else {
931 const char *cryptbuf;
932 cryptbuf = crypt(answer, passwd);
933 if (cryptbuf == NULL)
934 warnx(_("crypt failed: %m\n"));
935 else if (strcmp(cryptbuf, pwd->pw_passwd) == 0)
936 doshell++;
937 }
938
939 if (doshell) {
940 *usemask |= (1<<con->id);
941 sushell(pwd);
942 *usemask &= ~(1<<con->id);
943 failed++;
944 }
945
946 mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
947 mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
948 mask_signal(SIGINT, SIG_IGN, &saved_sigint);
949
950 if (failed) {
951 fprintf(stderr, _("Can not execute su shell\n\n"));
952 break;
953 }
954 fprintf(stderr, _("Login incorrect\n\n"));
955 }
956 if (alarm_rised) {
957 tcfinal(con);
958 warnx(_("Timed out\n\n"));
959 }
960 /*
961 * User pressed Control-D.
962 */
963 exit(0);
964 case -1:
965 warnx(_("Can not fork: %m\n"));
966 /* fall through */
967 default:
968 break;
969 }
970
971 ptr = ptr->next;
972
973 } while (ptr != &consoles);
974
975 while ((pid = wait(&status))) {
976 if (errno == ECHILD)
977 break;
978 if (pid < 0)
979 continue;
980 list_for_each(ptr, &consoles) {
981 con = list_entry(ptr, struct console, entry);
982 if (con->pid == pid) {
983 *usemask &= ~(1<<con->id);
984 continue;
985 }
986 if (kill(con->pid, 0) < 0) {
987 *usemask &= ~(1<<con->id);
988 continue;
989 }
990 if (*usemask & (1<<con->id))
991 continue;
992 kill(con->pid, SIGHUP);
993 usleep(5000);
994 kill(con->pid, SIGKILL);
995 }
996 }
997
998 mask_signal(SIGCHLD, SIG_DFL, NULL);
999 return EXIT_SUCCESS;
1000 }