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.
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>
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.
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.
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
28 #include <sys/types.h>
42 #include <sys/ioctl.h>
47 #ifdef HAVE_LIBSELINUX
48 # include <selinux/selinux.h>
49 # include <selinux/get_context_list.h>
54 # include <sys/param.h>
55 # include <linux/serial.h>
59 #include "closestream.h"
62 #include "pathnames.h"
63 #ifdef USE_PLYMOUTH_SUPPORT
64 # include "plymouth-ctrl.h"
68 #include "sulogin-consoles.h"
71 static unsigned int timeout
;
73 static volatile uint32_t openfd
; /* Remember higher file descriptors */
75 static struct sigaction saved_sigint
;
76 static struct sigaction saved_sigtstp
;
77 static struct sigaction saved_sigquit
;
78 static struct sigaction saved_sighup
;
79 static struct sigaction saved_sigchld
;
81 static volatile sig_atomic_t alarm_rised
;
82 static volatile sig_atomic_t sigchild
;
84 #define SULOGIN_PASSWORD_BUFSIZ 128
91 # warning "WEXITED is missing, sulogin may not work as expected"
95 static int locked_account_password(const char * const passwd
)
97 if (passwd
&& (*passwd
== '*' || *passwd
== '!'))
103 * Fix the tty modes and set reasonable defaults.
105 static void tcinit(struct console
*con
)
107 int flags
= 0, mode
= 0;
108 struct termios
*tio
= &con
->tio
;
109 const int fd
= con
->fd
;
110 #if defined(TIOCGSERIAL)
111 struct serial_struct serinfo
= { .flags
= 0 };
113 #ifdef USE_PLYMOUTH_SUPPORT
115 int i
= (plymouth_command(MAGIC_PING
)) ? PLYMOUTH_TERMIOS_FLAGS_DELAY
: 0;
117 plymouth_command(MAGIC_QUIT
);
120 * With plymouth the termios flags become changed after this
121 * function had changed the termios.
123 memset(&lock
, 0, sizeof(struct termios
));
124 if (ioctl(fd
, TIOCGLCKTRMIOS
, &lock
) < 0)
126 if (!lock
.c_iflag
&& !lock
.c_oflag
&& !lock
.c_cflag
&& !lock
.c_lflag
)
130 memset(&lock
, 0, sizeof(struct termios
));
131 ioctl(fd
, TIOCSLCKTRMIOS
, &lock
);
136 if (ioctl(fd
, TIOCGSERIAL
, &serinfo
) >= 0)
137 con
->flags
|= CON_SERIAL
;
142 if (!(con
->flags
& CON_SERIAL
)
143 && ioctl(fd
, KDGKBMODE
, &mode
) < 0)
144 con
->flags
|= CON_SERIAL
;
147 if (tcgetattr(fd
, tio
) < 0) {
149 #if defined(KDGKBMODE) || defined(TIOCGSERIAL)
150 if (con
->flags
& CON_SERIAL
) { /* Try to recover this */
152 # if defined(TIOCGSERIAL)
153 serinfo
.flags
|= ASYNC_SKIP_TEST
; /* Skip test of UART */
155 if (ioctl(fd
, TIOCSSERIAL
, &serinfo
) < 0)
157 if (ioctl(fd
, TIOCSERCONFIG
) < 0) /* Try to autoconfigure */
159 if (ioctl(fd
, TIOCGSERIAL
, &serinfo
) < 0)
160 goto tcgeterr
; /* Ouch */
162 if (tcgetattr(fd
, tio
) < 0) /* Retry to get tty attributes */
165 # if defined(TIOCGSERIAL)
171 FILE *fcerr
= fdopen(fd
, "w");
173 fprintf(fcerr
, _("tcgetattr failed"));
176 warn(_("tcgetattr failed"));
178 con
->flags
&= ~CON_SERIAL
;
180 con
->flags
|= CON_NOTTY
;
182 con
->flags
|= CON_EIO
;
189 /* Handle lines other than virtual consoles here */
190 #if defined(KDGKBMODE) || defined(TIOCGSERIAL)
191 if (con
->flags
& CON_SERIAL
)
194 speed_t ispeed
, ospeed
;
198 /* Flush input and output queues on modem lines */
199 tcflush(fd
, TCIOFLUSH
);
201 ispeed
= cfgetispeed(tio
);
202 ospeed
= cfgetospeed(tio
);
204 if (!ispeed
) ispeed
= TTYDEF_SPEED
;
205 if (!ospeed
) ospeed
= TTYDEF_SPEED
;
207 tio
->c_cflag
= CREAD
| CS8
| HUPCL
| (tio
->c_cflag
& CLOCAL
);
210 tio
->c_oflag
&= OPOST
| ONLCR
;
212 cfsetispeed(tio
, ispeed
);
213 cfsetospeed(tio
, ospeed
);
215 #ifdef HAVE_STRUCT_TERMIOS_C_LINE
218 tio
->c_cc
[VTIME
] = 0;
221 if (ioctl(fd
, TIOCGWINSZ
, &ws
) == 0) {
224 if (ws
.ws_row
== 0) {
228 if (ws
.ws_col
== 0) {
233 ignore_result( ioctl(fd
, TIOCSWINSZ
, &ws
) );
236 setlocale(LC_CTYPE
, "POSIX");
239 #if defined(IUTF8) && defined(KDGKBMODE)
240 /* Handle mode of current keyboard setup, e.g. for UTF-8 */
243 setlocale(LC_CTYPE
, "C.UTF-8");
244 flags
|= UL_TTY_UTF8
;
250 setlocale(LC_CTYPE
, "POSIX");
254 setlocale(LC_CTYPE
, "POSIX");
256 reset_virtual_console(tio
, flags
);
258 if (tcsetattr(fd
, TCSANOW
, tio
))
259 warn(_("tcsetattr failed"));
261 /* Enable blocking mode for read and write */
262 if ((flags
= fcntl(fd
, F_GETFL
, 0)) != -1)
263 ignore_result( fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
) );
267 * Finalize the tty modes on modem lines.
269 static void tcfinal(struct console
*con
)
271 struct termios
*tio
= &con
->tio
;
272 const int fd
= con
->fd
;
273 char *term
, *ttyname
= NULL
;
276 ttyname
= strncmp(con
->tty
, "/dev/", 5) == 0 ?
277 con
->tty
+ 5 : con
->tty
;
279 term
= get_terminal_default_type(ttyname
, con
->flags
& CON_SERIAL
);
281 xsetenv("TERM", term
, 0);
285 if (!(con
->flags
& CON_SERIAL
) || (con
->flags
& CON_NOTTY
))
288 tio
->c_iflag
|= (IXON
| IXOFF
);
289 tio
->c_lflag
|= (ICANON
| ISIG
| ECHO
|ECHOE
|ECHOK
|ECHOKE
);
290 tio
->c_oflag
|= OPOST
;
292 tio
->c_cc
[VINTR
] = CINTR
;
293 tio
->c_cc
[VQUIT
] = CQUIT
;
294 tio
->c_cc
[VERASE
] = con
->cp
.erase
;
295 tio
->c_cc
[VKILL
] = con
->cp
.kill
;
296 tio
->c_cc
[VEOF
] = CEOF
;
298 tio
->c_cc
[VSWTC
] = _POSIX_VDISABLE
;
299 #elif defined(VSWTCH)
300 tio
->c_cc
[VSWTCH
] = _POSIX_VDISABLE
;
302 tio
->c_cc
[VSTART
] = CSTART
;
303 tio
->c_cc
[VSTOP
] = CSTOP
;
304 tio
->c_cc
[VSUSP
] = CSUSP
;
305 tio
->c_cc
[VEOL
] = _POSIX_VDISABLE
;
307 if (con
->cp
.eol
== CR
) {
308 tio
->c_iflag
|= ICRNL
;
309 tio
->c_iflag
&= ~(INLCR
|IGNCR
);
310 tio
->c_oflag
|= ONLCR
;
311 tio
->c_oflag
&= ~(OCRNL
|ONLRET
);
314 switch (con
->cp
.parity
) {
317 tio
->c_cflag
&= ~(PARODD
| PARENB
);
318 tio
->c_iflag
&= ~(INPCK
| ISTRIP
);
320 case 1: /* odd parity */
321 tio
->c_cflag
|= PARODD
;
323 case 2: /* even parity */
324 tio
->c_cflag
|= PARENB
;
325 tio
->c_iflag
|= (INPCK
| ISTRIP
);
327 case (1 | 2): /* no parity bit */
328 tio
->c_cflag
&= ~CSIZE
;
333 /* Set line attributes */
334 tcsetattr(fd
, TCSANOW
, tio
);
340 static void alrm_handler(int sig
__attribute__((unused
)))
342 /* Timeout expired */
346 static void chld_handler(int sig
__attribute__((unused
)))
351 static void mask_signal(int signal
, void (*handler
)(int),
352 struct sigaction
*origaction
)
354 struct sigaction newaction
;
356 newaction
.sa_handler
= handler
;
357 sigemptyset(&newaction
.sa_mask
);
358 newaction
.sa_flags
= 0;
360 sigaction(signal
, &newaction
, origaction
);
363 static void unmask_signal(int signal
, struct sigaction
*sa
)
365 sigaction(signal
, sa
, NULL
);
369 * See if an encrypted password is valid. The encrypted password is checked for
370 * traditional-style DES and FreeBSD-style MD5 encryption.
372 static int valid(const char *pass
)
385 * up to 4 bytes for the signature e.g. $1$
387 for (s
= pass
+1; *s
&& *s
!= '$'; s
++);
392 if ((off
= (off_t
)(s
-pass
)) > 4 || off
< 3)
395 memset(id
, '\0', sizeof(id
));
396 strncpy(id
, pass
, off
);
399 * up to 16 bytes for the salt
401 for (; *s
&& *s
!= '$'; s
++);
406 if ((off_t
)(s
-pass
) > 16)
412 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
414 if ((strcmp(id
, "$1$") == 0) && (len
< 22 || len
> 24))
418 * the SHA-256 hash 43 bytes
420 if ((strcmp(id
, "$5$") == 0) && (len
< 42 || len
> 44))
424 * the SHA-512 hash 86 bytes
426 if ((strcmp(id
, "$6$") == 0) && (len
< 85 || len
> 87))
434 if (strlen(pass
) != 13)
437 for (s
= pass
; *s
; s
++) {
438 if ((*s
< '0' || *s
> '9') &&
439 (*s
< 'a' || *s
> 'z') &&
440 (*s
< 'A' || *s
> 'Z') &&
441 *s
!= '.' && *s
!= '/')
448 * Set a variable if the value is not NULL.
450 static inline void set(char **var
, char *val
)
457 * Get the root password entry.
459 static struct passwd
*getrootpwent(int try_manually
)
461 static struct passwd pwd
;
465 static char line
[2 * BUFSIZ
];
466 static char sline
[2 * BUFSIZ
];
470 * First, we try to get the password the standard way using normal
473 if ((pw
= getpwnam("root")) &&
474 !strcmp(pw
->pw_passwd
, "x") &&
475 (spw
= getspnam("root")))
476 pw
->pw_passwd
= spw
->sp_pwdp
;
478 if (pw
|| !try_manually
)
482 * If we come here, we could not retrieve the root password through
483 * library calls and we try to read the password and shadow files
486 pwd
.pw_name
= "root";
488 pwd
.pw_gecos
= "Super User";
494 if ((fp
= fopen(_PATH_PASSWD
, "r")) == NULL
) {
495 warn(_("cannot open %s"), _PATH_PASSWD
);
500 * Find root in the password file.
502 while ((p
= fgets(line
, sizeof(line
), fp
)) != NULL
) {
503 if (strncmp(line
, "root:", 5) != 0)
506 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
509 set(&pwd
.pw_gecos
, strsep(&p
, ":"));
510 set(&pwd
.pw_dir
, strsep(&p
, ":"));
511 set(&pwd
.pw_shell
, strsep(&p
, "\n"));
518 * If the encrypted password is valid or not found, return.
521 warnx(_("%s: no entry for root\n"), _PATH_PASSWD
);
524 if (valid(pwd
.pw_passwd
))
528 * The password is invalid. If there is a shadow password, try it.
530 *pwd
.pw_passwd
= '\0';
531 if ((fp
= fopen(_PATH_SHADOW_PASSWD
, "r")) == NULL
) {
532 warn(_("cannot open %s"), _PATH_PASSWD
);
535 while ((p
= fgets(sline
, sizeof(sline
), fp
)) != NULL
) {
536 if (strncmp(sline
, "root:", 5) != 0)
539 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
545 * If the password is still invalid, NULL it, and return.
548 warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD
);
549 *pwd
.pw_passwd
= '\0';
551 /* locked account passwords are valid too */
552 if (!locked_account_password(pwd
.pw_passwd
) && !valid(pwd
.pw_passwd
)) {
553 warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD
);
554 *pwd
.pw_passwd
= '\0';
560 * Ask by prompt for the password.
562 static void doprompt(const char *crypted
, struct console
*con
, int deny
)
566 if (con
->flags
& CON_SERIAL
) {
569 * For prompting: map NL in output to CR-NL
570 * otherwise we may see stairs in the output.
572 tty
.c_oflag
|= (ONLCR
| OPOST
);
573 tcsetattr(con
->fd
, TCSADRAIN
, &tty
);
576 con
->file
= fdopen(con
->fd
, "r+");
582 fprintf(con
->file
, _("\nCannot open access to console, the root account is locked.\n"
583 "See sulogin(8) man page for more details.\n\n"
584 "Press Enter to continue.\n"));
586 #if defined(USE_ONELINE)
587 if (crypted
[0] && !locked_account_password(crypted
))
588 fprintf(con
->file
, _("Give root password for login: "));
590 fprintf(con
->file
, _("Press Enter for login: "));
592 if (crypted
[0] && !locked_account_password(crypted
))
593 fprintf(con
->file
, _("Give root password for maintenance\n"));
595 fprintf(con
->file
, _("Press Enter for maintenance\n"));
596 fprintf(con
->file
, _("(or press Control-D to continue): "));
601 if (con
->flags
& CON_SERIAL
)
602 tcsetattr(con
->fd
, TCSADRAIN
, &con
->tio
);
606 * Make sure to have an own session and controlling terminal
608 static void setup(struct console
*con
)
611 const pid_t pid
= getpid(), pgrp
= getpgid(0), ppgrp
= getpgid(getppid());
614 if (con
->flags
& CON_NOTTY
)
616 if (con
->flags
& CON_EIO
)
619 ttypgrp
= tcgetpgrp(fd
);
622 * Only go through this trouble if the new
623 * tty doesn't fall in this process group.
625 if (pgrp
!= ttypgrp
&& ppgrp
!= ttypgrp
) {
626 if (pid
!= getsid(0)) {
627 if (pid
== getpgid(0))
628 setpgid(0, getpgid(getppid()));
632 mask_signal(SIGHUP
, SIG_IGN
, &saved_sighup
);
634 ioctl(STDIN_FILENO
, TIOCNOTTY
, (char *)1);
635 unmask_signal(SIGHUP
, &saved_sighup
);
636 if (fd
> STDIN_FILENO
) close(STDIN_FILENO
);
637 if (fd
> STDOUT_FILENO
) close(STDOUT_FILENO
);
638 if (fd
> STDERR_FILENO
) close(STDERR_FILENO
);
640 ioctl(fd
, TIOCSCTTY
, (char *)1);
641 tcsetpgrp(fd
, ppgrp
);
644 dup2(fd
, STDIN_FILENO
);
645 dup2(fd
, STDOUT_FILENO
);
646 dup2(fd
, STDERR_FILENO
);
647 con
->fd
= STDIN_FILENO
;
649 for (fd
= STDERR_FILENO
+1; fd
< 32; fd
++) {
650 if (openfd
& (1<<fd
)) {
658 * Ask for the password. Note that there is no default timeout as we normally
659 * skip this during boot.
661 static char *getpasswd(struct console
*con
)
665 static char pass
[SULOGIN_PASSWORD_BUFSIZ
], *ptr
;
671 const int fd
= con
->fd
;
673 if (con
->flags
& CON_EIO
)
681 tty
.c_iflag
&= ~(IUCLC
|IXON
|IXOFF
|IXANY
);
682 tty
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|TOSTOP
|ISIG
);
684 if ((con
->flags
& CON_NOTTY
) == 0)
685 tc
= (tcsetattr(fd
, TCSAFLUSH
, &tty
) == 0);
687 sigemptyset(&sa
.sa_mask
);
688 sa
.sa_handler
= alrm_handler
;
690 sigaction(SIGALRM
, &sa
, NULL
);
696 cp
->eol
= *ptr
= '\0';
698 eightbit
= ((con
->flags
& CON_SERIAL
) == 0 || (tty
.c_cflag
& (PARODD
|PARENB
)) == 0);
700 while (cp
->eol
== '\0') {
703 if (read(fd
, &c
, 1) < 1) {
704 if (errno
== EINTR
|| errno
== EAGAIN
) {
716 con
->flags
|= CON_EIO
;
719 warn(_("cannot read %s"), con
->tty
);
729 else if (c
!= (ascval
= (c
& 0177))) {
731 for (bits
= 1, mask
= 1; mask
& 0177; mask
<<= 1) {
735 cp
->parity
|= ((bits
& 1) ? 1 : 2);
755 while (ptr
> &pass
[0])
762 if ((size_t)(ptr
- &pass
[0]) >= (sizeof(pass
) -1 )) {
763 fprintf(stderr
, "sulogin: input overrun at %s\n\r", con
->tty
);
774 tcsetattr(fd
, TCSAFLUSH
, &con
->tio
);
778 #ifdef HAVE_EXPLICIT_BZERO
780 explicit_bzero(pass
, sizeof(pass
));
786 * Password was OK, execute a shell.
788 static void sushell(struct passwd
*pwd
)
790 char shell
[PATH_MAX
];
793 char const *su_shell
;
796 * Set directory and shell.
798 if (chdir(pwd
->pw_dir
) != 0) {
799 warn(_("%s: change directory failed"), pwd
->pw_dir
);
800 printf(_("Logging in with home = \"/\".\n"));
803 warn(_("change directory to system root failed"));
806 if ((p
= getenv("SUSHELL")) != NULL
)
808 else if ((p
= getenv("sushell")) != NULL
)
811 if (pwd
->pw_shell
[0])
812 su_shell
= pwd
->pw_shell
;
814 su_shell
= "/bin/sh";
816 if ((p
= strrchr(su_shell
, '/')) == NULL
)
821 snprintf(shell
, sizeof(shell
), profile
? "-%s" : "%s", p
);
824 * Set some important environment variables.
826 if (getcwd(home
, sizeof(home
)) == NULL
)
829 xsetenv("HOME", home
, 1);
830 xsetenv("LOGNAME", "root", 1);
831 xsetenv("USER", "root", 1);
833 xsetenv("SHLVL","0",1);
836 * Try to execute a shell.
838 xsetenv("SHELL", su_shell
, 1);
839 unmask_signal(SIGINT
, &saved_sigint
);
840 unmask_signal(SIGTSTP
, &saved_sigtstp
);
841 unmask_signal(SIGQUIT
, &saved_sigquit
);
842 mask_signal(SIGHUP
, SIG_DFL
, NULL
);
844 #ifdef HAVE_LIBSELINUX
845 if (is_selinux_enabled() > 0) {
850 if (getseuserbyname("root", &seuser
, &level
) == 0) {
851 if (get_default_context_with_level(seuser
, level
, 0, &scon
) == 0) {
852 if (setexeccon(scon
) != 0)
853 warnx(_("setexeccon failed"));
861 execl(su_shell
, shell
, (char *)NULL
);
862 warn(_("failed to execute %s"), su_shell
);
864 xsetenv("SHELL", "/bin/sh", 1);
865 execl("/bin/sh", profile
? "-sh" : "sh", (char *)NULL
);
866 warn(_("failed to execute %s"), "/bin/sh");
869 static void usage(void)
872 fputs(USAGE_HEADER
, out
);
874 " %s [options] [tty device]\n"), program_invocation_short_name
);
876 fputs(USAGE_SEPARATOR
, out
);
877 fputs(_("Single-user login.\n"), out
);
879 fputs(USAGE_OPTIONS
, out
);
880 fputs(_(" -p, --login-shell start a login shell\n"
881 " -t, --timeout <seconds> max time to wait for a password (default: no limit)\n"
882 " -e, --force examine password files directly if getpwnam(3) fails\n"),
885 fputs(USAGE_SEPARATOR
, out
);
886 fprintf(out
, USAGE_HELP_OPTIONS(26));
887 fprintf(out
, USAGE_MAN_TAIL("sulogin(8)"));
892 int main(int argc
, char **argv
)
894 struct list_head
*ptr
, consoles
;
898 const struct timespec sigwait
= { .tv_sec
= 0, .tv_nsec
= 50000000 };
899 siginfo_t status
= { 0 };
901 int c
, reconnect
= 0;
906 static const struct option longopts
[] = {
907 { "login-shell", no_argument
, NULL
, 'p' },
908 { "timeout", required_argument
, NULL
, 't' },
909 { "force", no_argument
, NULL
, 'e' },
910 { "help", no_argument
, NULL
, 'h' },
911 { "version", no_argument
, NULL
, 'V' },
915 INIT_LIST_HEAD(&consoles
);
918 * If we are init we need to set up an own session.
920 if ((pid
= getpid()) == 1) {
922 ignore_result( ioctl(STDIN_FILENO
, TIOCSCTTY
, (char *) 1) );
925 setlocale(LC_ALL
, "");
926 bindtextdomain(PACKAGE
, LOCALEDIR
);
928 close_stdout_atexit();
931 * See if we have a timeout flag.
933 while ((c
= getopt_long(argc
, argv
, "ehpt:V", longopts
, NULL
)) != -1) {
936 timeout
= strtou32_or_err(optarg
, _("invalid timeout argument"));
946 static const char *features
[] = {
947 #ifdef USE_SULOGIN_EMERGENCY_MOUNT
952 print_version_with_features(EXIT_SUCCESS
, features
);
957 /* Do not exit! getopt prints a warning. */
963 errx(EXIT_FAILURE
, _("only superuser can run this program"));
965 mask_signal(SIGQUIT
, SIG_IGN
, &saved_sigquit
);
966 mask_signal(SIGTSTP
, SIG_IGN
, &saved_sigtstp
);
967 mask_signal(SIGINT
, SIG_IGN
, &saved_sigint
);
968 mask_signal(SIGHUP
, SIG_IGN
, &saved_sighup
);
971 emergency_do_mounts();
972 atexit( emergency_do_umounts
);
975 * See if we need to open an other tty device.
980 if (!tty
|| *tty
== '\0')
981 tty
= getenv("CONSOLE");
984 * Detect possible consoles, use stdin as fallback.
985 * If an optional tty is given, reconnect it to stdin.
987 reconnect
= detect_consoles(tty
, STDIN_FILENO
, &consoles
);
990 * If previous stdin was not the specified tty and therefore reconnected
991 * to the specified tty also reconnect stdout and stderr.
994 if (isatty(STDOUT_FILENO
) == 0)
995 dup2(STDOUT_FILENO
, STDIN_FILENO
);
996 if (isatty(STDERR_FILENO
) == 0)
997 dup2(STDOUT_FILENO
, STDERR_FILENO
);
1003 if (list_empty(&consoles
)) {
1006 err(EXIT_FAILURE
, _("cannot open console"));
1010 * Get the root password.
1012 if ((pwd
= getrootpwent(opt_e
)) == NULL
) {
1013 warnx(_("cannot open password database"));
1015 return EXIT_FAILURE
;
1019 * Ask for the password on the consoles.
1021 list_for_each(ptr
, &consoles
) {
1022 con
= list_entry(ptr
, struct console
, entry
);
1023 if (con
->id
>= CONMAX
)
1026 openfd
|= (1 << con
->fd
);
1030 if ((con
->fd
= open(con
->tty
, O_RDWR
| O_NOCTTY
| O_NONBLOCK
)) < 0)
1032 openfd
|= (1 << con
->fd
);
1035 ptr
= (&consoles
)->next
;
1037 if (ptr
->next
== &consoles
) {
1038 con
= list_entry(ptr
, struct console
, entry
);
1043 mask_signal(SIGCHLD
, chld_handler
, &saved_sigchld
);
1045 con
= list_entry(ptr
, struct console
, entry
);
1046 if (con
->id
>= CONMAX
)
1048 if (con
->flags
& CON_EIO
)
1051 switch ((con
->pid
= fork())) {
1053 mask_signal(SIGCHLD
, SIG_DFL
, NULL
);
1054 dup2(con
->fd
, STDERR_FILENO
);
1058 const char *passwd
= pwd
->pw_passwd
;
1061 int deny
= !opt_e
&& locked_account_password(pwd
->pw_passwd
);
1063 doprompt(passwd
, con
, deny
);
1065 if ((answer
= getpasswd(con
)) == NULL
)
1068 #ifdef HAVE_EXPLICIT_BZERO
1069 explicit_bzero(answer
, SULOGIN_PASSWORD_BUFSIZ
);
1074 /* no password or locked account */
1075 if (!passwd
[0] || locked_account_password(passwd
))
1078 const char *cryptbuf
;
1079 cryptbuf
= crypt(answer
, passwd
);
1080 if (cryptbuf
== NULL
)
1081 warn(_("crypt failed"));
1082 else if (strcmp(cryptbuf
, pwd
->pw_passwd
) == 0)
1085 #ifdef HAVE_EXPLICIT_BZERO
1086 explicit_bzero(answer
, SULOGIN_PASSWORD_BUFSIZ
);
1089 /* sushell() unmask signals */
1092 mask_signal(SIGQUIT
, SIG_IGN
, &saved_sigquit
);
1093 mask_signal(SIGTSTP
, SIG_IGN
, &saved_sigtstp
);
1094 mask_signal(SIGINT
, SIG_IGN
, &saved_sigint
);
1096 fprintf(stderr
, _("cannot execute su shell\n\n"));
1099 fprintf(stderr
, _("Login incorrect\n\n"));
1103 warnx(_("Timed out\n\n"));
1106 * User pressed Control-D.
1110 warn(_("fork failed"));
1118 } while (ptr
!= &consoles
);
1124 ret
= waitid(P_ALL
, 0, &status
, WEXITED
);
1129 if (errno
== ECHILD
)
1135 errx(EXIT_FAILURE
, _("cannot wait on su shell\n\n"));
1139 list_for_each(ptr
, &consoles
) {
1140 con
= list_entry(ptr
, struct console
, entry
);
1146 if (con
->pid
== status
.si_pid
)
1149 kill(con
->pid
, SIGTERM
);
1155 sigaddset(&set
, SIGCHLD
);
1164 ret
= waitid(P_ALL
, 0, &status
, WEXITED
|WNOHANG
);
1167 if (errno
== ECHILD
)
1173 if (!ret
&& status
.si_pid
> 0) {
1174 list_for_each(ptr
, &consoles
) {
1175 con
= list_entry(ptr
, struct console
, entry
);
1181 if (con
->pid
== status
.si_pid
) {
1189 signum
= sigtimedwait(&set
, NULL
, &sigwait
);
1190 if (signum
!= SIGCHLD
&& signum
< 0 && errno
== EAGAIN
)
1195 mask_signal(SIGCHLD
, SIG_DFL
, NULL
);
1196 return EXIT_SUCCESS
;