diff options
| author | Bryan D. Payne <bdpayne@acm.org> | 2013-06-07 09:34:25 -0700 |
|---|---|---|
| committer | Thierry Carrez <thierry@openstack.org> | 2013-06-19 17:05:02 +0200 |
| commit | eeefb784f24c37d5f56a421e1ccc911cace9385e (patch) | |
| tree | 5df24df4e582068c4985ef11af4d2d9be88353f7 /keystoneclient/middleware/auth_token.py | |
| parent | 1e3cf4bb2f1370d4117ce1e1d934838e1a475f32 (diff) | |
| download | python-keystoneclient-eeefb784f24c37d5f56a421e1ccc911cace9385e.tar.gz | |
Fix memcache encryption middleware
This fixes lp1175367 and lp1175368 by redesigning the memcache crypt
middleware to not do dangerous things. It is forward compatible, but
will invalidate any existing ephemeral encrypted or signed memcache
entries.
Change-Id: Ice8724949a48bfad3b8b7c41b5f50a18a9ad9f42
Signed-off-by: Bryan D. Payne <bdpayne@acm.org>
Diffstat (limited to 'keystoneclient/middleware/auth_token.py')
| -rw-r--r-- | keystoneclient/middleware/auth_token.py | 131 |
1 files changed, 61 insertions, 70 deletions
diff --git a/keystoneclient/middleware/auth_token.py b/keystoneclient/middleware/auth_token.py index 7e3012c..e50f723 100644 --- a/keystoneclient/middleware/auth_token.py +++ b/keystoneclient/middleware/auth_token.py @@ -222,6 +222,7 @@ opts = [ CONF.register_opts(opts, group='keystone_authtoken') LIST_OF_VERSIONS_TO_ATTEMPT = ['v2.0', 'v3.0'] +CACHE_KEY_TEMPLATE = 'tokens/%s' def will_expire_soon(expiry): @@ -847,91 +848,81 @@ class AuthProtocol(object): env_key = self._header_to_env_var(key) return env.get(env_key, default) - def _protect_cache_value(self, token, data): - """ Encrypt or sign data if necessary. """ - try: - if self._memcache_security_strategy == 'ENCRYPT': - return memcache_crypt.encrypt_data(token, - self._memcache_secret_key, - data) - elif self._memcache_security_strategy == 'MAC': - return memcache_crypt.sign_data(token, data) - else: - return data - except: - msg = 'Failed to encrypt/sign cache data.' - self.LOG.exception(msg) - return data - - def _unprotect_cache_value(self, token, data): - """ Decrypt or verify signed data if necessary. """ - if data is None: - return data - - try: - if self._memcache_security_strategy == 'ENCRYPT': - return memcache_crypt.decrypt_data(token, - self._memcache_secret_key, - data) - elif self._memcache_security_strategy == 'MAC': - return memcache_crypt.verify_signed_data(token, data) - else: - return data - except: - msg = 'Failed to decrypt/verify cache data.' - self.LOG.exception(msg) - # this should have the same effect as data not found in cache - return None - - def _get_cache_key(self, token): - """ Return the cache key. - - Do not use clear token as key if memcache protection is on. - - """ - htoken = token - if self._memcache_security_strategy in ('ENCRYPT', 'MAC'): - derv_token = token + self._memcache_secret_key - htoken = memcache_crypt.hash_data(derv_token) - return 'tokens/%s' % htoken - - def _cache_get(self, token): + def _cache_get(self, token, ignore_expires=False): """Return token information from cache. If token is invalid raise InvalidUserToken return token only if fresh (not expired). """ + if self._cache and token: - key = self._get_cache_key(token) - cached = self._cache.get(key) - cached = self._unprotect_cache_value(token, cached) + if self._memcache_security_strategy is None: + key = CACHE_KEY_TEMPLATE % token + serialized = self._cache.get(key) + else: + keys = memcache_crypt.derive_keys( + token, + self._memcache_secret_key, + self._memcache_security_strategy) + cache_key = CACHE_KEY_TEMPLATE % ( + memcache_crypt.get_cache_key(keys)) + raw_cached = self._cache.get(cache_key) + try: + # unprotect_data will return None if raw_cached is None + serialized = memcache_crypt.unprotect_data(keys, + raw_cached) + except Exception: + msg = 'Failed to decrypt/verify cache data' + self.LOG.exception(msg) + # this should have the same effect as data not + # found in cache + serialized = None + + if serialized is None: + return None + + # Note that 'invalid' and (data, expires) are the only + # valid types of serialized cache entries, so there is not + # a collision with json.loads(serialized) == None. + cached = json.loads(serialized) if cached == 'invalid': self.LOG.debug('Cached Token %s is marked unauthorized', token) raise InvalidUserToken('Token authorization failed') - if cached: - data, expires = cached - if time.time() < float(expires): - self.LOG.debug('Returning cached token %s', token) - return data - else: - self.LOG.debug('Cached Token %s seems expired', token) - - def _cache_store(self, token, data, expires=None): - """ Store value into memcache. """ - key = self._get_cache_key(token) - data = self._protect_cache_value(token, data) - data_to_store = data - if expires: - data_to_store = (data, expires) + + data, expires = cached + if ignore_expires or time.time() < float(expires): + self.LOG.debug('Returning cached token %s', token) + return data + else: + self.LOG.debug('Cached Token %s seems expired', token) + + def _cache_store(self, token, data): + """ Store value into memcache. + + data may be the string 'invalid' or a tuple like (data, expires) + + """ + serialized_data = json.dumps(data) + if self._memcache_security_strategy is None: + cache_key = CACHE_KEY_TEMPLATE % token + data_to_store = serialized_data + else: + keys = memcache_crypt.derive_keys( + token, + self._memcache_secret_key, + self._memcache_security_strategy) + cache_key = CACHE_KEY_TEMPLATE % memcache_crypt.get_cache_key(keys) + data_to_store = memcache_crypt.protect_data(keys, serialized_data) + # we need to special-case set() because of the incompatibility between # Swift MemcacheRing and python-memcached. See # https://bugs.launchpad.net/swift/+bug/1095730 if self._use_keystone_cache: - self._cache.set(key, + self._cache.set(cache_key, data_to_store, time=self.token_cache_time) else: - self._cache.set(key, + self._cache.set(cache_key, data_to_store, timeout=self.token_cache_time) @@ -959,7 +950,7 @@ class AuthProtocol(object): """ if self._cache: self.LOG.debug('Storing %s token in memcache', token) - self._cache_store(token, data, expires) + self._cache_store(token, (data, expires)) def _cache_store_invalid(self, token): """Store invalid token in cache.""" |
