s3api: Block ETag header from CompleteMultipartUpload requests

Change-Id: I7df13e670f702a2956a9c8057fcb971f4bfb7319
Closes-Bug: 1810397
This commit is contained in:
Tim Burke 2019-01-03 12:12:15 -08:00 committed by Clay Gerrard
parent 32bf43990c
commit c51db382cb
3 changed files with 16 additions and 2 deletions

View File

@ -73,7 +73,7 @@ from six.moves.urllib.parse import quote, urlparse
from swift.common.middleware.s3api.controllers.base import Controller, \
bucket_operation, object_operation, check_container_existence
from swift.common.middleware.s3api.s3response import InvalidArgument, \
ErrorResponse, MalformedXML, \
ErrorResponse, MalformedXML, BadDigest, \
InvalidPart, BucketAlreadyExists, EntityTooSmall, InvalidPartOrder, \
InvalidRequest, HTTPOk, HTTPNoContent, NoSuchKey, NoSuchUpload, \
NoSuchBucket, BucketAlreadyOwnedByYou
@ -571,6 +571,15 @@ class UploadController(Controller):
xml = req.xml(MAX_COMPLETE_UPLOAD_BODY_SIZE)
if not xml:
raise InvalidRequest(msg='You must specify at least one part')
if 'content-md5' in req.headers:
# If an MD5 was provided, we need to verify it.
# Note that S3Request already took care of translating to ETag
if req.headers['etag'] != md5(xml).hexdigest():
raise BadDigest(content_md5=req.headers['content-md5'])
# We're only interested in the body here, in the
# multipart-upload controller -- *don't* let it get
# plumbed down to the object-server
del req.headers['etag']
complete_elem = fromstring(
xml, 'CompleteMultipartUpload', self.logger)

View File

@ -114,6 +114,9 @@ class FakeSwift(object):
if method == 'PUT' and obj:
input = env['wsgi.input'].read()
etag = md5(input).hexdigest()
if env.get('HTTP_ETAG', etag) != etag:
raise Exception('Client sent a bad ETag! Got %r, but '
'md5(body) = %r' % (env['HTTP_ETAG'], etag))
headers.setdefault('Etag', etag)
headers.setdefault('Content-Length', len(input))

View File

@ -667,10 +667,12 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(self._get_error_code(body), 'NoSuchBucket')
def test_object_multipart_upload_complete(self):
content_md5 = base64.b64encode(hashlib.md5(xml).digest())
req = Request.blank('/bucket/object?uploadId=X',
environ={'REQUEST_METHOD': 'POST'},
headers={'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header(), },
'Date': self.get_date_header(),
'Content-MD5': content_md5, },
body=xml)
status, headers, body = self.call_s3api(req)
elem = fromstring(body, 'CompleteMultipartUploadResult')