1 This upstream patch addresses CVE-2014-3517 and is tracked under |
|
2 Launchpad bug 1325128. It is addressed in the Juno trunk, Icehouse |
|
3 2014.1.2, and Havana 2013.2.4. It has been modified to apply cleanly |
|
4 into our current Havana implementation |
|
5 |
|
6 commit 1dd97d1335f6ec028d0e4440250f80802a2f1d18 |
|
7 Author: Grant Murphy <[email protected]> |
|
8 Date: Tue Jul 8 03:35:40 2014 +0000 |
|
9 |
|
10 Avoid possible timing attack in metadata api |
|
11 |
|
12 Introduce a constant time comparison function to |
|
13 nova utils for comparing authentication tokens. |
|
14 |
|
15 Conflicts: |
|
16 nova/tests/test_utils.py |
|
17 nova/utils.py |
|
18 |
|
19 Closes-bug: #1325128 |
|
20 Change-Id: I7374f2edc6f03c7da59cf73ae91a87147e53d0de |
|
21 (cherry picked from commit 9f59ca751f1a392ef24d8ab73a7bf5ce9655017e) |
|
22 |
|
23 diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py |
|
24 index 50387ab..74bb4f7 100644 |
|
25 --- a/nova/api/metadata/handler.py |
|
26 +++ b/nova/api/metadata/handler.py |
|
27 @@ -31,6 +31,7 @@ from nova import exception |
|
28 from nova.openstack.common.gettextutils import _ |
|
29 from nova.openstack.common import log as logging |
|
30 from nova.openstack.common import memorycache |
|
31 +from nova import utils |
|
32 from nova import wsgi |
|
33 |
|
34 CACHE_EXPIRATION = 15 # in seconds |
|
35 @@ -172,7 +173,7 @@ class MetadataRequestHandler(wsgi.Application): |
|
36 instance_id, |
|
37 hashlib.sha256).hexdigest() |
|
38 |
|
39 - if expected_signature != signature: |
|
40 + if not utils.constant_time_compare(expected_signature, signature): |
|
41 if instance_id: |
|
42 LOG.warn(_('X-Instance-ID-Signature: %(signature)s does not ' |
|
43 'match the expected value: %(expected_signature)s ' |
|
44 diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py |
|
45 index b38ea50..820fe09 100644 |
|
46 --- a/nova/tests/test_utils.py |
|
47 +++ b/nova/tests/test_utils.py |
|
48 @@ -1083,3 +1083,10 @@ class GetImageFromSystemMetadataTestCase(test.NoDBTestCase): |
|
49 |
|
50 # Verify that the foo1 key has not been inherited |
|
51 self.assertTrue("foo1" not in image) |
|
52 + |
|
53 + |
|
54 +class ConstantTimeCompareTestCase(test.NoDBTestCase): |
|
55 + def test_constant_time_compare(self): |
|
56 + self.assertTrue(utils.constant_time_compare("abcd1234", "abcd1234")) |
|
57 + self.assertFalse(utils.constant_time_compare("abcd1234", "a")) |
|
58 + self.assertFalse(utils.constant_time_compare("abcd1234", "ABCD234")) |
|
59 diff --git a/nova/utils.py b/nova/utils.py |
|
60 index 4757f3a..5f10a8a 100755 |
|
61 --- nova-2013.2.3/nova/utils.py.~2~ 2014-09-02 13:57:46.030039835 -0700 |
|
62 +++ nova-2013.2.3/nova/utils.py 2014-09-02 13:57:49.391998275 -0700 |
|
63 @@ -23,6 +23,7 @@ import contextlib |
|
64 import datetime |
|
65 import functools |
|
66 import hashlib |
|
67 +import hmac |
|
68 import inspect |
|
69 import os |
|
70 import pyclbr |
|
71 @@ -1288,3 +1289,20 @@ def get_boolean(value): |
|
72 return value |
|
73 else: |
|
74 return strutils.bool_from_string(value) |
|
75 + |
|
76 +if hasattr(hmac, 'compare_digest'): |
|
77 + constant_time_compare = hmac.compare_digest |
|
78 +else: |
|
79 + def constant_time_compare(first, second): |
|
80 + """Returns True if both string inputs are equal, otherwise False. |
|
81 + |
|
82 + This function should take a constant amount of time regardless of |
|
83 + how many characters in the strings match. |
|
84 + |
|
85 + """ |
|
86 + if len(first) != len(second): |
|
87 + return False |
|
88 + result = 0 |
|
89 + for x, y in zip(first, second): |
|
90 + result |= ord(x) ^ ord(y) |
|
91 + return result == 0 |
|