diff -r 0ca3f3d6c919 -r 5bd484384122 components/openstack/keystone/patches/06-CVE-2014-3476.patch --- a/components/openstack/keystone/patches/06-CVE-2014-3476.patch Fri Mar 20 03:13:26 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,341 +0,0 @@ -Upstream patch for CVE-2014-3476. This fix will be included in the -Juno-2 development milestone and in future 2013.2.4 and 2014.1.2 releases - -From 9162837329665b4316afc2270a602dc8ae11f6d2 Mon Sep 17 00:00:00 2001 -From: Adam Young -Date: Thu, 29 May 2014 13:56:17 -0400 -Subject: [PATCH] Block delegation escalation of privilege - -Forbids doing the following with either a trust - or oauth based token: - creating a trust - approving a request_token - listing request tokens - -Change-Id: I1528f9dd003f5e03cbc50b78e1b32dbbf85ffcc2 -Closes-Bug: 1324592 ---- - keystone/common/controller.py | 36 +++++++++++- - keystone/contrib/oauth1/controllers.py | 12 ++++ - keystone/tests/test_v3_auth.py | 62 ++++++++++++++++++++ - keystone/tests/test_v3_oauth1.py | 98 ++++++++++++++++++++++++++++++++ - keystone/trust/controllers.py | 9 +++ - 5 files changed, 216 insertions(+), 1 deletion(-) - -diff --git a/keystone/common/controller.py b/keystone/common/controller.py -index faadc09..d6c33df 100644 ---- a/keystone/common/controller.py -+++ b/keystone/common/controller.py -@@ -44,7 +44,7 @@ def _build_policy_check_credentials(self, action, context, kwargs): - # it would otherwise need to reload the token_ref from backing store. - wsgi.validate_token_bind(context, token_ref) - -- creds = {} -+ creds = {'is_delegated_auth': False} - if 'token_data' in token_ref and 'token' in token_ref['token_data']: - #V3 Tokens - token_data = token_ref['token_data']['token'] -@@ -66,9 +66,32 @@ def _build_policy_check_credentials(self, action, context, kwargs): - creds['roles'] = [] - for role in token_data['roles']: - creds['roles'].append(role['name']) -+ -+ trust = token_data.get('OS-TRUST:trust') -+ if trust is None: -+ creds['trust_id'] = None -+ creds['trustor_id'] = None -+ creds['trustee_id'] = None -+ else: -+ creds['trust_id'] = trust['id'] -+ creds['trustor_id'] = trust['trustor_user']['id'] -+ creds['trustee_id'] = trust['trustee_user']['id'] -+ creds['is_delegated_auth'] = True -+ -+ oauth1 = token_data.get('OS-OAUTH1') -+ if oauth1 is None: -+ creds['consumer_id'] = None -+ creds['access_token_id'] = None -+ else: -+ creds['consumer_id'] = oauth1['consumer_id'] -+ creds['access_token_id'] = oauth1['access_token_id'] -+ creds['is_delegated_auth'] = True -+ - else: - #v2 Tokens - creds = token_ref.get('metadata', {}).copy() -+ creds['is_delegated_auth'] = False -+ - try: - creds['user_id'] = token_ref['user'].get('id') - except AttributeError: -@@ -81,6 +104,16 @@ def _build_policy_check_credentials(self, action, context, kwargs): - # NOTE(vish): this is pretty inefficient - creds['roles'] = [self.identity_api.get_role(role)['name'] - for role in creds.get('roles', [])] -+ trust = token_ref.get('trust') -+ if trust is None: -+ creds['trust_id'] = None -+ creds['trustor_id'] = None -+ creds['trustee_id'] = None -+ else: -+ creds['trust_id'] = trust.get('id') -+ creds['trustor_id'] = trust.get('trustor_id') -+ creds['trustee_id'] = trust.get('trustee_id') -+ creds['is_delegated_auth'] = True - - return creds - -@@ -155,6 +188,7 @@ def protected(callback=None): - policy_dict.update(kwargs) - self.policy_api.enforce(creds, action, flatten(policy_dict)) - LOG.debug(_('RBAC: Authorization granted')) -+ context['environment'] = {'KEYSTONE_AUTH_CONTEXT': creds} - return f(self, context, *args, **kwargs) - return inner - return wrapper -diff --git a/keystone/contrib/oauth1/controllers.py b/keystone/contrib/oauth1/controllers.py -index b8c2441..d4024df 100644 ---- a/keystone/contrib/oauth1/controllers.py -+++ b/keystone/contrib/oauth1/controllers.py -@@ -86,6 +86,12 @@ class AccessTokenCrudV3(controller.V3Controller): - - @controller.protected() - def list_access_tokens(self, context, user_id): -+ auth_context = context.get('environment', -+ {}).get('KEYSTONE_AUTH_CONTEXT', {}) -+ if auth_context.get('is_delegated_auth'): -+ raise exception.Forbidden( -+ _('Cannot list request tokens' -+ ' with a token issued via delegation.')) - refs = self.oauth_api.list_access_tokens(user_id) - formatted_refs = ([self._format_token_entity(x) for x in refs]) - return AccessTokenCrudV3.wrap_collection(context, formatted_refs) -@@ -314,6 +320,12 @@ class OAuthControllerV3(controller.V3Controller): - there is not another easy way to make sure the user knows which roles - are being requested before authorizing. - """ -+ auth_context = context.get('environment', -+ {}).get('KEYSTONE_AUTH_CONTEXT', {}) -+ if auth_context.get('is_delegated_auth'): -+ raise exception.Forbidden( -+ _('Cannot authorize a request token' -+ ' with a token issued via delegation.')) - - req_token = self.oauth_api.get_request_token(request_token_id) - -diff --git a/keystone/tests/test_v3_auth.py b/keystone/tests/test_v3_auth.py -index e89e29f..f3e3ace 100644 ---- a/keystone/tests/test_v3_auth.py -+++ b/keystone/tests/test_v3_auth.py -@@ -2150,6 +2150,68 @@ class TestTrustAuth(TestAuthInfo): - self.assertEqual(r.result['token']['project']['name'], - self.project['name']) - -+ def test_impersonation_token_cannot_create_new_trust(self): -+ ref = self.new_trust_ref( -+ trustor_user_id=self.user_id, -+ trustee_user_id=self.trustee_user_id, -+ project_id=self.project_id, -+ impersonation=True, -+ expires=dict(minutes=1), -+ role_ids=[self.role_id]) -+ del ref['id'] -+ -+ r = self.post('/OS-TRUST/trusts', body={'trust': ref}) -+ trust = self.assertValidTrustResponse(r) -+ -+ auth_data = self.build_authentication_request( -+ user_id=self.trustee_user['id'], -+ password=self.trustee_user['password'], -+ trust_id=trust['id']) -+ r = self.post('/auth/tokens', body=auth_data) -+ -+ trust_token = r.headers['X-Subject-Token'] -+ -+ # Build second trust -+ ref = self.new_trust_ref( -+ trustor_user_id=self.user_id, -+ trustee_user_id=self.trustee_user_id, -+ project_id=self.project_id, -+ impersonation=True, -+ expires=dict(minutes=1), -+ role_ids=[self.role_id]) -+ del ref['id'] -+ -+ self.post('/OS-TRUST/trusts', -+ body={'trust': ref}, -+ token=trust_token, -+ expected_status=403) -+ -+ def test_delete_trust_revokes_tokens(self): -+ ref = self.new_trust_ref( -+ trustor_user_id=self.user_id, -+ trustee_user_id=self.trustee_user_id, -+ project_id=self.project_id, -+ impersonation=False, -+ expires=dict(minutes=1), -+ role_ids=[self.role_id]) -+ del ref['id'] -+ r = self.post('/OS-TRUST/trusts', body={'trust': ref}) -+ trust = self.assertValidTrustResponse(r) -+ trust_id = trust['id'] -+ auth_data = self.build_authentication_request( -+ user_id=self.trustee_user['id'], -+ password=self.trustee_user['password'], -+ trust_id=trust_id) -+ r = self.post('/auth/tokens', body=auth_data) -+ self.assertValidProjectTrustScopedTokenResponse( -+ r, self.trustee_user) -+ trust_token = r.headers['X-Subject-Token'] -+ self.delete('/OS-TRUST/trusts/%(trust_id)s' % { -+ 'trust_id': trust_id}, -+ expected_status=204) -+ headers = {'X-Subject-Token': trust_token} -+ self.head('/auth/tokens', headers=headers, expected_status=404) -+ - def test_delete_trust(self): - ref = self.new_trust_ref( - trustor_user_id=self.user_id, -diff --git a/keystone/tests/test_v3_oauth1.py b/keystone/tests/test_v3_oauth1.py -index 73a34d7..a83c86e 100644 ---- a/keystone/tests/test_v3_oauth1.py -+++ b/keystone/tests/test_v3_oauth1.py -@@ -16,6 +16,7 @@ - - import copy - import os -+import tempfile - import urlparse - import uuid - -@@ -26,6 +27,8 @@ from keystone import contrib - from keystone.contrib import oauth1 - from keystone.contrib.oauth1 import controllers - from keystone.openstack.common import importutils -+from keystone.openstack.common import jsonutils -+from keystone.policy.backends import rules - from keystone.tests import test_v3 - - -@@ -447,6 +450,101 @@ class AuthTokenTests(OAuthFlowTests): - self.assertTrue(len(tokens) > 0) - self.assertTrue(keystone_token_uuid in tokens) - -+ def _create_trust_get_token(self): -+ ref = self.new_trust_ref( -+ trustor_user_id=self.user_id, -+ trustee_user_id=self.user_id, -+ project_id=self.project_id, -+ impersonation=True, -+ expires=dict(minutes=1), -+ role_ids=[self.role_id]) -+ del ref['id'] -+ -+ r = self.post('/OS-TRUST/trusts', body={'trust': ref}) -+ trust = self.assertValidTrustResponse(r) -+ -+ auth_data = self.build_authentication_request( -+ user_id=self.user['id'], -+ password=self.user['password'], -+ trust_id=trust['id']) -+ r = self.post('/auth/tokens', body=auth_data) -+ -+ trust_token = r.headers['X-Subject-Token'] -+ return trust_token -+ -+ def _approve_request_token_url(self): -+ consumer = self._create_single_consumer() -+ consumer_id = consumer.get('id') -+ consumer_secret = consumer.get('secret') -+ self.consumer = oauth1.Consumer(consumer_id, consumer_secret) -+ self.assertIsNotNone(self.consumer.key) -+ -+ url, headers = self._create_request_token(self.consumer, -+ self.project_id) -+ content = self.post(url, headers=headers) -+ credentials = urlparse.parse_qs(content.result) -+ request_key = credentials.get('oauth_token')[0] -+ request_secret = credentials.get('oauth_token_secret')[0] -+ self.request_token = oauth1.Token(request_key, request_secret) -+ self.assertIsNotNone(self.request_token.key) -+ -+ url = self._authorize_request_token(request_key) -+ -+ return url -+ -+ def test_oauth_token_cannot_create_new_trust(self): -+ self.test_oauth_flow() -+ ref = self.new_trust_ref( -+ trustor_user_id=self.user_id, -+ trustee_user_id=self.user_id, -+ project_id=self.project_id, -+ impersonation=True, -+ expires=dict(minutes=1), -+ role_ids=[self.role_id]) -+ del ref['id'] -+ -+ self.post('/OS-TRUST/trusts', -+ body={'trust': ref}, -+ token=self.keystone_token_id, -+ expected_status=403) -+ -+ def test_oauth_token_cannot_authorize_request_token(self): -+ self.test_oauth_flow() -+ url = self._approve_request_token_url() -+ body = {'roles': [{'id': self.role_id}]} -+ self.put(url, body=body, token=self.keystone_token_id, -+ expected_status=403) -+ -+ def test_oauth_token_cannot_list_request_tokens(self): -+ self._set_policy({"identity:list_access_tokens": [], -+ "identity:create_consumer": [], -+ "identity:authorize_request_token": []}) -+ self.test_oauth_flow() -+ url = '/users/%s/OS-OAUTH1/access_tokens' % self.user_id -+ self.get(url, token=self.keystone_token_id, -+ expected_status=403) -+ -+ def _set_policy(self, new_policy): -+ _unused, self.tmpfilename = tempfile.mkstemp() -+ rules.reset() -+ self.opt(policy_file=self.tmpfilename) -+ with open(self.tmpfilename, "w") as policyfile: -+ policyfile.write(jsonutils.dumps(new_policy)) -+ self.addCleanup(os.remove, self.tmpfilename) -+ -+ def test_trust_token_cannot_authorize_request_token(self): -+ trust_token = self._create_trust_get_token() -+ url = self._approve_request_token_url() -+ body = {'roles': [{'id': self.role_id}]} -+ self.put(url, body=body, token=trust_token, expected_status=403) -+ -+ def test_trust_token_cannot_list_request_tokens(self): -+ self._set_policy({"identity:list_access_tokens": [], -+ "identity:create_trust": []}) -+ trust_token = self._create_trust_get_token() -+ url = '/users/%s/OS-OAUTH1/access_tokens' % self.user_id -+ self.get(url, token=trust_token, expected_status=403) -+ - - class MaliciousOAuth1Tests(OAuth1Tests): - -diff --git a/keystone/trust/controllers.py b/keystone/trust/controllers.py -index 1d54f51..7fdc8c2 100644 ---- a/keystone/trust/controllers.py -+++ b/keystone/trust/controllers.py -@@ -144,6 +144,15 @@ class TrustV3(controller.V3Controller): - - # TODO(ayoung): instead of raising ValidationError on the first - # problem, return a collection of all the problems. -+ -+ # Explicitly prevent a trust token from creating a new trust. -+ auth_context = context.get('environment', -+ {}).get('KEYSTONE_AUTH_CONTEXT', {}) -+ if auth_context.get('is_delegated_auth'): -+ raise exception.Forbidden( -+ _('Cannot create a trust' -+ ' with a token issued via delegation.')) -+ - if not trust: - raise exception.ValidationError(attribute='trust', - target='request')