components/openstack/swift/patches/CVE-2015-5223.patch
branchs11u3-sru
changeset 6035 c9748fcc32de
equal deleted inserted replaced
6016:a477397bba8b 6035:c9748fcc32de
       
     1 This upstream patch addresses CVE-2015-5223 in swift. It may be removed
       
     2 when swift 2.4.0 or later is integrated.
       
     3 
       
     4 From 0694e1911d10a18075ff99462c96781372422b2c Mon Sep 17 00:00:00 2001
       
     5 From: Clay Gerrard <[email protected]>
       
     6 Date: Thu, 23 Jul 2015 22:36:21 -0700
       
     7 Subject: Disallow unsafe tempurl operations to point to unauthorized data
       
     8 
       
     9 Do not allow PUT tempurls to create pointers to other data. Specifically
       
    10 disallow the creation of DLO object manifests by returning an error if a
       
    11 non-safe tempurl request includes an X-Object-Manifest header regardless of
       
    12 the value of the header.
       
    13 
       
    14 This prevents discoverability attacks which can use any PUT tempurl to probe
       
    15 for private data by creating a DLO object manifest and then using the PUT
       
    16 tempurl to head the object which would 404 if the prefix does not match any
       
    17 object data or form a valid DLO HEAD response if it does.
       
    18 
       
    19 This also prevents a tricky and potentially unexpected consequence of PUT
       
    20 tempurls which would make it unsafe to allow a user to download objects
       
    21 created by tempurl (even if they just created them) because the result of
       
    22 reading the object created via tempurl may not be the data which was uploaded.
       
    23 
       
    24 [CVE-2015-5223]
       
    25 
       
    26 Co-Authored-By: Kota Tsuyuzaki <[email protected]>
       
    27 
       
    28 Closes-Bug: 1453948
       
    29 
       
    30 Change-Id: I91161dfb0f089c3990aca1b4255b520299ef73c8
       
    31 
       
    32 --- swift-2.3.0/swift/common/middleware/tempurl.py.~1~	2015-04-30 09:57:42.000000000 -0400
       
    33 +++ swift-2.3.0/swift/common/middleware/tempurl.py	2015-11-03 17:11:02.364113024 -0500
       
    34 @@ -122,11 +122,13 @@
       
    35  from urlparse import parse_qs
       
    36  
       
    37  from swift.proxy.controllers.base import get_account_info, get_container_info
       
    38 -from swift.common.swob import HeaderKeyDict, HTTPUnauthorized
       
    39 +from swift.common.swob import HeaderKeyDict, HTTPUnauthorized, HTTPBadRequest
       
    40  from swift.common.utils import split_path, get_valid_utf8_str, \
       
    41      register_swift_info, get_hmac, streq_const_time, quote
       
    42  
       
    43  
       
    44 +DISALLOWED_INCOMING_HEADERS = 'x-object-manifest'
       
    45 +
       
    46  #: Default headers to remove from incoming requests. Simply a whitespace
       
    47  #: delimited list of header names and names can optionally end with '*' to
       
    48  #: indicate a prefix match. DEFAULT_INCOMING_ALLOW_HEADERS is a list of
       
    49 @@ -230,6 +232,10 @@
       
    50          #: The methods allowed with Temp URLs.
       
    51          self.methods = methods
       
    52  
       
    53 +        self.disallowed_headers = set(
       
    54 +            'HTTP_' + h.upper().replace('-', '_')
       
    55 +            for h in DISALLOWED_INCOMING_HEADERS.split())
       
    56 +
       
    57          headers = DEFAULT_INCOMING_REMOVE_HEADERS
       
    58          if 'incoming_remove_headers' in conf:
       
    59              headers = conf['incoming_remove_headers']
       
    60 @@ -323,6 +329,13 @@
       
    61                              for hmac in hmac_vals)
       
    62          if not is_valid_hmac:
       
    63              return self._invalid(env, start_response)
       
    64 +        # disallowed headers prevent accidently allowing upload of a pointer
       
    65 +        # to data that the PUT tempurl would not otherwise allow access for.
       
    66 +        # It should be safe to provide a GET tempurl for data that an
       
    67 +        # untrusted client just uploaded with a PUT tempurl.
       
    68 +        resp = self._clean_disallowed_headers(env, start_response)
       
    69 +        if resp:
       
    70 +            return resp
       
    71          self._clean_incoming_headers(env)
       
    72          env['swift.authorize'] = lambda req: None
       
    73          env['swift.authorize_override'] = True
       
    74 @@ -465,6 +478,22 @@
       
    75              body = '401 Unauthorized: Temp URL invalid\n'
       
    76          return HTTPUnauthorized(body=body)(env, start_response)
       
    77  
       
    78 +    def _clean_disallowed_headers(self, env, start_response):
       
    79 +        """
       
    80 +        Validate the absense of disallowed headers for "unsafe" operations.
       
    81 +
       
    82 +        :returns: None for safe operations or swob.HTTPBadResponse if the
       
    83 +                  request includes disallowed headers.
       
    84 +        """
       
    85 +        if env['REQUEST_METHOD'] in ('GET', 'HEAD', 'OPTIONS'):
       
    86 +            return
       
    87 +        for h in env:
       
    88 +            if h in self.disallowed_headers:
       
    89 +                return HTTPBadRequest(
       
    90 +                    body='The header %r is not allowed in this tempurl' %
       
    91 +                    h[len('HTTP_'):].title().replace('_', '-'))(
       
    92 +                        env, start_response)
       
    93 +
       
    94      def _clean_incoming_headers(self, env):
       
    95          """
       
    96          Removes any headers from the WSGI environment as per the
       
    97 --- swift-2.3.0/test/functional/tests.py.~1~	2015-04-30 09:57:42.000000000 -0400
       
    98 +++ swift-2.3.0/test/functional/tests.py	2015-11-03 15:27:42.202245458 -0500
       
    99 @@ -2732,6 +2732,42 @@
       
   100          self.assert_(new_obj.info(parms=put_parms,
       
   101                                    cfg={'no_auth_token': True}))
       
   102  
       
   103 +    def test_PUT_manifest_access(self):
       
   104 +        new_obj = self.env.container.file(Utils.create_name())
       
   105 +
       
   106 +        # give out a signature which allows a PUT to new_obj
       
   107 +        expires = int(time.time()) + 86400
       
   108 +        sig = self.tempurl_sig(
       
   109 +            'PUT', expires, self.env.conn.make_path(new_obj.path),
       
   110 +            self.env.tempurl_key)
       
   111 +        put_parms = {'temp_url_sig': sig,
       
   112 +                     'temp_url_expires': str(expires)}
       
   113 +
       
   114 +        # try to create manifest pointing to some random container
       
   115 +        try:
       
   116 +            new_obj.write('', {
       
   117 +                'x-object-manifest': '%s/foo' % 'some_random_container'
       
   118 +            }, parms=put_parms, cfg={'no_auth_token': True})
       
   119 +        except ResponseError as e:
       
   120 +            self.assertEqual(e.status, 400)
       
   121 +        else:
       
   122 +            self.fail('request did not error')
       
   123 +
       
   124 +        # create some other container
       
   125 +        other_container = self.env.account.container(Utils.create_name())
       
   126 +        if not other_container.create():
       
   127 +            raise ResponseError(self.conn.response)
       
   128 +
       
   129 +        # try to create manifest pointing to new container
       
   130 +        try:
       
   131 +            new_obj.write('', {
       
   132 +                'x-object-manifest': '%s/foo' % other_container
       
   133 +            }, parms=put_parms, cfg={'no_auth_token': True})
       
   134 +        except ResponseError as e:
       
   135 +            self.assertEqual(e.status, 400)
       
   136 +        else:
       
   137 +            self.fail('request did not error')
       
   138 +
       
   139      def test_HEAD(self):
       
   140          expires = int(time.time()) + 86400
       
   141          sig = self.tempurl_sig(
       
   142 --- swift-2.3.0/test/unit/common/middleware/test_tempurl.py.~1~	2015-04-30 09:57:42.000000000 -0400
       
   143 +++ swift-2.3.0/test/unit/common/middleware/test_tempurl.py	2015-11-03 15:27:42.202552552 -0500
       
   144 @@ -649,6 +649,25 @@
       
   145          self.assertTrue('Temp URL invalid' in resp.body)
       
   146          self.assertTrue('Www-Authenticate' in resp.headers)
       
   147  
       
   148 +    def test_disallowed_header_object_manifest(self):
       
   149 +        self.tempurl = tempurl.filter_factory({})(self.auth)
       
   150 +        method = 'PUT'
       
   151 +        expires = int(time() + 86400)
       
   152 +        path = '/v1/a/c/o'
       
   153 +        key = 'abc'
       
   154 +        hmac_body = '%s\n%s\n%s' % (method, expires, path)
       
   155 +        sig = hmac.new(key, hmac_body, sha1).hexdigest()
       
   156 +        req = self._make_request(
       
   157 +            path, method='PUT', keys=[key],
       
   158 +            headers={'x-object-manifest': 'private/secret'},
       
   159 +            environ={'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s' % (
       
   160 +                sig, expires)})
       
   161 +        resp = req.get_response(self.tempurl)
       
   162 +        self.assertEquals(resp.status_int, 400)
       
   163 +        self.assertTrue('header' in resp.body)
       
   164 +        self.assertTrue('not allowed' in resp.body)
       
   165 +        self.assertTrue('X-Object-Manifest' in resp.body)
       
   166 +
       
   167      def test_removed_incoming_header(self):
       
   168          self.tempurl = tempurl.filter_factory({
       
   169              'incoming_remove_headers': 'x-remove-this'})(self.auth)