Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 20 Jan 2018 01:28:56 +0000 (UTC)
From:      Ben Woods <woodsb02@FreeBSD.org>
To:        ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-branches@freebsd.org
Subject:   svn commit: r459493 - in branches/2018Q1: . net-p2p/transmission-cli/files net-p2p/transmission-daemon
Message-ID:  <201801200128.w0K1SuA1022513@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: woodsb02
Date: Sat Jan 20 01:28:56 2018
New Revision: 459493
URL: https://svnweb.freebsd.org/changeset/ports/459493

Log:
  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)

Added:
  branches/2018Q1/net-p2p/transmission-cli/files/patch-fix_dns_rebinding_vuln
     - copied unchanged from r459011, head/net-p2p/transmission-cli/files/patch-fix_dns_rebinding_vuln
  branches/2018Q1/net-p2p/transmission-daemon/pkg-message
     - copied unchanged from r459492, head/net-p2p/transmission-daemon/pkg-message
Modified:
  branches/2018Q1/UPDATING
  branches/2018Q1/net-p2p/transmission-daemon/Makefile
Directory Properties:
  branches/2018Q1/   (props changed)

Modified: branches/2018Q1/UPDATING
==============================================================================
--- branches/2018Q1/UPDATING	Sat Jan 20 01:20:19 2018	(r459492)
+++ branches/2018Q1/UPDATING	Sat Jan 20 01:28:56 2018	(r459493)
@@ -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

Copied: branches/2018Q1/net-p2p/transmission-cli/files/patch-fix_dns_rebinding_vuln (from r459011, head/net-p2p/transmission-cli/files/patch-fix_dns_rebinding_vuln)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ branches/2018Q1/net-p2p/transmission-cli/files/patch-fix_dns_rebinding_vuln	Sat Jan 20 01:28:56 2018	(r459493, copy of r459011, head/net-p2p/transmission-cli/files/patch-fix_dns_rebinding_vuln)
@@ -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";

Modified: branches/2018Q1/net-p2p/transmission-daemon/Makefile
==============================================================================
--- branches/2018Q1/net-p2p/transmission-daemon/Makefile	Sat Jan 20 01:20:19 2018	(r459492)
+++ branches/2018Q1/net-p2p/transmission-daemon/Makefile	Sat Jan 20 01:28:56 2018	(r459493)
@@ -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

Copied: branches/2018Q1/net-p2p/transmission-daemon/pkg-message (from r459492, head/net-p2p/transmission-daemon/pkg-message)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ branches/2018Q1/net-p2p/transmission-daemon/pkg-message	Sat Jan 20 01:28:56 2018	(r459493, copy of r459492, head/net-p2p/transmission-daemon/pkg-message)
@@ -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.
+------------------------------------------------------------------------



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201801200128.w0K1SuA1022513>