From 5ba835f3e145e7ec3e71beabaf8711244159dc37 Mon Sep 17 00:00:00 2001 From: Colleen Murphy Date: Mon, 11 Apr 2016 19:13:56 -0700 Subject: [PATCH] Only confirm token binding on one token When using service tokens and kerberos, the REMOTE_USER will be the service user, and hence the token bind confirmation will always fail to validate the client user's token, making it impossible to use token binding with service tokens. This patch adds a test to expose the issue and fixes the problem by only validating the token binding for the service token when both tokens are in the request. Change-Id: I7ba2283e8e58b89f1e42bc738c7e77284321e3a5 Closes-bug: #1413433 --- keystonemiddleware/auth_token/__init__.py | 3 +- .../auth_token/test_auth_token_middleware.py | 41 ++++++++++++++++++- .../tests/unit/client_fixtures.py | 29 +++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/keystonemiddleware/auth_token/__init__.py b/keystonemiddleware/auth_token/__init__.py index 75a6ec5e..89fed656 100644 --- a/keystonemiddleware/auth_token/__init__.py +++ b/keystonemiddleware/auth_token/__init__.py @@ -491,7 +491,8 @@ class BaseAuthProtocol(object): try: data, user_auth_ref = self._do_fetch_token(request.user_token) self._validate_token(user_auth_ref) - self._confirm_token_bind(user_auth_ref, request) + if not request.service_token: + self._confirm_token_bind(user_auth_ref, request) except ksm_exceptions.InvalidToken: self.log.info(_LI('Invalid user token')) request.user_token_valid = False diff --git a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py index ae34a6e1..942f2c03 100644 --- a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py +++ b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py @@ -2049,6 +2049,35 @@ class CommonCompositeAuthTests(object): expected_status=403) self.assertEqual(FakeApp.FORBIDDEN, resp.body) + def assert_kerberos_composite_bind(self, user_token, service_token, + bind_level): + conf = { + 'enforce_token_bind': bind_level, + 'auth_version': self.auth_version, + } + self.set_middleware(conf=conf) + + req = webob.Request.blank('/') + req.headers['X-Auth-Token'] = user_token + req.headers['X-Service-Token'] = service_token + + req.environ['REMOTE_USER'] = self.examples.SERVICE_KERBEROS_BIND + req.environ['AUTH_TYPE'] = 'Negotiate' + + resp = req.get_response(self.middleware) + + self.assertEqual(200, resp.status_int) + self.assertEqual(FakeApp.SUCCESS, resp.body) + self.assertIn('keystone.token_info', req.environ) + + def test_composite_auth_with_bind(self): + token = self.token_dict['uuid_token_bind'] + service_token = self.token_dict['uuid_service_token_bind'] + + self.assert_kerberos_composite_bind(token, + service_token, + bind_level='required') + class v2CompositeAuthTests(BaseAuthTokenMiddlewareTest, CommonCompositeAuthTests, @@ -2069,9 +2098,13 @@ class v2CompositeAuthTests(BaseAuthTokenMiddlewareTest, uuid_token_default = self.examples.UUID_TOKEN_DEFAULT uuid_service_token_default = self.examples.UUID_SERVICE_TOKEN_DEFAULT + uuid_token_bind = self.examples.UUID_TOKEN_BIND + uuid_service_token_bind = self.examples.UUID_SERVICE_TOKEN_BIND self.token_dict = { 'uuid_token_default': uuid_token_default, 'uuid_service_token_default': uuid_service_token_default, + 'uuid_token_bind': uuid_token_bind, + 'uuid_service_token_bind': uuid_service_token_bind, } self.requests_mock.get(BASE_URI, @@ -2086,7 +2119,9 @@ class v2CompositeAuthTests(BaseAuthTokenMiddlewareTest, status_code=200) for token in (self.examples.UUID_TOKEN_DEFAULT, - self.examples.UUID_SERVICE_TOKEN_DEFAULT,): + self.examples.UUID_SERVICE_TOKEN_DEFAULT, + self.examples.UUID_TOKEN_BIND, + self.examples.UUID_SERVICE_TOKEN_BIND): text = self.examples.JSON_TOKEN_RESPONSES[token] self.requests_mock.get('%s/v2.0/tokens/%s' % (BASE_URI, token), text=text) @@ -2120,9 +2155,13 @@ class v3CompositeAuthTests(BaseAuthTokenMiddlewareTest, uuid_token_default = self.examples.v3_UUID_TOKEN_DEFAULT uuid_serv_token_default = self.examples.v3_UUID_SERVICE_TOKEN_DEFAULT + uuid_token_bind = self.examples.v3_UUID_TOKEN_BIND + uuid_service_token_bind = self.examples.v3_UUID_SERVICE_TOKEN_BIND self.token_dict = { 'uuid_token_default': uuid_token_default, 'uuid_service_token_default': uuid_serv_token_default, + 'uuid_token_bind': uuid_token_bind, + 'uuid_service_token_bind': uuid_service_token_bind, } self.requests_mock.get(BASE_URI, json=VERSION_LIST_v3, status_code=300) diff --git a/keystonemiddleware/tests/unit/client_fixtures.py b/keystonemiddleware/tests/unit/client_fixtures.py index ea36a018..f150b4e6 100644 --- a/keystonemiddleware/tests/unit/client_fixtures.py +++ b/keystonemiddleware/tests/unit/client_fixtures.py @@ -105,6 +105,7 @@ class Examples(fixtures.Fixture): self.SIGNING_CERT = f.read() self.KERBEROS_BIND = 'USER@REALM' + self.SERVICE_KERBEROS_BIND = 'SERVICE_USER@SERVICE_REALM' self.SIGNING_KEY_FILE = os.path.join(KEYDIR, 'signing_key.pem') with open(self.SIGNING_KEY_FILE) as f: @@ -127,7 +128,9 @@ class Examples(fixtures.Fixture): self.v3_UUID_TOKEN_UNKNOWN_BIND = '7ed9781b62cd4880b8d8c6788ab1d1e2' self.UUID_SERVICE_TOKEN_DEFAULT = 'fe4c0710ec2f492748596c1b53ab124' + self.UUID_SERVICE_TOKEN_BIND = '5e43439613d34a13a7e03b2762bd08ab' self.v3_UUID_SERVICE_TOKEN_DEFAULT = 'g431071bbc2f492748596c1b53cb229' + self.v3_UUID_SERVICE_TOKEN_BIND = 'be705e4426d0449a89e35ae21c380a05' revoked_token = self.REVOKED_TOKEN if isinstance(revoked_token, six.text_type): @@ -321,6 +324,17 @@ class Examples(fixtures.Fixture): token['access']['token']['bind'] = {'kerberos': self.KERBEROS_BIND} self.TOKEN_RESPONSES[self.UUID_TOKEN_BIND] = token + token = fixture.V2Token(token_id=self.UUID_SERVICE_TOKEN_BIND, + tenant_id=SERVICE_PROJECT_ID, + tenant_name=SERVICE_PROJECT_NAME, + user_id=SERVICE_USER_ID, + user_name=SERVICE_USER_NAME) + token.add_role(SERVICE_ROLE_NAME1) + token.add_role(SERVICE_ROLE_NAME2) + token['access']['token']['bind'] = { + 'kerberos': self.SERVICE_KERBEROS_BIND} + self.TOKEN_RESPONSES[self.UUID_SERVICE_TOKEN_BIND] = token + token = fixture.V2Token(token_id=self.UUID_TOKEN_UNKNOWN_BIND, tenant_id=PROJECT_ID, tenant_name=PROJECT_NAME, @@ -405,6 +419,21 @@ class Examples(fixtures.Fixture): token['token']['bind'] = {'kerberos': self.KERBEROS_BIND} self.TOKEN_RESPONSES[self.v3_UUID_TOKEN_BIND] = token + token = fixture.V3Token(user_id=SERVICE_USER_ID, + user_name=SERVICE_USER_NAME, + user_domain_id=SERVICE_DOMAIN_ID, + user_domain_name=SERVICE_DOMAIN_NAME, + project_id=SERVICE_PROJECT_ID, + project_name=SERVICE_PROJECT_NAME, + project_domain_id=SERVICE_DOMAIN_ID, + project_domain_name=SERVICE_DOMAIN_NAME) + token.add_role(name=SERVICE_ROLE_NAME1) + token.add_role(name=SERVICE_ROLE_NAME2) + svc = token.add_service(self.SERVICE_TYPE) + svc.add_endpoint('public', self.SERVICE_URL) + token['token']['bind'] = {'kerberos': self.SERVICE_KERBEROS_BIND} + self.TOKEN_RESPONSES[self.v3_UUID_SERVICE_TOKEN_BIND] = token + token = fixture.V3Token(user_id=USER_ID, user_name=USER_NAME, user_domain_id=DOMAIN_ID,