components/openstack/nova/patches/04-CVE-2014-0134-partial.patch
author Drew Fisher <drew.fisher@oracle.com>
Fri, 13 Jun 2014 09:10:23 -0700
branchs11-update
changeset 3178 77584387a894
permissions -rw-r--r--
PSARC/2014/207 OpenStack Glance Update to Havana PSARC/2014/208 OpenStack Cinder Update to Havana PSARC/2014/209 OpenStack Keystone Update to Havana PSARC/2014/210 OpenStack Nova Update to Havana 18416146 Neutron agents (L3 and DHCP) should cleanup resources when they are disabled 18562372 Failed to create a new project under Horizon 18645763 ZFSSA Cinder Driver support 18686327 evs agent silently ignores user-specified pool allocation ranges 18702697 fibre channel volumes should be supported in the cinder volume driver 18734289 nova won't terminate failed kz deployments 18738371 cinder-volume:setup should account for commented-out zfs_volume_base 18738374 cinder-volume:setup should check for existence of configuration file 18826190 nova-compute fails due to nova.utils.to_bytes 18855698 Update OpenStack to Havana 2013.2.3 18855710 Update python-cinderclient to 1.0.9 18855743 Update python-keystoneclient to 0.8.0 18855754 Update python-neutronclient to 2.3.4 18855764 Update python-novaclient to 2.17.0 18855793 Update python-swiftclient to 2.1.0 18856992 External networks can be deleted even when floating IP addresses are in use 18857784 bake in some more openstack configuration 18884923 Incorrect locale facets in python modules for openstack 18913890 the error in _get_view_and_lun may cause the failure of deleting volumes 18943044 Disable 'Security Groups' tab in Horizon dashboard 18969275 problem in SERVICE/KEYSTONE

This proposed upstream patch is a follow-up to the original fix for
CVE-2014-0134 (Launchpad bug 1221190) but is tracked under the same CVE
and Launchpad bug as the original fix. It is designated as such below
('Partial-bug: #1221190').

From 63064a709162ba1a8a9a19643bd3acdf57c0c0b4 Mon Sep 17 00:00:00 2001
From: Nikola Dipanov <[email protected]>
Date: Wed, 9 Apr 2014 15:50:20 +0200
Subject: [PATCH] Avoid the possibility of truncating disk info file

Commit dc8de42 makes nova persist image format to a file to avoid
attacks based on changing it later. However the way it was implemented
leaves a small window of opportunity for the file to be truncated before
it gets written back to effectively making it possible for data to get
lost leaving us with a potential problem next time it is attempted to be
read.

This patch changes the way file is updated to be atomic, thus closing
the race window (and also removes the chown that we did not really
need).

It is worth noting that a better solution to this would be
to allow the code calling the imagebackend to write the file (once!)
and make it impossible to update after the boot process is done. This
approach would require more refactoring of the libvirt driver code, and
may be done in the future.

Partial-bug: #1221190
Change-Id: Ia1b073f38e096989f34d1774a12a1b4151773fc7
(cherry picked from commit d416f4310bb946b4b127201ec3c37e530d988714)
---
 etc/nova/rootwrap.d/compute.filters          |    1 -
 nova/tests/virt/libvirt/test_imagebackend.py |   21 ---------------------
 nova/utils.py                                |   14 --------------
 nova/virt/libvirt/imagebackend.py            |   25 +++++++++++++------------
 4 files changed, 13 insertions(+), 48 deletions(-)

diff --git a/etc/nova/rootwrap.d/compute.filters b/etc/nova/rootwrap.d/compute.filters
index e98c3f2..ac67180 100644
--- a/etc/nova/rootwrap.d/compute.filters
+++ b/etc/nova/rootwrap.d/compute.filters
@@ -41,7 +41,6 @@ mkdir: CommandFilter, mkdir, root
 # nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log
 # nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log
 # nova/virt/libvirt/connection.py: 'chown', 'root', basepath('disk')
-# nova/utils.py: 'chown', owner_uid, path
 chown: CommandFilter, chown, root
 
 # nova/virt/disk/vfs/localfs.py: 'chmod'
diff --git a/nova/tests/virt/libvirt/test_imagebackend.py b/nova/tests/virt/libvirt/test_imagebackend.py
index 5424f7b..80ade57 100644
--- a/nova/tests/virt/libvirt/test_imagebackend.py
+++ b/nova/tests/virt/libvirt/test_imagebackend.py
@@ -29,7 +29,6 @@ from nova.openstack.common import uuidutils
 from nova import test
 from nova.tests import fake_processutils
 from nova.tests.virt.libvirt import fake_libvirt_utils
-from nova import utils
 from nova.virt.libvirt import imagebackend
 
 CONF = cfg.CONF
@@ -68,10 +67,6 @@ class _ImageTestCase(object):
             'nova.virt.libvirt.imagebackend.libvirt_utils',
             fake_libvirt_utils))
 
-        def fake_chown(path, owner_uid=None):
-            return None
-        self.stubs.Set(utils, 'chown', fake_chown)
-
     def tearDown(self):
         super(_ImageTestCase, self).tearDown()
         shutil.rmtree(self.INSTANCES_PATH)
@@ -128,10 +123,6 @@ class RawTestCase(_ImageTestCase, test.NoDBTestCase):
         super(RawTestCase, self).setUp()
         self.stubs.Set(imagebackend.Raw, 'correct_format', lambda _: None)
 
-        def fake_chown(path, owner_uid=None):
-            return None
-        self.stubs.Set(utils, 'chown', fake_chown)
-
     def prepare_mocks(self):
         fn = self.mox.CreateMockAnything()
         self.mox.StubOutWithMock(imagebackend.utils.synchronized,
@@ -246,10 +237,6 @@ class RawTestCase(_ImageTestCase, test.NoDBTestCase):
         self.mox.StubOutWithMock(os.path, 'exists')
         self.mox.StubOutWithMock(imagebackend.images, 'qemu_img_info')
 
-        def fake_chown(path, owner_uid=None):
-            return None
-        self.stubs.Set(utils, 'chown', fake_chown)
-
         os.path.exists(self.PATH).AndReturn(True)
         os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
         info = self.mox.CreateMockAnything()
@@ -278,10 +265,6 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
         self.QCOW2_BASE = (self.TEMPLATE_PATH +
                            '_%d' % (self.SIZE / (1024 * 1024 * 1024)))
 
-        def fake_chown(path, owner_uid=None):
-            return None
-        self.stubs.Set(utils, 'chown', fake_chown)
-
     def prepare_mocks(self):
         fn = self.mox.CreateMockAnything()
         self.mox.StubOutWithMock(imagebackend.utils.synchronized,
@@ -873,10 +856,6 @@ class BackendTestCase(test.NoDBTestCase):
     def setUp(self):
         super(BackendTestCase, self).setUp()
 
-        def fake_chown(path, owner_uid=None):
-            return None
-        self.stubs.Set(utils, 'chown', fake_chown)
-
     def get_image(self, use_cow, image_type):
         return imagebackend.Backend(use_cow).image(self.INSTANCE,
                                                    self.NAME,
diff --git a/nova/utils.py b/nova/utils.py
index 4757f3a..599cb64 100755
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -924,20 +924,6 @@ def temporary_chown(path, owner_uid=None):
             execute('chown', orig_uid, path, run_as_root=True)
 
 
-def chown(path, owner_uid=None):
-    """chown a path.
-
-    :param owner_uid: UID of owner (defaults to current user)
-    """
-    if owner_uid is None:
-        owner_uid = os.getuid()
-
-    orig_uid = os.stat(path).st_uid
-
-    if orig_uid != owner_uid:
-        execute('chown', owner_uid, path, run_as_root=True)
-
-
 @contextlib.contextmanager
 def tempdir(**kwargs):
     argdict = kwargs.copy()
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
index ed11c90..29131d9 100644
--- a/nova/virt/libvirt/imagebackend.py
+++ b/nova/virt/libvirt/imagebackend.py
@@ -264,20 +264,21 @@ class Image(object):
                             lock_path=self.lock_path)
         def write_to_disk_info_file():
             # Use os.open to create it without group or world write permission.
-            fd = os.open(self.disk_info_path, os.O_RDWR | os.O_CREAT, 0o644)
-            with os.fdopen(fd, "r+") as disk_info_file:
+            fd = os.open(self.disk_info_path, os.O_RDONLY | os.O_CREAT, 0o644)
+            with os.fdopen(fd, "r") as disk_info_file:
                 line = disk_info_file.read().rstrip()
                 dct = _dict_from_line(line)
-                if self.path in dct:
-                    msg = _("Attempted overwrite of an existing value.")
-                    raise exception.InvalidDiskInfo(reason=msg)
-                dct.update({self.path: driver_format})
-                disk_info_file.seek(0)
-                disk_info_file.truncate()
-                disk_info_file.write('%s\n' % jsonutils.dumps(dct))
-            # Ensure the file is always owned by the nova user so qemu can't
-            # write it.
-            utils.chown(self.disk_info_path, owner_uid=os.getuid())
+
+            if self.path in dct:
+                msg = _("Attempted overwrite of an existing value.")
+                raise exception.InvalidDiskInfo(reason=msg)
+            dct.update({self.path: driver_format})
+
+            tmp_path = self.disk_info_path + ".tmp"
+            fd = os.open(tmp_path, os.O_WRONLY | os.O_CREAT, 0o644)
+            with os.fdopen(fd, "w") as tmp_file:
+                tmp_file.write('%s\n' % jsonutils.dumps(dct))
+            os.rename(tmp_path, self.disk_info_path)
 
         try:
             if (self.disk_info_path is not None and
-- 
1.7.9.2