[openstack-glance/f21] Glance store disk space exhaustion CVE-2014-5356

Flavio Percoco flaper87 at fedoraproject.org
Tue Sep 2 08:21:50 UTC 2014


commit e6cab7dd10a8e4f08d1485a71d6905b26a753f33
Author: Flavio Percoco <flaper87 at gmail.com>
Date:   Tue Sep 2 09:28:10 2014 +0200

    Glance store disk space exhaustion CVE-2014-5356
    
    This also updates glance to the latest stable upstream.

 .gitignore                                         |    1 +
 ... => 0001-Remove-runtime-dep-on-python-pbr.patch |    4 +-
 ...-Don-t-access-the-net-while-building-docs.patch |    2 +-
 0003-avoid-unsupported-storage-drivers.patch       |    2 +-
 ...ify-calling-process-we-are-ready-to-serve.patch |    2 +-
 0005-Enforce-image_size_cap-on-v2-upload.patch     |  180 ++++++++++++++++++++
 openstack-glance.spec                              |   16 ++-
 sources                                            |    2 +-
 8 files changed, 198 insertions(+), 11 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index eadd2aa..06b11b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@
 /glance-2014.1.rc2.tar.gz
 /glance-2014.1.tar.gz
 /glance-2014.1.1.tar.gz
+/glance-2014.1.2.tar.gz
diff --git a/0002-Remove-runtime-dep-on-python-pbr.patch b/0001-Remove-runtime-dep-on-python-pbr.patch
similarity index 91%
rename from 0002-Remove-runtime-dep-on-python-pbr.patch
rename to 0001-Remove-runtime-dep-on-python-pbr.patch
index 1fe514b..d4eae1e 100644
--- a/0002-Remove-runtime-dep-on-python-pbr.patch
+++ b/0001-Remove-runtime-dep-on-python-pbr.patch
@@ -1,6 +1,6 @@
-From 8c9d56ad93ae19905a8214b590edb7cddc896a66 Mon Sep 17 00:00:00 2001
+From d86b20d8e9977521de3ec79e05d22702d0aee2c3 Mon Sep 17 00:00:00 2001
 From: John Bresnahan <jbresnah at redhat.com>
-Date: Mon, 9 Sep 2013 17:00:28 -1000
+Date: Wed, 18 Sep 2013 16:43:45 -1000
 Subject: [PATCH] Remove runtime dep on python pbr
 
 ---
diff --git a/0001-Don-t-access-the-net-while-building-docs.patch b/0002-Don-t-access-the-net-while-building-docs.patch
similarity index 93%
rename from 0001-Don-t-access-the-net-while-building-docs.patch
rename to 0002-Don-t-access-the-net-while-building-docs.patch
index 1b03e5a..d817020 100644
--- a/0001-Don-t-access-the-net-while-building-docs.patch
+++ b/0002-Don-t-access-the-net-while-building-docs.patch
@@ -1,4 +1,4 @@
-From 6488de036b95663d4cfe808cfd979a4121fd9782 Mon Sep 17 00:00:00 2001
+From 4c7dc9f86feee605d109a32c65e9f2d5d21ca87f Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <pbrady at redhat.com>
 Date: Fri, 6 Jan 2012 17:12:54 +0000
 Subject: [PATCH] Don't access the net while building docs
diff --git a/0003-avoid-unsupported-storage-drivers.patch b/0003-avoid-unsupported-storage-drivers.patch
index 36d4d60..60b2a21 100644
--- a/0003-avoid-unsupported-storage-drivers.patch
+++ b/0003-avoid-unsupported-storage-drivers.patch
@@ -1,4 +1,4 @@
-From ad3a631632f7dce41ffac61b754a6b401514c573 Mon Sep 17 00:00:00 2001
+From 631ca8d06146666eec5c07d7f46e91a564a2cbee Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P at draigBrady.com>
 Date: Wed, 9 Apr 2014 10:31:27 +0100
 Subject: [PATCH] avoid unsupported storage drivers
diff --git a/0004-notify-calling-process-we-are-ready-to-serve.patch b/0004-notify-calling-process-we-are-ready-to-serve.patch
index 4a3cad4..91782ff 100644
--- a/0004-notify-calling-process-we-are-ready-to-serve.patch
+++ b/0004-notify-calling-process-we-are-ready-to-serve.patch
@@ -1,4 +1,4 @@
-From d1635beebfacd2f792b19550dff0aa4cb69c87ac Mon Sep 17 00:00:00 2001
+From 47a01a9112a12164fc040019a402e0df42eb34d2 Mon Sep 17 00:00:00 2001
 From: Alan Pevec <apevec at redhat.com>
 Date: Tue, 11 Feb 2014 22:36:00 +0100
 Subject: [PATCH] notify calling process we are ready to serve
diff --git a/0005-Enforce-image_size_cap-on-v2-upload.patch b/0005-Enforce-image_size_cap-on-v2-upload.patch
new file mode 100644
index 0000000..c0bbed1
--- /dev/null
+++ b/0005-Enforce-image_size_cap-on-v2-upload.patch
@@ -0,0 +1,180 @@
+From ffb4983840d60a9d85d251dbf7d50008cd006f74 Mon Sep 17 00:00:00 2001
+From: Tom Leaman <thomas.leaman at hp.com>
+Date: Fri, 2 May 2014 10:09:20 +0000
+Subject: [PATCH] Enforce image_size_cap on v2 upload
+
+image_size_cap should be checked and enforced on upload
+
+Enforcement is in two places:
+- on image metadata save
+- during image save to backend store
+
+(cherry picked from commit 92ab00fca6926eaf3f7f92a955a5e07140063718)
+Conflicts:
+	glance/location.py
+	glance/tests/functional/v2/test_images.py
+
+Closes-Bug: 1315321
+Change-Id: I45bfb360703617bc394e9e27fe17adf43b09c0e1
+Co-Author: Manuel Desbonnet <manuel.desbonnet at hp.com>
+---
+ glance/db/__init__.py                     |  5 ++++
+ glance/store/__init__.py                  |  5 +++-
+ glance/tests/functional/__init__.py       |  2 ++
+ glance/tests/functional/v2/test_images.py | 42 +++++++++++++++++++++++++++++++
+ glance/tests/unit/test_store_image.py     |  6 +++--
+ glance/tests/unit/utils.py                |  5 +++-
+ 6 files changed, 61 insertions(+), 4 deletions(-)
+
+diff --git a/glance/db/__init__.py b/glance/db/__init__.py
+index a6e804c..a59447d 100644
+--- a/glance/db/__init__.py
++++ b/glance/db/__init__.py
+@@ -27,6 +27,7 @@ from glance.openstack.common import importutils
+ 
+ 
+ CONF = cfg.CONF
++CONF.import_opt('image_size_cap', 'glance.common.config')
+ CONF.import_opt('metadata_encryption_key', 'glance.common.config')
+ 
+ 
+@@ -150,6 +151,8 @@ class ImageRepo(object):
+ 
+     def add(self, image):
+         image_values = self._format_image_to_db(image)
++        if image_values['size'] > CONF.image_size_cap:
++            raise exception.ImageSizeLimitExceeded
+         # the updated_at value is not set in the _format_image_to_db
+         # function since it is specific to image create
+         image_values['updated_at'] = image.updated_at
+@@ -161,6 +164,8 @@ class ImageRepo(object):
+ 
+     def save(self, image):
+         image_values = self._format_image_to_db(image)
++        if image_values['size'] > CONF.image_size_cap:
++            raise exception.ImageSizeLimitExceeded
+         try:
+             new_values = self.db_api.image_update(self.context,
+                                                   image.image_id,
+diff --git a/glance/store/__init__.py b/glance/store/__init__.py
+index 7af3813..c73d3da 100644
+--- a/glance/store/__init__.py
++++ b/glance/store/__init__.py
+@@ -719,7 +719,10 @@ class ImageProxy(glance.domain.proxy.Image):
+             size = 0  # NOTE(markwash): zero -> unknown size
+         location, size, checksum, loc_meta = self.store_api.add_to_backend(
+             self.context, CONF.default_store,
+-            self.image.image_id, utils.CooperativeReader(data), size)
++            self.image.image_id,
++            utils.LimitingReader(utils.CooperativeReader(data),
++                                 CONF.image_size_cap),
++            size)
+         self.image.locations = [{'url': location, 'metadata': loc_meta}]
+         self.image.size = size
+         self.image.checksum = checksum
+diff --git a/glance/tests/functional/__init__.py b/glance/tests/functional/__init__.py
+index 537a42f..2f116f0 100644
+--- a/glance/tests/functional/__init__.py
++++ b/glance/tests/functional/__init__.py
+@@ -280,6 +280,7 @@ class ApiServer(Server):
+         self.pid_file = pid_file or os.path.join(self.test_dir, "api.pid")
+         self.scrubber_datadir = os.path.join(self.test_dir, "scrubber")
+         self.log_file = os.path.join(self.test_dir, "api.log")
++        self.image_size_cap = 1099511627776
+         self.s3_store_host = "s3.amazonaws.com"
+         self.s3_store_access_key = ""
+         self.s3_store_secret_key = ""
+@@ -341,6 +342,7 @@ metadata_encryption_key = %(metadata_encryption_key)s
+ registry_host = 127.0.0.1
+ registry_port = %(registry_port)s
+ log_file = %(log_file)s
++image_size_cap = %(image_size_cap)d
+ s3_store_host = %(s3_store_host)s
+ s3_store_access_key = %(s3_store_access_key)s
+ s3_store_secret_key = %(s3_store_secret_key)s
+diff --git a/glance/tests/functional/v2/test_images.py b/glance/tests/functional/v2/test_images.py
+index a309e64..4247434 100644
+--- a/glance/tests/functional/v2/test_images.py
++++ b/glance/tests/functional/v2/test_images.py
+@@ -451,6 +451,48 @@ class TestImages(functional.FunctionalTest):
+ 
+         self.stop_servers()
+ 
++    def test_image_size_cap(self):
++        self.api_server.image_size_cap = 128
++        self.start_servers(**self.__dict__.copy())
++        # create an image
++        path = self._url('/v2/images')
++        headers = self._headers({'content-type': 'application/json'})
++        data = jsonutils.dumps({'name': 'image-size-cap-test-image',
++                                'type': 'kernel', 'disk_format': 'aki',
++                                'container_format': 'aki'})
++        response = requests.post(path, headers=headers, data=data)
++        self.assertEqual(201, response.status_code)
++
++        image = jsonutils.loads(response.text)
++        image_id = image['id']
++
++        #try to populate it with oversized data
++        path = self._url('/v2/images/%s/file' % image_id)
++        headers = self._headers({'Content-Type': 'application/octet-stream'})
++
++        class StreamSim(object):
++            # Using a one-shot iterator to force chunked transfer in the PUT
++            # request
++            def __init__(self, size):
++                self.size = size
++
++            def __iter__(self):
++                yield 'Z' * self.size
++
++        response = requests.put(path, headers=headers, data=StreamSim(
++                                self.api_server.image_size_cap + 1))
++        self.assertEqual(413, response.status_code)
++
++        # hashlib.md5('Z'*129).hexdigest()
++        #     == '76522d28cb4418f12704dfa7acd6e7ee'
++        # If the image has this checksum, it means that the whole stream was
++        # accepted and written to the store, which should not be the case.
++        path = self._url('/v2/images/{0}'.format(image_id))
++        headers = self._headers({'content-type': 'application/json'})
++        response = requests.get(path, headers=headers)
++        image_checksum = jsonutils.loads(response.text).get('checksum')
++        self.assertNotEqual(image_checksum, '76522d28cb4418f12704dfa7acd6e7ee')
++
+     def test_permissions(self):
+         # Create an image that belongs to TENANT1
+         path = self._url('/v2/images')
+diff --git a/glance/tests/unit/test_store_image.py b/glance/tests/unit/test_store_image.py
+index eb8d333..424915b 100644
+--- a/glance/tests/unit/test_store_image.py
++++ b/glance/tests/unit/test_store_image.py
+@@ -119,8 +119,10 @@ class TestStoreImage(utils.BaseTestCase):
+ 
+         self.stubs.Set(unit_test_utils.FakeStoreAPI, 'get_from_backend',
+                        fake_get_from_backend)
+-
+-        self.assertEqual(image1.get_data().fd, 'ZZZ')
++        # This time, image1.get_data() returns the data wrapped in a
++        # LimitingReader|CooperativeReader pipeline, so peeking under
++        # the hood of those objects to get at the underlying string.
++        self.assertEqual(image1.get_data().data.fd, 'ZZZ')
+         image1.locations.pop(0)
+         self.assertEqual(len(image1.locations), 1)
+         image2.delete()
+diff --git a/glance/tests/unit/utils.py b/glance/tests/unit/utils.py
+index a43dea3..4186787 100644
+--- a/glance/tests/unit/utils.py
++++ b/glance/tests/unit/utils.py
+@@ -148,7 +148,10 @@ class FakeStoreAPI(object):
+             if image_id in location:
+                 raise exception.Duplicate()
+         if not size:
+-            size = len(data.fd)
++            # 'data' is a string wrapped in a LimitingReader|CooperativeReader
++            # pipeline, so peek under the hood of those objects to get at the
++            # string itself.
++            size = len(data.data.fd)
+         if (current_store_size + size) > store_max_size:
+             raise exception.StorageFull()
+         if context.user == USER2:
diff --git a/openstack-glance.spec b/openstack-glance.spec
index 04737fe..cb1d411 100644
--- a/openstack-glance.spec
+++ b/openstack-glance.spec
@@ -1,6 +1,6 @@
 Name:             openstack-glance
-Version:          2014.1.1
-Release:          4%{?dist}
+Version:          2014.1.2
+Release:          1%{?dist}
 Summary:          OpenStack Image Service
 
 Group:            Applications/System
@@ -18,12 +18,13 @@ Source7:          glance-cache-dist.conf
 Source8:          glance-scrubber-dist.conf
 
 #
-# patches_base=2014.1.1
+# patches_base=2014.1.2
 #
-Patch0001: 0001-Don-t-access-the-net-while-building-docs.patch
-Patch0002: 0002-Remove-runtime-dep-on-python-pbr.patch
+Patch0001: 0001-Remove-runtime-dep-on-python-pbr.patch
+Patch0002: 0002-Don-t-access-the-net-while-building-docs.patch
 Patch0003: 0003-avoid-unsupported-storage-drivers.patch
 Patch0004: 0004-notify-calling-process-we-are-ready-to-serve.patch
+Patch0005: 0005-Enforce-image_size_cap-on-v2-upload.patch
 
 BuildArch:        noarch
 BuildRequires:    python2-devel
@@ -115,6 +116,7 @@ This package contains documentation files for glance.
 %patch0002 -p1
 %patch0003 -p1
 %patch0004 -p1
+%patch0005 -p1
 
 # Remove bundled egg-info
 rm -rf glance.egg-info
@@ -316,6 +318,10 @@ fi
 %doc doc/build/html
 
 %changelog
+* Tue Sep 02 2014 Flavio Percoco <flavio at redhat.com> - 2014.1.2-1
+- Update to latest Icehouse release
+- Glance store disk space exhaustion CVE-2014-5356
+
 * Mon Jun 23 2014 Jon Bernard <jobernar at redhat.com> - 2014.1.1-4
 - Update to latest Icehouse release
 - Include patch to improve systemd integration
diff --git a/sources b/sources
index 19e9a65..eb231a3 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-43ce26d964589d851f2a21c8d070fca9  glance-2014.1.1.tar.gz
+39d5f7e6931923faceb99a49fc768dd6  glance-2014.1.2.tar.gz


More information about the scm-commits mailing list