su: FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY

When this feature is enabled, blank passwords are not accepted by su
unless the user is on a secure TTY defined in /etc/securetty. This
resembles the default PAM configuration of some Linux distros which
specify the nullok_secure option for pam_unix.so.

Based on patch by Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-04-13 12:57:04 +02:00
parent 517a82c5b6
commit 335681ca8e
3 changed files with 25 additions and 7 deletions

View File

@ -1486,6 +1486,7 @@ int check_securetty(const char *short_tty) FAST_FUNC;
#else #else
static ALWAYS_INLINE int check_securetty(const char *short_tty UNUSED_PARAM) { return 1; } static ALWAYS_INLINE int check_securetty(const char *short_tty UNUSED_PARAM) { return 1; }
#endif #endif
#define CHECKPASS_PW_HAS_EMPTY_PASSWORD 2
int check_password(const struct passwd *pw, const char *plaintext) FAST_FUNC; int check_password(const struct passwd *pw, const char *plaintext) FAST_FUNC;
int ask_and_check_password_extended(const struct passwd *pw, int timeout, const char *prompt) FAST_FUNC; int ask_and_check_password_extended(const struct passwd *pw, int timeout, const char *prompt) FAST_FUNC;
int ask_and_check_password(const struct passwd *pw) FAST_FUNC; int ask_and_check_password(const struct passwd *pw) FAST_FUNC;

View File

@ -88,7 +88,7 @@ int FAST_FUNC check_password(const struct passwd *pw, const char *plaintext)
/* Ask the user for a password. /* Ask the user for a password.
* Return 1 without asking if PW has an empty password. * Return CHECKPASS_PW_HAS_EMPTY_PASSWORD without asking if PW has an empty password.
* Return -1 on EOF, error while reading input, or timeout. * Return -1 on EOF, error while reading input, or timeout.
* Return 1 if the user gives the correct password for entry PW, * Return 1 if the user gives the correct password for entry PW,
* 0 if not. * 0 if not.
@ -105,7 +105,7 @@ int FAST_FUNC ask_and_check_password_extended(const struct passwd *pw,
pw_pass = get_passwd(pw, buffer); pw_pass = get_passwd(pw, buffer);
if (!pw_pass[0]) /* empty password field? */ if (!pw_pass[0]) /* empty password field? */
return 1; return CHECKPASS_PW_HAS_EMPTY_PASSWORD;
plaintext = bb_ask(STDIN_FILENO, timeout, prompt); plaintext = bb_ask(STDIN_FILENO, timeout, prompt);
if (!plaintext) { if (!plaintext) {

View File

@ -23,6 +23,11 @@
//config: bool "If user's shell is not in /etc/shells, disallow -s PROG" //config: bool "If user's shell is not in /etc/shells, disallow -s PROG"
//config: default y //config: default y
//config: depends on SU //config: depends on SU
//config:
//config:config FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY
//config: bool "Disallow blank passwords from TTYs other than specified in /etc/securetty"
//config: default n
//config: depends on SU
//applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */ //applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */
//applet:IF_SU(APPLET(su, BB_DIR_BIN, BB_SUID_REQUIRE)) //applet:IF_SU(APPLET(su, BB_DIR_BIN, BB_SUID_REQUIRE))
@ -79,6 +84,7 @@ int su_main(int argc UNUSED_PARAM, char **argv)
char user_buf[64]; char user_buf[64];
#endif #endif
const char *old_user; const char *old_user;
int r;
/* Note: we don't use "'+': stop at first non-option" idiom here. /* Note: we don't use "'+': stop at first non-option" idiom here.
* For su, "SCRIPT ARGS" or "-c CMD ARGS" do not stop option parsing: * For su, "SCRIPT ARGS" or "-c CMD ARGS" do not stop option parsing:
@ -99,6 +105,11 @@ int su_main(int argc UNUSED_PARAM, char **argv)
argv++; argv++;
} }
tty = xmalloc_ttyname(STDIN_FILENO);
if (!tty)
tty = "none";
tty = skip_dev_pfx(tty);
if (ENABLE_FEATURE_SU_SYSLOG) { if (ENABLE_FEATURE_SU_SYSLOG) {
/* The utmp entry (via getlogin) is probably the best way to /* The utmp entry (via getlogin) is probably the best way to
* identify the user, especially if someone su's from a su-shell. * identify the user, especially if someone su's from a su-shell.
@ -112,20 +123,26 @@ int su_main(int argc UNUSED_PARAM, char **argv)
pw = getpwuid(cur_uid); pw = getpwuid(cur_uid);
old_user = pw ? xstrdup(pw->pw_name) : ""; old_user = pw ? xstrdup(pw->pw_name) : "";
} }
tty = xmalloc_ttyname(2);
if (!tty) {
tty = "none";
}
openlog(applet_name, 0, LOG_AUTH); openlog(applet_name, 0, LOG_AUTH);
} }
pw = xgetpwnam(opt_username); pw = xgetpwnam(opt_username);
if (cur_uid == 0 || ask_and_check_password(pw) > 0) { r = 1;
if (cur_uid != 0)
r = ask_and_check_password(pw);
if (r > 0) {
if (ENABLE_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY
&& r == CHECKPASS_PW_HAS_EMPTY_PASSWORD
&& !check_securetty(tty)
) {
goto fail;
}
if (ENABLE_FEATURE_SU_SYSLOG) if (ENABLE_FEATURE_SU_SYSLOG)
syslog(LOG_NOTICE, "%c %s %s:%s", syslog(LOG_NOTICE, "%c %s %s:%s",
'+', tty, old_user, opt_username); '+', tty, old_user, opt_username);
} else { } else {
fail:
if (ENABLE_FEATURE_SU_SYSLOG) if (ENABLE_FEATURE_SU_SYSLOG)
syslog(LOG_NOTICE, "%c %s %s:%s", syslog(LOG_NOTICE, "%c %s %s:%s",
'-', tty, old_user, opt_username); '-', tty, old_user, opt_username);