Browse Source

Merge "s3api: Block ETag header from CompleteMultipartUpload requests" into stable/stein

changes/17/676917/1
Zuul 1 month ago
parent
commit
99c7795ca8

+ 10
- 1
swift/common/middleware/s3api/controllers/multi_upload.py View File

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

+ 3
- 0
test/unit/common/middleware/s3api/helpers.py View File

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

+ 3
- 1
test/unit/common/middleware/s3api/test_multi_upload.py View File

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

Loading…
Cancel
Save