summaryrefslogtreecommitdiff
path: root/swift/common/middleware/s3api/s3request.py
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2019-03-20 23:52:53 +0000
committerGerrit Code Review <review@openstack.org>2019-03-20 23:52:53 +0000
commit3e7370ae2796236b35df94536c0b9e1045160e64 (patch)
tree103b9e40b170f92bd64e0bdd8ff80ebefe48ac87 /swift/common/middleware/s3api/s3request.py
parent28eabbff28f80e3563e5b316f9bf2eb12bd34109 (diff)
parent3a8f5dbf9c49fdf1cf2d0b7ba35b82f25f88e634 (diff)
downloadswift-3e7370ae2796236b35df94536c0b9e1045160e64.tar.gz
Merge "Verify client input for v4 signatures"
Diffstat (limited to 'swift/common/middleware/s3api/s3request.py')
-rw-r--r--swift/common/middleware/s3api/s3request.py60
1 files changed, 53 insertions, 7 deletions
diff --git a/swift/common/middleware/s3api/s3request.py b/swift/common/middleware/s3api/s3request.py
index 86dd6f75f..ffb2f8844 100644
--- a/swift/common/middleware/s3api/s3request.py
+++ b/swift/common/middleware/s3api/s3request.py
@@ -24,7 +24,8 @@ import six
from six.moves.urllib.parse import quote, unquote, parse_qsl
import string
-from swift.common.utils import split_path, json, get_swift_info
+from swift.common.utils import split_path, json, get_swift_info, \
+ close_if_possible
from swift.common import swob
from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \
HTTP_NO_CONTENT, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, \
@@ -110,6 +111,34 @@ def _header_acl_property(resource):
doc='Get and set the %s acl property' % resource)
+class HashingInput(object):
+ """
+ wsgi.input wrapper to verify the hash of the input as it's read.
+ """
+ def __init__(self, reader, content_length, hasher, expected_hex_hash):
+ self._input = reader
+ self._to_read = content_length
+ self._hasher = hasher()
+ self._expected = expected_hex_hash
+
+ def read(self, size=None):
+ chunk = self._input.read(size)
+ self._hasher.update(chunk)
+ self._to_read -= len(chunk)
+ if self._to_read < 0 or (size > len(chunk) and self._to_read) or (
+ self._to_read == 0 and
+ self._hasher.hexdigest() != self._expected):
+ self.close()
+ # Since we don't return the last chunk, the PUT never completes
+ raise swob.HTTPUnprocessableEntity(
+ 'The X-Amz-Content-SHA56 you specified did not match '
+ 'what we received.')
+ return chunk
+
+ def close(self):
+ close_if_possible(self._input)
+
+
class SigV4Mixin(object):
"""
A request class mixin to provide S3 signature v4 functionality
@@ -401,6 +430,20 @@ class SigV4Mixin(object):
raise InvalidRequest(msg)
else:
hashed_payload = self.headers['X-Amz-Content-SHA256']
+ if self.content_length == 0:
+ if hashed_payload != sha256().hexdigest():
+ raise BadDigest(
+ 'The X-Amz-Content-SHA56 you specified did not match '
+ 'what we received.')
+ elif self.content_length:
+ self.environ['wsgi.input'] = HashingInput(
+ self.environ['wsgi.input'],
+ self.content_length,
+ sha256,
+ hashed_payload)
+ # else, not provided -- Swift will kick out a 411 Length Required
+ # which will get translated back to a S3-style response in
+ # S3Request._swift_error_codes
cr.append(hashed_payload)
return '\n'.join(cr).encode('utf-8')
@@ -1267,12 +1310,15 @@ class S3Request(swob.Request):
sw_req = self.to_swift_req(method, container, obj, headers=headers,
body=body, query=query)
- sw_resp = sw_req.get_response(app)
-
- # reuse account and tokens
- _, self.account, _ = split_path(sw_resp.environ['PATH_INFO'],
- 2, 3, True)
- self.account = utf8encode(self.account)
+ try:
+ sw_resp = sw_req.get_response(app)
+ except swob.HTTPException as err:
+ sw_resp = err
+ else:
+ # reuse account and tokens
+ _, self.account, _ = split_path(sw_resp.environ['PATH_INFO'],
+ 2, 3, True)
+ self.account = utf8encode(self.account)
resp = S3Response.from_swift_resp(sw_resp)
status = resp.status_int # pylint: disable-msg=E1101