From 94bc4f05605ac6d34551b3bd3f37ef1d4cb8404f Mon Sep 17 00:00:00 2001 From: NYNEX Date: Tue, 4 Sep 2018 09:20:06 -0400 Subject: [PATCH] Update dropbear --- package/network/services/dropbear/Config.in | 9 + package/network/services/dropbear/Makefile | 10 +- .../services/dropbear/files/dropbear.init | 8 +- .../patches/010-runtime-maxauthtries.patch | 130 +++++++++++ .../020-Wait-to-fail-invalid-usernames.patch | 221 ++++++++++++++++++ .../dropbear/patches/100-pubkey_path.patch | 4 +- ...610-skip-default-keys-in-custom-runs.patch | 4 +- 7 files changed, 377 insertions(+), 9 deletions(-) create mode 100644 package/network/services/dropbear/patches/010-runtime-maxauthtries.patch create mode 100644 package/network/services/dropbear/patches/020-Wait-to-fail-invalid-usernames.patch diff --git a/package/network/services/dropbear/Config.in b/package/network/services/dropbear/Config.in index 7c2edd79f2..ca0af9d5e0 100644 --- a/package/network/services/dropbear/Config.in +++ b/package/network/services/dropbear/Config.in @@ -32,6 +32,15 @@ config DROPBEAR_ECC Increases binary size by about 23 kB (MIPS). +config DROPBEAR_ZLIB + bool "Enable compression" + default n + help + Enables compression using shared zlib library. + + Increases binary size by about 0.1 kB (MIPS) and requires additional 62 kB (MIPS) + for a shared zlib library. + config DROPBEAR_UTMP bool "Utmp support" default n diff --git a/package/network/services/dropbear/Makefile b/package/network/services/dropbear/Makefile index 05df1bc6ac..c1fa3c93ca 100644 --- a/package/network/services/dropbear/Makefile +++ b/package/network/services/dropbear/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dropbear PKG_VERSION:=2017.75 -PKG_RELEASE:=2 +PKG_RELEASE:=5 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:= \ @@ -23,7 +23,10 @@ PKG_LICENSE_FILES:=LICENSE libtomcrypt/LICENSE libtommath/LICENSE PKG_BUILD_PARALLEL:=1 PKG_USE_MIPS16:=0 -PKG_CONFIG_DEPENDS:=CONFIG_TARGET_INIT_PATH CONFIG_DROPBEAR_ECC CONFIG_DROPBEAR_CURVE25519 +PKG_CONFIG_DEPENDS:= \ + CONFIG_TARGET_INIT_PATH CONFIG_DROPBEAR_ECC \ + CONFIG_DROPBEAR_CURVE25519 CONFIG_DROPBEAR_ZLIB \ + CONFIG_DROPBEAR_UTMP CONFIG_DROPBEAR_PUTUTLINE include $(INCLUDE_DIR)/package.mk @@ -44,6 +47,7 @@ define Package/dropbear SECTION:=net CATEGORY:=Base system TITLE:=Small SSH2 client/server + DEPENDS:= +DROPBEAR_ZLIB:zlib endef define Package/dropbear/description @@ -74,7 +78,7 @@ CONFIGURE_ARGS += \ --disable-loginfunc \ $(if $(CONFIG_DROPBEAR_PUTUTLINE),,--disable-pututline) \ --disable-pututxline \ - --disable-zlib \ + $(if $(CONFIG_DROPBEAR_ZLIB),,--disable-zlib) \ --enable-bundled-libtom TARGET_CFLAGS += -DARGTYPE=3 -ffunction-sections -fdata-sections diff --git a/package/network/services/dropbear/files/dropbear.init b/package/network/services/dropbear/files/dropbear.init index 5c21359cbf..3d8cb2ca5e 100755 --- a/package/network/services/dropbear/files/dropbear.init +++ b/package/network/services/dropbear/files/dropbear.init @@ -41,6 +41,7 @@ validate_section_dropbear() 'Port:list(port):22' \ 'SSHKeepAlive:uinteger:300' \ 'IdleTimeout:uinteger:0' \ + 'MaxAuthTries:uinteger:3' \ 'mdns:bool:1' } @@ -49,7 +50,7 @@ dropbear_instance() local PasswordAuth enable Interface GatewayPorts \ RootPasswordAuth RootLogin rsakeyfile \ BannerFile Port SSHKeepAlive IdleTimeout \ - mdns ipaddrs + MaxAuthTries mdns ipaddrs validate_section_dropbear "${1}" || { echo "validation failed" @@ -78,6 +79,7 @@ dropbear_instance() append_ports "${ipaddrs}" "${Port}" [ "${IdleTimeout}" -ne 0 ] && procd_append_param command -I "${IdleTimeout}" [ "${SSHKeepAlive}" -ne 0 ] && procd_append_param command -K "${SSHKeepAlive}" + [ "${MaxAuthTries}" -ne 0 ] && procd_append_param command -T "${MaxAuthTries}" [ "${mdns}" -ne 0 ] && procd_add_mdns "ssh" "tcp" "$Port" "daemon=dropbear" procd_set_param respawn procd_close_instance @@ -109,7 +111,9 @@ keygen() load_interfaces() { config_get interface "$1" Interface - interfaces=" ${interface} ${interfaces}" + config_get enable "$1" enable 1 + + [ "${enable}" = "1" ] && interfaces=" ${interface} ${interfaces}" } start_service() diff --git a/package/network/services/dropbear/patches/010-runtime-maxauthtries.patch b/package/network/services/dropbear/patches/010-runtime-maxauthtries.patch new file mode 100644 index 0000000000..26db3181f2 --- /dev/null +++ b/package/network/services/dropbear/patches/010-runtime-maxauthtries.patch @@ -0,0 +1,130 @@ +From 46b22e57d91e33a591d0fba97da52672af4d6ed2 Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant +Date: Mon, 29 May 2017 10:25:09 +0100 +Subject: [PATCH] dropbear server: support -T max auth tries + +Add support for '-T n' for a run-time specification for maximum number +of authentication attempts where 'n' is between 1 and compile time +option MAX_AUTH_TRIES. + +A default number of tries can be specified at compile time using +'DEFAULT_AUTH_TRIES' which itself defaults to MAX_AUTH_TRIES for +backwards compatibility. + +Signed-off-by: Kevin Darbyshire-Bryant +--- + options.h | 7 +++++++ + runopts.h | 1 + + svr-auth.c | 2 +- + svr-runopts.c | 17 +++++++++++++++++ + 4 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/options.h b/options.h +index 0c51bb1..4d22704 100644 +--- a/options.h ++++ b/options.h +@@ -284,6 +284,13 @@ Homedir is prepended unless path begins with / */ + #define MAX_AUTH_TRIES 10 + #endif + ++/* Default maximum number of failed authentication tries. ++ * defaults to MAX_AUTH_TRIES */ ++ ++#ifndef DEFAULT_AUTH_TRIES ++#define DEFAULT_AUTH_TRIES MAX_AUTH_TRIES ++#endif ++ + /* The default file to store the daemon's process ID, for shutdown + scripts etc. This can be overridden with the -P flag */ + #ifndef DROPBEAR_PIDFILE +diff --git a/runopts.h b/runopts.h +index f7c869d..2f7da63 100644 +--- a/runopts.h ++++ b/runopts.h +@@ -96,6 +96,7 @@ typedef struct svr_runopts { + int noauthpass; + int norootpass; + int allowblankpass; ++ unsigned int maxauthtries; + + #ifdef ENABLE_SVR_REMOTETCPFWD + int noremotetcp; +diff --git a/svr-auth.c b/svr-auth.c +index 577ea88..6a7ce0b 100644 +--- a/svr-auth.c ++++ b/svr-auth.c +@@ -362,7 +362,7 @@ void send_msg_userauth_failure(int partial, int incrfail) { + ses.authstate.failcount++; + } + +- if (ses.authstate.failcount >= MAX_AUTH_TRIES) { ++ if (ses.authstate.failcount >= svr_opts.maxauthtries) { + char * userstr; + /* XXX - send disconnect ? */ + TRACE(("Max auth tries reached, exiting")) +diff --git a/svr-runopts.c b/svr-runopts.c +index 8f60059..1e7440f 100644 +--- a/svr-runopts.c ++++ b/svr-runopts.c +@@ -73,6 +73,7 @@ static void printhelp(const char * progname) { + "-g Disable password logins for root\n" + "-B Allow blank password logins\n" + #endif ++ "-T <1 to %d> Maximum authentication tries (default %d)\n" + #ifdef ENABLE_SVR_LOCALTCPFWD + "-j Disable local port forwarding\n" + #endif +@@ -106,6 +107,7 @@ static void printhelp(const char * progname) { + #ifdef DROPBEAR_ECDSA + ECDSA_PRIV_FILENAME, + #endif ++ MAX_AUTH_TRIES, DEFAULT_AUTH_TRIES, + DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE, + DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT); + } +@@ -118,6 +120,7 @@ void svr_getopts(int argc, char ** argv) { + char* recv_window_arg = NULL; + char* keepalive_arg = NULL; + char* idle_timeout_arg = NULL; ++ char* maxauthtries_arg = NULL; + char* keyfile = NULL; + char c; + +@@ -130,6 +133,7 @@ void svr_getopts(int argc, char ** argv) { + svr_opts.noauthpass = 0; + svr_opts.norootpass = 0; + svr_opts.allowblankpass = 0; ++ svr_opts.maxauthtries = DEFAULT_AUTH_TRIES; + svr_opts.inetdmode = 0; + svr_opts.portcount = 0; + svr_opts.hostkey = NULL; +@@ -234,6 +238,9 @@ void svr_getopts(int argc, char ** argv) { + case 'I': + next = &idle_timeout_arg; + break; ++ case 'T': ++ next = &maxauthtries_arg; ++ break; + #if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH) + case 's': + svr_opts.noauthpass = 1; +@@ -330,6 +337,16 @@ void svr_getopts(int argc, char ** argv) { + dropbear_exit("Bad recv window '%s'", recv_window_arg); + } + } ++ ++ if (maxauthtries_arg) { ++ unsigned int val = 0; ++ if (m_str_to_uint(maxauthtries_arg, &val) == DROPBEAR_FAILURE || ++ val == 0 || val > MAX_AUTH_TRIES) { ++ dropbear_exit("Bad maxauthtries '%s'", maxauthtries_arg); ++ } ++ svr_opts.maxauthtries = val; ++ } ++ + + if (keepalive_arg) { + unsigned int val; +-- +2.7.4 + diff --git a/package/network/services/dropbear/patches/020-Wait-to-fail-invalid-usernames.patch b/package/network/services/dropbear/patches/020-Wait-to-fail-invalid-usernames.patch new file mode 100644 index 0000000000..593dca930d --- /dev/null +++ b/package/network/services/dropbear/patches/020-Wait-to-fail-invalid-usernames.patch @@ -0,0 +1,221 @@ +From 52adbb34c32d3e2e1bcdb941e20a6f81138b8248 Mon Sep 17 00:00:00 2001 +From: Matt Johnston +Date: Thu, 23 Aug 2018 23:43:12 +0800 +Subject: [PATCH 2/2] Wait to fail invalid usernames + +--- + auth.h | 6 +++--- + svr-auth.c | 19 +++++-------------- + svr-authpam.c | 26 ++++++++++++++++++++++---- + svr-authpasswd.c | 27 ++++++++++++++------------- + svr-authpubkey.c | 11 ++++++++++- + 5 files changed, 54 insertions(+), 35 deletions(-) + +--- a/auth.h ++++ b/auth.h +@@ -37,9 +37,9 @@ void recv_msg_userauth_request(void); + void send_msg_userauth_failure(int partial, int incrfail); + void send_msg_userauth_success(void); + void send_msg_userauth_banner(buffer *msg); +-void svr_auth_password(void); +-void svr_auth_pubkey(void); +-void svr_auth_pam(void); ++void svr_auth_password(int valid_user); ++void svr_auth_pubkey(int valid_user); ++void svr_auth_pam(int valid_user); + + #ifdef ENABLE_SVR_PUBKEY_OPTIONS + int svr_pubkey_allows_agentfwd(void); +--- a/svr-auth.c ++++ b/svr-auth.c +@@ -176,10 +176,8 @@ void recv_msg_userauth_request() { + if (methodlen == AUTH_METHOD_PASSWORD_LEN && + strncmp(methodname, AUTH_METHOD_PASSWORD, + AUTH_METHOD_PASSWORD_LEN) == 0) { +- if (valid_user) { +- svr_auth_password(); +- goto out; +- } ++ svr_auth_password(valid_user); ++ goto out; + } + } + #endif +@@ -191,10 +189,8 @@ void recv_msg_userauth_request() { + if (methodlen == AUTH_METHOD_PASSWORD_LEN && + strncmp(methodname, AUTH_METHOD_PASSWORD, + AUTH_METHOD_PASSWORD_LEN) == 0) { +- if (valid_user) { +- svr_auth_pam(); +- goto out; +- } ++ svr_auth_pam(valid_user); ++ goto out; + } + } + #endif +@@ -204,12 +200,7 @@ void recv_msg_userauth_request() { + if (methodlen == AUTH_METHOD_PUBKEY_LEN && + strncmp(methodname, AUTH_METHOD_PUBKEY, + AUTH_METHOD_PUBKEY_LEN) == 0) { +- if (valid_user) { +- svr_auth_pubkey(); +- } else { +- /* pubkey has no failure delay */ +- send_msg_userauth_failure(0, 0); +- } ++ svr_auth_pubkey(valid_user); + goto out; + } + #endif +--- a/svr-authpam.c ++++ b/svr-authpam.c +@@ -178,13 +178,14 @@ pamConvFunc(int num_msg, + * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it + * gets very messy trying to send the interactive challenges, and read the + * interactive responses, over the network. */ +-void svr_auth_pam() { ++void svr_auth_pam(int valid_user) { + + struct UserDataS userData = {NULL, NULL}; + struct pam_conv pamConv = { + pamConvFunc, + &userData /* submitted to pamvConvFunc as appdata_ptr */ + }; ++ const char* printable_user = NULL; + + pam_handle_t* pamHandlep = NULL; + +@@ -204,12 +205,23 @@ void svr_auth_pam() { + + password = buf_getstring(ses.payload, &passwordlen); + ++ /* We run the PAM conversation regardless of whether the username is valid ++ in case the conversation function has an inherent delay. ++ Use ses.authstate.username rather than ses.authstate.pw_name. ++ After PAM succeeds we then check the valid_user flag too */ ++ + /* used to pass data to the PAM conversation function - don't bother with + * strdup() etc since these are touched only by our own conversation + * function (above) which takes care of it */ +- userData.user = ses.authstate.pw_name; ++ userData.user = ses.authstate.username; + userData.passwd = password; + ++ if (ses.authstate.pw_name) { ++ printable_user = ses.authstate.pw_name; ++ } else { ++ printable_user = ""; ++ } ++ + /* Init pam */ + if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) { + dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s", +@@ -236,7 +248,7 @@ void svr_auth_pam() { + rc, pam_strerror(pamHandlep, rc)); + dropbear_log(LOG_WARNING, + "Bad PAM password attempt for '%s' from %s", +- ses.authstate.pw_name, ++ printable_user, + svr_ses.addrstring); + send_msg_userauth_failure(0, 1); + goto cleanup; +@@ -247,12 +259,18 @@ void svr_auth_pam() { + rc, pam_strerror(pamHandlep, rc)); + dropbear_log(LOG_WARNING, + "Bad PAM password attempt for '%s' from %s", +- ses.authstate.pw_name, ++ printable_user, + svr_ses.addrstring); + send_msg_userauth_failure(0, 1); + goto cleanup; + } + ++ if (!valid_user) { ++ /* PAM auth succeeded but the username isn't allowed in for another reason ++ (checkusername() failed) */ ++ send_msg_userauth_failure(0, 1); ++ } ++ + /* successful authentication */ + dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s", + ses.authstate.pw_name, +--- a/svr-authpasswd.c ++++ b/svr-authpasswd.c +@@ -48,22 +48,14 @@ static int constant_time_strcmp(const ch + + /* Process a password auth request, sending success or failure messages as + * appropriate */ +-void svr_auth_password() { ++void svr_auth_password(int valid_user) { + + char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */ + char * testcrypt = NULL; /* crypt generated from the user's password sent */ +- char * password; ++ char * password = NULL; + unsigned int passwordlen; +- + unsigned int changepw; + +- passwdcrypt = ses.authstate.pw_passwd; +- +-#ifdef DEBUG_HACKCRYPT +- /* debugging crypt for non-root testing with shadows */ +- passwdcrypt = DEBUG_HACKCRYPT; +-#endif +- + /* check if client wants to change password */ + changepw = buf_getbool(ses.payload); + if (changepw) { +@@ -73,12 +65,21 @@ void svr_auth_password() { + } + + password = buf_getstring(ses.payload, &passwordlen); +- +- /* the first bytes of passwdcrypt are the salt */ +- testcrypt = crypt(password, passwdcrypt); ++ if (valid_user) { ++ /* the first bytes of passwdcrypt are the salt */ ++ passwdcrypt = ses.authstate.pw_passwd; ++ testcrypt = crypt(password, passwdcrypt); ++ } + m_burn(password, passwordlen); + m_free(password); + ++ /* After we have got the payload contents we can exit if the username ++ is invalid. Invalid users have already been logged. */ ++ if (!valid_user) { ++ send_msg_userauth_failure(0, 1); ++ return; ++ } ++ + if (testcrypt == NULL) { + /* crypt() with an invalid salt like "!!" */ + dropbear_log(LOG_WARNING, "User account '%s' is locked", +--- a/svr-authpubkey.c ++++ b/svr-authpubkey.c +@@ -79,7 +79,7 @@ static int checkfileperm(char * filename + + /* process a pubkey auth request, sending success or failure message as + * appropriate */ +-void svr_auth_pubkey() { ++void svr_auth_pubkey(int valid_user) { + + unsigned char testkey; /* whether we're just checking if a key is usable */ + char* algo = NULL; /* pubkey algo */ +@@ -102,6 +102,15 @@ void svr_auth_pubkey() { + keybloblen = buf_getint(ses.payload); + keyblob = buf_getptr(ses.payload, keybloblen); + ++ if (!valid_user) { ++ /* Return failure once we have read the contents of the packet ++ required to validate a public key. ++ Avoids blind user enumeration though it isn't possible to prevent ++ testing for user existence if the public key is known */ ++ send_msg_userauth_failure(0, 0); ++ goto out; ++ } ++ + /* check if the key is valid */ + if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) { + send_msg_userauth_failure(0, 0); diff --git a/package/network/services/dropbear/patches/100-pubkey_path.patch b/package/network/services/dropbear/patches/100-pubkey_path.patch index 401c7e1ba5..274d3af46a 100644 --- a/package/network/services/dropbear/patches/100-pubkey_path.patch +++ b/package/network/services/dropbear/patches/100-pubkey_path.patch @@ -1,6 +1,6 @@ --- a/svr-authpubkey.c +++ b/svr-authpubkey.c -@@ -220,14 +220,20 @@ static int checkpubkey(char* algo, unsig +@@ -229,14 +229,20 @@ static int checkpubkey(char* algo, unsig goto out; } @@ -29,7 +29,7 @@ /* open the file as the authenticating user. */ origuid = getuid(); -@@ -396,26 +402,35 @@ static int checkpubkeyperms() { +@@ -405,26 +411,35 @@ static int checkpubkeyperms() { goto out; } diff --git a/package/network/services/dropbear/patches/610-skip-default-keys-in-custom-runs.patch b/package/network/services/dropbear/patches/610-skip-default-keys-in-custom-runs.patch index f6453a4626..a555a9e498 100644 --- a/package/network/services/dropbear/patches/610-skip-default-keys-in-custom-runs.patch +++ b/package/network/services/dropbear/patches/610-skip-default-keys-in-custom-runs.patch @@ -1,6 +1,6 @@ --- a/svr-runopts.c +++ b/svr-runopts.c -@@ -488,6 +488,7 @@ void load_all_hostkeys() { +@@ -505,6 +505,7 @@ void load_all_hostkeys() { m_free(hostkey_file); } @@ -8,7 +8,7 @@ #ifdef DROPBEAR_RSA loadhostkey(RSA_PRIV_FILENAME, 0); #endif -@@ -499,6 +500,7 @@ void load_all_hostkeys() { +@@ -516,6 +517,7 @@ void load_all_hostkeys() { #ifdef DROPBEAR_ECDSA loadhostkey(ECDSA_PRIV_FILENAME, 0); #endif -- 2.25.1