From e6194113a3c81563590eabf8f761ccb988bb917c Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Fri, 8 Jan 2016 16:38:31 -0800 Subject: [PATCH] Validate X-Timestamps Previously, attempting to PUT a new object with an X-Timestamp header less than or equal to zero (ie, for a timestamp on or before 1970-01-01 00:00:00) would cause the object-server to 500. While we're at it, cap X-Timestamp at 9999999999 (2286-11-20 17:46:40) so we don't get an eleventh digit before the decimal point. Closes-Bug: 1532471 Change-Id: I23666ec8a067d829eaf9bfe54bd086c320b3429e --- swift/common/utils.py | 4 ++++ swift/obj/server.py | 2 +- test/unit/obj/test_server.py | 39 ++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/swift/common/utils.py b/swift/common/utils.py index 831f651ff1..e36122a098 100644 --- a/swift/common/utils.py +++ b/swift/common/utils.py @@ -778,6 +778,10 @@ class Timestamp(object): raise ValueError( 'delta must be greater than %d' % (-1 * self.raw)) self.timestamp = float(self.raw * PRECISION) + if self.timestamp < 0: + raise ValueError('timestamp cannot be negative') + if self.timestamp >= 10000000000: + raise ValueError('timestamp too large') def __repr__(self): return INTERNAL_FORMAT % (self.timestamp, self.offset) diff --git a/swift/obj/server.py b/swift/obj/server.py index 2944bccca7..06fb1f564f 100644 --- a/swift/obj/server.py +++ b/swift/obj/server.py @@ -558,7 +558,7 @@ class ObjectController(BaseStorageServer): return HTTPInsufficientStorage(drive=device, request=request) except (DiskFileNotExist, DiskFileQuarantined): orig_metadata = {} - orig_timestamp = 0 + orig_timestamp = Timestamp(0) # Checks for If-None-Match if request.if_none_match is not None and orig_metadata: diff --git a/test/unit/obj/test_server.py b/test/unit/obj/test_server.py index ef32f29b06..adc4941fd7 100755 --- a/test/unit/obj/test_server.py +++ b/test/unit/obj/test_server.py @@ -762,6 +762,45 @@ class TestObjectController(unittest.TestCase): self.assertEqual(resp.status_int, 409) self.assertEqual(resp.headers['X-Backend-Timestamp'], orig_timestamp) + def test_PUT_new_object_really_old_timestamp(self): + req = Request.blank( + '/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Timestamp': '-1', # 1969-12-31 23:59:59 + 'Content-Length': '6', + 'Content-Type': 'application/octet-stream'}) + req.body = 'VERIFY' + resp = req.get_response(self.object_controller) + self.assertEqual(resp.status_int, 400) + + req = Request.blank( + '/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Timestamp': '1', # 1970-01-01 00:00:01 + 'Content-Length': '6', + 'Content-Type': 'application/octet-stream'}) + req.body = 'VERIFY' + resp = req.get_response(self.object_controller) + self.assertEqual(resp.status_int, 201) + + def test_PUT_object_really_new_timestamp(self): + req = Request.blank( + '/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Timestamp': '9999999999', # 2286-11-20 17:46:40 + 'Content-Length': '6', + 'Content-Type': 'application/octet-stream'}) + req.body = 'VERIFY' + resp = req.get_response(self.object_controller) + self.assertEqual(resp.status_int, 201) + + # roll over to 11 digits before the decimal + req = Request.blank( + '/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Timestamp': '10000000000', + 'Content-Length': '6', + 'Content-Type': 'application/octet-stream'}) + req.body = 'VERIFY' + resp = req.get_response(self.object_controller) + self.assertEqual(resp.status_int, 400) + def test_PUT_no_etag(self): req = Request.blank( '/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},