render / rpms / libvirt

Forked from rpms/libvirt 10 months ago
Clone
c401cc
From 7db35a9943e9ff5e3427870fdab6ecdba335f08f Mon Sep 17 00:00:00 2001
c401cc
Message-Id: <7db35a9943e9ff5e3427870fdab6ecdba335f08f@dist-git>
c401cc
From: "Daniel P. Berrange" <berrange@redhat.com>
c401cc
Date: Tue, 18 Feb 2014 15:45:40 -0700
c401cc
Subject: [PATCH] CVE-2013-6456: Avoid unsafe use of /proc/$PID/root in LXC
c401cc
 disk hotplug
c401cc
c401cc
Rewrite lxcDomainAttachDeviceDiskLive function to use the
c401cc
virProcessRunInMountNamespace helper. This avoids risk of
c401cc
a malicious guest replacing /dev with a absolute symlink,
c401cc
tricking the driver into changing the host OS filesystem.
c401cc
c401cc
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
c401cc
(cherry picked from commit 4dd3a7d5bc44980135a1b11810ba9aeab42a4a59)
c401cc
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
c401cc
---
c401cc
 src/lxc/lxc_driver.c | 185 +++++++++++++++++++++++++++++++++++++++------------
c401cc
 1 file changed, 141 insertions(+), 44 deletions(-)
c401cc
c401cc
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
c401cc
index cf45d8d..e38f037 100644
c401cc
--- a/src/lxc/lxc_driver.c
c401cc
+++ b/src/lxc/lxc_driver.c
c401cc
@@ -3062,6 +3062,115 @@ cleanup:
c401cc
 }
c401cc
 
c401cc
 
c401cc
+struct lxcDomainAttachDeviceMknodData {
c401cc
+    virLXCDriverPtr driver;
c401cc
+    mode_t mode;
c401cc
+    dev_t dev;
c401cc
+    virDomainObjPtr vm;
c401cc
+    virDomainDeviceDefPtr def;
c401cc
+    char *file;
c401cc
+};
c401cc
+
c401cc
+static int
c401cc
+lxcDomainAttachDeviceMknodHelper(pid_t pid ATTRIBUTE_UNUSED,
c401cc
+                                 void *opaque)
c401cc
+{
c401cc
+    struct lxcDomainAttachDeviceMknodData *data = opaque;
c401cc
+    int ret = -1;
c401cc
+
c401cc
+    virSecurityManagerPostFork(data->driver->securityManager);
c401cc
+
c401cc
+    if (virFileMakeParentPath(data->file) < 0) {
c401cc
+        virReportSystemError(errno,
c401cc
+                             _("Unable to create %s"), data->file);
c401cc
+        goto cleanup;
c401cc
+    }
c401cc
+
c401cc
+    /* Yes, the device name we're creating may not
c401cc
+     * actually correspond to the major:minor number
c401cc
+     * we're using, but we've no other option at this
c401cc
+     * time. Just have to hope that containerized apps
c401cc
+     * don't get upset that the major:minor is different
c401cc
+     * to that normally implied by the device name
c401cc
+     */
c401cc
+    VIR_DEBUG("Creating dev %s (%d,%d)",
c401cc
+              data->file, major(data->dev), minor(data->dev));
c401cc
+    if (mknod(data->file, data->mode, data->dev) < 0) {
c401cc
+        virReportSystemError(errno,
c401cc
+                             _("Unable to create device %s"),
c401cc
+                             data->file);
c401cc
+        goto cleanup;
c401cc
+    }
c401cc
+
c401cc
+    if (lxcContainerChown(data->vm->def, data->file) < 0)
c401cc
+        goto cleanup;
c401cc
+
c401cc
+    /* Labelling normally operates on src, but we need
c401cc
+     * to actually label the dst here, so hack the config */
c401cc
+    switch (data->def->type) {
c401cc
+    case VIR_DOMAIN_DEVICE_DISK: {
c401cc
+        virDomainDiskDefPtr def = data->def->data.disk;
c401cc
+        char *tmpsrc = def->src;
c401cc
+        def->src = data->file;
c401cc
+        if (virSecurityManagerSetImageLabel(data->driver->securityManager,
c401cc
+                                            data->vm->def, def) < 0) {
c401cc
+            def->src = tmpsrc;
c401cc
+            goto cleanup;
c401cc
+        }
c401cc
+        def->src = tmpsrc;
c401cc
+    }   break;
c401cc
+
c401cc
+    default:
c401cc
+        virReportError(VIR_ERR_INTERNAL_ERROR,
c401cc
+                       _("Unexpected device type %d"),
c401cc
+                       data->def->type);
c401cc
+        goto cleanup;
c401cc
+    }
c401cc
+
c401cc
+    ret = 0;
c401cc
+
c401cc
+ cleanup:
c401cc
+    if (ret < 0)
c401cc
+        unlink(data->file);
c401cc
+    return ret;
c401cc
+}
c401cc
+
c401cc
+
c401cc
+static int
c401cc
+lxcDomainAttachDeviceMknod(virLXCDriverPtr driver,
c401cc
+                           mode_t mode,
c401cc
+                           dev_t dev,
c401cc
+                           virDomainObjPtr vm,
c401cc
+                           virDomainDeviceDefPtr def,
c401cc
+                           char *file)
c401cc
+{
c401cc
+    virLXCDomainObjPrivatePtr priv = vm->privateData;
c401cc
+    struct lxcDomainAttachDeviceMknodData data;
c401cc
+
c401cc
+    memset(&data, 0, sizeof(data));
c401cc
+
c401cc
+    data.driver = driver;
c401cc
+    data.mode = mode;
c401cc
+    data.dev = dev;
c401cc
+    data.vm = vm;
c401cc
+    data.def = def;
c401cc
+    data.file = file;
c401cc
+
c401cc
+    if (virSecurityManagerPreFork(driver->securityManager) < 0)
c401cc
+        return -1;
c401cc
+
c401cc
+    if (virProcessRunInMountNamespace(priv->initpid,
c401cc
+                                      lxcDomainAttachDeviceMknodHelper,
c401cc
+                                      &data) < 0) {
c401cc
+        virSecurityManagerPostFork(driver->securityManager);
c401cc
+        return -1;
c401cc
+    }
c401cc
+
c401cc
+    virSecurityManagerPostFork(driver->securityManager);
c401cc
+    return 0;
c401cc
+}
c401cc
+
c401cc
+
c401cc
 static int
c401cc
 lxcDomainAttachDeviceDiskLive(virLXCDriverPtr driver,
c401cc
                               virDomainObjPtr vm,
c401cc
@@ -3070,11 +3179,9 @@ lxcDomainAttachDeviceDiskLive(virLXCDriverPtr driver,
c401cc
     virLXCDomainObjPrivatePtr priv = vm->privateData;
c401cc
     virDomainDiskDefPtr def = dev->data.disk;
c401cc
     int ret = -1;
c401cc
-    char *dst = NULL;
c401cc
     struct stat sb;
c401cc
-    bool created = false;
c401cc
-    mode_t mode = 0;
c401cc
-    char *tmpsrc = def->src;
c401cc
+    char *file = NULL;
c401cc
+    int perms;
c401cc
 
c401cc
     if (!priv->initpid) {
c401cc
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
c401cc
@@ -3118,51 +3225,44 @@ lxcDomainAttachDeviceDiskLive(virLXCDriverPtr driver,
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
-    if (virAsprintf(&dst, "/proc/%llu/root/dev/%s",
c401cc
-                    (unsigned long long)priv->initpid, def->dst) < 0)
c401cc
-        goto cleanup;
c401cc
-
c401cc
-    if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0)
c401cc
+    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES)) {
c401cc
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
c401cc
+                       _("devices cgroup isn't mounted"));
c401cc
         goto cleanup;
c401cc
+    }
c401cc
 
c401cc
-    mode = 0700 | S_IFBLK;
c401cc
+    perms = (def->readonly ?
c401cc
+             VIR_CGROUP_DEVICE_READ :
c401cc
+             VIR_CGROUP_DEVICE_RW) |
c401cc
+        VIR_CGROUP_DEVICE_MKNOD;
c401cc
 
c401cc
-    /* Yes, the device name we're creating may not
c401cc
-     * actually correspond to the major:minor number
c401cc
-     * we're using, but we've no other option at this
c401cc
-     * time. Just have to hope that containerized apps
c401cc
-     * don't get upset that the major:minor is different
c401cc
-     * to that normally implied by the device name
c401cc
-     */
c401cc
-    VIR_DEBUG("Creating dev %s (%d,%d) from %s",
c401cc
-              dst, major(sb.st_rdev), minor(sb.st_rdev), def->src);
c401cc
-    if (mknod(dst, mode, sb.st_rdev) < 0) {
c401cc
-        virReportSystemError(errno,
c401cc
-                             _("Unable to create device %s"),
c401cc
-                             dst);
c401cc
+    if (virCgroupAllowDevice(priv->cgroup,
c401cc
+                             'b',
c401cc
+                             major(sb.st_rdev),
c401cc
+                             minor(sb.st_rdev),
c401cc
+                             perms) < 0)
c401cc
         goto cleanup;
c401cc
-    }
c401cc
 
c401cc
-    if (lxcContainerChown(vm->def, dst) < 0)
c401cc
+    if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks + 1) < 0)
c401cc
         goto cleanup;
c401cc
 
c401cc
-    created = true;
c401cc
-
c401cc
-    /* Labelling normally operates on src, but we need
c401cc
-     * to actually label the dst here, so hack the config */
c401cc
-    def->src = dst;
c401cc
-    if (virSecurityManagerSetImageLabel(driver->securityManager,
c401cc
-                                        vm->def, def) < 0)
c401cc
+    if (virAsprintf(&file,
c401cc
+                    "/dev/%s", def->dst) < 0)
c401cc
         goto cleanup;
c401cc
 
c401cc
-    if (virCgroupAllowDevicePath(priv->cgroup, def->src,
c401cc
-                                 (def->readonly ?
c401cc
-                                  VIR_CGROUP_DEVICE_READ :
c401cc
-                                  VIR_CGROUP_DEVICE_RW) |
c401cc
-                                 VIR_CGROUP_DEVICE_MKNOD) != 0) {
c401cc
-        virReportError(VIR_ERR_INTERNAL_ERROR,
c401cc
-                       _("cannot allow device %s for domain %s"),
c401cc
-                       def->src, vm->def->name);
c401cc
+    if (lxcDomainAttachDeviceMknod(driver,
c401cc
+                                   0700 | S_IFBLK,
c401cc
+                                   sb.st_rdev,
c401cc
+                                   vm,
c401cc
+                                   dev,
c401cc
+                                   file) < 0) {
c401cc
+        if (virCgroupDenyDevice(priv->cgroup,
c401cc
+                                'b',
c401cc
+                                major(sb.st_rdev),
c401cc
+                                minor(sb.st_rdev),
c401cc
+                                perms) < 0)
c401cc
+            VIR_WARN("cannot deny device %s for domain %s",
c401cc
+                     def->src, vm->def->name);
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
@@ -3171,11 +3271,8 @@ lxcDomainAttachDeviceDiskLive(virLXCDriverPtr driver,
c401cc
     ret = 0;
c401cc
 
c401cc
 cleanup:
c401cc
-    def->src = tmpsrc;
c401cc
     virDomainAuditDisk(vm, NULL, def->src, "attach", ret == 0);
c401cc
-    if (dst && created && ret < 0)
c401cc
-        unlink(dst);
c401cc
-    VIR_FREE(dst);
c401cc
+    VIR_FREE(file);
c401cc
     return ret;
c401cc
 }
c401cc
 
c401cc
-- 
c401cc
1.9.0
c401cc