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

The v1 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, a different vulnerability than CVE-2012-5482.

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 Washenberger
author_date: 2012-11-07 09:59:56-05:00
msg: Delete from store after registry delete.

Because we rely on the registry to determine authorization in the glance
v1 api, we must attempt a registry delete before deleting an image from
the image store.

This patch includes the test for the bug, which was posted separately
on the bug.

Fixes bug 1065187.

Change-Id: I1a06b7c7421524066c684539e2f3516c4ed2c475
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: @@ -827,22 +827,28 @@ class Controller(controller.BaseController):
             raise HTTPForbidden(explanation=msg, request=req,
                                 content_type="text/plain")
 
-        status = 'deleted'
+        if image['location'] and CONF.delayed_delete:
+            status = 'pending_delete'
+        else:
+            status = 'deleted'
+
         try:
+            # Delete the image from the registry first, since we rely on it
+            # for authorization checks.
+            # See https://bugs.launchpad.net/glance/+bug/1065187
+            registry.update_image_metadata(req.context, id, {'status': status})
+            registry.delete_image_metadata(req.context, id)
+
             # The image's location field may be None in the case
             # of a saving or queued image, therefore don't ask a backend
             # to delete the image if the backend doesn't yet store it.
             # See https://bugs.launchpad.net/glance/+bug/747799
             if image['location']:
                 if CONF.delayed_delete:
-                    status = 'pending_delete'
                     schedule_delayed_delete_from_backend(image['location'], id)
                 else:
                     safe_delete_from_backend(image['location'],
                                              req.context, id)
-
-            registry.update_image_metadata(req.context, id, {'status': status})
-            registry.delete_image_metadata(req.context, id)
         except exception.NotFound, e:
             msg = ("Failed to find image to delete: %(e)s" % locals())
             for line in msg.split('\n'):

programming_language: Python
filename: stubs.py
change_type: ModificationType.MODIFY
diff: @@ -60,7 +60,13 @@ class FakeRegistryConnection(object):
 
     def getresponse(self):
         mapper = routes.Mapper()
-        api = context.UnauthenticatedContextMiddleware(rserver.API(mapper))
+        server = rserver.API(mapper)
+        # NOTE(markwash): we need to pass through context auth information if
+        # we have it.
+        if 'X-Auth-Token' in self.req.headers:
+            api = utils.FakeAuthMiddleware(server)
+        else:
+            api = context.UnauthenticatedContextMiddleware(server)
         webob_res = self.req.get_response(api)
 
         return utils.FakeHTTPResponse(status=webob_res.status_int,

programming_language: Python
filename: test_api.py
change_type: ModificationType.MODIFY
diff: @@ -3015,6 +3015,26 @@ class TestGlanceAPI(base.IsolatedUnitTest):
         res = req.get_response(self.api)
         self.assertEquals(res.status_int, webob.exc.HTTPNotFound.code)
 
+    def test_delete_not_allowed(self):
+        # Verify we can get the image data
+        req = webob.Request.blank("/images/%s" % UUID2)
+        req.method = 'GET'
+        req.headers['X-Auth-Token'] = 'user:tenant:'
+        res = req.get_response(self.api)
+        self.assertEqual(res.status_int, 200)
+        self.assertEqual(len(res.body), 19)
+
+        # Verify we cannot delete the image
+        req.method = 'DELETE'
+        res = req.get_response(self.api)
+        self.assertEqual(res.status_int, 403)
+
+        # Verify the image data is still there
+        req.method = 'GET'
+        res = req.get_response(self.api)
+        self.assertEqual(res.status_int, 200)
+        self.assertEqual(len(res.body), 19)
+
     def test_delete_queued_image(self):
         """Delete an image in a queued state
 

programming_language: Python
filename: utils.py
change_type: ModificationType.MODIFY
diff: @@ -372,6 +372,7 @@ class FakeAuthMiddleware(wsgi.Middleware):
             'tenant': tenant,
             'roles': roles,
             'is_admin': self.is_admin,
+            'auth_tok': auth_tok,
         }
 
         req.context = context.RequestContext(**kwargs)

programming_language: Python