Fix v2 token user ref with trust impersonation=True

The v2 token controller incorrectly checks for a string instead
of a boolean, which results in the wrong user ID (trustee, when
it should be the trustor) when impersonation=True.  So fix the
comparison and tests, adding a test which illustrates the issue.

This patchset also closes the gap that allows EC2 credentials to
be issued from trust-scoped tokens, allowing privilege escalation
since EC2 tokens have no concept of trust-scoping/role
restrictions in the Grizzly release.

Change-Id: Ic94f30f2354c9fda20531bb598387368fde8a096
Closes-Bug: #1239303
Related-Bug: #1242597
This commit is contained in:
Steven Hardy 2013-10-13 10:44:52 +01:00 committed by Morgan Fainberg
parent 23825691be
commit 8fcc18c42b
3 changed files with 49 additions and 7 deletions

View File

@ -207,6 +207,9 @@ class Ec2Controller(controller.V2Controller):
if not self._is_admin(context):
self._assert_identity(context, user_id)
# Disallow trust-scoped tokens from creating credentials.
self._assert_not_trust_scoped(context)
self._assert_valid_user_id(context, user_id)
self._assert_valid_project_id(context, tenant_id)
@ -308,6 +311,22 @@ class Ec2Controller(controller.V2Controller):
except exception.Forbidden:
return False
def _assert_not_trust_scoped(self, context):
try:
token_ref = self.token_api.get_token(
context, token_id=context['token_id'])
except exception.TokenNotFound as e:
raise exception.Unauthorized(e)
# NOTE(morganfainberg): In Grizzly, it is not allowed to use a
# trust scoped token to create an EC2 credential, this is due to
# privilege escalation possibility (there is no way to correlate
# the trust to the EC2 credential and limit roles to the trust).
if 'trust' in token_ref:
raise exception.Forbidden()
if 'trust_id' in token_ref.get('metadata', {}):
raise exception.Forbidden()
def _assert_owner(self, context, user_id, credential_id):
"""Ensure the provided user owns the credential.

View File

@ -201,7 +201,7 @@ class Auth(controller.V2Controller):
context, trust_ref['trustee_user_id'])
if not trustee_user_ref['enabled']:
raise exception.Forbidden()()
if trust_ref['impersonation'] == 'True':
if trust_ref['impersonation'] is True:
current_user_ref = trustor_user_ref
else:
current_user_ref = trustee_user_ref

View File

@ -19,6 +19,7 @@ import uuid
from keystone import auth
from keystone import config
from keystone.contrib import ec2
from keystone import exception
from keystone import identity
from keystone.openstack.common import timeutils
@ -517,7 +518,7 @@ class AuthWithTrust(AuthTest):
self.sample_data = {'trustor_user_id': self.trustor['id'],
'trustee_user_id': self.trustee['id'],
'project_id': self.tenant_bar['id'],
'impersonation': 'True',
'impersonation': True,
'roles': [{'id': self.role_browser['id']},
{'name': self.role_member['name']}]}
expires_at = timeutils.strtime(timeutils.utcnow() +
@ -525,7 +526,7 @@ class AuthWithTrust(AuthTest):
fmt=TIME_FORMAT)
self.create_trust(expires_at=expires_at)
def create_trust(self, expires_at=None, impersonation='True'):
def create_trust(self, expires_at=None, impersonation=True):
username = self.trustor['name'],
password = 'foo2'
body_dict = _build_user_auth(username=username, password=password)
@ -586,20 +587,42 @@ class AuthWithTrust(AuthTest):
self.assertIn(role['id'], role_ids)
def test_create_trust_no_impersonation(self):
self.create_trust(expires_at=None, impersonation='False')
self.create_trust(expires_at=None, impersonation=False)
self.assertEquals(self.new_trust['trustor_user_id'],
self.trustor['id'])
self.assertEquals(self.new_trust['trustee_user_id'],
self.trustee['id'])
self.assertEquals(self.new_trust['impersonation'],
'False')
self.assertIs(self.new_trust['impersonation'], False)
auth_response = self.fetch_v2_token_from_trust()
token_user = auth_response['access']['user']
self.assertEquals(token_user['id'],
self.new_trust['trustee_user_id'])
#TODO Endpoints
def test_create_trust_impersonation(self):
self.create_trust(expires_at=None)
self.assertEqual(self.new_trust['trustor_user_id'], self.trustor['id'])
self.assertEqual(self.new_trust['trustee_user_id'], self.trustee['id'])
self.assertIs(self.new_trust['impersonation'], True)
auth_response = self.fetch_v2_token_from_trust()
token_user = auth_response['access']['user']
self.assertEqual(token_user['id'], self.new_trust['trustor_user_id'])
def test_disallow_ec2_credential_from_trust_scoped_token(self):
ec2_manager = ec2.Manager()
self.ec2_controller = ec2.Ec2Controller()
self.test_create_trust_impersonation()
auth_response = self.fetch_v2_token_from_trust()
# ensure it is not possible to create an ec2 token from a trust
context = {'token_id': auth_response['access']['token']['id'],
'is_admin': False}
self.assertRaises(exception.Forbidden,
self.ec2_controller.create_credential,
context=context,
user_id=self.user_foo['id'],
tenant_id=self.tenant_bar['id'])
def test_token_from_trust_wrong_user_fails(self):
new_trust = self.create_trust()
request_body = self.build_v2_token_request('FOO', 'foo2')