]>
git.wh0rd.org - sysvinit.git/blob - src/sulogin.c
2 * sulogin This program gives Linux machines a reasonable
3 * secure way to boot single user. It forces the
4 * user to supply the root password before a
7 * If there is a shadow password file and the
8 * encrypted root password is "x" the shadow
9 * password will be used.
11 * Version: @(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl
13 * Copyright (C) 1998-2003 Miquel van Smoorenburg.
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <sys/types.h>
42 #include <sys/ttydefaults.h>
44 #include <sys/ioctl.h>
45 #if defined(__GLIBC__)
50 # include <selinux/selinux.h>
51 # include <selinux/get_context_list.h>
57 #define F_PASSWD "/etc/passwd"
58 #define F_SHADOW "/etc/shadow"
59 #define BINSH "/bin/sh"
60 #define STATICSH "/bin/sash"
62 char *Version
= "@(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl";
67 static void (*saved_sigint
) = SIG_DFL
;
68 static void (*saved_sigtstp
) = SIG_DFL
;
69 static void (*saved_sigquit
) = SIG_DFL
;
71 static volatile sig_atomic_t alarm_rised
;
77 #if defined(SANE_TIO) && (SANE_TIO == 1)
79 * Fix the tty modes and set reasonable defaults.
80 * (I'm not sure if this is needed under Linux, but..)
88 /* Skip serial console */
89 if (ioctl (0, TIOCMGET
, (char*)&serial
) == 0)
96 /* Use defaults of <sys/ttydefaults.h> for base settings */
97 tty
.c_iflag
|= TTYDEF_IFLAG
;
98 tty
.c_oflag
|= TTYDEF_OFLAG
;
99 tty
.c_lflag
|= TTYDEF_LFLAG
;
100 tty
.c_cflag
|= (TTYDEF_SPEED
| TTYDEF_CFLAG
);
102 /* Sane setting, allow eight bit characters, no carriage return delay
103 * the same result as `stty sane cr0 pass8'
105 tty
.c_iflag
|= (BRKINT
| ICRNL
| IMAXBEL
);
106 tty
.c_iflag
&= ~(IGNBRK
| INLCR
| IGNCR
| IXOFF
| IUCLC
| IXANY
| ISTRIP
);
107 tty
.c_oflag
|= (OPOST
| ONLCR
| NL0
| CR0
| TAB0
| BS0
| VT0
| FF0
);
108 tty
.c_oflag
&= ~(OLCUC
| OCRNL
| ONOCR
| ONLRET
| OFILL
| OFDEL
|\
109 NLDLY
|CRDLY
|TABDLY
|BSDLY
|VTDLY
|FFDLY
);
110 tty
.c_lflag
|= (ISIG
| ICANON
| IEXTEN
| ECHO
|ECHOE
|ECHOK
|ECHOCTL
|ECHOKE
);
111 tty
.c_lflag
&= ~(ECHONL
| NOFLSH
| XCASE
| TOSTOP
| ECHOPRT
);
112 tty
.c_cflag
|= (CREAD
| CS8
| B9600
);
113 tty
.c_cflag
&= ~(PARENB
);
115 /* VTIME and VMIN can overlap with VEOF and VEOL since they are
116 * only used for non-canonical mode. We just set the at the
117 * beginning, so nothing bad should happen.
121 tty
.c_cc
[VINTR
] = CINTR
;
122 tty
.c_cc
[VQUIT
] = CQUIT
;
123 tty
.c_cc
[VERASE
] = CERASE
; /* ASCII DEL (0177) */
124 tty
.c_cc
[VKILL
] = CKILL
;
125 tty
.c_cc
[VEOF
] = CEOF
;
126 tty
.c_cc
[VSWTC
] = _POSIX_VDISABLE
;
127 tty
.c_cc
[VSTART
] = CSTART
;
128 tty
.c_cc
[VSTOP
] = CSTOP
;
129 tty
.c_cc
[VSUSP
] = CSUSP
;
130 tty
.c_cc
[VEOL
] = _POSIX_VDISABLE
;
131 tty
.c_cc
[VREPRINT
] = CREPRINT
;
132 tty
.c_cc
[VDISCARD
] = CDISCARD
;
133 tty
.c_cc
[VWERASE
] = CWERASE
;
134 tty
.c_cc
[VLNEXT
] = CLNEXT
;
135 tty
.c_cc
[VEOL2
] = _POSIX_VDISABLE
;
137 tcsetattr(0, TCSANOW
, &tty
);
149 void alrm_handler(int sig
__attribute__((unused
)))
151 void alrm_handler(int sig
)
154 /* Timeout expired */
157 signal(SIGINT
, saved_sigint
);
158 signal(SIGTSTP
, saved_sigtstp
);
159 signal(SIGQUIT
, saved_sigquit
);
161 /* Never use exit(3) or stdio(3) within a signal handler */
165 * See if an encrypted password is valid. The encrypted
166 * password is checked for traditional-style DES and
167 * FreeBSD-style MD5 encryption.
170 int valid(const char *pass
)
177 if (pass
[0] == 0) return 1;
179 if (pass
[0] != '$') goto check_des
;
182 * up to 4 bytes for the signature e.g. $1$
184 for(s
= pass
+1; *s
&& *s
!= '$'; s
++)
186 if (*s
++ != '$') return 0;
187 if ((off
= (off_t
)(s
-pass
)) > 4 || off
< 3) return 0;
189 memset(id
, '\0', sizeof(id
));
190 strncpy(id
, pass
, off
);
193 * up to 16 bytes for the salt
195 for(; *s
&& *s
!= '$'; s
++)
197 if (*s
++ != '$') return 0;
198 if ((off_t
)(s
-pass
) > 16) return 0;
202 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
204 if ((strcmp(id
, "$1$") == 0) && (len
< 22 || len
> 24)) return 0;
207 * the SHA-256 hash 43 bytes
209 if ((strcmp(id
, "$5$") == 0) && (len
< 42 || len
> 44)) return 0;
212 * the SHA-512 hash 86 bytes
214 if ((strcmp(id
, "$6$") == 0) && (len
< 85 || len
> 87)) return 0;
223 if (strlen(pass
) != 13) return 0;
224 for (s
= pass
; *s
; s
++) {
225 if ((*s
< '0' || *s
> '9') &&
226 (*s
< 'a' || *s
> 'z') &&
227 (*s
< 'A' || *s
> 'Z') &&
228 *s
!= '.' && *s
!= '/') return 0;
235 * Set a variable if the value is not NULL.
238 void set(char **var
, char *val
)
244 * Get the root password entry.
247 struct passwd
*getrootpwent(int try_manually
)
249 static struct passwd pwd
;
253 static char line
[256];
254 static char sline
[256];
258 * First, we try to get the password the standard
259 * way using normal library calls.
261 if ((pw
= getpwnam("root")) &&
262 !strcmp(pw
->pw_passwd
, "x") &&
263 (spw
= getspnam("root")))
264 pw
->pw_passwd
= spw
->sp_pwdp
;
265 if (pw
|| !try_manually
) return pw
;
268 * If we come here, we could not retrieve the root
269 * password through library calls and we try to
270 * read the password and shadow files manually.
272 pwd
.pw_name
= "root";
274 pwd
.pw_gecos
= "Super User";
280 if ((fp
= fopen(F_PASSWD
, "r")) == NULL
) {
286 * Find root in the password file.
288 while((p
= fgets(line
, 256, fp
)) != NULL
) {
289 if (strncmp(line
, "root:", 5) != 0)
292 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
293 (void)strsep(&p
, ":");
294 (void)strsep(&p
, ":");
295 set(&pwd
.pw_gecos
, strsep(&p
, ":"));
296 set(&pwd
.pw_dir
, strsep(&p
, ":"));
297 set(&pwd
.pw_shell
, strsep(&p
, "\n"));
304 * If the encrypted password is valid
305 * or not found, return.
308 fprintf(stderr
, "%s: no entry for root\n", F_PASSWD
);
311 if (valid(pwd
.pw_passwd
)) return &pwd
;
314 * The password is invalid. If there is a
315 * shadow password, try it.
317 strcpy(pwd
.pw_passwd
, "");
318 if ((fp
= fopen(F_SHADOW
, "r")) == NULL
) {
319 fprintf(stderr
, "%s: root password garbled\n", F_PASSWD
);
322 while((p
= fgets(sline
, 256, fp
)) != NULL
) {
323 if (strncmp(sline
, "root:", 5) != 0)
326 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
332 * If the password is still invalid,
333 * NULL it, and return.
336 fprintf(stderr
, "%s: no entry for root\n", F_SHADOW
);
337 strcpy(pwd
.pw_passwd
, "");
339 if (!valid(pwd
.pw_passwd
)) {
340 fprintf(stderr
, "%s: root password garbled\n", F_SHADOW
);
341 strcpy(pwd
.pw_passwd
, ""); }
346 * Ask for the password. Note that there is no
347 * default timeout as we normally skip this during boot.
350 char *getpasswd(char *crypted
)
353 struct termios old
, tty
;
354 static char pass
[128];
357 #if defined(USE_ONELINE)
359 printf("Give root password for login: ");
361 printf("Press enter for login: ");
364 printf("Give root password for maintenance\n");
366 printf("Press enter for maintenance");
367 printf("(or type Control-D to continue): ");
373 tty
.c_iflag
&= ~(IUCLC
|IXON
|IXOFF
|IXANY
);
374 tty
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|TOSTOP
);
375 tcsetattr(0, TCSANOW
, &tty
);
377 pass
[sizeof(pass
) - 1] = 0;
379 sa
.sa_handler
= alrm_handler
;
381 sigaction(SIGALRM
, &sa
, NULL
);
382 if (timeout
) alarm(timeout
);
384 if (read(0, pass
, sizeof(pass
) - 1) <= 0)
387 for(i
= 0; i
< (int)sizeof(pass
) && pass
[i
]; i
++)
388 if (pass
[i
] == '\r' || pass
[i
] == '\n') {
394 tcsetattr(0, TCSANOW
, &old
);
401 * Password was OK, execute a shell.
404 void sushell(struct passwd
*pwd
)
412 * Set directory and shell.
414 (void)chdir(pwd
->pw_dir
);
415 if ((p
= getenv("SUSHELL")) != NULL
)
417 else if ((p
= getenv("sushell")) != NULL
)
420 if (pwd
->pw_shell
[0])
421 sushell
= pwd
->pw_shell
;
425 if ((p
= strrchr(sushell
, '/')) == NULL
)
429 snprintf(shell
, sizeof(shell
), profile
? "-%s" : "%s", p
);
432 * Set some important environment variables.
434 getcwd(home
, sizeof(home
));
435 setenv("HOME", home
, 1);
436 setenv("LOGNAME", "root", 1);
437 setenv("USER", "root", 1);
439 setenv("SHLVL","0",1);
442 * Try to execute a shell.
444 setenv("SHELL", sushell
, 1);
445 signal(SIGINT
, saved_sigint
);
446 signal(SIGTSTP
, saved_sigtstp
);
447 signal(SIGQUIT
, saved_sigquit
);
449 if (is_selinux_enabled() > 0) {
450 security_context_t scon
=NULL
;
453 if (getseuserbyname("root", &seuser
, &level
) == 0)
454 if (get_default_context_with_level(seuser
, level
, 0, &scon
) == 0) {
455 if (setexeccon(scon
) != 0)
456 fprintf(stderr
, "setexeccon faile\n");
463 execl(sushell
, shell
, NULL
);
466 setenv("SHELL", BINSH
, 1);
467 execl(BINSH
, profile
? "-sh" : "sh", NULL
);
470 /* Fall back to staticly linked shell if both the users shell
471 and /bin/sh failed to execute. */
472 setenv("SHELL", STATICSH
, 1);
473 execl(STATICSH
, STATICSH
, NULL
);
480 fprintf(stderr
, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n");
483 int main(int argc
, char **argv
)
490 pid_t pid
, pgrp
, ppgrp
, ttypgrp
;
493 * See if we have a timeout flag.
496 while((c
= getopt(argc
, argv
, "ept:")) != EOF
) switch(c
) {
498 timeout
= atoi(optarg
);
512 if (geteuid() != 0) {
513 fprintf(stderr
, "sulogin: only root can run sulogin.\n");
518 * See if we need to open an other tty device.
520 saved_sigint
= signal(SIGINT
, SIG_IGN
);
521 saved_sigquit
= signal(SIGQUIT
, SIG_IGN
);
522 saved_sigtstp
= signal(SIGTSTP
, SIG_IGN
);
523 if (optind
< argc
) tty
= argv
[optind
];
525 if (tty
|| (tty
= getenv("CONSOLE"))) {
527 if ((fd
= open(tty
, O_RDWR
)) < 0) {
533 fprintf(stderr
, "%s: not a tty\n", tty
);
538 * Only go through this trouble if the new
539 * tty doesn't fall in this process group.
543 ppgrp
= getpgid(getppid());
544 ttypgrp
= tcgetpgrp(fd
);
546 if (pgrp
!= ttypgrp
&& ppgrp
!= ttypgrp
) {
547 if (pid
!= getsid(0)) {
548 if (pid
== getpgid(0))
549 setpgid(0, getpgid(getppid()));
553 signal(SIGHUP
, SIG_IGN
);
555 ioctl(0, TIOCNOTTY
, (char *)1);
556 signal(SIGHUP
, SIG_DFL
);
562 if ((fd
= open(tty
, O_RDWR
|O_NOCTTY
)) < 0) {
565 ioctl(0, TIOCSCTTY
, (char *)1);
566 tcsetpgrp(fd
, ppgrp
);
577 } else if (getpid() == 1) {
578 /* We are init. We hence need to set a session anyway */
580 if (ioctl(0, TIOCSCTTY
, (char *)1))
581 perror("ioctl(TIOCSCTTY)");
584 #if defined(SANE_TIO) && (SANE_TIO == 1)
589 * Get the root password.
591 if ((pwd
= getrootpwent(opt_e
)) == NULL
) {
592 fprintf(stderr
, "sulogin: cannot open password database!\n");
597 * Ask for the password.
601 if ((p
= getpasswd(pwd
->pw_passwd
)) == NULL
) break;
602 if (pwd
->pw_passwd
[0] == 0 ||
603 strcmp(crypt(p
, pwd
->pw_passwd
), pwd
->pw_passwd
) == 0) {
607 signal(SIGQUIT
, SIG_IGN
);
608 signal(SIGTSTP
, SIG_IGN
);
609 signal(SIGINT
, SIG_IGN
);
611 printf("Can not execute su shell.\n");
614 printf("Login incorrect.\n");
618 printf("Timed out.\n");
621 * User may pressed Control-D.