CVE-2012-4457: Improper Authentication (CWE-287)
value: OpenStack Keystone Essex before 2012.1.2 and Folsom before folsom-3 does not properly handle authorization tokens for disabled tenants, which allows remote authenticated users to access the tenant's resources by requesting a token for the tenant.
CWE description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
Published: 2012-10-09T15:55Z
Repository: https://github.com/openstack/keystone
Language: Python
Files changed: keystone/service.py, tests/test_keystoneclient.py
Commit date: 2012-07-16 16:08:34 -0500
Commit message: Raise unauthorized if tenant disabled (bug 988920)

If the client attempts to explicitly authenticate against a disabled
tenant, keystone should return HTTP 401 Unauthorized.

Change-Id: I49fe56b6ef8d9f2fc6b9357472dae8964bb9cb9c

Diff: diff --git a/keystone/service.py b/keystone/service.py
index 913b8761..eec858ee 100644
--- a/keystone/service.py
+++ b/keystone/service.py
@@ -18,11 +18,10 @@ import uuid
 
 import routes
 
 from keystone import catalog
 from keystone.common import logging
-from keystone.common import utils
 from keystone.common import wsgi
 from keystone import exception
 from keystone import identity
 from keystone.openstack.common import timeutils
 from keystone import policy
@@ -282,10 +281,15 @@ class TokenController(wsgi.Application):
 
                 # If the user is disabled don't allow them to authenticate
                 if not user_ref.get('enabled', True):
                     LOG.warning('User %s is disabled' % user_id)
                     raise exception.Unauthorized()
+
+                # If the tenant is disabled don't allow them to authenticate
+                if tenant_ref and not tenant_ref.get('enabled', True):
+                    LOG.warning('Tenant %s is disabled' % tenant_id)
+                    raise exception.Unauthorized()
             except AssertionError as e:
                 raise exception.Unauthorized(e.message)
 
             token_ref = self.token_api.create_token(
                 context,
@@ -352,10 +356,18 @@ class TokenController(wsgi.Application):
                     metadata=metadata_ref)
             except exception.TenantNotFound:
                 tenant_ref = None
                 metadata_ref = {}
                 catalog_ref = {}
+            except exception.MetadataNotFound:
+                metadata_ref = {}
+                catalog_ref = {}
+
+            # If the tenant is disabled don't allow them to authenticate
+            if tenant_ref and not tenant_ref.get('enabled', True):
+                LOG.warning('Tenant %s is disabled' % tenant_id)
+                raise exception.Unauthorized()
 
             token_ref = self.token_api.create_token(
                 context, token_id, dict(id=token_id,
                                         user=user_ref,
                                         tenant=tenant_ref,
diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py
index 69d4ffc2..944ca790 100644
--- a/tests/test_keystoneclient.py
+++ b/tests/test_keystoneclient.py
@@ -178,10 +178,57 @@ class KeystoneClientTests(object):
         user_ref['name'] = None
         self.assertRaises(client_exceptions.AuthorizationFailure,
                           self.get_client,
                           user_ref)
 
+    def test_authenticate_disabled_tenant(self):
+        from keystoneclient import exceptions as client_exceptions
+
+        admin_client = self.get_client(admin=True)
+
+        tenant = {
+            'name': uuid.uuid4().hex,
+            'description': uuid.uuid4().hex,
+            'enabled': False,
+        }
+        tenant_ref = admin_client.tenants.create(
+            tenant_name=tenant['name'],
+            description=tenant['description'],
+            enabled=tenant['enabled'])
+        tenant['id'] = tenant_ref.id
+
+        user = {
+            'name': uuid.uuid4().hex,
+            'password': uuid.uuid4().hex,
+            'email': uuid.uuid4().hex,
+            'tenant_id': tenant['id'],
+        }
+        user_ref = admin_client.users.create(
+            name=user['name'],
+            password=user['password'],
+            email=user['email'],
+            tenant_id=user['tenant_id'])
+        user['id'] = user_ref.id
+
+        # password authentication
+        self.assertRaises(
+            client_exceptions.Unauthorized,
+            self._client,
+            username=user['name'],
+            password=user['password'],
+            tenant_id=tenant['id'])
+
+        # token authentication
+        client = self._client(
+            username=user['name'],
+            password=user['password'])
+        self.assertRaises(
+            client_exceptions.Unauthorized,
+            self._client,
+            token=client.auth_token,
+            tenant_id=tenant['id'])
+
     # FIXME(ja): this test should require the "keystone:admin" roled
     #            (probably the role set via --keystone_admin_role flag)
     # FIXME(ja): add a test that admin endpoint is only sent to admin user
     # FIXME(ja): add a test that admin endpoint returns unauthorized if not
     #            admin

Vulnerable code: from keystone.common import utils
Fixed code: 
                # If the tenant is disabled don't allow them to authenticate
                if tenant_ref and not tenant_ref.get('enabled', True):
                    LOG.warning('Tenant %s is disabled' % tenant_id)
                    raise exception.Unauthorized()
            except exception.MetadataNotFound:
                metadata_ref = {}
                catalog_ref = {}

            # If the tenant is disabled don't allow them to authenticate
            if tenant_ref and not tenant_ref.get('enabled', True):
                LOG.warning('Tenant %s is disabled' % tenant_id)
                raise exception.Unauthorized()
    def test_authenticate_disabled_tenant(self):
        from keystoneclient import exceptions as client_exceptions

        admin_client = self.get_client(admin=True)

        tenant = {
            'name': uuid.uuid4().hex,
            'description': uuid.uuid4().hex,
            'enabled': False,
        }
        tenant_ref = admin_client.tenants.create(
            tenant_name=tenant['name'],
            description=tenant['description'],
            enabled=tenant['enabled'])
        tenant['id'] = tenant_ref.id

        user = {
            'name': uuid.uuid4().hex,
            'password': uuid.uuid4().hex,
            'email': uuid.uuid4().hex,
            'tenant_id': tenant['id'],
        }
        user_ref = admin_client.users.create(
            name=user['name'],
            password=user['password'],
            email=user['email'],
            tenant_id=user['tenant_id'])
        user['id'] = user_ref.id

        # password authentication
        self.assertRaises(
            client_exceptions.Unauthorized,
            self._client,
            username=user['name'],
            password=user['password'],
            tenant_id=tenant['id'])

        # token authentication
        client = self._client(
            username=user['name'],
            password=user['password'])
        self.assertRaises(
            client_exceptions.Unauthorized,
            self._client,
            token=client.auth_token,
            tenant_id=tenant['id'])