|
|
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 |
|