1 |
prometheanfire 13/02/26 21:44:05 |
2 |
|
3 |
Added: nova-folsom-3-CVE-2013-0335.patch |
4 |
Log: |
5 |
sys-cluster/nova - fixing bug 459364 CVE-2013-0335 |
6 |
|
7 |
(Portage version: 2.1.11.50/cvs/Linux x86_64, signed Manifest commit with key 0x2471eb3e40ac5ac3) |
8 |
|
9 |
Revision Changes Path |
10 |
1.1 sys-cluster/nova/files/nova-folsom-3-CVE-2013-0335.patch |
11 |
|
12 |
file : http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/nova-folsom-3-CVE-2013-0335.patch?rev=1.1&view=markup |
13 |
plain: http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/nova-folsom-3-CVE-2013-0335.patch?rev=1.1&content-type=text/plain |
14 |
|
15 |
Index: nova-folsom-3-CVE-2013-0335.patch |
16 |
=================================================================== |
17 |
From 05a3374992bc8ba53ddc9c491b51c4b59eed0a72 Mon Sep 17 00:00:00 2001 |
18 |
From: John Herndon <john.herndon@××.com> |
19 |
Date: Fri, 22 Feb 2013 20:43:58 +0000 |
20 |
Subject: [PATCH] VNC Token Validation |
21 |
|
22 |
Force console auth service to flush all tokens |
23 |
associated with an instance when it is deleted. |
24 |
This will fix a bug where the console for the |
25 |
wrong instance can be connected to via the console |
26 |
if the correct circumstances occur. This change also |
27 |
makes a call to veriry vnc console tokens when a |
28 |
user attempts to connect to a console. This ensures |
29 |
the user is connecting to the correct console. |
30 |
|
31 |
bug 1125378 |
32 |
Change-Id: I0d83ec6c4dbfef1af912a200ee15f8052f72da96 |
33 |
--- |
34 |
nova/common/memorycache.py | 5 +++ |
35 |
nova/compute/api.py | 3 +- |
36 |
nova/compute/manager.py | 12 ++++++ |
37 |
nova/compute/rpcapi.py | 7 ++++ |
38 |
nova/consoleauth/manager.py | 41 +++++++++++++++++- |
39 |
nova/consoleauth/rpcapi.py | 10 ++++- |
40 |
nova/tests/compute/test_compute.py | 52 ++++++++++++++++++++++- |
41 |
nova/tests/compute/test_rpcapi.py | 5 +++ |
42 |
nova/tests/consoleauth/test_consoleauth.py | 67 +++++++++++++++++++++++++++++- |
43 |
nova/tests/consoleauth/test_rpcapi.py | 6 ++- |
44 |
10 files changed, 200 insertions(+), 8 deletions(-) |
45 |
|
46 |
diff --git a/nova/common/memorycache.py b/nova/common/memorycache.py |
47 |
index 502f833..3a1935e 100644 |
48 |
--- a/nova/common/memorycache.py |
49 |
+++ b/nova/common/memorycache.py |
50 |
@@ -62,3 +62,8 @@ class Client(object): |
51 |
new_value = int(value) + delta |
52 |
self.cache[key] = (self.cache[key][0], str(new_value)) |
53 |
return new_value |
54 |
+ |
55 |
+ def delete(self, key, time=0): |
56 |
+ """Deletes the value associated with a key.""" |
57 |
+ if key in self.cache: |
58 |
+ del self.cache[key] |
59 |
diff --git a/nova/compute/api.py b/nova/compute/api.py |
60 |
index df7b215..3860226 100644 |
61 |
--- a/nova/compute/api.py |
62 |
+++ b/nova/compute/api.py |
63 |
@@ -1853,7 +1853,8 @@ class API(base.Base): |
64 |
|
65 |
self.consoleauth_rpcapi.authorize_console(context, |
66 |
connect_info['token'], console_type, connect_info['host'], |
67 |
- connect_info['port'], connect_info['internal_access_path']) |
68 |
+ connect_info['port'], connect_info['internal_access_path'], |
69 |
+ instance["uuid"]) |
70 |
|
71 |
return {'url': connect_info['access_url']} |
72 |
|
73 |
diff --git a/nova/compute/manager.py b/nova/compute/manager.py |
74 |
index 90de5a4..5b0d1ea 100644 |
75 |
--- a/nova/compute/manager.py |
76 |
+++ b/nova/compute/manager.py |
77 |
@@ -52,6 +52,7 @@ from nova.compute import rpcapi as compute_rpcapi |
78 |
from nova.compute import task_states |
79 |
from nova.compute import utils as compute_utils |
80 |
from nova.compute import vm_states |
81 |
+from nova import consoleauth |
82 |
import nova.context |
83 |
from nova import exception |
84 |
from nova import flags |
85 |
@@ -235,6 +236,7 @@ class ComputeManager(manager.SchedulerDependentManager): |
86 |
self.compute_api = compute.API() |
87 |
self.compute_rpcapi = compute_rpcapi.ComputeAPI() |
88 |
self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI() |
89 |
+ self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI() |
90 |
|
91 |
super(ComputeManager, self).__init__(service_name="compute", |
92 |
*args, **kwargs) |
93 |
@@ -926,6 +928,10 @@ class ComputeManager(manager.SchedulerDependentManager): |
94 |
self._notify_about_instance_usage(context, instance, "delete.end", |
95 |
system_metadata=system_meta) |
96 |
|
97 |
+ if FLAGS.vnc_enabled: |
98 |
+ self.consoleauth_rpcapi.delete_tokens_for_instance(context, |
99 |
+ instance["uuid"]) |
100 |
+ |
101 |
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) |
102 |
@wrap_instance_fault |
103 |
def terminate_instance(self, context, instance): |
104 |
@@ -1989,6 +1995,12 @@ class ComputeManager(manager.SchedulerDependentManager): |
105 |
return connection_info |
106 |
|
107 |
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) |
108 |
+ @wrap_instance_fault |
109 |
+ def validate_console_port(self, ctxt, instance, port, console_type): |
110 |
+ console_info = self.driver.get_vnc_console(instance) |
111 |
+ return console_info['port'] == port |
112 |
+ |
113 |
+ @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) |
114 |
@reverts_task_state |
115 |
@wrap_instance_fault |
116 |
def reserve_block_device_name(self, context, instance, device): |
117 |
diff --git a/nova/compute/rpcapi.py b/nova/compute/rpcapi.py |
118 |
index 2e3873c..afec290 100644 |
119 |
--- a/nova/compute/rpcapi.py |
120 |
+++ b/nova/compute/rpcapi.py |
121 |
@@ -259,6 +259,13 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): |
122 |
instance=instance_p, console_type=console_type), |
123 |
topic=_compute_topic(self.topic, ctxt, None, instance)) |
124 |
|
125 |
+ def validate_console_port(self, ctxt, instance, port, console_type): |
126 |
+ instance_p = jsonutils.to_primitive(instance) |
127 |
+ return self.call(ctxt, self.make_msg('validate_console_port', |
128 |
+ instance=instance_p, port=port, console_type=console_type), |
129 |
+ topic=_compute_topic(self.topic, ctxt, |
130 |
+ None, instance)) |
131 |
+ |
132 |
def host_maintenance_mode(self, ctxt, host_param, mode, host): |
133 |
'''Set host maintenance mode |
134 |
|
135 |
diff --git a/nova/consoleauth/manager.py b/nova/consoleauth/manager.py |
136 |
index 61efdd0..e715f98 100644 |
137 |
--- a/nova/consoleauth/manager.py |
138 |
+++ b/nova/consoleauth/manager.py |
139 |
@@ -20,6 +20,8 @@ |
140 |
|
141 |
import time |
142 |
|
143 |
+from nova.compute import rpcapi as compute_rpcapi |
144 |
+from nova.db import api as db |
145 |
from nova import flags |
146 |
from nova import manager |
147 |
from nova.openstack.common import cfg |
148 |
@@ -56,10 +58,21 @@ class ConsoleAuthManager(manager.Manager): |
149 |
from nova.common import memorycache as memcache |
150 |
self.mc = memcache.Client(FLAGS.memcached_servers, |
151 |
debug=0) |
152 |
+ self.compute_rpcapi = compute_rpcapi.ComputeAPI() |
153 |
+ |
154 |
+ def _get_tokens_for_instance(self, instance_uuid): |
155 |
+ tokens_str = self.mc.get(instance_uuid.encode('UTF-8')) |
156 |
+ if not tokens_str: |
157 |
+ tokens = [] |
158 |
+ else: |
159 |
+ tokens = jsonutils.loads(tokens_str) |
160 |
+ return tokens |
161 |
|
162 |
def authorize_console(self, context, token, console_type, host, port, |
163 |
- internal_access_path): |
164 |
+ internal_access_path, instance_uuid=None): |
165 |
+ |
166 |
token_dict = {'token': token, |
167 |
+ 'instance_uuid': instance_uuid, |
168 |
'console_type': console_type, |
169 |
'host': host, |
170 |
'port': port, |
171 |
@@ -67,11 +80,35 @@ class ConsoleAuthManager(manager.Manager): |
172 |
'last_activity_at': time.time()} |
173 |
data = jsonutils.dumps(token_dict) |
174 |
self.mc.set(token.encode('UTF-8'), data, FLAGS.console_token_ttl) |
175 |
+ if instance_uuid is not None: |
176 |
+ tokens = self._get_tokens_for_instance(instance_uuid) |
177 |
+ tokens.append(token) |
178 |
+ self.mc.set(instance_uuid.encode('UTF-8'), |
179 |
+ jsonutils.dumps(tokens)) |
180 |
+ |
181 |
LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) |
182 |
|
183 |
+ def _validate_token(self, context, token): |
184 |
+ instance_uuid = token['instance_uuid'] |
185 |
+ if instance_uuid is None: |
186 |
+ return False |
187 |
+ instance = db.instance_get_by_uuid(context, instance_uuid) |
188 |
+ return self.compute_rpcapi.validate_console_port(context, |
189 |
+ instance, |
190 |
+ token['port'], |
191 |
+ token['console_type']) |
192 |
+ |
193 |
def check_token(self, context, token): |
194 |
token_str = self.mc.get(token.encode('UTF-8')) |
195 |
token_valid = (token_str is not None) |
196 |
LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) |
197 |
if token_valid: |
198 |
- return jsonutils.loads(token_str) |
199 |
+ token = jsonutils.loads(token_str) |
200 |
+ if self._validate_token(context, token): |
201 |
+ return token |
202 |
+ |
203 |
+ def delete_tokens_for_instance(self, context, instance_uuid): |
204 |
+ tokens = self._get_tokens_for_instance(instance_uuid) |
205 |
+ for token in tokens: |
206 |
+ self.mc.delete(token) |
207 |
+ self.mc.delete(instance_uuid.encode('UTF-8')) |
208 |
diff --git a/nova/consoleauth/rpcapi.py b/nova/consoleauth/rpcapi.py |
209 |
index 2fafe3f..b3b34c1 100644 |
210 |
--- a/nova/consoleauth/rpcapi.py |
211 |
+++ b/nova/consoleauth/rpcapi.py |
212 |
@@ -49,14 +49,20 @@ class ConsoleAuthAPI(nova.openstack.common.rpc.proxy.RpcProxy): |
213 |
default_version=self.BASE_RPC_API_VERSION) |
214 |
|
215 |
def authorize_console(self, ctxt, token, console_type, host, port, |
216 |
- internal_access_path): |
217 |
+ internal_access_path, instance_uuid=None): |
218 |
# The remote side doesn't return anything, but we want to block |
219 |
# until it completes. |
220 |
return self.call(ctxt, |
221 |
self.make_msg('authorize_console', |
222 |
token=token, console_type=console_type, |
223 |
host=host, port=port, |
224 |
- internal_access_path=internal_access_path)) |
225 |
+ internal_access_path=internal_access_path, |
226 |
+ instance_uuid=instance_uuid)) |
227 |
|
228 |
def check_token(self, ctxt, token): |
229 |
return self.call(ctxt, self.make_msg('check_token', token=token)) |
230 |
+ |
231 |
+ def delete_tokens_for_instance(self, ctxt, instance_uuid): |
232 |
+ return self.call(ctxt, |
233 |
+ self.make_msg('delete_tokens_for_instance', |
234 |
+ instance_uuid=instance_uuid)) |
235 |
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py |
236 |
index 10b89fd..b745798 100644 |
237 |
--- a/nova/tests/compute/test_compute.py |
238 |
+++ b/nova/tests/compute/test_compute.py |
239 |
@@ -1372,6 +1372,24 @@ class ComputeTestCase(BaseTestCase): |
240 |
self.compute._delete_instance(self.context, |
241 |
instance=jsonutils.to_primitive(instance)) |
242 |
|
243 |
+ def test_delete_instance_deletes_console_auth_tokens(self): |
244 |
+ instance = self._create_fake_instance() |
245 |
+ self.flags(vnc_enabled=True) |
246 |
+ |
247 |
+ self.tokens_deleted = False |
248 |
+ |
249 |
+ def fake_delete_tokens(*args, **kwargs): |
250 |
+ self.tokens_deleted = True |
251 |
+ |
252 |
+ cauth_rpcapi = self.compute.consoleauth_rpcapi |
253 |
+ self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance', |
254 |
+ fake_delete_tokens) |
255 |
+ |
256 |
+ self.compute._delete_instance(self.context, |
257 |
+ instance=jsonutils.to_primitive(instance)) |
258 |
+ |
259 |
+ self.assertTrue(self.tokens_deleted) |
260 |
+ |
261 |
def test_instance_termination_exception_sets_error(self): |
262 |
"""Test that we handle InstanceTerminationFailure |
263 |
which is propagated up from the underlying driver. |
264 |
@@ -4505,7 +4523,9 @@ class ComputeAPITestCase(BaseTestCase): |
265 |
'console_type': fake_console_type, |
266 |
'host': 'fake_console_host', |
267 |
'port': 'fake_console_port', |
268 |
- 'internal_access_path': 'fake_access_path'} |
269 |
+ 'internal_access_path': 'fake_access_path', |
270 |
+ 'instance_uuid': fake_instance["uuid"]} |
271 |
+ |
272 |
fake_connect_info2 = copy.deepcopy(fake_connect_info) |
273 |
fake_connect_info2['access_url'] = 'fake_console_url' |
274 |
|
275 |
@@ -4539,6 +4559,36 @@ class ComputeAPITestCase(BaseTestCase): |
276 |
|
277 |
db.instance_destroy(self.context, instance['uuid']) |
278 |
|
279 |
+ def test_validate_console_port(self): |
280 |
+ self.flags(vnc_enabled=True) |
281 |
+ instance = jsonutils.to_primitive(self._create_fake_instance()) |
282 |
+ |
283 |
+ def fake_driver_get_console(*args, **kwargs): |
284 |
+ return {'host': "fake_host", 'port': "5900", |
285 |
+ 'internal_access_path': None} |
286 |
+ self.stubs.Set(self.compute.driver, "get_vnc_console", |
287 |
+ fake_driver_get_console) |
288 |
+ |
289 |
+ self.assertTrue(self.compute.validate_console_port(self.context, |
290 |
+ instance, |
291 |
+ "5900", |
292 |
+ "novnc")) |
293 |
+ |
294 |
+ def test_validate_console_port_wrong_port(self): |
295 |
+ self.flags(vnc_enabled=True) |
296 |
+ instance = jsonutils.to_primitive(self._create_fake_instance()) |
297 |
+ |
298 |
+ def fake_driver_get_console(*args, **kwargs): |
299 |
+ return {'host': "fake_host", 'port': "5900", |
300 |
+ 'internal_access_path': None} |
301 |
+ self.stubs.Set(self.compute.driver, "get_vnc_console", |
302 |
+ fake_driver_get_console) |
303 |
+ |
304 |
+ self.assertFalse(self.compute.validate_console_port(self.context, |
305 |
+ instance, |
306 |
+ "wrongport", |
307 |
+ "novnc")) |
308 |
+ |
309 |
def test_console_output(self): |
310 |
fake_instance = {'uuid': 'fake_uuid', |
311 |
'host': 'fake_compute_host'} |
312 |
diff --git a/nova/tests/compute/test_rpcapi.py b/nova/tests/compute/test_rpcapi.py |
313 |
index 559a56a..c42047b 100644 |
314 |
--- a/nova/tests/compute/test_rpcapi.py |
315 |
+++ b/nova/tests/compute/test_rpcapi.py |
316 |
@@ -168,6 +168,11 @@ class ComputeRpcAPITestCase(test.TestCase): |
317 |
self._test_compute_api('get_vnc_console', 'call', |
318 |
instance=self.fake_instance, console_type='type') |
319 |
|
320 |
+ def test_validate_console_port(self): |
321 |
+ self._test_compute_api('validate_console_port', 'call', |
322 |
+ instance=self.fake_instance, port="5900", |
323 |
+ console_type="novnc") |
324 |
+ |
325 |
def test_host_maintenance_mode(self): |
326 |
self._test_compute_api('host_maintenance_mode', 'call', |
327 |
host_param='param', mode='mode', host='host') |
328 |
diff --git a/nova/tests/consoleauth/test_consoleauth.py b/nova/tests/consoleauth/test_consoleauth.py |
329 |
index da50eb8..4bea85a 100644 |
330 |
--- a/nova/tests/consoleauth/test_consoleauth.py |
331 |
+++ b/nova/tests/consoleauth/test_consoleauth.py |
332 |
@@ -45,8 +45,73 @@ class ConsoleauthTestCase(test.TestCase): |
333 |
"""Test that tokens expire correctly.""" |
334 |
token = 'mytok' |
335 |
self.flags(console_token_ttl=1) |
336 |
+ |
337 |
+ def fake_validate_token(*args, **kwargs): |
338 |
+ return True |
339 |
+ self.stubs.Set(self.manager, |
340 |
+ "_validate_token", |
341 |
+ fake_validate_token) |
342 |
+ |
343 |
self.manager.authorize_console(self.context, token, 'novnc', |
344 |
- '127.0.0.1', 'host', '') |
345 |
+ '127.0.0.1', '8080', 'host', "1234") |
346 |
self.assertTrue(self.manager.check_token(self.context, token)) |
347 |
time.sleep(1.1) |
348 |
self.assertFalse(self.manager.check_token(self.context, token)) |
349 |
+ |
350 |
+ def test_multiple_tokens_for_instance(self): |
351 |
+ tokens = ["token" + str(i) for i in xrange(10)] |
352 |
+ instance = "12345" |
353 |
+ |
354 |
+ def fake_validate_token(*args, **kwargs): |
355 |
+ return True |
356 |
+ |
357 |
+ self.stubs.Set(self.manager, "_validate_token", |
358 |
+ fake_validate_token) |
359 |
+ for token in tokens: |
360 |
+ self.manager.authorize_console(self.context, token, 'novnc', |
361 |
+ '127.0.0.1', '8080', 'host', |
362 |
+ instance) |
363 |
+ |
364 |
+ for token in tokens: |
365 |
+ self.assertTrue(self.manager.check_token(self.context, token)) |
366 |
+ |
367 |
+ def test_delete_tokens_for_instance(self): |
368 |
+ instance = "12345" |
369 |
+ tokens = ["token" + str(i) for i in xrange(10)] |
370 |
+ |
371 |
+ def fake_validate_token(*args, **kwargs): |
372 |
+ return True |
373 |
+ self.stubs.Set(self.manager, "_validate_token", |
374 |
+ fake_validate_token) |
375 |
+ |
376 |
+ for token in tokens: |
377 |
+ self.manager.authorize_console(self.context, token, 'novnc', |
378 |
+ '127.0.0.1', '8080', 'host', |
379 |
+ instance) |
380 |
+ self.manager.delete_tokens_for_instance(self.context, instance) |
381 |
+ stored_tokens = self.manager._get_tokens_for_instance(instance) |
382 |
+ |
383 |
+ self.assertEqual(len(stored_tokens), 0) |
384 |
+ |
385 |
+ for token in tokens: |
386 |
+ self.assertFalse(self.manager.check_token(self.context, token)) |
387 |
+ |
388 |
+ def test_wrong_token_has_port(self): |
389 |
+ token = 'mytok' |
390 |
+ |
391 |
+ def fake_validate_token(*args, **kwargs): |
392 |
+ return False |
393 |
+ |
394 |
+ self.stubs.Set(self.manager, "_validate_token", |
395 |
+ fake_validate_token) |
396 |
+ |
397 |
+ self.manager.authorize_console(self.context, token, 'novnc', |
398 |
+ '127.0.0.1', '8080', 'host', |
399 |
+ instance_uuid='instance') |
400 |
+ self.assertFalse(self.manager.check_token(self.context, token)) |
401 |
+ |
402 |
+ def test_console_no_instance_uuid(self): |
403 |
+ self.manager.authorize_console(self.context, "token", 'novnc', |
404 |
+ '127.0.0.1', '8080', 'host', |
405 |
+ instance_uuid=None) |
406 |
+ self.assertFalse(self.manager.check_token(self.context, "token")) |
407 |
diff --git a/nova/tests/consoleauth/test_rpcapi.py b/nova/tests/consoleauth/test_rpcapi.py |
408 |
index 10484c7..c1e7a46 100644 |
409 |
--- a/nova/tests/consoleauth/test_rpcapi.py |
410 |
+++ b/nova/tests/consoleauth/test_rpcapi.py |
411 |
@@ -68,7 +68,11 @@ class ConsoleAuthRpcAPITestCase(test.TestCase): |
412 |
def test_authorize_console(self): |
413 |
self._test_consoleauth_api('authorize_console', token='token', |
414 |
console_type='ctype', host='h', port='p', |
415 |
- internal_access_path='iap') |
416 |
+ internal_access_path='iap', instance_uuid="1234") |
417 |
|
418 |
def test_check_token(self): |
419 |
self._test_consoleauth_api('check_token', token='t') |
420 |
+ |
421 |
+ def test_delete_tokens_for_instnace(self): |
422 |
+ self._test_consoleauth_api('delete_tokens_for_instance', |
423 |
+ instance_uuid="instance") |
424 |
-- |
425 |
1.7.12.4 |