978cfa
From 20f7d039446f71c40ea240c0a9f6e2465659ec68 Mon Sep 17 00:00:00 2001
978cfa
From: Lumir Balhar <lbalhar@redhat.com>
978cfa
Date: Mon, 13 Jan 2020 09:31:42 +0100
978cfa
Subject: [PATCH 1/2] CVE-2018-20060
978cfa
978cfa
---
978cfa
 poolmanager.py | 11 ++++++++++-
978cfa
 util/retry.py  | 11 ++++++++++-
978cfa
 2 files changed, 20 insertions(+), 2 deletions(-)
978cfa
978cfa
diff --git a/poolmanager.py b/poolmanager.py
978cfa
index 1023dcb..b87f315 100644
978cfa
--- a/poolmanager.py
978cfa
+++ b/poolmanager.py
978cfa
@@ -156,8 +156,9 @@ class PoolManager(RequestMethods):
978cfa
 
978cfa
         kw['assert_same_host'] = False
978cfa
         kw['redirect'] = False
978cfa
+
978cfa
         if 'headers' not in kw:
978cfa
-            kw['headers'] = self.headers
978cfa
+            kw['headers'] = self.headers.copy()
978cfa
 
978cfa
         if self.proxy is not None and u.scheme == "http":
978cfa
             response = conn.urlopen(method, url, **kw)
978cfa
@@ -179,6 +180,14 @@ class PoolManager(RequestMethods):
978cfa
         if not isinstance(retries, Retry):
978cfa
             retries = Retry.from_int(retries, redirect=redirect)
978cfa
 
978cfa
+        # Strip headers marked as unsafe to forward to the redirected location.
978cfa
+        # Check remove_headers_on_redirect to avoid a potential network call within
978cfa
+        # conn.is_same_host() which may use socket.gethostbyname() in the future.
978cfa
+        if (retries.remove_headers_on_redirect
978cfa
+                and not conn.is_same_host(redirect_location)):
978cfa
+            for header in retries.remove_headers_on_redirect:
978cfa
+                kw['headers'].pop(header, None)
978cfa
+
978cfa
         try:
978cfa
             retries = retries.increment(method, url, response=response, _pool=conn)
978cfa
         except MaxRetryError:
978cfa
diff --git a/util/retry.py b/util/retry.py
978cfa
index 2d3aa20..d6bd74e 100644
978cfa
--- a/util/retry.py
978cfa
+++ b/util/retry.py
978cfa
@@ -103,6 +103,11 @@ class Retry(object):
978cfa
         exhausted, to raise a MaxRetryError, or to return a response with a
978cfa
         response code in the 3xx range.
978cfa
 
978cfa
+    :param iterable remove_headers_on_redirect:
978cfa
+        Sequence of headers to remove from the request when a response
978cfa
+        indicating a redirect is returned before firing off the redirected
978cfa
+        request
978cfa
+
978cfa
     :param bool raise_on_status: Similar meaning to ``raise_on_redirect``:
978cfa
         whether we should raise an exception, or return a response,
978cfa
         if status falls in ``status_forcelist`` range and retries have
978cfa
@@ -112,13 +117,15 @@ class Retry(object):
978cfa
     DEFAULT_METHOD_WHITELIST = frozenset([
978cfa
         'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
978cfa
 
978cfa
+    DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(['Authorization'])
978cfa
+
978cfa
     #: Maximum backoff time.
978cfa
     BACKOFF_MAX = 120
978cfa
 
978cfa
     def __init__(self, total=10, connect=None, read=None, redirect=None,
978cfa
                  method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None,
978cfa
                  backoff_factor=0, raise_on_redirect=True, raise_on_status=True,
978cfa
-                 _observed_errors=0):
978cfa
+                 _observed_errors=0, remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST):
978cfa
 
978cfa
         self.total = total
978cfa
         self.connect = connect
978cfa
@@ -135,6 +142,7 @@ class Retry(object):
978cfa
         self.raise_on_redirect = raise_on_redirect
978cfa
         self.raise_on_status = raise_on_status
978cfa
         self._observed_errors = _observed_errors  # TODO: use .history instead?
978cfa
+        self.remove_headers_on_redirect = remove_headers_on_redirect
978cfa
 
978cfa
     def new(self, **kw):
978cfa
         params = dict(
978cfa
@@ -146,6 +154,7 @@ class Retry(object):
978cfa
             raise_on_redirect=self.raise_on_redirect,
978cfa
             raise_on_status=self.raise_on_status,
978cfa
             _observed_errors=self._observed_errors,
978cfa
+            remove_headers_on_redirect=self.remove_headers_on_redirect,
978cfa
         )
978cfa
         params.update(kw)
978cfa
         return type(self)(**params)
978cfa
-- 
978cfa
2.24.1
978cfa