summaryrefslogtreecommitdiff
path: root/keystoneclient/middleware/auth_token.py
diff options
context:
space:
mode:
authorBryan D. Payne <bdpayne@acm.org>2013-06-07 09:34:25 -0700
committerThierry Carrez <thierry@openstack.org>2013-06-19 17:05:02 +0200
commiteeefb784f24c37d5f56a421e1ccc911cace9385e (patch)
tree5df24df4e582068c4985ef11af4d2d9be88353f7 /keystoneclient/middleware/auth_token.py
parent1e3cf4bb2f1370d4117ce1e1d934838e1a475f32 (diff)
downloadpython-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.py131
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."""