CVE-2012-5482 (published 2012-11-11T13:00Z)

The v2 API in OpenStack Glance Grizzly, Folsom (2012.2), and Essex (2012.1) allows remote authenticated users to delete arbitrary non-protected images via an image deletion request.  NOTE: this vulnerability exists because of an incomplete fix for CVE-2012-4573.

Severity: MEDIUM
CVSS v2 base score: 5.5 (AV:N/AC:L/Au:S/C:N/I:P/A:P)

Weakness information:
cwe_id: CWE-264
cwe_name: Permissions, Privileges, and Access Controls
description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.

Fix information:
repo_url: https://github.com/openstack/glance
commit_details: repo_url: https://github.com/openstack/glance
author: Mark J. Washenberger
author_date: 2012-11-08 10:56:07-08:00
msg: Ensure authorization before deleting from store

This fixes bug 1076506.

Change-Id: I3794c14fe523a9a27e943d73dd0248489d2b91f6
repository: repo_url: https://github.com/openstack/glance
repo_name: openstack/glance
description: OpenStack Image Management (Glance). Mirror of code maintained at opendev.org.
repo_language: Python
owner: openstack
file_changes: filename: images.py
change_type: ModificationType.MODIFY
diff: @@ -260,19 +260,22 @@ class ImagesController(object):
                     % locals())
             raise webob.exc.HTTPForbidden(explanation=msg)
 
-        status = 'deleted'
-        if image['location']:
-            if CONF.delayed_delete:
-                status = 'pending_delete'
-                self.store_api.schedule_delayed_delete_from_backend(
-                                image['location'], id)
-            else:
-                self.store_api.safe_delete_from_backend(image['location'],
-                                                        req.context, id)
+        if image['location'] and CONF.delayed_delete:
+            status = 'pending_delete'
+        else:
+            status = 'deleted'
 
         try:
             self.db_api.image_update(req.context, image_id, {'status': status})
             self.db_api.image_destroy(req.context, image_id)
+
+            if image['location']:
+                if CONF.delayed_delete:
+                    self.store_api.schedule_delayed_delete_from_backend(
+                                    image['location'], id)
+                else:
+                    self.store_api.safe_delete_from_backend(image['location'],
+                                                            req.context, id)
         except (exception.NotFound, exception.Forbidden):
             msg = ("Failed to find image %(image_id)s to delete" % locals())
             LOG.info(msg)

programming_language: Python
filename: test_images.py
change_type: ModificationType.MODIFY
diff: @@ -218,6 +218,12 @@ class TestImages(functional.FunctionalTest):
         self.assertEqual(201, response.status_code)
         image_id = json.loads(response.text)['id']
 
+        # Upload some image data
+        path = self._url('/v2/images/%s/file' % image_id)
+        headers = self._headers({'Content-Type': 'application/octet-stream'})
+        response = requests.put(path, headers=headers, data='ZZZZZ')
+        self.assertEqual(201, response.status_code)
+
         # TENANT1 should see the image in their list
         path = self._url('/v2/images')
         response = requests.get(path, headers=self._headers())
@@ -300,6 +306,12 @@ class TestImages(functional.FunctionalTest):
         response = requests.delete(path, headers=headers)
         self.assertEqual(404, response.status_code)
 
+        # Image data should still be present after the failed delete
+        path = self._url('/v2/images/%s/file' % image_id)
+        response = requests.get(path, headers=self._headers())
+        self.assertEqual(200, response.status_code)
+        self.assertEqual(response.text, 'ZZZZZ')
+
         self.stop_servers()
 
     def test_tag_lifecycle(self):

programming_language: Python