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>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include <sys/types.h>
39 #include <sys/ioctl.h>
44 #ifdef HAVE_LIBSELINUX
45 # include <selinux/selinux.h>
46 # include <selinux/get_context_list.h>
50 #include "closestream.h"
52 #include "pathnames.h"
56 static unsigned int timeout
;
59 struct sigaction saved_sigint
;
60 struct sigaction saved_sigtstp
;
61 struct sigaction saved_sigquit
;
66 static void alrm_handler(int sig
__attribute__((unused
)))
71 static void mask_signal(int signal
, void (*handler
)(int),
72 struct sigaction
*origaction
)
74 struct sigaction newaction
;
76 newaction
.sa_handler
= handler
;
77 sigemptyset(&newaction
.sa_mask
);
78 newaction
.sa_flags
= 0;
80 sigaction(signal
, NULL
, origaction
);
81 sigaction(signal
, &newaction
, NULL
);
84 static void unmask_signal(int signal
, struct sigaction
*sa
)
86 sigaction(signal
, sa
, NULL
);
90 * See if an encrypted password is valid. The encrypted password is checked for
91 * traditional-style DES and FreeBSD-style MD5 encryption.
93 static int valid(const char *pass
)
106 * up to 4 bytes for the signature e.g. $1$
108 for (s
= pass
+1; *s
&& *s
!= '$'; s
++);
113 if ((off
= (off_t
)(s
-pass
)) > 4 || off
< 3)
116 memset(id
, '\0', sizeof(id
));
117 strncpy(id
, pass
, off
);
120 * up to 16 bytes for the salt
122 for (; *s
&& *s
!= '$'; s
++);
127 if ((off_t
)(s
-pass
) > 16)
133 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
135 if ((strcmp(id
, "$1$") == 0) && (len
< 22 || len
> 24))
139 * the SHA-256 hash 43 bytes
141 if ((strcmp(id
, "$5$") == 0) && (len
< 42 || len
> 44))
145 * the SHA-512 hash 86 bytes
147 if ((strcmp(id
, "$6$") == 0) && (len
< 85 || len
> 87))
155 if (strlen(pass
) != 13)
158 for (s
= pass
; *s
; s
++) {
159 if ((*s
< '0' || *s
> '9') &&
160 (*s
< 'a' || *s
> 'z') &&
161 (*s
< 'A' || *s
> 'Z') &&
162 *s
!= '.' && *s
!= '/')
169 * Set a variable if the value is not NULL.
171 static inline void set(char **var
, char *val
)
178 * Get the root password entry.
180 static struct passwd
*getrootpwent(int try_manually
)
182 static struct passwd pwd
;
186 static char line
[256];
187 static char sline
[256];
191 * First, we try to get the password the standard way using normal
194 if ((pw
= getpwnam("root")) &&
195 !strcmp(pw
->pw_passwd
, "x") &&
196 (spw
= getspnam("root")))
197 pw
->pw_passwd
= spw
->sp_pwdp
;
199 if (pw
|| !try_manually
)
203 * If we come here, we could not retrieve the root password through
204 * library calls and we try to read the password and shadow files
207 pwd
.pw_name
= "root";
209 pwd
.pw_gecos
= "Super User";
215 if ((fp
= fopen(_PATH_PASSWD
, "r")) == NULL
) {
216 warn(_("%s: open failed"), _PATH_PASSWD
);
221 * Find root in the password file.
223 while ((p
= fgets(line
, 256, fp
)) != NULL
) {
224 if (strncmp(line
, "root:", 5) != 0)
227 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
230 set(&pwd
.pw_gecos
, strsep(&p
, ":"));
231 set(&pwd
.pw_dir
, strsep(&p
, ":"));
232 set(&pwd
.pw_shell
, strsep(&p
, "\n"));
240 * If the encrypted password is valid or not found, return.
243 warnx(_("%s: no entry for root\n"), _PATH_PASSWD
);
246 if (valid(pwd
.pw_passwd
))
250 * The password is invalid. If there is a shadow password, try it.
252 strcpy(pwd
.pw_passwd
, "");
253 if ((fp
= fopen(_PATH_SHADOW_PASSWD
, "r")) == NULL
) {
254 warn(_("%s: open failed"), _PATH_PASSWD
);
257 while ((p
= fgets(sline
, 256, fp
)) != NULL
) {
258 if (strncmp(sline
, "root:", 5) != 0)
261 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
267 * If the password is still invalid, NULL it, and return.
270 warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD
);
271 strcpy(pwd
.pw_passwd
, "");
273 if (!valid(pwd
.pw_passwd
)) {
274 warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD
);
275 strcpy(pwd
.pw_passwd
, "");
281 * Ask for the password. Note that there is no default timeout as we normally
282 * skip this during boot.
284 static char *getpasswd(char *crypted
)
287 struct termios old
, tty
;
288 static char pass
[128];
293 printf(_("Give root password for maintenance\n"));
295 printf(_("Press enter for maintenance"));
296 printf(_("(or type Control-D to continue): "));
301 tty
.c_iflag
&= ~(IUCLC
|IXON
|IXOFF
|IXANY
);
302 tty
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|TOSTOP
);
303 tcsetattr(0, TCSANOW
, &tty
);
305 pass
[sizeof(pass
) - 1] = 0;
307 sa
.sa_handler
= alrm_handler
;
309 sigaction(SIGALRM
, &sa
, NULL
);
313 if (read(0, pass
, sizeof(pass
) - 1) <= 0)
316 for (i
= 0; i
< sizeof(pass
) && pass
[i
]; i
++)
317 if (pass
[i
] == '\r' || pass
[i
] == '\n') {
323 tcsetattr(0, TCSANOW
, &old
);
330 * Password was OK, execute a shell.
332 static void sushell(struct passwd
*pwd
)
334 char shell
[PATH_MAX
];
340 * Set directory and shell.
342 if (chdir(pwd
->pw_dir
) != 0) {
343 warn(_("%s: change directory failed"), pwd
->pw_dir
);
344 printf(_("Logging in with home = \"/\".\n"));
347 warn(_("change directory to system root failed"));
350 if ((p
= getenv("SUSHELL")) != NULL
)
352 else if ((p
= getenv("sushell")) != NULL
)
355 if (pwd
->pw_shell
[0])
356 sushell
= pwd
->pw_shell
;
360 if ((p
= strrchr(sushell
, '/')) == NULL
)
365 snprintf(shell
, sizeof(shell
), profile
? "-%s" : "%s", p
);
368 * Set some important environment variables.
370 if (getcwd(home
, sizeof(home
)) != NULL
)
371 setenv("HOME", home
, 1);
373 setenv("LOGNAME", "root", 1);
374 setenv("USER", "root", 1);
376 setenv("SHLVL","0",1);
379 * Try to execute a shell.
381 setenv("SHELL", sushell
, 1);
382 unmask_signal(SIGINT
, &saved_sigint
);
383 unmask_signal(SIGTSTP
, &saved_sigtstp
);
384 unmask_signal(SIGQUIT
, &saved_sigquit
);
386 #ifdef HAVE_LIBSELINUX
387 if (is_selinux_enabled() > 0) {
388 security_context_t scon
=NULL
;
391 if (getseuserbyname("root", &seuser
, &level
) == 0) {
392 if (get_default_context_with_level(seuser
, level
, 0, &scon
) == 0) {
393 if (setexeccon(scon
) != 0)
394 warnx(_("setexeccon failed"));
402 execl(sushell
, shell
, NULL
);
403 warn(_("%s: exec failed"), sushell
);
405 setenv("SHELL", "/bin/sh", 1);
406 execl("/bin/sh", profile
? "-sh" : "sh", NULL
);
407 warn(_("%s: exec failed"), "/bin/sh");
410 static void fixtty(void)
415 /* Skip serial console */
416 if (ioctl(STDIN_FILENO
, TIOCMGET
, (char *) &x
) == 0)
419 #if defined(IUTF8) && defined(KDGKBMODE)
420 /* Detect mode of current keyboard setup, e.g. for UTF-8 */
421 if (ioctl(STDIN_FILENO
, KDGKBMODE
, &x
) == 0 && x
== K_UNICODE
) {
422 setlocale(LC_CTYPE
, "C.UTF-8");
426 setlocale(LC_CTYPE
, "POSIX");
428 memset(&tp
, 0, sizeof(struct termios
));
429 if (tcgetattr(STDIN_FILENO
, &tp
) < 0) {
430 warn(_("tcgetattr failed"));
434 reset_virtual_console(&tp
, fl
);
436 if (tcsetattr(0, TCSADRAIN
, &tp
))
437 warn(_("tcsetattr failed"));
440 static void usage(FILE *out
)
442 fputs(USAGE_HEADER
, out
);
444 " %s [options] [tty device]\n"), program_invocation_short_name
);
446 fputs(USAGE_OPTIONS
, out
);
447 fputs(_(" -p, --login-shell start a login shell\n"
448 " -t, --timeout <seconds> max time to wait for a password (default: no limit)\n"
449 " -e, --force examine password files directly if getpwnam(3) fails\n"),
452 fputs(USAGE_SEPARATOR
, out
);
453 fputs(USAGE_HELP
, out
);
454 fputs(USAGE_VERSION
, out
);
455 fprintf(out
, USAGE_MAN_TAIL("sulogin(8)"));
458 int main(int argc
, char **argv
)
465 pid_t pid
, pgrp
, ppgrp
, ttypgrp
;
466 struct sigaction saved_sighup
;
468 static const struct option longopts
[] = {
469 { "login-shell", 0, 0, 'p' },
470 { "timeout", 1, 0, 't' },
471 { "force", 0, 0, 'e' },
472 { "help", 0, 0, 'h' },
473 { "version", 0, 0, 'V' },
477 setlocale(LC_ALL
, "");
478 bindtextdomain(PACKAGE
, LOCALEDIR
);
480 atexit(close_stdout
);
483 * See if we have a timeout flag.
485 while ((c
= getopt_long(argc
, argv
, "ehpt:V", longopts
, NULL
)) != -1) {
488 timeout
= strtoul_or_err(optarg
, _("failed to parse timeout"));
497 printf(UTIL_LINUX_VERSION
);
510 errx(EXIT_FAILURE
, _("only root can run this program."));
513 * See if we need to open an other tty device.
515 mask_signal(SIGQUIT
, SIG_IGN
, &saved_sigquit
);
516 mask_signal(SIGTSTP
, SIG_IGN
, &saved_sigtstp
);
517 mask_signal(SIGINT
, SIG_IGN
, &saved_sigint
);
521 if (tty
|| (tty
= getenv("CONSOLE"))) {
523 if ((fd
= open(tty
, O_RDWR
)) < 0) {
524 warn(_("%s: open failed"), tty
);
529 warn(_("%s: not a tty"), tty
);
534 * Only go through this trouble if the new tty doesn't
535 * fall in this process group.
539 ppgrp
= getpgid(getppid());
540 ttypgrp
= tcgetpgrp(fd
);
542 if (pgrp
!= ttypgrp
&& ppgrp
!= ttypgrp
) {
543 if (pid
!= getsid(0)) {
544 if (pid
== getpgid(0))
545 setpgid(0, getpgid(getppid()));
549 sigaction(SIGHUP
, NULL
, &saved_sighup
);
551 ioctl(0, TIOCNOTTY
, (char *)1);
552 sigaction(SIGHUP
, &saved_sighup
, NULL
);
558 if ((fd
= open(tty
, O_RDWR
|O_NOCTTY
)) < 0)
559 warn(_("%s: open failed"), tty
);
561 ioctl(0, TIOCSCTTY
, (char *)1);
562 tcsetpgrp(fd
, ppgrp
);
573 } else if (getpid() == 1) {
574 /* We are init. We hence need to set a session anyway */
576 if (ioctl(0, TIOCSCTTY
, (char *)1))
577 warn(_("TIOCSCTTY: ioctl failed"));
583 * Get the root password.
585 if ((pwd
= getrootpwent(opt_e
)) == NULL
) {
586 warnx(_("cannot open password database."));
591 * Ask for the password.
594 if ((p
= getpasswd(pwd
->pw_passwd
)) == NULL
)
596 if (pwd
->pw_passwd
[0] == 0 ||
597 strcmp(crypt(p
, pwd
->pw_passwd
), pwd
->pw_passwd
) == 0)
599 mask_signal(SIGQUIT
, SIG_IGN
, &saved_sigquit
);
600 mask_signal(SIGTSTP
, SIG_IGN
, &saved_sigtstp
);
601 mask_signal(SIGINT
, SIG_IGN
, &saved_sigint
);
602 fprintf(stderr
, _("Login incorrect\n\n"));
606 * User pressed Control-D.