From 164c7ccf408f587af9ec6aaac02b7e9144c41610 Mon Sep 17 00:00:00 2001 From: Adam Young Date: Thu, 29 May 2014 13:56:17 -0400 Subject: [PATCH] Block Delegation esclation of Priveledges Forbids: creating a trust with a trust token. creating a trust with an oauth token. approving a request_token with a trust token. approving a request_token with an oauth token Change-Id: I1528f9dd003f5e03cbc50b78e1b32dbbf85ffcc2 Closes-Bug: 1324592 --- keystone/common/authorization.py | 29 ++++++++++++++ keystone/contrib/oauth1/controllers.py | 9 +++++ keystone/tests/test_v3_auth.py | 36 +++++++++++++++++ keystone/tests/test_v3_oauth1.py | 71 ++++++++++++++++++++++++++++++++++ keystone/trust/controllers.py | 10 +++++ 5 files changed, 155 insertions(+) diff --git a/keystone/common/authorization.py b/keystone/common/authorization.py index 1aae1b0301258e8fcd1e5bb9f4871dbf5b1d77e6..b440f10e57dd92de028838079f4091f6f97fc493 100644 --- a/keystone/common/authorization.py +++ b/keystone/common/authorization.py @@ -68,6 +68,24 @@ def v3_token_to_auth_context(token): creds['group_ids'] = [ g['id'] for g in token_data['user'].get(federation.FEDERATION, {}).get( 'groups', [])] + + 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'] + + 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'] return creds @@ -86,6 +104,17 @@ def v2_token_to_auth_context(token): if 'roles' in token_data['user']: creds['roles'] = [role['name'] for role in token_data['user']['roles']] + + trust = token_data.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') + return creds diff --git a/keystone/contrib/oauth1/controllers.py b/keystone/contrib/oauth1/controllers.py index 46b45051f9cfd1f3eeec85ea7be74ec834d77bf7..711a8c3c37e9a9813a3312f882af3ec693089b48 100644 --- a/keystone/contrib/oauth1/controllers.py +++ b/keystone/contrib/oauth1/controllers.py @@ -310,6 +310,15 @@ 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('trust_id') is not None: + raise exception.Forbidden( + _('Cannot authorize a request token' + ' with a trust scoped token.')) + if auth_context.get('access_token_id') is not None: + raise exception.Forbidden( + _('Cannot authorize a request token with an oauth token.')) 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 006eb5daf7b64fd620c1af2621a51275d2520f37..ff3136e6597685fa476315a1313cfda67b8b2aa6 100644 --- a/keystone/tests/test_v3_auth.py +++ b/keystone/tests/test_v3_auth.py @@ -2827,6 +2827,42 @@ 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 assertTrustTokensRevoked(self, trust_id): revocation_response = self.get('/OS-REVOKE/events', expected_status=200) diff --git a/keystone/tests/test_v3_oauth1.py b/keystone/tests/test_v3_oauth1.py index 525b4b0e90040f2d28825d436a19e5506bd96f14..33816442e6fb0d09b218f9ac7ce7bc3058c520a9 100644 --- a/keystone/tests/test_v3_oauth1.py +++ b/keystone/tests/test_v3_oauth1.py @@ -476,6 +476,77 @@ class AuthTokenTests(OAuthFlowTests): self.assertRaises(exception.TokenNotFound, self.token_api.get_token, self.keystone_token_id) + 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['id'] + consumer_secret = consumer['secret'] + self.consumer = {'key': consumer_id, 'secret': consumer_secret} + self.assertIsNotNone(self.consumer['secret']) + + url, headers = self._create_request_token(self.consumer, + self.project_id) + content = self.post(url, headers=headers) + credentials = urllib.parse.parse_qs(content.result) + request_key = credentials['oauth_token'][0] + request_secret = credentials['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_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) + class MaliciousOAuth1Tests(OAuth1Tests): diff --git a/keystone/trust/controllers.py b/keystone/trust/controllers.py index ae661f944789a3cd5c4fa8fe6ec08ce90507ad64..08a9c99ea8d573fcc516697d95fecb79c35f4286 100644 --- a/keystone/trust/controllers.py +++ b/keystone/trust/controllers.py @@ -124,6 +124,16 @@ class TrustV3(controller.V3Controller): The user creating the trust must be the trustor. """ + # Explicitly prevent a trust token from creating a new trust. + auth_context = context.get('environment', + {}).get('KEYSTONE_AUTH_CONTEXT', {}) + if auth_context.get('trust_id') is not None: + raise exception.Forbidden( + _('Cannot create a trust with a trust scoped token.')) + if auth_context.get('access_token_id') is not None: + raise exception.Forbidden( + _('Cannot create a trust with an oauth token.')) + if not trust: raise exception.ValidationError(attribute='trust', target='request') -- 1.9.0