Moderate severityNVD Advisory· Published Nov 5, 2013· Updated Apr 29, 2026
CVE-2013-4497
CVE-2013-4497
Description
The XenAPI backend in OpenStack Compute (Nova) Folsom, Grizzly, and Havana before 2013.2 does not properly apply security groups (1) when resizing an image or (2) during live migration, which allows remote attackers to bypass intended restrictions.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
novaPyPI | < 12.0.0a0 | 12.0.0a0 |
Affected products
5Patches
401de658210fdxenapi: apply firewall rules in finish_migrate
1 file changed · +15 −0
nova/virt/xenapi/vmops.py+15 −0 modified@@ -277,8 +277,23 @@ def finish_migration(self, context, migration, instance, disk_info, self._attach_mapped_block_devices(instance, block_device_info) + try: + self.firewall_driver.setup_basic_filtering( + instance, network_info) + except NotImplementedError: + # NOTE(salvatore-orlando): setup_basic_filtering might be + # empty or not implemented at all, as basic filter could + # be implemented with VIF rules created by xapi plugin + pass + + self.firewall_driver.prepare_instance_filter(instance, + network_info) + # 5. Start VM self._start(instance, vm_ref=vm_ref) + + self.firewall_driver.apply_instance_filter(instance, network_info) + self._update_instance_progress(context, instance, step=5, total_steps=RESIZE_TOTAL_STEPS)
df2ea2e3acdexenapi: enforce filters after live-migration
3 files changed · +41 −3
nova/tests/test_xenapi.py+21 −1 modified@@ -2723,7 +2723,27 @@ def test_post_live_migration_at_destination(self): # ensure method is present stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) self.conn = xenapi_conn.XenAPIDriver(fake.FakeVirtAPI(), False) - self.conn.post_live_migration_at_destination(None, None, None, None) + + fake_instance = "instance" + fake_network_info = "network_info" + + def fake_fw(instance, network_info): + self.assertEquals(instance, fake_instance) + self.assertEquals(network_info, fake_network_info) + fake_fw.called += 1 + + fake_fw.called = 0 + _vmops = self.conn._vmops + self.stubs.Set(_vmops.firewall_driver, + 'setup_basic_filtering', fake_fw) + self.stubs.Set(_vmops.firewall_driver, + 'prepare_instance_filter', fake_fw) + self.stubs.Set(_vmops.firewall_driver, + 'apply_instance_filter', fake_fw) + + self.conn.post_live_migration_at_destination(None, fake_instance, + fake_network_info, None) + self.assertEqual(fake_fw.called, 3) def test_check_can_live_migrate_destination_with_block_migration(self): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
nova/virt/xenapi/driver.py+2 −2 modified@@ -1,4 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2010 Citrix Systems, Inc. # Copyright 2010 OpenStack Foundation @@ -514,7 +513,8 @@ def post_live_migration_at_destination(self, ctxt, instance_ref, :params : block_migration: if true, post operation of block_migraiton. """ # TODO(JohnGarbutt) look at moving/downloading ramdisk and kernel - pass + self._vmops.post_live_migration_at_destination(ctxt, instance_ref, + network_info, block_device_info, block_device_info) def unfilter_instance(self, instance_ref, network_info): """Removes security groups configured for an instance."""
nova/virt/xenapi/vmops.py+18 −0 modified@@ -1737,6 +1737,24 @@ def live_migrate(self, context, instance, destination_hostname, recover_method(context, instance, destination_hostname, block_migration) + def post_live_migration_at_destination(self, context, instance, + network_info, block_migration, + block_device_info): + # FIXME(johngarbutt): we should block all traffic until we have + # applied security groups, however this requires changes to XenServer + try: + self.firewall_driver.setup_basic_filtering( + instance, network_info) + except NotImplementedError: + # NOTE(salvatore-orlando): setup_basic_filtering might be + # empty or not implemented at all, as basic filter could + # be implemented with VIF rules created by xapi plugin + pass + + self.firewall_driver.prepare_instance_filter(instance, + network_info) + self.firewall_driver.apply_instance_filter(instance, network_info) + def get_per_instance_usage(self): """Get usage info about each active instance.""" usage = {}
5cced7a6dd32xenapi: enforce filters after live-migration
3 files changed · +45 −16
nova/tests/virt/xenapi/test_xenapi.py+21 −1 modified@@ -3298,7 +3298,27 @@ def test_post_live_migration_at_destination(self): # ensure method is present stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) self.conn = xenapi_conn.XenAPIDriver(fake.FakeVirtAPI(), False) - self.conn.post_live_migration_at_destination(None, None, None, None) + + fake_instance = "instance" + fake_network_info = "network_info" + + def fake_fw(instance, network_info): + self.assertEquals(instance, fake_instance) + self.assertEquals(network_info, fake_network_info) + fake_fw.called += 1 + + fake_fw.called = 0 + _vmops = self.conn._vmops + self.stubs.Set(_vmops.firewall_driver, + 'setup_basic_filtering', fake_fw) + self.stubs.Set(_vmops.firewall_driver, + 'prepare_instance_filter', fake_fw) + self.stubs.Set(_vmops.firewall_driver, + 'apply_instance_filter', fake_fw) + + self.conn.post_live_migration_at_destination(None, fake_instance, + fake_network_info, None) + self.assertEqual(fake_fw.called, 3) def test_check_can_live_migrate_destination_with_block_migration(self): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
nova/virt/xenapi/driver.py+2 −3 modified@@ -1,4 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2010 Citrix Systems, Inc. # Copyright 2010 OpenStack Foundation @@ -555,8 +554,8 @@ def post_live_migration_at_destination(self, ctxt, instance_ref, :params network_info: instance network information :params : block_migration: if true, post operation of block_migraiton. """ - # TODO(JohnGarbutt) look at moving/downloading ramdisk and kernel - pass + self._vmops.post_live_migration_at_destination(ctxt, instance_ref, + network_info, block_device_info, block_device_info) def unfilter_instance(self, instance_ref, network_info): """Removes security groups configured for an instance."""
nova/virt/xenapi/vmops.py+22 −12 modified@@ -451,18 +451,7 @@ def inject_instance_data_step(undo_mgr, vm_ref, vdis): @step def setup_network_step(undo_mgr, vm_ref): self._create_vifs(instance, vm_ref, network_info) - - try: - self.firewall_driver.setup_basic_filtering( - instance, network_info) - except NotImplementedError: - # NOTE(salvatore-orlando): setup_basic_filtering might be - # empty or not implemented at all, as basic filter could - # be implemented with VIF rules created by xapi plugin - pass - - self.firewall_driver.prepare_instance_filter(instance, - network_info) + self._prepare_instance_filter(instance, network_info) @step def boot_instance_step(undo_mgr, vm_ref): @@ -690,6 +679,19 @@ def _configure_new_instance_with_agent(self, instance, vm_ref, # Reset network config agent.resetnetwork() + def _prepare_instance_filter(self, instance, network_info): + try: + self.firewall_driver.setup_basic_filtering( + instance, network_info) + except NotImplementedError: + # NOTE(salvatore-orlando): setup_basic_filtering might be + # empty or not implemented at all, as basic filter could + # be implemented with VIF rules created by xapi plugin + pass + + self.firewall_driver.prepare_instance_filter(instance, + network_info) + def _get_vm_opaque_ref(self, instance, check_rescue=False): """Get xapi OpaqueRef from a db record. :param check_rescue: if True will return the 'name'-rescue vm if it @@ -1904,6 +1906,14 @@ def live_migrate(self, context, instance, destination_hostname, recover_method(context, instance, destination_hostname, block_migration) + def post_live_migration_at_destination(self, context, instance, + network_info, block_migration, + block_device_info): + # FIXME(johngarbutt): we should block all traffic until we have + # applied security groups, however this requires changes to XenServer + self._prepare_instance_filter(instance, network_info) + self.firewall_driver.apply_instance_filter(instance, network_info) + def get_per_instance_usage(self): """Get usage info about each active instance.""" usage = {}
ba0d007fb78bxenapi: ensure finish_migration cleans on errors
3 files changed · +112 −63
nova/tests/virt/xenapi/stubs.py+5 −0 modified@@ -299,6 +299,9 @@ def fake_move_disks(self, instance, disk_info): vdi_rec['other_config']['nova_disk_type'] = 'root' return {'uuid': vdi_rec['uuid'], 'ref': vdi_ref} + def fake_wait_for_instance_to_start(self, *args): + pass + def fake_get_vdi(session, vm_ref): vdi_ref_parent = fake.create_vdi('derp-parent', fakesr) vdi_rec_parent = fake.get_record('VDI', vdi_ref_parent) @@ -320,6 +323,8 @@ def fake_generate_ephemeral(*args): pass stubs.Set(vmops.VMOps, '_destroy', fake_destroy) + stubs.Set(vmops.VMOps, '_wait_for_instance_to_start', + fake_wait_for_instance_to_start) stubs.Set(vm_utils, 'move_disks', fake_move_disks) stubs.Set(vm_utils, 'scan_default_sr', fake_sr) stubs.Set(vm_utils, 'get_vdi_for_vm_safely', fake_get_vdi)
nova/tests/virt/xenapi/test_vmops.py+47 −17 modified@@ -404,7 +404,8 @@ def test_spawn_performs_rollback_and_throws_exception(self): self.assertRaises(test.TestingException, self._test_spawn, throw_exception=test.TestingException()) - def test_finish_migration(self): + def _test_finish_migration(self, power_on=True, resize_instance=True, + throw_exception=None): self._stub_out_common() self.mox.StubOutWithMock(vm_utils, "move_disks") self.mox.StubOutWithMock(self.vmops, "_attach_mapped_block_devices") @@ -416,49 +417,78 @@ def test_finish_migration(self): disk_info = "disk_info" network_info = "net_info" image_meta = {"id": "image_id"} - resize_instance = True block_device_info = "bdi" session = self.vmops._session - root_vdi = "root_vdi" + self.vmops._ensure_instance_name_unique(name_label) + self.vmops._ensure_enough_free_mem(instance) + + di_type = "di_type" + vm_utils.determine_disk_image_type(image_meta).AndReturn(di_type) + + root_vdi = {"ref": "fake_ref"} vdis = {"root": root_vdi} vm_utils.move_disks(self.vmops._session, instance, disk_info).AndReturn(root_vdi) - self.vmops._resize_up_root_vdi(instance, root_vdi) - kernel_file = "kernel" ramdisk_file = "ramdisk" vm_utils.create_kernel_and_ramdisk(context, session, instance, name_label).AndReturn((kernel_file, ramdisk_file)) - di_type = "di_type" - vm_utils.determine_disk_image_type(image_meta).AndReturn(di_type) - self.vmops._ensure_instance_name_unique(name_label) - self.vmops._ensure_enough_free_mem(instance) vm_ref = "fake_vm_ref" self.vmops._create_vm_record(context, instance, name_label, vdis, di_type, kernel_file, ramdisk_file).AndReturn(vm_ref) - self.vmops._attach_disks(instance, vm_ref, name_label, vdis, di_type) + if resize_instance: + self.vmops._resize_up_root_vdi(instance, root_vdi) + self.vmops._attach_disks(instance, vm_ref, name_label, vdis, di_type, + None, None) + self.vmops._attach_mapped_block_devices(instance, block_device_info) + self.vmops._inject_instance_metadata(instance, vm_ref) + self.vmops._inject_auto_disk_config(instance, vm_ref) self.vmops._file_inject_vm_settings(instance, vm_ref, vdis, network_info) - self.vmops._create_vifs(instance, vm_ref, network_info) self.vmops.inject_network_info(instance, network_info, vm_ref) - self.vmops._inject_instance_metadata(instance, vm_ref) - self.vmops._attach_mapped_block_devices(instance, block_device_info) + self.vmops._create_vifs(instance, vm_ref, network_info) + self.vmops.firewall_driver.setup_basic_filtering(instance, + network_info).AndRaise(NotImplementedError) + self.vmops.firewall_driver.prepare_instance_filter(instance, + network_info) - self.vmops._start(instance, vm_ref) + if power_on: + self.vmops._start(instance, vm_ref) + self.vmops._wait_for_instance_to_start(instance, vm_ref) - self.vmops._update_instance_progress(context, instance, - step=5, total_steps=5) + self.vmops.firewall_driver.apply_instance_filter(instance, + network_info) + + last_call = self.vmops._update_instance_progress(context, instance, + step=5, total_steps=5) + if throw_exception: + last_call.AndRaise(throw_exception) + self.vmops._destroy(instance, vm_ref, network_info=network_info) + vm_utils.destroy_kernel_ramdisk(self.vmops._session, instance, + kernel_file, ramdisk_file) + vm_utils.safe_destroy_vdis(self.vmops._session, ["fake_ref"]) self.mox.ReplayAll() self.vmops.finish_migration(context, migration, instance, disk_info, network_info, image_meta, resize_instance, - block_device_info) + block_device_info, power_on) + + def test_finish_migration(self): + self._test_finish_migration() + + def test_finish_migration_no_power_on(self): + self._test_finish_migration(power_on=False, resize_instance=False) + + def test_finish_migrate_performs_rollback_on_error(self): + self.assertRaises(test.TestingException, self._test_finish_migration, + power_on=False, resize_instance=False, + throw_exception=test.TestingException()) def test_remove_hostname(self): vm, vm_ref = self.create_vm("dummy")
nova/virt/xenapi/vmops.py+60 −46 modified@@ -274,39 +274,32 @@ def _restore_orig_vm_and_cleanup_orphan(self, instance, def finish_migration(self, context, migration, instance, disk_info, network_info, image_meta, resize_instance, block_device_info=None, power_on=True): - root_vdi = vm_utils.move_disks(self._session, instance, disk_info) - vdis = {'root': root_vdi} - if resize_instance: - self._resize_up_root_vdi(instance, root_vdi) + def null_step_decorator(f): + return f - name_label = instance['name'] + def create_disks_step(undo_mgr, disk_image_type, image_meta, + name_label): + #TODO(johngarbutt) clean up the move_disks if this is not run + root_vdi = vm_utils.move_disks(self._session, instance, disk_info) - kernel_file, ramdisk_file = vm_utils.create_kernel_and_ramdisk( - context, self._session, instance, name_label) - - disk_image_type = vm_utils.determine_disk_image_type(image_meta) - self._ensure_instance_name_unique(name_label) - self._ensure_enough_free_mem(instance) - vm_ref = self._create_vm_record(context, instance, name_label, - vdis, disk_image_type, kernel_file, ramdisk_file) - - self._attach_disks(instance, vm_ref, name_label, vdis, - disk_image_type) + def undo_create_disks(): + vm_utils.safe_destroy_vdis(self._session, [root_vdi['ref']]) - self._file_inject_vm_settings(instance, vm_ref, vdis, network_info) - self._create_vifs(instance, vm_ref, network_info) - self.inject_network_info(instance, network_info, vm_ref) - self._inject_instance_metadata(instance, vm_ref) + undo_mgr.undo_with(undo_create_disks) + return {'root': root_vdi} - self._attach_mapped_block_devices(instance, block_device_info) + def completed_callback(): + self._update_instance_progress(context, instance, + step=5, + total_steps=RESIZE_TOTAL_STEPS) - # 5. Start VM - if power_on: - self._start(instance, vm_ref) - self._update_instance_progress(context, instance, - step=5, - total_steps=RESIZE_TOTAL_STEPS) + self._spawn(context, instance, image_meta, null_step_decorator, + create_disks_step, first_boot=False, injected_files=None, + admin_password=None, network_info=network_info, + block_device_info=block_device_info, name_label=None, + rescue=False, power_on=power_on, resize=resize_instance, + completed_callback=completed_callback) def _start(self, instance, vm_ref=None, bad_volumes_callback=None): """Power on a VM instance.""" @@ -336,6 +329,7 @@ def _start(self, instance, vm_ref=None, bad_volumes_callback=None): def spawn(self, context, instance, image_meta, injected_files, admin_password, network_info=None, block_device_info=None, name_label=None, rescue=False): + if block_device_info: LOG.debug(_("Block device information present: %s") % block_device_info, instance=instance) @@ -345,18 +339,9 @@ def spawn(self, context, instance, image_meta, injected_files, step = make_step_decorator(context, instance, self._update_instance_progress) - if name_label is None: - name_label = instance['name'] - - self._ensure_instance_name_unique(name_label) - self._ensure_enough_free_mem(instance) - - @step - def determine_disk_image_type_step(undo_mgr): - return vm_utils.determine_disk_image_type(image_meta) - @step - def create_disks_step(undo_mgr, disk_image_type, image_meta): + def create_disks_step(undo_mgr, disk_image_type, image_meta, + name_label): vdis = vm_utils.get_vdis_for_instance(context, self._session, instance, name_label, image_meta.get('id'), disk_image_type, block_device_info=block_device_info) @@ -369,6 +354,25 @@ def undo_create_disks(): undo_mgr.undo_with(undo_create_disks) return vdis + self._spawn(context, instance, image_meta, step, create_disks_step, + True, injected_files, admin_password, + network_info, block_device_info, name_label, rescue) + + def _spawn(self, context, instance, image_meta, step, create_disks_step, + first_boot, injected_files=None, admin_password=None, + network_info=None, block_device_info=None, + name_label=None, rescue=False, power_on=True, resize=True, + completed_callback=None): + if name_label is None: + name_label = instance['name'] + + self._ensure_instance_name_unique(name_label) + self._ensure_enough_free_mem(instance) + + @step + def determine_disk_image_type_step(undo_mgr): + return vm_utils.determine_disk_image_type(image_meta) + @step def create_kernel_ramdisk_step(undo_mgr): kernel_file, ramdisk_file = vm_utils.create_kernel_and_ramdisk( @@ -410,12 +414,15 @@ def attach_disks_step(undo_mgr, vm_ref, vdis, disk_image_type): instance=instance) root_vdi = vdis.get('root') - if root_vdi: + if root_vdi and resize: self._resize_up_root_vdi(instance, root_vdi) self._attach_disks(instance, vm_ref, name_label, vdis, disk_image_type, admin_password, injected_files) + if not first_boot: + self._attach_mapped_block_devices(instance, + block_device_info) if rescue: # NOTE(johannes): Attach root disk to rescue VM now, before @@ -427,7 +434,8 @@ def attach_disks_step(undo_mgr, vm_ref, vdis, disk_image_type): def inject_instance_data_step(undo_mgr, vm_ref, vdis): self._inject_instance_metadata(instance, vm_ref) self._inject_auto_disk_config(instance, vm_ref) - self._inject_hostname(instance, vm_ref, rescue) + if first_boot: + self._inject_hostname(instance, vm_ref, rescue) self._file_inject_vm_settings(instance, vm_ref, vdis, network_info) self.inject_network_info(instance, network_info, vm_ref) @@ -449,14 +457,16 @@ def setup_network_step(undo_mgr, vm_ref): @step def boot_instance_step(undo_mgr, vm_ref): - self._start(instance, vm_ref) - self._wait_for_instance_to_start(instance, vm_ref) + if power_on: + self._start(instance, vm_ref) + self._wait_for_instance_to_start(instance, vm_ref) @step def configure_booted_instance_step(undo_mgr, vm_ref): - self._configure_new_instance_with_agent(instance, vm_ref, - injected_files, admin_password) - self._remove_hostname(instance, vm_ref) + if first_boot: + self._configure_new_instance_with_agent(instance, vm_ref, + injected_files, admin_password) + self._remove_hostname(instance, vm_ref) @step def apply_security_group_filters_step(undo_mgr): @@ -471,7 +481,8 @@ def apply_security_group_filters_step(undo_mgr): # first step is something that completes rather quickly. disk_image_type = determine_disk_image_type_step(undo_mgr) - vdis = create_disks_step(undo_mgr, disk_image_type, image_meta) + vdis = create_disks_step(undo_mgr, disk_image_type, image_meta, + name_label) kernel_file, ramdisk_file = create_kernel_ramdisk_step(undo_mgr) vm_ref = create_vm_record_step(undo_mgr, vdis, disk_image_type, @@ -485,6 +496,9 @@ def apply_security_group_filters_step(undo_mgr): configure_booted_instance_step(undo_mgr, vm_ref) apply_security_group_filters_step(undo_mgr) + + if completed_callback: + completed_callback() except Exception: msg = _("Failed to spawn, rolling back") undo_mgr.rollback_and_reraise(msg=msg, instance=instance)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-27q4-38qf-m25hghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2013-4497ghsaADVISORY
- www.openwall.com/lists/oss-security/2013/11/03/2nvdWEB
- www.openwall.com/lists/oss-security/2013/11/03/3nvdWEB
- bugs.launchpad.net/nova/+bug/1073306nvdWEB
- bugs.launchpad.net/nova/+bug/1202266nvdWEB
- github.com/openstack/nova/commit/01de658210fd65171bfbf5450c93673b5ce0bd9eghsaWEB
- github.com/openstack/nova/commit/5cced7a6dd32d231c606e25dbf762d199bf9cca7ghsaWEB
- github.com/openstack/nova/commit/ba0d007fb78bd1182c3c0b808dbd7ccc84640e80ghsaWEB
- github.com/openstack/nova/commit/df2ea2e3acdede21b40d47b7adbeac04213d031bghsaWEB
News mentions
0No linked articles in our index yet.