Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 702653 Details for
Bug 915586
CVE-2013-0335 OpenStack nova: VNC proxy can connect to the wrong VM
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
openstack-folsom-CVE-2013-0335.patch
openstack-folsom-CVE-2013-0335.patch (text/plain), 23.67 KB, created by
Kurt Seifried
on 2013-02-26 07:38:04 UTC
(
hide
)
Description:
openstack-folsom-CVE-2013-0335.patch
Filename:
MIME Type:
Creator:
Kurt Seifried
Created:
2013-02-26 07:38:04 UTC
Size:
23.67 KB
patch
obsolete
>commit 3b0f4cf6bea33e6ee1893f6e872d968b0c309f88 >Author: John Herndon <john.herndon@hp.com> >Date: Tue Feb 19 22:53:49 2013 +0000 > > Flush tokens on instance delete > > Force console auth service to flush all tokens > associated with an instance when it is deleted. > This will fix bug 1125378, where the console for > the wrong instance can be connected to via the > console if the correct circumstances occur. This > change also adds a call to validate the token > when it is used. This check will ensure that all > tokens are valid for their target instances. > Tokens can become scrambled when a compute node is > restarted, because the virt driver may not > assign ports in the same way. > > Change-Id: I0d83ec6c4dbfef1af912a200ee15f8052f72da96 > fixes: bug 1125378 > >diff --git a/nova/common/memorycache.py b/nova/common/memorycache.py >index f89e4b2..c124784 100644 >--- a/nova/common/memorycache.py >+++ b/nova/common/memorycache.py >@@ -83,3 +83,8 @@ class Client(object): > new_value = int(value) + delta > self.cache[key] = (self.cache[key][0], str(new_value)) > return new_value >+ >+ def delete(self, key, time=0): >+ """Deletes the value associated with a key.""" >+ if key in self.cache: >+ del self.cache[key] >diff --git a/nova/compute/api.py b/nova/compute/api.py >index cc07a99..f917e37 100644 >--- a/nova/compute/api.py >+++ b/nova/compute/api.py >@@ -2189,8 +2189,9 @@ class API(base.Base): > instance=instance, console_type=console_type) > > self.consoleauth_rpcapi.authorize_console(context, >- connect_info['token'], console_type, connect_info['host'], >- connect_info['port'], connect_info['internal_access_path']) >+ connect_info['token'], console_type, >+ connect_info['host'], connect_info['port'], >+ connect_info['internal_access_path'], instance['uuid']) > > return {'url': connect_info['access_url']} > >@@ -2207,10 +2208,11 @@ class API(base.Base): > """Get a url to an instance Console.""" > connect_info = self.compute_rpcapi.get_spice_console(context, > instance=instance, console_type=console_type) >- >+ print connect_info > self.consoleauth_rpcapi.authorize_console(context, >- connect_info['token'], console_type, connect_info['host'], >- connect_info['port'], connect_info['internal_access_path']) >+ connect_info['token'], console_type, >+ connect_info['host'], connect_info['port'], >+ connect_info['internal_access_path'], instance['uuid']) > > return {'url': connect_info['access_url']} > >diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py >index 1e30331..22e31a8 100644 >--- a/nova/compute/cells_api.py >+++ b/nova/compute/cells_api.py >@@ -465,7 +465,8 @@ class ComputeCellsAPI(compute_api.API): > > self.consoleauth_rpcapi.authorize_console(context, > connect_info['token'], console_type, connect_info['host'], >- connect_info['port'], connect_info['internal_access_path']) >+ connect_info['port'], connect_info['internal_access_path'], >+ instance_uuid=instance['uuid']) > return {'url': connect_info['access_url']} > > @wrap_check_policy >@@ -480,7 +481,8 @@ class ComputeCellsAPI(compute_api.API): > > self.consoleauth_rpcapi.authorize_console(context, > connect_info['token'], console_type, connect_info['host'], >- connect_info['port'], connect_info['internal_access_path']) >+ connect_info['port'], connect_info['internal_access_path'], >+ instance_uuid=instance['uuid']) > return {'url': connect_info['access_url']} > > @validate_cell >diff --git a/nova/compute/manager.py b/nova/compute/manager.py >index afeb9f0..4a6ab8e 100755 >--- a/nova/compute/manager.py >+++ b/nova/compute/manager.py >@@ -50,6 +50,7 @@ from nova.compute import task_states > from nova.compute import utils as compute_utils > from nova.compute import vm_states > from nova import conductor >+from nova import consoleauth > import nova.context > from nova import exception > from nova import hooks >@@ -317,7 +318,7 @@ class ComputeVirtAPI(virtapi.VirtAPI): > class ComputeManager(manager.SchedulerDependentManager): > """Manages the running instances from creation to destruction.""" > >- RPC_API_VERSION = '2.25' >+ RPC_API_VERSION = '2.26' > > def __init__(self, compute_driver=None, *args, **kwargs): > """Load configuration options and connect to the hypervisor.""" >@@ -335,6 +336,8 @@ class ComputeManager(manager.SchedulerDependentManager): > self.conductor_api = conductor.API() > self.is_quantum_security_groups = ( > openstack_driver.is_quantum_security_groups()) >+ self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI() >+ > super(ComputeManager, self).__init__(service_name="compute", > *args, **kwargs) > >@@ -1223,6 +1226,10 @@ class ComputeManager(manager.SchedulerDependentManager): > self._notify_about_instance_usage(context, instance, "delete.end", > system_metadata=system_meta) > >+ if CONF.vnc_enabled or CONF.spice.enabled: >+ self.consoleauth_rpcapi.delete_tokens_for_instance(context, >+ instance['uuid']) >+ > @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) > @wrap_instance_event > @wrap_instance_fault >@@ -2555,6 +2562,16 @@ class ComputeManager(manager.SchedulerDependentManager): > > return connect_info > >+ @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) >+ @wrap_instance_fault >+ def validate_console_port(self, ctxt, instance, port, console_type): >+ if console_type == "spice-html5": >+ console_info = self.driver.get_spice_console(instance) >+ else: >+ console_info = self.driver.get_vnc_console(instance) >+ >+ return console_info['port'] == port >+ > def _attach_volume_boot(self, context, instance, volume, mountpoint): > """Attach a volume to an instance at boot time. So actual attach > is done by instance creation""" >diff --git a/nova/compute/rpcapi.py b/nova/compute/rpcapi.py >index 0be9972..67dfc6a 100644 >--- a/nova/compute/rpcapi.py >+++ b/nova/compute/rpcapi.py >@@ -161,6 +161,8 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): > 2.23 - Remove network_info from reboot_instance > 2.24 - Added get_spice_console method > 2.25 - Add attach_interface() and detach_interface() >+ 2.26 - Add validate_console_token to ensure the service connects to >+ vnc on the correct port > ''' > > # >@@ -321,6 +323,14 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): > topic=_compute_topic(self.topic, ctxt, None, instance), > version='2.24') > >+ def validate_console_port(self, ctxt, instance, port, console_type): >+ instance_p = jsonutils.to_primitive(instance) >+ return self.call(ctxt, self.make_msg('validate_console_port', >+ instance=instance_p, port=port, console_type=console_type), >+ topic=_compute_topic(self.topic, ctxt, >+ None, instance), >+ version='2.26') >+ > def host_maintenance_mode(self, ctxt, host_param, mode, host): > '''Set host maintenance mode > >diff --git a/nova/consoleauth/manager.py b/nova/consoleauth/manager.py >index 74321a2..56e94df 100644 >--- a/nova/consoleauth/manager.py >+++ b/nova/consoleauth/manager.py >@@ -23,6 +23,8 @@ import time > from oslo.config import cfg > > from nova.common import memorycache >+from nova.compute import rpcapi as compute_rpcapi >+from nova.conductor import api as conductor_api > from nova import manager > from nova.openstack.common import jsonutils > from nova.openstack.common import log as logging >@@ -46,15 +48,27 @@ CONF.register_opts(consoleauth_opts) > class ConsoleAuthManager(manager.Manager): > """Manages token based authentication.""" > >- RPC_API_VERSION = '1.1' >+ RPC_API_VERSION = '1.2' > > def __init__(self, scheduler_driver=None, *args, **kwargs): > super(ConsoleAuthManager, self).__init__(*args, **kwargs) > self.mc = memorycache.get_client() >+ self.compute_rpcapi = compute_rpcapi.ComputeAPI() >+ self.conductor_api = conductor_api.API() >+ >+ def _get_tokens_for_instance(self, instance_uuid): >+ tokens_str = self.mc.get(instance_uuid.encode('UTF-8')) >+ if not tokens_str: >+ tokens = [] >+ else: >+ tokens = jsonutils.loads(tokens_str) >+ return tokens > > def authorize_console(self, context, token, console_type, host, port, >- internal_access_path): >+ internal_access_path, instance_uuid=None): >+ > token_dict = {'token': token, >+ 'instance_uuid': instance_uuid, > 'console_type': console_type, > 'host': host, > 'port': port, >@@ -62,14 +76,39 @@ class ConsoleAuthManager(manager.Manager): > 'last_activity_at': time.time()} > data = jsonutils.dumps(token_dict) > self.mc.set(token.encode('UTF-8'), data, CONF.console_token_ttl) >+ if instance_uuid is not None: >+ tokens = self._get_tokens_for_instance(instance_uuid) >+ tokens.append(token) >+ self.mc.set(instance_uuid.encode('UTF-8'), >+ jsonutils.dumps(tokens)) >+ > LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) > >+ def _validate_token(self, context, token): >+ instance_uuid = token['instance_uuid'] >+ if instance_uuid is None: >+ return False >+ instance = self.conductor_api.instance_get_by_uuid(context, >+ instance_uuid) >+ return self.compute_rpcapi.validate_console_port(context, >+ instance, >+ token['port'], >+ token['console_type']) >+ > def check_token(self, context, token): > token_str = self.mc.get(token.encode('UTF-8')) > token_valid = (token_str is not None) > LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) > if token_valid: >- return jsonutils.loads(token_str) >+ token = jsonutils.loads(token_str) >+ if self._validate_token(context, token): >+ return token >+ >+ def delete_tokens_for_instance(self, context, instance_uuid): >+ tokens = self._get_tokens_for_instance(instance_uuid) >+ for token in tokens: >+ self.mc.delete(token) >+ self.mc.delete(instance_uuid.encode('UTF-8')) > > def get_backdoor_port(self, context): > return self.backdoor_port >diff --git a/nova/consoleauth/rpcapi.py b/nova/consoleauth/rpcapi.py >index 813143f..474f3ad 100644 >--- a/nova/consoleauth/rpcapi.py >+++ b/nova/consoleauth/rpcapi.py >@@ -32,6 +32,8 @@ class ConsoleAuthAPI(nova.openstack.common.rpc.proxy.RpcProxy): > > 1.0 - Initial version. > 1.1 - Added get_backdoor_port() >+ 1.2 - Added instance_uuid to authorize_console, and >+ delete_tokens_for_instance > ''' > > # >@@ -50,18 +52,26 @@ class ConsoleAuthAPI(nova.openstack.common.rpc.proxy.RpcProxy): > default_version=self.BASE_RPC_API_VERSION) > > def authorize_console(self, ctxt, token, console_type, host, port, >- internal_access_path): >+ internal_access_path, instance_uuid=None): > # The remote side doesn't return anything, but we want to block > # until it completes. > return self.call(ctxt, > self.make_msg('authorize_console', > token=token, console_type=console_type, > host=host, port=port, >- internal_access_path=internal_access_path)) >+ internal_access_path=internal_access_path, >+ instance_uuid=instance_uuid), >+ version="1.2") > > def check_token(self, ctxt, token): > return self.call(ctxt, self.make_msg('check_token', token=token)) > >+ def delete_tokens_for_instance(self, ctxt, instance_uuid): >+ return self.call(ctxt, >+ self.make_msg('delete_tokens_for_instance', >+ instance_uuid=instance_uuid), >+ version="1.2") >+ > def get_backdoor_port(self, ctxt, host): > return self.call(ctxt, self.make_msg('get_backdoor_port'), > version='1.1') >diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py >index e19470d..02bbaaa 100644 >--- a/nova/tests/compute/test_compute.py >+++ b/nova/tests/compute/test_compute.py >@@ -1420,6 +1420,54 @@ class ComputeTestCase(BaseTestCase): > > self.compute.terminate_instance(self.context, instance=instance) > >+ def test_validate_console_port_vnc(self): >+ self.flags(vnc_enabled=True) >+ self.flags(enabled=True, group='spice') >+ instance = jsonutils.to_primitive(self._create_fake_instance()) >+ >+ def fake_driver_get_console(*args, **kwargs): >+ return {'host': "fake_host", 'port': "5900", >+ 'internal_access_path': None} >+ self.stubs.Set(self.compute.driver, "get_vnc_console", >+ fake_driver_get_console) >+ >+ self.assertTrue(self.compute.validate_console_port(self.context, >+ instance, >+ "5900", >+ "novnc")) >+ >+ def test_validate_console_port_spice(self): >+ self.flags(vnc_enabled=True) >+ self.flags(enabled=True, group='spice') >+ instance = jsonutils.to_primitive(self._create_fake_instance()) >+ >+ def fake_driver_get_console(*args, **kwargs): >+ return {'host': "fake_host", 'port': "5900", >+ 'internal_access_path': None} >+ self.stubs.Set(self.compute.driver, "get_spice_console", >+ fake_driver_get_console) >+ >+ self.assertTrue(self.compute.validate_console_port(self.context, >+ instance, >+ "5900", >+ "spice-html5")) >+ >+ def test_validate_console_port_wrong_port(self): >+ self.flags(vnc_enabled=True) >+ self.flags(enabled=True, group='spice') >+ instance = jsonutils.to_primitive(self._create_fake_instance()) >+ >+ def fake_driver_get_console(*args, **kwargs): >+ return {'host': "fake_host", 'port': "5900", >+ 'internal_access_path': None} >+ self.stubs.Set(self.compute.driver, "get_vnc_console", >+ fake_driver_get_console) >+ >+ self.assertFalse(self.compute.validate_console_port(self.context, >+ instance, >+ "wrongport", >+ "spice-html5")) >+ > def test_xvpvnc_vnc_console(self): > # Make sure we can a vnc console for an instance. > self.flags(vnc_enabled=True) >@@ -1715,6 +1763,25 @@ class ComputeTestCase(BaseTestCase): > instance=jsonutils.to_primitive(instance), > bdms={}) > >+ def test_delete_instance_deletes_console_auth_tokens(self): >+ instance = self._create_fake_instance() >+ self.flags(vnc_enabled=True) >+ >+ self.tokens_deleted = False >+ >+ def fake_delete_tokens(*args, **kwargs): >+ self.tokens_deleted = True >+ >+ cauth_rpcapi = self.compute.consoleauth_rpcapi >+ self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance', >+ fake_delete_tokens) >+ >+ self.compute._delete_instance(self.context, >+ instance=jsonutils.to_primitive(instance), >+ bdms={}) >+ >+ self.assertTrue(self.tokens_deleted) >+ > def test_instance_termination_exception_sets_error(self): > """Test that we handle InstanceTerminationFailure > which is propagated up from the underlying driver. >@@ -5735,7 +5802,8 @@ class ComputeAPITestCase(BaseTestCase): > 'console_type': fake_console_type, > 'host': 'fake_console_host', > 'port': 'fake_console_port', >- 'internal_access_path': 'fake_access_path'} >+ 'internal_access_path': 'fake_access_path', >+ 'instance_uuid': fake_instance['uuid']} > fake_connect_info2 = copy.deepcopy(fake_connect_info) > fake_connect_info2['access_url'] = 'fake_console_url' > >@@ -5747,7 +5815,7 @@ class ComputeAPITestCase(BaseTestCase): > 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION} > rpc_msg2 = {'method': 'authorize_console', > 'args': fake_connect_info, >- 'version': '1.0'} >+ 'version': '1.2'} > > rpc.call(self.context, 'compute.%s' % fake_instance['host'], > rpc_msg1, None).AndReturn(fake_connect_info2) >@@ -5779,7 +5847,8 @@ class ComputeAPITestCase(BaseTestCase): > 'console_type': fake_console_type, > 'host': 'fake_console_host', > 'port': 'fake_console_port', >- 'internal_access_path': 'fake_access_path'} >+ 'internal_access_path': 'fake_access_path', >+ 'instance_uuid': fake_instance['uuid']} > fake_connect_info2 = copy.deepcopy(fake_connect_info) > fake_connect_info2['access_url'] = 'fake_console_url' > >@@ -5791,7 +5860,7 @@ class ComputeAPITestCase(BaseTestCase): > 'version': '2.24'} > rpc_msg2 = {'method': 'authorize_console', > 'args': fake_connect_info, >- 'version': '1.0'} >+ 'version': '1.2'} > > rpc.call(self.context, 'compute.%s' % fake_instance['host'], > rpc_msg1, None).AndReturn(fake_connect_info2) >diff --git a/nova/tests/compute/test_rpcapi.py b/nova/tests/compute/test_rpcapi.py >index a78a138..6c40a95 100644 >--- a/nova/tests/compute/test_rpcapi.py >+++ b/nova/tests/compute/test_rpcapi.py >@@ -171,6 +171,12 @@ class ComputeRpcAPITestCase(test.TestCase): > instance=self.fake_instance, console_type='type', > version='2.24') > >+ def test_validate_console_port(self): >+ self._test_compute_api('validate_console_port', 'call', >+ instance=self.fake_instance, port="5900", >+ console_type="novnc", >+ version="2.26") >+ > def test_host_maintenance_mode(self): > self._test_compute_api('host_maintenance_mode', 'call', > host_param='param', mode='mode', host='host') >diff --git a/nova/tests/consoleauth/test_consoleauth.py b/nova/tests/consoleauth/test_consoleauth.py >index 15397a4..54e3d22 100644 >--- a/nova/tests/consoleauth/test_consoleauth.py >+++ b/nova/tests/consoleauth/test_consoleauth.py >@@ -42,12 +42,74 @@ class ConsoleauthTestCase(test.TestCase): > self.useFixture(test.TimeOverride()) > token = 'mytok' > self.flags(console_token_ttl=1) >+ >+ def fake_validate_console_port(*args, **kwargs): >+ return True >+ self.stubs.Set(self.manager.compute_rpcapi, >+ "validate_console_port", >+ fake_validate_console_port) >+ > self.manager.authorize_console(self.context, token, 'novnc', >- '127.0.0.1', 'host', '') >+ '127.0.0.1', '8080', 'host', >+ 'instance') > self.assertTrue(self.manager.check_token(self.context, token)) > timeutils.advance_time_seconds(1) > self.assertFalse(self.manager.check_token(self.context, token)) > >+ def test_multiple_tokens_for_instance(self): >+ tokens = ["token" + str(i) for i in xrange(10)] >+ instance = "12345" >+ >+ def fake_validate_console_port(*args, **kwargs): >+ return True >+ >+ self.stubs.Set(self.manager.compute_rpcapi, >+ "validate_console_port", >+ fake_validate_console_port) >+ for token in tokens: >+ self.manager.authorize_console(self.context, token, 'novnc', >+ '127.0.0.1', '8080', 'host', >+ instance) >+ >+ for token in tokens: >+ self.assertTrue(self.manager.check_token(self.context, token)) >+ >+ def test_delete_tokens_for_instance(self): >+ instance = "12345" >+ tokens = ["token" + str(i) for i in xrange(10)] >+ for token in tokens: >+ self.manager.authorize_console(self.context, token, 'novnc', >+ '127.0.0.1', '8080', 'host', >+ instance) >+ self.manager.delete_tokens_for_instance(self.context, instance) >+ stored_tokens = self.manager._get_tokens_for_instance(instance) >+ >+ self.assertEqual(len(stored_tokens), 0) >+ >+ for token in tokens: >+ self.assertFalse(self.manager.check_token(self.context, token)) >+ >+ def test_wrong_token_has_port(self): >+ token = 'mytok' >+ >+ def fake_validate_console_port(*args, **kwargs): >+ return False >+ >+ self.stubs.Set(self.manager.compute_rpcapi, >+ "validate_console_port", >+ fake_validate_console_port) >+ >+ self.manager.authorize_console(self.context, token, 'novnc', >+ '127.0.0.1', '8080', 'host', >+ instance_uuid='instance') >+ self.assertFalse(self.manager.check_token(self.context, token)) >+ >+ def test_console_no_instance_uuid(self): >+ self.manager.authorize_console(self.context, "token", 'novnc', >+ '127.0.0.1', '8080', 'host', >+ instance_uuid=None) >+ self.assertFalse(self.manager.check_token(self.context, "token")) >+ > def test_get_backdoor_port(self): > self.manager.backdoor_port = 59697 > port = self.manager.get_backdoor_port(self.context) >diff --git a/nova/tests/consoleauth/test_rpcapi.py b/nova/tests/consoleauth/test_rpcapi.py >index 15af5fd..53ca2e5 100644 >--- a/nova/tests/consoleauth/test_rpcapi.py >+++ b/nova/tests/consoleauth/test_rpcapi.py >@@ -65,11 +65,17 @@ class ConsoleAuthRpcAPITestCase(test.TestCase): > def test_authorize_console(self): > self._test_consoleauth_api('authorize_console', token='token', > console_type='ctype', host='h', port='p', >- internal_access_path='iap') >+ internal_access_path='iap', instance_uuid="instance", >+ version="1.2") > > def test_check_token(self): > self._test_consoleauth_api('check_token', token='t') > >+ def test_delete_tokens_for_instnace(self): >+ self._test_consoleauth_api('delete_tokens_for_instance', >+ instance_uuid="instance", >+ version='1.2') >+ > def test_get_backdoor_port(self): > self._test_consoleauth_api('get_backdoor_port', host='fake_host', > version='1.1')
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 915586
: 702653