summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'sys-cluster/nova/files/cve-2015-7548-stable-liberty-0001.patch')
-rw-r--r--sys-cluster/nova/files/cve-2015-7548-stable-liberty-0001.patch267
1 files changed, 267 insertions, 0 deletions
diff --git a/sys-cluster/nova/files/cve-2015-7548-stable-liberty-0001.patch b/sys-cluster/nova/files/cve-2015-7548-stable-liberty-0001.patch
new file mode 100644
index 000000000000..9f2429df1abc
--- /dev/null
+++ b/sys-cluster/nova/files/cve-2015-7548-stable-liberty-0001.patch
@@ -0,0 +1,267 @@
+From f41488f828fda1370e1b017503711248a810d432 Mon Sep 17 00:00:00 2001
+From: Matthew Booth <mbooth@redhat.com>
+Date: Wed, 9 Dec 2015 15:36:32 +0000
+Subject: [PATCH 1/3] Fix format detection in libvirt snapshot
+
+The libvirt driver was using automatic format detection during
+snapshot for disks stored on the local filesystem. This opened an
+exploit if nova was configured to use local file storage, and
+additionally to store those files in raw format by specifying
+use_cow_images = False in nova.conf. An authenticated user could write
+a qcow2 header to their guest image with a backing file on the host.
+libvirt.utils.get_disk_type() would then misdetect the type of this
+image as qcow2 and pass this to the Qcow2 image backend, whose
+snapshot_extract method interprets the image as qcow2 and writes the
+backing file to glance. The authenticated user can then download the
+host file from glance.
+
+This patch makes 2 principal changes. libvirt.utils.get_disk_type,
+which ought to be removed entirely as soon as possible, is updated to
+no longer do format detection if the format can't be determined from
+the path. Its name is changed to get_disk_type_from_path to reflect
+its actual function.
+
+libvirt.utils.find_disk is updated to return both the path and format
+of the root disk, rather than just the path. This is the most reliable
+source of this information, as it reflects the actual format in use.
+The previous format detection function of get_disk_type is replaced by
+the format taken from libvirt.
+
+We replace a call to get_disk_type in _rebase_with_qemu_img with an
+explicit call to qemu_img_info, as the other behaviour of
+get_disk_type was not relevant in this context. qemu_img_info is safe
+from the backing file exploit when called on a file known to be a
+qcow2 image. As the file in this context is a volume snapshot, this is
+a safe use.
+
+(cherry picked from commit c69fbad4860a1ce931d80f3f0ce0f90da29e8e5f)
+
+ Conflicts:
+ nova/tests/unit/virt/libvirt/test_driver.py
+ nova/tests/unit/virt/libvirt/test_utils.py
+ nova/virt/libvirt/driver.py
+ nova/virt/libvirt/utils.py
+
+ Most about method _rebase_with_qemu_img which does not exist.
+
+Partial-Bug: #1524274
+Change-Id: I94c1c0d26215c061f71c3f95e1a6bf3a58fa19ea
+---
+ nova/tests/unit/virt/libvirt/fake_libvirt_utils.py | 10 +++--
+ nova/tests/unit/virt/libvirt/test_utils.py | 44 +++-------------------
+ nova/virt/libvirt/driver.py | 25 +++++++++---
+ nova/virt/libvirt/utils.py | 26 ++++++++++---
+ 4 files changed, 51 insertions(+), 54 deletions(-)
+
+diff --git a/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py b/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py
+index 302ccee..52d1e85 100644
+--- a/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py
++++ b/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py
+@@ -40,7 +40,9 @@ def get_disk_backing_file(path):
+ return disk_backing_files.get(path, None)
+
+
+-def get_disk_type(path):
++def get_disk_type_from_path(path):
++ if disk_type in ('raw', 'qcow2'):
++ return None
+ return disk_type
+
+
+@@ -99,11 +101,11 @@ def file_open(path, mode=None):
+
+ def find_disk(virt_dom):
+ if disk_type == 'lvm':
+- return "/dev/nova-vg/lv"
++ return ("/dev/nova-vg/lv", "raw")
+ elif disk_type in ['raw', 'qcow2']:
+- return "filename"
++ return ("filename", disk_type)
+ else:
+- return "unknown_type_disk"
++ return ("unknown_type_disk", None)
+
+
+ def load_file(path):
+diff --git a/nova/tests/unit/virt/libvirt/test_utils.py b/nova/tests/unit/virt/libvirt/test_utils.py
+index ac7ea8d..6773bea 100644
+--- a/nova/tests/unit/virt/libvirt/test_utils.py
++++ b/nova/tests/unit/virt/libvirt/test_utils.py
+@@ -39,24 +39,6 @@ CONF = cfg.CONF
+
+ class LibvirtUtilsTestCase(test.NoDBTestCase):
+
+- @mock.patch('os.path.exists', return_value=True)
+- @mock.patch('nova.utils.execute')
+- def test_get_disk_type(self, mock_execute, mock_exists):
+- path = "disk.config"
+- example_output = """image: disk.config
+-file format: raw
+-virtual size: 64M (67108864 bytes)
+-cluster_size: 65536
+-disk size: 96K
+-blah BLAH: bb
+-"""
+- mock_execute.return_value = (example_output, '')
+- disk_type = libvirt_utils.get_disk_type(path)
+- mock_execute.assert_called_once_with('env', 'LC_ALL=C', 'LANG=C',
+- 'qemu-img', 'info', path)
+- mock_exists.assert_called_once_with(path)
+- self.assertEqual('raw', disk_type)
+-
+ @mock.patch('nova.utils.execute')
+ def test_copy_image_local(self, mock_execute):
+ libvirt_utils.copy_image('src', 'dest')
+@@ -77,37 +59,21 @@ blah BLAH: bb
+ on_completion=None, on_execute=None, compression=True)
+
+ @mock.patch('os.path.exists', return_value=True)
+- def test_disk_type(self, mock_exists):
++ def test_disk_type_from_path(self, mock_exists):
+ # Seems like lvm detection
+ # if its in /dev ??
+ for p in ['/dev/b', '/dev/blah/blah']:
+- d_type = libvirt_utils.get_disk_type(p)
++ d_type = libvirt_utils.get_disk_type_from_path(p)
+ self.assertEqual('lvm', d_type)
+
+ # Try rbd detection
+- d_type = libvirt_utils.get_disk_type('rbd:pool/instance')
++ d_type = libvirt_utils.get_disk_type_from_path('rbd:pool/instance')
+ self.assertEqual('rbd', d_type)
+
+ # Try the other types
+- template_output = """image: %(path)s
+-file format: %(format)s
+-virtual size: 64M (67108864 bytes)
+-cluster_size: 65536
+-disk size: 96K
+-"""
+ path = '/myhome/disk.config'
+- for f in ['raw', 'qcow2']:
+- output = template_output % ({
+- 'format': f,
+- 'path': path,
+- })
+- with mock.patch('nova.utils.execute',
+- return_value=(output, '')) as mock_execute:
+- d_type = libvirt_utils.get_disk_type(path)
+- mock_execute.assert_called_once_with(
+- 'env', 'LC_ALL=C', 'LANG=C',
+- 'qemu-img', 'info', path)
+- self.assertEqual(f, d_type)
++ d_type = libvirt_utils.get_disk_type_from_path(path)
++ self.assertIsNone(d_type)
+
+ @mock.patch('os.path.exists', return_value=True)
+ @mock.patch('nova.utils.execute')
+diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
+index fc1c909..51b1e4b 100644
+--- a/nova/virt/libvirt/driver.py
++++ b/nova/virt/libvirt/driver.py
+@@ -1338,10 +1338,23 @@ class LibvirtDriver(driver.ComputeDriver):
+
+ snapshot = self._image_api.get(context, image_id)
+
+- disk_path = libvirt_utils.find_disk(virt_dom)
+- source_format = libvirt_utils.get_disk_type(disk_path)
+-
+- image_format = CONF.libvirt.snapshot_image_format or source_format
++ # source_format is an on-disk format
++ # source_type is a backend type
++ disk_path, source_format = libvirt_utils.find_disk(virt_dom)
++ source_type = libvirt_utils.get_disk_type_from_path(disk_path)
++
++ # We won't have source_type for raw or qcow2 disks, because we can't
++ # determine that from the path. We should have it from the libvirt
++ # xml, though.
++ if source_type is None:
++ source_type = source_format
++ # For lxc instances we won't have it either from libvirt xml
++ # (because we just gave libvirt the mounted filesystem), or the path,
++ # so source_type is still going to be None. In this case,
++ # snapshot_backend is going to default to CONF.libvirt.images_type
++ # below, which is still safe.
++
++ image_format = CONF.libvirt.snapshot_image_format or source_type
+
+ # NOTE(bfilippov): save lvm and rbd as raw
+ if image_format == 'lvm' or image_format == 'rbd':
+@@ -1367,7 +1380,7 @@ class LibvirtDriver(driver.ComputeDriver):
+ if (self._host.has_min_version(MIN_LIBVIRT_LIVESNAPSHOT_VERSION,
+ MIN_QEMU_LIVESNAPSHOT_VERSION,
+ host.HV_DRIVER_QEMU)
+- and source_format not in ('lvm', 'rbd')
++ and source_type not in ('lvm', 'rbd')
+ and not CONF.ephemeral_storage_encryption.enabled
+ and not CONF.workarounds.disable_libvirt_livesnapshot):
+ live_snapshot = True
+@@ -1402,7 +1415,7 @@ class LibvirtDriver(driver.ComputeDriver):
+
+ snapshot_backend = self.image_backend.snapshot(instance,
+ disk_path,
+- image_type=source_format)
++ image_type=source_type)
+
+ if live_snapshot:
+ LOG.info(_LI("Beginning live snapshot process"),
+diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
+index 5573927..062b2fb 100644
+--- a/nova/virt/libvirt/utils.py
++++ b/nova/virt/libvirt/utils.py
+@@ -334,13 +334,20 @@ def find_disk(virt_dom):
+ """
+ xml_desc = virt_dom.XMLDesc(0)
+ domain = etree.fromstring(xml_desc)
++ driver = None
+ if CONF.libvirt.virt_type == 'lxc':
+- source = domain.find('devices/filesystem/source')
++ filesystem = domain.find('devices/filesystem')
++ driver = filesystem.find('driver')
++
++ source = filesystem.find('source')
+ disk_path = source.get('dir')
+ disk_path = disk_path[0:disk_path.rfind('rootfs')]
+ disk_path = os.path.join(disk_path, 'disk')
+ else:
+- source = domain.find('devices/disk/source')
++ disk = domain.find('devices/disk')
++ driver = disk.find('driver')
++
++ source = disk.find('source')
+ disk_path = source.get('file') or source.get('dev')
+ if not disk_path and CONF.libvirt.images_type == 'rbd':
+ disk_path = source.get('name')
+@@ -351,17 +358,26 @@ def find_disk(virt_dom):
+ raise RuntimeError(_("Can't retrieve root device path "
+ "from instance libvirt configuration"))
+
+- return disk_path
++ if driver is not None:
++ format = driver.get('type')
++ # This is a legacy quirk of libvirt/xen. Everything else should
++ # report the on-disk format in type.
++ if format == 'aio':
++ format = 'raw'
++ else:
++ format = None
++ return (disk_path, format)
+
+
+-def get_disk_type(path):
++def get_disk_type_from_path(path):
+ """Retrieve disk type (raw, qcow2, lvm) for given file."""
+ if path.startswith('/dev'):
+ return 'lvm'
+ elif path.startswith('rbd:'):
+ return 'rbd'
+
+- return images.qemu_img_info(path).file_format
++ # We can't reliably determine the type from this path
++ return None
+
+
+ def get_fs_info(path):
+--
+2.5.0
+