* src/qemu_driver.c: Set a restrictive block device whitelist for all QEMU guests. Update whitelist when hotplugging disks. * src/cgroup.h, src/cgroup.c: Add some more convenience methods for dealing with block device whitelists. --- src/cgroup.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cgroup.h | 12 ++++++ src/qemu_driver.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 195 insertions(+), 2 deletions(-) diff --git a/src/cgroup.c b/src/cgroup.c index d9c3141..21e0dd4 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -782,6 +782,104 @@ int virCgroupAllowDeviceMajor(virCgroupPtr group, return rc; } +int virCgroupAllowDevicePath(virCgroupPtr group, + const char *path) +{ + struct stat sb; + + if (stat(path, &sb) < 0) + return -errno; + + if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) + return -EINVAL; + + return virCgroupAllowDevice(group, + S_ISCHR(sb.st_mode) ? 'c' : 'b', + major(sb.st_rdev), + minor(sb.st_rdev)); +} + +/** + * virCgroupDenyDevice: + * + * @group: The cgroup to deny a device for + * @type: The device type (i.e., 'c' or 'b') + * @major: The major number of the device + * @minor: The minor number of the device + * + * Returns: 0 on success + */ +int virCgroupDenyDevice(virCgroupPtr group, + char type, + int major, + int minor) +{ + int rc; + char *devstr = NULL; + + if (virAsprintf(&devstr, "%c %i:%i rwm", type, major, minor) == -1) { + rc = -ENOMEM; + goto out; + } + + rc = virCgroupSetValueStr(group, + VIR_CGROUP_CONTROLLER_DEVICES, + "devices.deny", + devstr); +out: + VIR_FREE(devstr); + + return rc; +} + +/** + * virCgroupDenyDeviceMajor: + * + * @group: The cgroup to deny an entire device major type for + * @type: The device type (i.e., 'c' or 'b') + * @major: The major number of the device type + * + * Returns: 0 on success + */ +int virCgroupDenyDeviceMajor(virCgroupPtr group, + char type, + int major) +{ + int rc; + char *devstr = NULL; + + if (virAsprintf(&devstr, "%c %i:* rwm", type, major) == -1) { + rc = -ENOMEM; + goto out; + } + + rc = virCgroupSetValueStr(group, + VIR_CGROUP_CONTROLLER_DEVICES, + "devices.deny", + devstr); + out: + VIR_FREE(devstr); + + return rc; +} + +int virCgroupDenyDevicePath(virCgroupPtr group, + const char *path) +{ + struct stat sb; + + if (stat(path, &sb) < 0) + return -errno; + + if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) + return -EINVAL; + + return virCgroupDenyDevice(group, + S_ISCHR(sb.st_mode) ? 'c' : 'b', + major(sb.st_rdev), + minor(sb.st_rdev)); +} + int virCgroupSetCpuShares(virCgroupPtr group, unsigned long shares) { return virCgroupSetValueU64(group, diff --git a/src/cgroup.h b/src/cgroup.h index 11c44f9..89bd4a1 100644 --- a/src/cgroup.h +++ b/src/cgroup.h @@ -38,6 +38,18 @@ int virCgroupAllowDevice(virCgroupPtr group, int virCgroupAllowDeviceMajor(virCgroupPtr group, char type, int major); +int virCgroupAllowDevicePath(virCgroupPtr group, + const char *path); + +int virCgroupDenyDevice(virCgroupPtr group, + char type, + int major, + int minor); +int virCgroupDenyDeviceMajor(virCgroupPtr group, + char type, + int major); +int virCgroupDenyDevicePath(virCgroupPtr group, + const char *path); int virCgroupSetCpuShares(virCgroupPtr group, unsigned long shares); int virCgroupGetCpuShares(virCgroupPtr group, unsigned long *shares); diff --git a/src/qemu_driver.c b/src/qemu_driver.c index f6d3f52..33e9cfa 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -1378,12 +1378,19 @@ error: return -1; } +static const char *const devs[] = { + "/dev/null", "/dev/full", "/dev/zero", + "/dev/random", "/dev/urandom", + "/dev/ptmx", "/dev/kvm", "/dev/kqemu", +}; + static int qemuSetupCgroup(virConnectPtr conn, struct qemud_driver *driver ATTRIBUTE_UNUSED, virDomainObjPtr vm) { virCgroupPtr cgroup = NULL; int rc; + unsigned int i; if (virCgroupHaveSupport() != 0) return 0; /* Not supported, so claim success */ @@ -1395,6 +1402,54 @@ static int qemuSetupCgroup(virConnectPtr conn, goto cleanup; } + rc = virCgroupDenyAllDevices(cgroup); + if (rc != 0) { + if (rc == -EPERM) { + VIR_WARN0("Group devices ACL is not accessible, disabling whitelisting"); + goto done; + } + + virReportSystemError(conn, -rc, + _("Unable to deny all devices for %s"), vm->def->name); + goto cleanup; + } + + for (i = 0; i < vm->def->ndisks ; i++) { + if (vm->def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_BLOCK || + vm->def->disks[i]->src == NULL) + continue; + + rc = virCgroupAllowDevicePath(cgroup, + vm->def->disks[i]->src); + if (rc != 0) { + virReportSystemError(conn, -rc, + _("Unable to allow device %s for %s"), + vm->def->disks[i]->src, vm->def->name); + goto cleanup; + } + } + + rc = virCgroupAllowDeviceMajor(cgroup, 'c', 136); + if (rc != 0) { + virReportSystemError(conn, -rc, + _("unable to allow device %s"), + devs[i]); + goto cleanup; + } + + for (i = 0; i < ARRAY_CARDINALITY(devs) ; i++) { + rc = virCgroupAllowDevicePath(cgroup, + devs[i]); + if (rc < 0 && + rc != -ENOENT) { + virReportSystemError(conn, -rc, + _("unable to allow device %s"), + devs[i]); + goto cleanup; + } + } + +done: virCgroupFree(&cgroup); return 0; @@ -4637,6 +4692,7 @@ static int qemudDomainAttachDevice(virDomainPtr dom, struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; virDomainDeviceDefPtr dev = NULL; + virCgroupPtr cgroup = NULL; int ret = -1; qemuDriverLock(driver); @@ -4662,6 +4718,26 @@ static int qemudDomainAttachDevice(virDomainPtr dom, if (dev->type == VIR_DOMAIN_DEVICE_DISK) { + if (virCgroupHaveSupport() == 0) { + if (virCgroupForDomain(vm->def, "qemu", &cgroup) !=0 ) { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Unable to create cgroup for %s\n"), + vm->def->name); + goto cleanup; + } + if (dev->data.disk->src != NULL && + virCgroupAllowDevicePath(cgroup, + dev->data.disk->src) < 0) { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unable to allow device %s"), + dev->data.disk->src); + goto cleanup; + } + } + + if (driver->securityDriver) + driver->securityDriver->domainSetSecurityImageLabel(dom->conn, vm, dev->data.disk); + switch (dev->data.disk->device) { case VIR_DOMAIN_DISK_DEVICE_CDROM: case VIR_DOMAIN_DISK_DEVICE_FLOPPY: @@ -4690,7 +4766,7 @@ static int qemudDomainAttachDevice(virDomainPtr dom, qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, _("disk bus '%s' cannot be hotplugged."), virDomainDiskBusTypeToString(dev->data.disk->bus)); - goto cleanup; + /* fallthrough */ } break; @@ -4698,7 +4774,11 @@ static int qemudDomainAttachDevice(virDomainPtr dom, qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, _("disk device type '%s' cannot be hotplugged"), virDomainDiskDeviceTypeToString(dev->data.disk->device)); - goto cleanup; + /* Fallthrough */ + } + if (ret != 0) { + virCgroupDenyDevicePath(cgroup, + dev->data.disk->src); } } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV && dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && @@ -4718,6 +4798,9 @@ static int qemudDomainAttachDevice(virDomainPtr dom, ret = -1; cleanup: + if (cgroup) + virCgroupFree(&cgroup); + if (ret < 0) { if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 1) < 0) VIR_WARN0("Fail to restore disk device ownership"); -- 1.6.2.5 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list