MFH: r459011 r459013 r459492

net-p2p/transmission-daemon: Mitigate DNS rebinding attack

Incorporate upstream pull request 468, proposed by Tavis Ormandy from
Google Project Zero, which mitigates this attack by requiring a host
whitelist for requests that cannot be proven to be secure, but it can
be disabled if a user does not want security.

PR:		225150
Submitted by:	Tavis Ormandy
Approved by:	crees (maintainer)
Obtained from:	https://github.com/transmission/transmission/pull/468#issuecomment-357098126
Security:	https://www.vuxml.org/freebsd/3e5b8bd3-0c32-452f-a60e-beab7b762351.html

Add note to UPDATING for net-p2p/transmission-daemon explaining how to
allow client access with the new DNS rebinding mitigations.

PR:		225150
Security:	https://www.vuxml.org/freebsd/3e5b8bd3-0c32-452f-a60e-beab7b762351.html

net-p2p/transmission-daemon: Improve UPDATING entry and add pkg-message

This will ensure users who do not read UPDATING are still presented with
the message about how to allow clients to connect to the daemon using
DNS when they upgrade the package.

PR:		225150
Reported by:	swills
Security:	https://www.vuxml.org/freebsd/3e5b8bd3-0c32-452f-a60e-beab7b762351.html

Approved by:	ports-secteam (swills)
This commit is contained in:
Ben Woods 2018-01-20 01:28:56 +00:00
parent 6739b20a60
commit a2389f045c
Notes: svn2git 2021-03-31 03:12:20 +00:00
svn path=/branches/2018Q1/; revision=459493
4 changed files with 346 additions and 1 deletions

View File

@ -5,6 +5,30 @@ they are unavoidable.
You should get into the habit of checking this file for changes each time
you update your ports collection, before attempting any port upgrades.
20180115
AFFECTS: users of net-p2p/transmission-daemon
AUTHOR: woodsb02@FreeBSD.org
The transmission-daemon port has been updated to 2.92_4 to incorporate
a patch which mitigates DNS rebinding attacks. This will prevent
clients from being able to connect to the transmission daemon using
DNS with any hostname other than localhost, unless one of the
following is done:
- Enable password authentication, then any hostname is allowed.
This can be achieved by either:
- setting rpc-authentication-required to true, and adding
credentials to the rpc-username and rpc-password fields in
settings.json (must be done whilst the transmission service is
stopped); or
- running transmission-daemon with the following arguments
(these can be set with transmission_flags in /etc/rc.conf):
-t -u USERNAME -v PASSWORD
OR
- Add the allowed server hostnames to the rpc-host-whitelist setting
in settings.json (must be done whilst the transmission service is
stopped). Note that this value is NOT a list of allowed CLIENTS,
but instead a list of allowed SERVER hostnames.
20171230:
AFFECTS: users of net-im/ejabberd
AUTHOR: ashish@FreeBSD.org

View File

@ -0,0 +1,302 @@
Fix a weakness that allows remote code execution via the Transmission
RPC server using DNS rebinding:
https://bugs.chromium.org/p/project-zero/issues/detail?id=1447
Patch adapted from Tavis Ormandy's patch on the Transmission master
branch to the Transmission 2.92 release by Leo Famulari
<leo@famulari.name>:
https://github.com/transmission/transmission/pull/468/commits
From fe2d3c6e75088f3d9b6040ce06da3d530358bc2f Mon Sep 17 00:00:00 2001
From: Tavis Ormandy <taviso@google.com>
Date: Thu, 11 Jan 2018 10:00:41 -0800
Subject: [PATCH] mitigate dns rebinding attacks against daemon
---
libtransmission/quark.c | 2 +
libtransmission/quark.h | 2 +
libtransmission/rpc-server.c | 116 +++++++++++++++++++++++++++++++++++++----
libtransmission/rpc-server.h | 4 ++
libtransmission/session.c | 2 +
libtransmission/transmission.h | 1 +
libtransmission/web.c | 3 ++
7 files changed, 121 insertions(+), 9 deletions(-)
diff --git a/libtransmission/quark.c b/libtransmission/quark.c
index 30cc2bca4..b4fd7aabd 100644
--- libtransmission/quark.c.orig
+++ libtransmission/quark.c
@@ -289,6 +289,8 @@ static const struct tr_key_struct my_static[] =
{ "rpc-authentication-required", 27 },
{ "rpc-bind-address", 16 },
{ "rpc-enabled", 11 },
+ { "rpc-host-whitelist", 18 },
+ { "rpc-host-whitelist-enabled", 26 },
{ "rpc-password", 12 },
{ "rpc-port", 8 },
{ "rpc-url", 7 },
diff --git a/libtransmission/quark.h b/libtransmission/quark.h
index 7f5212733..17464be8f 100644
--- libtransmission/quark.h.orig
+++ libtransmission/quark.h
@@ -291,6 +291,8 @@ enum
TR_KEY_rpc_authentication_required,
TR_KEY_rpc_bind_address,
TR_KEY_rpc_enabled,
+ TR_KEY_rpc_host_whitelist,
+ TR_KEY_rpc_host_whitelist_enabled,
TR_KEY_rpc_password,
TR_KEY_rpc_port,
TR_KEY_rpc_url,
diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c
index a3485f3fa..292cd5fce 100644
--- libtransmission/rpc-server.c.orig
+++ libtransmission/rpc-server.c
@@ -52,6 +52,7 @@ struct tr_rpc_server
bool isEnabled;
bool isPasswordEnabled;
bool isWhitelistEnabled;
+ bool isHostWhitelistEnabled;
tr_port port;
char * url;
struct in_addr bindAddress;
@@ -63,6 +64,7 @@ struct tr_rpc_server
char * password;
char * whitelistStr;
tr_list * whitelist;
+ tr_list * hostWhitelist;
char * sessionId;
time_t sessionIdExpiresAt;
@@ -588,6 +590,49 @@ isAddressAllowed (const tr_rpc_server * server, const char * address)
return false;
}
+static bool isHostnameAllowed(tr_rpc_server const* server, struct evhttp_request* req)
+{
+ /* If password auth is enabled, any hostname is permitted. */
+ if (server->isPasswordEnabled)
+ {
+ return true;
+ }
+
+ char const* const host = evhttp_find_header(req->input_headers, "Host");
+
+ // If whitelist is disabled, no restrictions.
+ if (!server->isHostWhitelistEnabled)
+ return true;
+
+ /* No host header, invalid request. */
+ if (host == NULL)
+ {
+ return false;
+ }
+
+ /* Host header might include the port. */
+ char* const hostname = tr_strndup(host, strcspn(host, ":"));
+
+ /* localhost or ipaddress is always acceptable. */
+ if (strcmp(hostname, "localhost") == 0 || strcmp(hostname, "localhost.") == 0 || tr_addressIsIP(hostname))
+ {
+ tr_free(hostname);
+ return true;
+ }
+
+ /* Otherwise, hostname must be whitelisted. */
+ for (tr_list* l = server->hostWhitelist; l != NULL; l = l->next) {
+ if (tr_wildmat(hostname, l->data))
+ {
+ tr_free(hostname);
+ return true;
+ }
+ }
+
+ tr_free(hostname);
+ return false;
+}
+
static bool
test_session_id (struct tr_rpc_server * server, struct evhttp_request * req)
{
@@ -663,6 +708,23 @@ handle_request (struct evhttp_request * req, void * arg)
handle_upload (req, server);
}
#ifdef REQUIRE_SESSION_ID
+ else if (!isHostnameAllowed(server, req))
+ {
+ char* tmp = tr_strdup_printf(
+ "<p>Transmission received your request, but the hostname was unrecognized.</p>"
+ "<p>To fix this, choose one of the following options:"
+ "<ul>"
+ "<li>Enable password authentication, then any hostname is allowed.</li>"
+ "<li>Add the hostname you want to use to the whitelist in settings.</li>"
+ "</ul></p>"
+ "<p>If you're editing settings.json, see the 'rpc-host-whitelist' and 'rpc-host-whitelist-enabled' entries.</p>"
+ "<p>This requirement has been added to help prevent "
+ "<a href=\"https://en.wikipedia.org/wiki/DNS_rebinding\">DNS Rebinding</a> "
+ "attacks.</p>");
+ send_simple_response(req, 421, tmp);
+ tr_free(tmp);
+ }
+
else if (!test_session_id (server, req))
{
const char * sessionId = get_current_session_id (server);
@@ -674,7 +736,7 @@ handle_request (struct evhttp_request * req, void * arg)
"<li> When you get this 409 error message, resend your request with the updated header"
"</ol></p>"
"<p>This requirement has been added to help prevent "
- "<a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF</a> "
+ "<a href=\"https://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF</a> "
"attacks.</p>"
"<p><code>%s: %s</code></p>",
TR_RPC_SESSION_ID_HEADER, sessionId);
@@ -875,19 +937,14 @@ tr_rpcGetUrl (const tr_rpc_server * server)
return server->url ? server->url : "";
}
-void
-tr_rpcSetWhitelist (tr_rpc_server * server, const char * whitelistStr)
+static void
+tr_rpcSetList (char const* whitelistStr, tr_list** list)
{
void * tmp;
const char * walk;
- /* keep the string */
- tmp = server->whitelistStr;
- server->whitelistStr = tr_strdup (whitelistStr);
- tr_free (tmp);
-
/* clear out the old whitelist entries */
- while ((tmp = tr_list_pop_front (&server->whitelist)))
+ while ((tmp = tr_list_pop_front (list)) != NULL)
tr_free (tmp);
/* build the new whitelist entries */
@@ -896,7 +953,7 @@ tr_rpcSetWhitelist (tr_rpc_server * server, const char * whitelistStr)
const char * delimiters = " ,;";
const size_t len = strcspn (walk, delimiters);
char * token = tr_strndup (walk, len);
- tr_list_append (&server->whitelist, token);
+ tr_list_append (list, token);
if (strcspn (token, "+-") < len)
tr_logAddNamedInfo (MY_NAME, "Adding address to whitelist: %s (And it has a '+' or '-'! Are you using an old ACL by mistake?)", token);
else
@@ -909,6 +966,21 @@ tr_rpcSetWhitelist (tr_rpc_server * server, const char * whitelistStr)
}
}
+void tr_rpcSetHostWhitelist(tr_rpc_server* server, char const* whitelistStr)
+{
+ tr_rpcSetList(whitelistStr, &server->hostWhitelist);
+}
+
+void tr_rpcSetWhitelist(tr_rpc_server* server, char const* whitelistStr)
+{
+ /* keep the string */
+ char* const tmp = server->whitelistStr;
+ server->whitelistStr = tr_strdup(whitelistStr);
+ tr_free(tmp);
+
+ tr_rpcSetList(whitelistStr, &server->whitelist);
+}
+
const char*
tr_rpcGetWhitelist (const tr_rpc_server * server)
{
@@ -930,6 +1002,11 @@ tr_rpcGetWhitelistEnabled (const tr_rpc_server * server)
return server->isWhitelistEnabled;
}
+void tr_rpcSetHostWhitelistEnabled(tr_rpc_server* server, bool isEnabled)
+{
+ server->isHostWhitelistEnabled = isEnabled;
+}
+
/****
***** PASSWORD
****/
@@ -1063,6 +1140,28 @@ tr_rpcInit (tr_session * session, tr_variant * settings)
else
tr_rpcSetWhitelistEnabled (s, boolVal);
+ key = TR_KEY_rpc_host_whitelist_enabled;
+
+ if (!tr_variantDictFindBool(settings, key, &boolVal))
+ {
+ missing_settings_key(key);
+ }
+ else
+ {
+ tr_rpcSetHostWhitelistEnabled(s, boolVal);
+ }
+
+ key = TR_KEY_rpc_host_whitelist;
+
+ if (!tr_variantDictFindStr(settings, key, &str, NULL) && str != NULL)
+ {
+ missing_settings_key(key);
+ }
+ else
+ {
+ tr_rpcSetHostWhitelist(s, str);
+ }
+
key = TR_KEY_rpc_authentication_required;
if (!tr_variantDictFindBool (settings, key, &boolVal))
missing_settings_key (key);
diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h
index e0302c5ea..8c9e6b24e 100644
--- libtransmission/rpc-server.h.orig
+++ libtransmission/rpc-server.h
@@ -49,6 +49,10 @@ void tr_rpcSetWhitelist (tr_rpc_server * server,
const char* tr_rpcGetWhitelist (const tr_rpc_server * server);
+void tr_rpcSetHostWhitelistEnabled(tr_rpc_server* server, bool isEnabled);
+
+void tr_rpcSetHostWhitelist(tr_rpc_server* server, char const* whitelist);
+
void tr_rpcSetPassword (tr_rpc_server * server,
const char * password);
diff --git a/libtransmission/session.c b/libtransmission/session.c
index 844cadba8..58b717913 100644
--- libtransmission/session.c.orig
+++ libtransmission/session.c
@@ -359,6 +359,8 @@ tr_sessionGetDefaultSettings (tr_variant * d)
tr_variantDictAddStr (d, TR_KEY_rpc_username, "");
tr_variantDictAddStr (d, TR_KEY_rpc_whitelist, TR_DEFAULT_RPC_WHITELIST);
tr_variantDictAddBool (d, TR_KEY_rpc_whitelist_enabled, true);
+ tr_variantDictAddStr(d, TR_KEY_rpc_host_whitelist, TR_DEFAULT_RPC_HOST_WHITELIST);
+ tr_variantDictAddBool(d, TR_KEY_rpc_host_whitelist_enabled, true);
tr_variantDictAddInt (d, TR_KEY_rpc_port, atoi (TR_DEFAULT_RPC_PORT_STR));
tr_variantDictAddStr (d, TR_KEY_rpc_url, TR_DEFAULT_RPC_URL_STR);
tr_variantDictAddBool (d, TR_KEY_scrape_paused_torrents_enabled, true);
diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h
index 4f76adfd6..e213a8f4e 100644
--- libtransmission/transmission.h.orig
+++ libtransmission/transmission.h
@@ -123,6 +123,7 @@ const char* tr_getDefaultDownloadDir (void);
#define TR_DEFAULT_BIND_ADDRESS_IPV4 "0.0.0.0"
#define TR_DEFAULT_BIND_ADDRESS_IPV6 "::"
#define TR_DEFAULT_RPC_WHITELIST "127.0.0.1"
+#define TR_DEFAULT_RPC_HOST_WHITELIST ""
#define TR_DEFAULT_RPC_PORT_STR "9091"
#define TR_DEFAULT_RPC_URL_STR "/transmission/"
#define TR_DEFAULT_PEER_PORT_STR "51413"
diff --git a/libtransmission/web.c b/libtransmission/web.c
index ee495e9fc..c7f062730 100644
--- libtransmission/web.c.orig
+++ libtransmission/web.c
@@ -594,6 +594,7 @@ tr_webGetResponseStr (long code)
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
+ case 421: return "Misdirected Request";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";

View File

@ -1,6 +1,6 @@
# $FreeBSD$
PORTREVISION= 3
PORTREVISION= 4
PKGNAMESUFFIX= -daemon
MAINTAINER= crees@FreeBSD.org
@ -12,6 +12,7 @@ DESCR= ${.CURDIR}/pkg-descr
MASTERDIR= ${.CURDIR}/../transmission-cli
PLIST= ${.CURDIR}/pkg-plist
SLAVEPORT= daemon
PKGMESSAGE= ${.CURDIR}/pkg-message
USE_RC_SUBR= transmission
USERS= transmission

View File

@ -0,0 +1,18 @@
------------------------------------------------------------------------
To allow clients to connect to the transmission daemon using DNS with
any hostname other than localhost, do one of the following:
- Enable password authentication, then any hostname is allowed.
This can be achieved by either:
- setting rpc-authentication-required to true, and adding
credentials to the rpc-username and rpc-password fields in
settings.json (must be done whilst the transmission service is
stopped); or
- running transmission-daemon with the following arguments
(these can be set with transmission_flags in /etc/rc.conf):
-t -u USERNAME -v PASSWORD
OR
- Add the allowed server hostnames to the rpc-host-whitelist setting
in settings.json (must be done whilst the transmission service is
stopped). Note that this value is NOT a list of allowed CLIENTS,
but instead a list of allowed SERVER hostnames.
------------------------------------------------------------------------