From bdc0dde21f86620e5c4ba4e9043052957837997a Mon Sep 17 00:00:00 2001 From: Grant Murphy Date: Tue, 8 Jul 2014 03:35:40 +0000 Subject: [PATCH 1/1] Avoid possible timing attack in metadata api Introduce a constant time comparison function to nova utils for comparing authentication tokens. Original code taken from: https://github.com/openstack/python-keystoneclient/blob/master/keystoneclient/middleware/memcache_crypt.py#L86 Change-Id: I7374f2edc6f03c7da59cf73ae91a87147e53d0de Closes-bug: #1325128 --- nova/api/metadata/handler.py | 3 ++- nova/tests/test_utils.py | 7 +++++++ nova/utils.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index 50387ab..74bb4f7 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -31,6 +31,7 @@ from nova import exception from nova.openstack.common.gettextutils import _ from nova.openstack.common import log as logging from nova.openstack.common import memorycache +from nova import utils from nova import wsgi CACHE_EXPIRATION = 15 # in seconds @@ -172,7 +173,7 @@ class MetadataRequestHandler(wsgi.Application): instance_id, hashlib.sha256).hexdigest() - if expected_signature != signature: + if not utils.constant_time_compare(expected_signature, signature): if instance_id: LOG.warn(_('X-Instance-ID-Signature: %(signature)s does not ' 'match the expected value: %(expected_signature)s ' diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index b38ea50..820fe09 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -1083,3 +1083,10 @@ class GetImageFromSystemMetadataTestCase(test.NoDBTestCase): # Verify that the foo1 key has not been inherited self.assertTrue("foo1" not in image) + + +class ConstantTimeCompareTestCase(test.NoDBTestCase): + def test_constant_time_compare(self): + self.assertTrue(utils.constant_time_compare("abcd1234", "abcd1234")) + self.assertFalse(utils.constant_time_compare("abcd1234", "a")) + self.assertFalse(utils.constant_time_compare("abcd1234", "ABCD234")) diff --git a/nova/utils.py b/nova/utils.py index 4757f3a..dd07799 100755 --- a/nova/utils.py +++ b/nova/utils.py @@ -23,6 +23,7 @@ import contextlib import datetime import functools import hashlib +import hmac import inspect import os import pyclbr @@ -39,6 +40,7 @@ from xml.sax import saxutils import eventlet import netaddr from oslo.config import cfg +import six from nova import exception from nova.openstack.common import excutils @@ -1302,3 +1304,29 @@ def get_boolean(value): return value else: return strutils.bool_from_string(value) + + +# NOTE(gm) Constant time comparison taken from keystone. This is a +# candidate for inclusion in oslo. +# +# Original code: master/keystoneclient/middleware/memcache_crypt.py#L86 +if sys.version_info >= (3, 3): + constant_time_compare = hmac.compare_digest +else: + def constant_time_compare(first, second): + """Returns True if both string inputs are equal, otherwise False. + + This function should take a constant amount of time regardless of + how many characters in the strings match. + + """ + if len(first) != len(second): + return False + result = 0 + if six.PY3 and isinstance(first, bytes) and isinstance(second, bytes): + for x, y in zip(first, second): + result |= x ^ y + else: + for x, y in zip(first, second): + result |= ord(x) ^ ord(y) + return result == 0 -- 1.9.3