Package: release.debian.org Severity: normal Tags: jessie User: [email protected] Usertags: pu
Dear release team, I've prepared an update for Glance CVE-2015-5251. The debdiff is attached. The resulting binaries may be found here: http://sid.gplhost.com/jessie-proposed-updates/glance/ Please authorize me to upload this to jessie-proposed-updates. Cheers, Thomas Goirand (zigo)
diff -Nru glance-2014.1.3/debian/changelog glance-2014.1.3/debian/changelog --- glance-2014.1.3/debian/changelog 2015-01-29 15:22:59.000000000 +0000 +++ glance-2014.1.3/debian/changelog 2015-11-13 13:30:43.000000000 +0000 @@ -1,3 +1,10 @@ +glance (2014.1.3-12+deb8u1) jessie-proposed-updates; urgency=medium + + * CVE-2015-5251: Glance image status manipulation. Applied upstream patch + after rebasing it from Juno to Icehouse (Closes: #799931). + + -- Thomas Goirand <[email protected]> Fri, 13 Nov 2015 14:22:12 +0100 + glance (2014.1.3-12) unstable; urgency=high * CVE-2014-9623: Glance user storage quota bypass. Applied upstream patch: diff -Nru glance-2014.1.3/debian/patches/CVE-2015-5251_Prevent_image_status_being_directly_modified_via_v1.patch glance-2014.1.3/debian/patches/CVE-2015-5251_Prevent_image_status_being_directly_modified_via_v1.patch --- glance-2014.1.3/debian/patches/CVE-2015-5251_Prevent_image_status_being_directly_modified_via_v1.patch 1970-01-01 00:00:00.000000000 +0000 +++ glance-2014.1.3/debian/patches/CVE-2015-5251_Prevent_image_status_being_directly_modified_via_v1.patch 2015-11-13 13:30:43.000000000 +0000 @@ -0,0 +1,171 @@ +Description: Prevent image status being directly modified via v1 + Users shouldn't be able to change an image's status directly via the v1 API. + . + Some existing consumers of Glance set the x-image-meta-status header in + requests to the Glance API, eg: + . + https://github.com/openstack/nova/blob/master/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance#L184 + . + We should try to prevent users setting 'status' via v1, but without breaking + existing benign API calls such as these. + . + I've adopted the following approach (which has some prior art in 'protected properties'). + . + If a PUT request is received which contains an x-image-meta-status header: + . + * The user provided status is ignored if it matches the current image + status (this prevents benign calls such as the nova one above from + breaking). The usual code (eg 200) will be returned. + . + * If the user provided status doesn't match the current image status (ie + there is a real attempt to change the value) 403 will be returned. This + will break any calls which currently intentionally change the status. +Author: Stuart McLaren <[email protected]> +Date: Tue, 11 Aug 2015 10:37:09 +0000 (+0000) +X-Git-Url: https://review.openstack.org/gitweb?p=openstack%2Fglance.git;a=commitdiff_plain;h=45be8e1c620c50f3cbca76f561945200a8843bc8 +Bug-Ubuntu: https://bugs.launchpad.net/glance/+bug/1482371 +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=799931 +Change-Id: I44fadf32abb57c962b67467091c3f51c1ccc25e6 +Origin: upstream, https://review.openstack.org/#/c/226338/ +Last-Update: 2015-10-13 + +--- glance-2014.1.3.orig/glance/api/v1/__init__.py ++++ glance-2014.1.3/glance/api/v1/__init__.py +@@ -21,3 +21,6 @@ SUPPORTED_PARAMS = ('limit', 'marker', ' + + # Metadata which only an admin can change once the image is active + ACTIVE_IMMUTABLE = ('size', 'checksum') ++ ++# Metadata which cannot be changed (irrespective of the current image state) ++IMMUTABLE = ('status',) +--- glance-2014.1.3.orig/glance/api/v1/images.py ++++ glance-2014.1.3/glance/api/v1/images.py +@@ -53,6 +53,7 @@ LOG = logging.getLogger(__name__) + SUPPORTED_PARAMS = glance.api.v1.SUPPORTED_PARAMS + SUPPORTED_FILTERS = glance.api.v1.SUPPORTED_FILTERS + ACTIVE_IMMUTABLE = glance.api.v1.ACTIVE_IMMUTABLE ++IMMUTABLE = glance.api.v1.IMMUTABLE + + CONF = cfg.CONF + CONF.import_opt('disk_formats', 'glance.common.config', group='image_format') +@@ -843,6 +844,14 @@ class Controller(controller.BaseControll + request=req, + content_type="text/plain") + ++ for key in IMMUTABLE: ++ if (image_meta.get(key) is not None and ++ image_meta.get(key) != orig_image_meta.get(key)): ++ msg = _("Forbidden to modify '%s' of image.") % key ++ raise HTTPForbidden(explanation=msg, ++ request=req, ++ content_type="text/plain") ++ + # The default behaviour for a PUT /images/<IMAGE_ID> is to + # override any properties that were previously set. This, however, + # leads to a number of issues for the common use case where a caller +--- glance-2014.1.3.orig/glance/tests/functional/v1/test_api.py ++++ glance-2014.1.3/glance/tests/functional/v1/test_api.py +@@ -550,3 +550,92 @@ class TestApi(functional.FunctionalTest) + self.assertEqual(len(images), 0) + + self.stop_servers() ++ ++ def test_status_cannot_be_manipulated_directly(self): ++ self.cleanup() ++ self.start_servers(**self.__dict__.copy()) ++ headers = minimal_headers('Image1') ++ ++ # Create a 'queued' image ++ http = httplib2.Http() ++ headers = {'Content-Type': 'application/octet-stream', ++ 'X-Image-Meta-Disk-Format': 'raw', ++ 'X-Image-Meta-Container-Format': 'bare'} ++ path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port) ++ response, content = http.request(path, 'POST', headers=headers, ++ body=None) ++ self.assertEqual(201, response.status) ++ image = jsonutils.loads(content)['image'] ++ self.assertEqual('queued', image['status']) ++ ++ # Ensure status of 'queued' image can't be changed ++ path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port, ++ image['id']) ++ http = httplib2.Http() ++ headers = {'X-Image-Meta-Status': 'active'} ++ response, content = http.request(path, 'PUT', headers=headers) ++ self.assertEqual(403, response.status) ++ response, content = http.request(path, 'HEAD') ++ self.assertEqual(200, response.status) ++ self.assertEqual('queued', response['x-image-meta-status']) ++ ++ # We allow 'setting' to the same status ++ http = httplib2.Http() ++ headers = {'X-Image-Meta-Status': 'queued'} ++ response, content = http.request(path, 'PUT', headers=headers) ++ self.assertEqual(200, response.status) ++ response, content = http.request(path, 'HEAD') ++ self.assertEqual(200, response.status) ++ self.assertEqual('queued', response['x-image-meta-status']) ++ ++ # Make image active ++ http = httplib2.Http() ++ headers = {'Content-Type': 'application/octet-stream'} ++ response, content = http.request(path, 'PUT', headers=headers, ++ body='data') ++ self.assertEqual(200, response.status) ++ image = jsonutils.loads(content)['image'] ++ self.assertEqual('active', image['status']) ++ ++ # Ensure status of 'active' image can't be changed ++ http = httplib2.Http() ++ headers = {'X-Image-Meta-Status': 'queued'} ++ response, content = http.request(path, 'PUT', headers=headers) ++ self.assertEqual(403, response.status) ++ response, content = http.request(path, 'HEAD') ++ self.assertEqual(200, response.status) ++ self.assertEqual('active', response['x-image-meta-status']) ++ ++ # We allow 'setting' to the same status ++ http = httplib2.Http() ++ headers = {'X-Image-Meta-Status': 'active'} ++ response, content = http.request(path, 'PUT', headers=headers) ++ self.assertEqual(200, response.status) ++ response, content = http.request(path, 'HEAD') ++ self.assertEqual(200, response.status) ++ self.assertEqual('active', response['x-image-meta-status']) ++ ++ # Create a 'queued' image, ensure 'status' header is ignored ++ http = httplib2.Http() ++ path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port) ++ headers = {'Content-Type': 'application/octet-stream', ++ 'X-Image-Meta-Status': 'active'} ++ response, content = http.request(path, 'POST', headers=headers, ++ body=None) ++ self.assertEqual(201, response.status) ++ image = jsonutils.loads(content)['image'] ++ self.assertEqual('queued', image['status']) ++ ++ # Create an 'active' image, ensure 'status' header is ignored ++ http = httplib2.Http() ++ path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port) ++ headers = {'Content-Type': 'application/octet-stream', ++ 'X-Image-Meta-Disk-Format': 'raw', ++ 'X-Image-Meta-Status': 'queued', ++ 'X-Image-Meta-Container-Format': 'bare'} ++ response, content = http.request(path, 'POST', headers=headers, ++ body='data') ++ self.assertEqual(201, response.status) ++ image = jsonutils.loads(content)['image'] ++ self.assertEqual('active', image['status']) ++ self.stop_servers() +--- glance-2014.1.3.orig/glance/tests/integration/legacy_functional/test_v1_api.py ++++ glance-2014.1.3/glance/tests/integration/legacy_functional/test_v1_api.py +@@ -357,6 +357,8 @@ class TestApi(base.ApiTest): + path = "/v1/images" + response, content = self.http.request(path, 'POST', headers=headers) + self.assertEqual(response.status, 201) ++ image = jsonutils.loads(content)['image'] ++ self.assertEqual('active', image['status']) + + # 2. HEAD image-location + # Verify image size is zero and the status is active diff -Nru glance-2014.1.3/debian/patches/series glance-2014.1.3/debian/patches/series --- glance-2014.1.3/debian/patches/series 2015-01-29 15:22:59.000000000 +0000 +++ glance-2014.1.3/debian/patches/series 2015-11-13 13:30:43.000000000 +0000 @@ -4,3 +4,4 @@ restrict_client_download_and_delete_files_in_glance-api.patch CVE-2015-1195_Prevent_file_swift_config_and_filesystem_schemes.patch CVE-2014-9623_Cleanup_chunks_for_deleted_image_that_was_saving_icehouse.patch +CVE-2015-5251_Prevent_image_status_being_directly_modified_via_v1.patch

