+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
+--- a/libtransmission/quark.c
++++ b/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
+--- a/libtransmission/quark.h
++++ b/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
+--- a/libtransmission/rpc-server.c
++++ b/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(
++ "Transmission received your request, but the hostname was unrecognized.
"
++ "To fix this, choose one of the following options:"
++ "
"
++ "- Enable password authentication, then any hostname is allowed.
"
++ "- Add the hostname you want to use to the whitelist in settings.
"
++ "