VYPR
High severityNVD Advisory· Published Aug 25, 2014· Updated May 6, 2026

CVE-2014-5252

CVE-2014-5252

Description

The V3 API in OpenStack Identity (Keystone) 2014.1.x before 2014.1.2.1 and Juno before Juno-3 updates the issued_at value for UUID v2 tokens, which allows remote authenticated users to bypass the token expiration and retain access via a verification (1) GET or (2) HEAD request to v3/auth/tokens/.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
keystonePyPI
< 8.0.0a08.0.0a0

Affected products

5
  • cpe:2.3:a:openstack:keystone:2014.1:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:openstack:keystone:2014.1:*:*:*:*:*:*:*
    • cpe:2.3:a:openstack:keystone:2014.1.2:*:*:*:*:*:*:*
    • cpe:2.3:a:openstack:keystone:juno-1:*:*:*:*:*:*:*
    • cpe:2.3:a:openstack:keystone:juno-2:*:*:*:*:*:*:*
  • cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*

Patches

3
bdb88c662ac2

Fix for V2 token issued_at time changing

https://github.com/openstack/keystoneBrant KnudsonJul 25, 2014via ghsa
2 files changed · +14 11
  • keystone/tests/test_v3_auth.py+2 6 modified
    @@ -370,8 +370,7 @@ def test_v2_v3_token_intermix(self):
             v3_issued_at = timeutils.parse_isotime(
                 token_data['token']['issued_at'])
     
    -        # FIXME(blk-u): the following should be assertEqual, see bug 1348820
    -        self.assertNotEqual(v2_issued_at, v3_issued_at)
    +        self.assertEqual(v2_issued_at, v3_issued_at)
     
         def test_rescoping_token(self):
             expires = self.token_data['token']['expires_at']
    @@ -1225,9 +1224,6 @@ def get_v2_token(self):
         def test_revoke_v2_token_no_check(self):
             # Test that a V2 token can be revoked without validating it first.
     
    -        # NOTE(blk-u): This doesn't work right. The token should be invalid
    -        # after being revoked but it's not. See bug 1348820.
    -
             token = self.get_v2_token()
     
             self.delete('/auth/tokens',
    @@ -1236,7 +1232,7 @@ def test_revoke_v2_token_no_check(self):
     
             self.head('/auth/tokens',
                       headers={'X-Subject-Token': token},
    -                  expected_status=200)  # FIXME(blk-u): This should be 404
    +                  expected_status=404)
     
     
     @dependency.requires('revoke_api')
    
  • keystone/token/providers/common.py+12 5 modified
    @@ -315,18 +315,20 @@ def _populate_service_catalog(self, token_data, user_id,
                 # TODO(ayoung): Enforce Endpoints for trust
                 token_data['catalog'] = service_catalog
     
    -    def _populate_token_dates(self, token_data, expires=None, trust=None):
    +    def _populate_token_dates(self, token_data, expires=None, trust=None,
    +                              issued_at=None):
             if not expires:
                 expires = token.default_expire_time()
             if not isinstance(expires, six.string_types):
                 expires = timeutils.isotime(expires, subsecond=True)
             token_data['expires_at'] = expires
    -        token_data['issued_at'] = timeutils.isotime(subsecond=True)
    +        token_data['issued_at'] = (issued_at or
    +                                   timeutils.isotime(subsecond=True))
     
         def get_token_data(self, user_id, method_names, extras,
                            domain_id=None, project_id=None, expires=None,
                            trust=None, token=None, include_catalog=True,
    -                       bind=None, access_token=None):
    +                       bind=None, access_token=None, issued_at=None):
             token_data = {'methods': method_names,
                           'extras': extras}
     
    @@ -350,7 +352,8 @@ def get_token_data(self, user_id, method_names, extras,
             if include_catalog:
                 self._populate_service_catalog(token_data, user_id, domain_id,
                                                project_id, trust)
    -        self._populate_token_dates(token_data, expires=expires, trust=trust)
    +        self._populate_token_dates(token_data, expires=expires, trust=trust,
    +                                   issued_at=issued_at)
             self._populate_oauth_section(token_data, access_token)
             return {'token': token_data}
     
    @@ -648,13 +651,17 @@ def _validate_v3_token_ref(self, token_ref):
                 project_ref = token_ref.get('tenant')
                 if project_ref:
                     project_id = project_ref['id']
    +
    +            issued_at = token_ref['token_data']['access']['token']['issued_at']
    +
                 token_data = self.v3_token_data_helper.get_token_data(
                     token_ref['user']['id'],
                     ['password', 'token'],
                     {},
                     project_id=project_id,
                     bind=token_ref.get('bind'),
    -                expires=token_ref['expires'])
    +                expires=token_ref['expires'],
    +                issued_at=issued_at)
             return token_data
     
         def validate_token(self, token_id):
    
a4c73e4382cb

Fix for V2 token issued_at time changing

https://github.com/openstack/keystoneBrant KnudsonJul 25, 2014via ghsa
2 files changed · +14 11
  • keystone/tests/test_v3_auth.py+2 6 modified
    @@ -370,8 +370,7 @@ def test_v2_v3_token_intermix(self):
             v3_issued_at = timeutils.parse_isotime(
                 token_data['token']['issued_at'])
     
    -        # FIXME(blk-u): the following should be assertEqual, see bug 1348820
    -        self.assertNotEqual(v2_issued_at, v3_issued_at)
    +        self.assertEqual(v2_issued_at, v3_issued_at)
     
         def test_rescoping_token(self):
             expires = self.token_data['token']['expires_at']
    @@ -1248,9 +1247,6 @@ def get_v2_token(self):
         def test_revoke_v2_token_no_check(self):
             # Test that a V2 token can be revoked without validating it first.
     
    -        # NOTE(blk-u): This doesn't work right. The token should be invalid
    -        # after being revoked but it's not. See bug 1348820.
    -
             token = self.get_v2_token()
     
             self.delete('/auth/tokens',
    @@ -1259,7 +1255,7 @@ def test_revoke_v2_token_no_check(self):
     
             self.head('/auth/tokens',
                       headers={'X-Subject-Token': token},
    -                  expected_status=200)  # FIXME(blk-u): This should be 404
    +                  expected_status=404)
     
     
     @dependency.requires('revoke_api')
    
  • keystone/token/providers/common.py+12 5 modified
    @@ -310,18 +310,20 @@ def _populate_service_catalog(self, token_data, user_id,
                 # TODO(ayoung): Enforce Endpoints for trust
                 token_data['catalog'] = service_catalog
     
    -    def _populate_token_dates(self, token_data, expires=None, trust=None):
    +    def _populate_token_dates(self, token_data, expires=None, trust=None,
    +                              issued_at=None):
             if not expires:
                 expires = provider.default_expire_time()
             if not isinstance(expires, six.string_types):
                 expires = timeutils.isotime(expires, subsecond=True)
             token_data['expires_at'] = expires
    -        token_data['issued_at'] = timeutils.isotime(subsecond=True)
    +        token_data['issued_at'] = (issued_at or
    +                                   timeutils.isotime(subsecond=True))
     
         def get_token_data(self, user_id, method_names, extras,
                            domain_id=None, project_id=None, expires=None,
                            trust=None, token=None, include_catalog=True,
    -                       bind=None, access_token=None):
    +                       bind=None, access_token=None, issued_at=None):
             token_data = {'methods': method_names,
                           'extras': extras}
     
    @@ -345,7 +347,8 @@ def get_token_data(self, user_id, method_names, extras,
             if include_catalog:
                 self._populate_service_catalog(token_data, user_id, domain_id,
                                                project_id, trust)
    -        self._populate_token_dates(token_data, expires=expires, trust=trust)
    +        self._populate_token_dates(token_data, expires=expires, trust=trust,
    +                                   issued_at=issued_at)
             self._populate_oauth_section(token_data, access_token)
             return {'token': token_data}
     
    @@ -633,13 +636,17 @@ def _validate_v3_token_ref(self, token_ref):
                 project_ref = token_ref.get('tenant')
                 if project_ref:
                     project_id = project_ref['id']
    +
    +            issued_at = token_ref['token_data']['access']['token']['issued_at']
    +
                 token_data = self.v3_token_data_helper.get_token_data(
                     token_ref['user']['id'],
                     ['password', 'token'],
                     {},
                     project_id=project_id,
                     bind=token_ref.get('bind'),
    -                expires=token_ref['expires'])
    +                expires=token_ref['expires'],
    +                issued_at=issued_at)
             return token_data
     
         def validate_token(self, token_id):
    
556fb8603116

Add tests related to V2 token issued_at time changing

https://github.com/openstack/keystoneBrant KnudsonJul 25, 2014via ghsa
1 file changed · +37 12
  • keystone/tests/test_v3_auth.py+37 12 modified
    @@ -365,6 +365,14 @@ def test_v2_v3_token_intermix(self):
             self.assertEqual(v2_token_data['access']['user']['roles'][0]['name'],
                              token_data['token']['roles'][0]['name'])
     
    +        v2_issued_at = timeutils.parse_isotime(
    +            v2_token_data['access']['token']['issued_at'])
    +        v3_issued_at = timeutils.parse_isotime(
    +            token_data['token']['issued_at'])
    +
    +        # FIXME(blk-u): the following should be assertEqual, see bug 1348820
    +        self.assertNotEqual(v2_issued_at, v3_issued_at)
    +
         def test_rescoping_token(self):
             expires = self.token_data['token']['expires_at']
             auth_data = self.build_authentication_request(
    @@ -1224,6 +1232,35 @@ def test_deleting_project_deletes_grants(self):
             # Make sure that we get a NotFound(404) when heading that role.
             self.head(role_path, expected_status=404)
     
    +    def get_v2_token(self):
    +        body = {
    +            'auth': {
    +                'passwordCredentials': {
    +                    'username': self.default_domain_user['name'],
    +                    'password': self.default_domain_user['password'],
    +                }
    +            },
    +        }
    +
    +        r = self.admin_request(method='POST', path='/v2.0/tokens', body=body)
    +        return r.json_body['access']['token']['id']
    +
    +    def test_revoke_v2_token_no_check(self):
    +        # Test that a V2 token can be revoked without validating it first.
    +
    +        # NOTE(blk-u): This doesn't work right. The token should be invalid
    +        # after being revoked but it's not. See bug 1348820.
    +
    +        token = self.get_v2_token()
    +
    +        self.delete('/auth/tokens',
    +                    headers={'X-Subject-Token': token},
    +                    expected_status=204)
    +
    +        self.head('/auth/tokens',
    +                  headers={'X-Subject-Token': token},
    +                  expected_status=200)  # FIXME(blk-u): This should be 404
    +
     
     @dependency.requires('revoke_api')
     class TestTokenRevokeApi(TestTokenRevokeById):
    @@ -1286,18 +1323,6 @@ def test_revoke_token(self):
                                        expected_status=200).json_body
             self.assertValidRevokedTokenResponse(events_response, self.user['id'])
     
    -    def get_v2_token(self):
    -        body = {
    -            'auth': {
    -                'passwordCredentials': {
    -                    'username': self.default_domain_user['name'],
    -                    'password': self.default_domain_user['password'],
    -                },
    -            },
    -        }
    -        r = self.admin_request(method='POST', path='/v2.0/tokens', body=body)
    -        return r.json_body['access']['token']['id']
    -
         def test_revoke_v2_token(self):
             token = self.get_v2_token()
             headers = {'X-Subject-Token': token}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.