]>
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>
43 #include <sys/ioctl.h>
44 #if defined(__GLIBC__)
49 # include <selinux/selinux.h>
50 # include <selinux/get_context_list.h>
56 #define F_PASSWD "/etc/passwd"
57 #define F_SHADOW "/etc/shadow"
58 #define BINSH "/bin/sh"
59 #define STATICSH "/bin/sash"
61 char *Version
= "@(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl";
72 * Fix the tty modes and set reasonable defaults.
73 * (I'm not sure if this is needed under Linux, but..)
82 * Set or adjust tty modes.
84 tty
.c_iflag
&= ~(INLCR
|IGNCR
|IUCLC
);
86 tty
.c_oflag
&= ~(OCRNL
|OLCUC
|ONOCR
|ONLRET
|OFILL
);
87 tty
.c_oflag
|= OPOST
|ONLCR
;
88 tty
.c_cflag
|= CLOCAL
;
89 tty
.c_lflag
= ISIG
|ICANON
|ECHO
|ECHOE
|ECHOK
|ECHOCTL
|ECHOKE
;
92 * Set the most important characters */
96 tty
.c_cc
[VERASE
] = 127;
101 tty
.c_cc
[VSTART
] = 17;
102 tty
.c_cc
[VSTOP
] = 19;
103 tty
.c_cc
[VSUSP
] = 26;
105 tcsetattr(0, TCSANOW
, &tty
);
118 * See if an encrypted password is valid. The encrypted
119 * password is checked for traditional-style DES and
120 * FreeBSD-style MD5 encryption.
122 int valid(char *pass
)
127 if (pass
[0] == 0) return 1;
130 * 3 bytes for the signature $1$
131 * up to 8 bytes for the salt
133 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
135 if (strncmp(pass
, "$1$", 3) == 0) {
136 for(s
= pass
+ 3; *s
&& *s
!= '$'; s
++)
138 if (*s
++ != '$') return 0;
140 if (len
< 22 || len
> 24) return 0;
146 if (strlen(pass
) != 13) return 0;
147 for (s
= pass
; *s
; s
++) {
148 if ((*s
< '0' || *s
> '9') &&
149 (*s
< 'a' || *s
> 'z') &&
150 (*s
< 'A' || *s
> 'Z') &&
151 *s
!= '.' && *s
!= '/') return 0;
158 * Set a variable if the value is not NULL.
160 void set(char **var
, char *val
)
166 * Get the root password entry.
168 struct passwd
*getrootpwent(int try_manually
)
170 static struct passwd pwd
;
174 static char line
[256];
175 static char sline
[256];
179 * First, we try to get the password the standard
180 * way using normal library calls.
182 if ((pw
= getpwnam("root")) &&
183 !strcmp(pw
->pw_passwd
, "x") &&
184 (spw
= getspnam("root")))
185 pw
->pw_passwd
= spw
->sp_pwdp
;
186 if (pw
|| !try_manually
) return pw
;
189 * If we come here, we could not retrieve the root
190 * password through library calls and we try to
191 * read the password and shadow files manually.
193 pwd
.pw_name
= "root";
195 pwd
.pw_gecos
= "Super User";
201 if ((fp
= fopen(F_PASSWD
, "r")) == NULL
) {
207 * Find root in the password file.
209 while((p
= fgets(line
, 256, fp
)) != NULL
) {
210 if (strncmp(line
, "root:", 5) != 0)
213 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
214 (void)strsep(&p
, ":");
215 (void)strsep(&p
, ":");
216 set(&pwd
.pw_gecos
, strsep(&p
, ":"));
217 set(&pwd
.pw_dir
, strsep(&p
, ":"));
218 set(&pwd
.pw_shell
, strsep(&p
, "\n"));
225 * If the encrypted password is valid
226 * or not found, return.
229 fprintf(stderr
, "%s: no entry for root\n", F_PASSWD
);
232 if (valid(pwd
.pw_passwd
)) return &pwd
;
235 * The password is invalid. If there is a
236 * shadow password, try it.
238 strcpy(pwd
.pw_passwd
, "");
239 if ((fp
= fopen(F_SHADOW
, "r")) == NULL
) {
240 fprintf(stderr
, "%s: root password garbled\n", F_PASSWD
);
243 while((p
= fgets(sline
, 256, fp
)) != NULL
) {
244 if (strncmp(sline
, "root:", 5) != 0)
247 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
253 * If the password is still invalid,
254 * NULL it, and return.
257 fprintf(stderr
, "%s: no entry for root\n", F_SHADOW
);
258 strcpy(pwd
.pw_passwd
, "");
260 if (!valid(pwd
.pw_passwd
)) {
261 fprintf(stderr
, "%s: root password garbled\n", F_SHADOW
);
262 strcpy(pwd
.pw_passwd
, ""); }
267 * Ask for the password. Note that there is no
268 * default timeout as we normally skip this during boot.
270 char *getpasswd(char *crypted
)
273 struct termios old
, tty
;
274 static char pass
[128];
279 printf("Give root password for maintenance\n");
281 printf("Press enter for maintenance\n");
282 printf("(or type Control-D to continue): ");
287 tty
.c_iflag
&= ~(IUCLC
|IXON
|IXOFF
|IXANY
);
288 tty
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|TOSTOP
);
289 tcsetattr(0, TCSANOW
, &tty
);
291 pass
[sizeof(pass
) - 1] = 0;
293 sa
.sa_handler
= alrm_handler
;
295 sigaction(SIGALRM
, &sa
, NULL
);
296 if (timeout
) alarm(timeout
);
298 if (read(0, pass
, sizeof(pass
) - 1) <= 0)
301 for(i
= 0; i
< sizeof(pass
) && pass
[i
]; i
++)
302 if (pass
[i
] == '\r' || pass
[i
] == '\n') {
308 tcsetattr(0, TCSANOW
, &old
);
315 * Password was OK, execute a shell.
317 void sushell(struct passwd
*pwd
)
325 * Set directory and shell.
327 (void)chdir(pwd
->pw_dir
);
328 if ((p
= getenv("SUSHELL")) != NULL
)
330 else if ((p
= getenv("sushell")) != NULL
)
333 if (pwd
->pw_shell
[0])
334 sushell
= pwd
->pw_shell
;
338 if ((p
= strrchr(sushell
, '/')) == NULL
)
342 snprintf(shell
, sizeof(shell
), profile
? "-%s" : "%s", p
);
345 * Set some important environment variables.
347 getcwd(home
, sizeof(home
));
348 setenv("HOME", home
, 1);
349 setenv("LOGNAME", "root", 1);
350 setenv("USER", "root", 1);
352 setenv("SHLVL","0",1);
355 * Try to execute a shell.
357 setenv("SHELL", sushell
, 1);
358 signal(SIGINT
, SIG_DFL
);
359 signal(SIGTSTP
, SIG_DFL
);
360 signal(SIGQUIT
, SIG_DFL
);
362 if (is_selinux_enabled
> 0) {
363 security_context_t scon
=NULL
;
366 if (getseuserbyname("root", &seuser
, &level
) == 0)
367 if (get_default_context_with_level(seuser
, level
, 0, &scon
) > 0) {
368 if (setexeccon(scon
) != 0)
369 fprintf(stderr
, "setexeccon faile\n");
376 execl(sushell
, shell
, NULL
);
379 setenv("SHELL", BINSH
, 1);
380 execl(BINSH
, profile
? "-sh" : "sh", NULL
);
383 /* Fall back to staticly linked shell if both the users shell
384 and /bin/sh failed to execute. */
385 setenv("SHELL", STATICSH
, 1);
386 execl(STATICSH
, STATICSH
, NULL
);
392 fprintf(stderr
, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n");
395 int main(int argc
, char **argv
)
402 pid_t pid
, pgrp
, ppgrp
, ttypgrp
;
405 * See if we have a timeout flag.
408 while((c
= getopt(argc
, argv
, "ept:")) != EOF
) switch(c
) {
410 timeout
= atoi(optarg
);
424 if (geteuid() != 0) {
425 fprintf(stderr
, "sulogin: only root can run sulogin.\n");
430 * See if we need to open an other tty device.
432 signal(SIGINT
, SIG_IGN
);
433 signal(SIGQUIT
, SIG_IGN
);
434 signal(SIGTSTP
, SIG_IGN
);
435 if (optind
< argc
) tty
= argv
[optind
];
437 if ((fd
= open(tty
, O_RDWR
)) < 0) {
439 } else if (!isatty(fd
)) {
440 fprintf(stderr
, "%s: not a tty\n", tty
);
445 * Only go through this trouble if the new
446 * tty doesn't fall in this process group.
450 ppgrp
= getpgid(getppid());
451 ioctl(fd
, TIOCGPGRP
, &ttypgrp
);
453 if (pgrp
!= ttypgrp
&& ppgrp
!= ttypgrp
) {
454 if (pid
!= getsid(0)) {
455 if (pid
== getpgid(0))
456 setpgid(0, getpgid(getppid()));
460 signal(SIGHUP
, SIG_IGN
);
461 ioctl(0, TIOCNOTTY
, (char *)1);
462 signal(SIGHUP
, SIG_DFL
);
467 fd
= open(tty
, O_RDWR
);
468 ioctl(0, TIOCSCTTY
, (char *)1);
474 } else if (getpid() == 1) {
475 /* We are init. We hence need to set a session anyway */
477 if (ioctl(0, TIOCSCTTY
, (char *)1))
478 perror("ioctl(TIOCSCTTY)");
482 * Get the root password.
484 if ((pwd
= getrootpwent(opt_e
)) == NULL
) {
485 fprintf(stderr
, "sulogin: cannot open password database!\n");
490 * Ask for the password.
493 if ((p
= getpasswd(pwd
->pw_passwd
)) == NULL
) break;
494 if (pwd
->pw_passwd
[0] == 0 ||
495 strcmp(crypt(p
, pwd
->pw_passwd
), pwd
->pw_passwd
) == 0)
497 printf("Login incorrect.\n");
501 * User pressed Control-D.