[libvirt] [PATCH v4 0/5] Per-guest configurable user/group for QEMU processes

This is a v4 patch series that updates the libvirt's security driver mechanism to support per-guest configurable user and group for QEMU processes running together with other security drivers, such as SELinux and AppArmor. Marcelo Cerri (5): Internal refactory of data structures Multiple security drivers in XML data Update security layer to handle many security labels Support for multiple default security drivers in QEMU config Update the remote API daemon/remote.c | 63 ++++ docs/formatdomain.html.in | 11 +- docs/schemas/capability.rng | 18 +- docs/schemas/domaincommon.rng | 30 ++- include/libvirt/libvirt.h.in | 2 + python/generator.py | 1 + src/conf/capabilities.c | 17 +- src/conf/capabilities.h | 6 +- src/conf/domain_audit.c | 14 +- src/conf/domain_conf.c | 343 +++++++++++++++----- src/conf/domain_conf.h | 20 +- src/driver.h | 4 + src/libvirt.c | 47 +++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 1 + src/lxc/lxc_conf.c | 8 +- src/lxc/lxc_controller.c | 8 +- src/lxc/lxc_driver.c | 11 +- src/lxc/lxc_process.c | 23 +- src/qemu/qemu.conf | 6 +- src/qemu/qemu_conf.c | 38 ++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 218 +++++++++++--- src/qemu/qemu_process.c | 50 ++- src/remote/remote_driver.c | 46 +++ src/remote/remote_protocol.x | 17 +- src/remote_protocol-structs | 11 + src/security/security_apparmor.c | 118 +++++-- src/security/security_dac.c | 324 +++++++++++++++++-- src/security/security_manager.c | 101 +++++-- src/security/security_manager.h | 8 +- src/security/security_selinux.c | 263 +++++++++++----- src/security/security_stack.c | 237 +++++++++----- src/security/security_stack.h | 13 + src/test/test_driver.c | 11 +- .../qemuxml2argv-seclabel-dynamic-override.xml | 4 +- .../qemuxml2argv-seclabel-dynamic.xml | 2 +- 37 files changed, 1653 insertions(+), 448 deletions(-)

This patch updates the structures that store information about each domain and each hypervisor to support multiple security labels and drivers. It also updates all the remaining code to use the new fields. Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/conf/capabilities.c | 17 +++-- src/conf/capabilities.h | 6 ++- src/conf/domain_audit.c | 14 +++-- src/conf/domain_conf.c | 84 ++++++++++++++++------- src/conf/domain_conf.h | 11 ++- src/lxc/lxc_conf.c | 8 ++- src/lxc/lxc_controller.c | 8 +- src/lxc/lxc_driver.c | 11 ++-- src/lxc/lxc_process.c | 23 ++++--- src/qemu/qemu_driver.c | 15 +++-- src/qemu/qemu_process.c | 18 +++--- src/security/security_manager.c | 12 ++-- src/security/security_selinux.c | 142 ++++++++++++++++++++------------------ src/test/test_driver.c | 11 ++- 14 files changed, 227 insertions(+), 153 deletions(-) diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 8b9b516..9a0bc3d 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -181,8 +181,13 @@ virCapabilitiesFree(virCapsPtr caps) { VIR_FREE(caps->host.migrateTrans); VIR_FREE(caps->host.arch); - VIR_FREE(caps->host.secModel.model); - VIR_FREE(caps->host.secModel.doi); + + for (i = 0; i < caps->host.nsecModels; i++) { + VIR_FREE(caps->host.secModels[i].model); + VIR_FREE(caps->host.secModels[i].doi); + } + VIR_FREE(caps->host.secModels); + virCPUDefFree(caps->host.cpu); VIR_FREE(caps); @@ -767,10 +772,12 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBufferAddLit(&xml, " </topology>\n"); } - if (caps->host.secModel.model) { + if (caps->host.nsecModels) { virBufferAddLit(&xml, " <secmodel>\n"); - virBufferAsprintf(&xml, " <model>%s</model>\n", caps->host.secModel.model); - virBufferAsprintf(&xml, " <doi>%s</doi>\n", caps->host.secModel.doi); + virBufferAsprintf(&xml, " <model>%s</model>\n", + caps->host.secModels[0].model); + virBufferAsprintf(&xml, " <doi>%s</doi>\n", + caps->host.secModels[0].doi); virBufferAddLit(&xml, " </secmodel>\n"); } diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 460b273..e43f404 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -93,6 +93,7 @@ struct _virCapsHostNUMACell { }; typedef struct _virCapsHostSecModel virCapsHostSecModel; +typedef virCapsHostSecModel *virCapsHostSecModelPtr; struct _virCapsHostSecModel { char *model; char *doi; @@ -116,7 +117,10 @@ struct _virCapsHost { size_t nnumaCell; size_t nnumaCell_max; virCapsHostNUMACellPtr *numaCell; - virCapsHostSecModel secModel; + + size_t nsecModels; + virCapsHostSecModelPtr secModels; + virCPUDefPtr cpu; unsigned char host_uuid[VIR_UUID_BUFLEN]; }; diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 5300371..59db649 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -618,6 +618,7 @@ virDomainAuditSecurityLabel(virDomainObjPtr vm, bool success) char uuidstr[VIR_UUID_STRING_BUFLEN]; char *vmname; const char *virt; + int i; virUUIDFormat(vm->def->uuid, uuidstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { @@ -630,11 +631,14 @@ virDomainAuditSecurityLabel(virDomainObjPtr vm, bool success) virt = "?"; } - VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_ID, success, - "virt=%s %s uuid=%s vm-ctx=%s img-ctx=%s", - virt, vmname, uuidstr, - VIR_AUDIT_STR(vm->def->seclabel.label), - VIR_AUDIT_STR(vm->def->seclabel.imagelabel)); + for (i = 0; i < vm->def->nseclabels; i++) { + VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_ID, success, + "virt=%s %s uuid=%s vm-ctx=%s img-ctx=%s model=%s", + virt, vmname, uuidstr, + VIR_AUDIT_STR(vm->def->seclabels[i]->label), + VIR_AUDIT_STR(vm->def->seclabels[i]->imagelabel), + VIR_AUDIT_STR(vm->def->seclabels[i]->model)); + } VIR_FREE(vmname); } diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 62f5f5b..98daf32 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -869,12 +869,15 @@ virDomainGraphicsListenDefClear(virDomainGraphicsListenDefPtr def) } static void -virSecurityLabelDefClear(virSecurityLabelDefPtr def) +virSecurityLabelDefFree(virSecurityLabelDefPtr def) { + if (!def) + return; VIR_FREE(def->model); VIR_FREE(def->label); VIR_FREE(def->imagelabel); VIR_FREE(def->baselabel); + VIR_FREE(def); } @@ -883,6 +886,7 @@ virSecurityDeviceLabelDefFree(virSecurityDeviceLabelDefPtr def) { if (!def) return; + VIR_FREE(def->model); VIR_FREE(def->label); VIR_FREE(def); } @@ -968,7 +972,11 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) virStorageEncryptionFree(def->encryption); virDomainDeviceInfoClear(&def->info); - virSecurityDeviceLabelDefFree(def->seclabel); + if (def->seclabels) { + for (i = 0; i < def->nseclabels; i++) + virSecurityDeviceLabelDefFree(def->seclabels[i]); + VIR_FREE(def->seclabels); + } for (i = 0 ; i < def->nhosts ; i++) virDomainDiskHostDefFree(&def->hosts[i]); @@ -1629,7 +1637,9 @@ void virDomainDefFree(virDomainDefPtr def) virDomainMemballoonDefFree(def->memballoon); - virSecurityLabelDefClear(&def->seclabel); + for (i = 0; i < def->nseclabels; i++) + virSecurityLabelDefFree(def->seclabels[i]); + VIR_FREE(def->seclabels); virCPUDefFree(def->cpu); @@ -3064,10 +3074,10 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, { char *p; - if (virXPathNode("./seclabel", ctxt) == NULL) + if (virXPathNode("./seclabel[1]", ctxt) == NULL) return 0; - p = virXPathStringLimit("string(./seclabel/@type)", + p = virXPathStringLimit("string(./seclabel[1]/@type)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { def->type = VIR_DOMAIN_SECLABEL_DYNAMIC; @@ -3081,7 +3091,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, } } - p = virXPathStringLimit("string(./seclabel/@relabel)", + p = virXPathStringLimit("string(./seclabel[1]/@relabel)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p != NULL) { if (STREQ(p, "yes")) { @@ -3121,7 +3131,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, if (def->type == VIR_DOMAIN_SECLABEL_STATIC || (!(flags & VIR_DOMAIN_XML_INACTIVE) && def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel/label[1])", + p = virXPathStringLimit("string(./seclabel[1]/label[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, @@ -3136,7 +3146,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, if (!def->norelabel && (!(flags & VIR_DOMAIN_XML_INACTIVE) && def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel/imagelabel[1])", + p = virXPathStringLimit("string(./seclabel[1]/imagelabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, @@ -3148,7 +3158,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, /* Only parse baselabel for dynamic label type */ if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { - p = virXPathStringLimit("string(./seclabel/baselabel[1])", + p = virXPathStringLimit("string(./seclabel[1]/baselabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); def->baselabel = p; } @@ -3160,7 +3170,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, def->baselabel || (!(flags & VIR_DOMAIN_XML_INACTIVE) && def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel/@model)", + p = virXPathStringLimit("string(./seclabel[1]/@model)", VIR_SECURITY_MODEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, @@ -3173,7 +3183,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, return 0; error: - virSecurityLabelDefClear(def); + virSecurityLabelDefFree(def); return -1; } @@ -3187,7 +3197,7 @@ virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr *def, *def = NULL; - if (virXPathNode("./seclabel", ctxt) == NULL) + if (virXPathNode("./seclabel[1]", ctxt) == NULL) return 0; /* Can't use overrides if top-level doesn't allow relabeling. */ @@ -3203,7 +3213,7 @@ virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr *def, return -1; } - p = virXPathStringLimit("string(./seclabel/@relabel)", + p = virXPathStringLimit("string(./seclabel[1]/@relabel)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p != NULL) { if (STREQ(p, "yes")) { @@ -3222,7 +3232,7 @@ virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr *def, (*def)->norelabel = false; } - p = virXPathStringLimit("string(./seclabel/label[1])", + p = virXPathStringLimit("string(./seclabel[1]/label[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); (*def)->label = p; @@ -3656,10 +3666,15 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if (virSecurityDeviceLabelDefParseXML(&def->seclabel, + if ((VIR_ALLOC(def->seclabels) < 0)) { + virReportOOMError(); + goto error; + } + if (virSecurityDeviceLabelDefParseXML(&def->seclabels[0], vmSeclabel, ctxt) < 0) goto error; + def->nseclabels = 1; ctxt->node = saved_node; } @@ -7104,7 +7119,7 @@ static int virDomainLifecycleParseXML(xmlXPathContextPtr ctxt, } virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, - const virDomainDefPtr def, + virDomainDefPtr def, const char *xmlStr, unsigned int flags) { @@ -7123,10 +7138,19 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; } + if (!def->seclabels) { + if ((VIR_ALLOC(def->seclabels) < 0) || + (VIR_ALLOC(def->seclabels[0])) < 0 ) { + virReportOOMError(); + goto error; + } + } + if (xmlStrEqual(node->name, BAD_CAST "disk")) { dev->type = VIR_DOMAIN_DEVICE_DISK; if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node, ctxt, - NULL, &def->seclabel, flags))) + NULL, def->seclabels[0], + flags))) goto error; } else if (xmlStrEqual(node->name, BAD_CAST "lease")) { dev->type = VIR_DOMAIN_DEVICE_LEASE; @@ -8064,7 +8088,13 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, /* analysis of security label, done early even though we format it * late, so devices can refer to this for defaults */ - if (virSecurityLabelDefParseXML(&def->seclabel, ctxt, flags) == -1) + if ((VIR_ALLOC(def->seclabels) < 0) || + (VIR_ALLOC(def->seclabels[0]) < 0)) { + virReportOOMError(); + goto error; + } + def->nseclabels = 1; + if (virSecurityLabelDefParseXML(def->seclabels[0], ctxt, flags) == -1) goto error; /* Extract domain memory */ @@ -8664,7 +8694,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - &def->seclabel, + def->seclabels[0], flags); if (!disk) goto error; @@ -11183,10 +11213,10 @@ virDomainDiskDefFormat(virBufferPtr buf, if (def->startupPolicy) virBufferEscapeString(buf, " startupPolicy='%s'", startupPolicy); - if (def->seclabel) { + if (def->seclabels && def->seclabels[0]) { virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); - virSecurityDeviceLabelDefFormat(buf, def->seclabel); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[0]); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -11196,10 +11226,10 @@ virDomainDiskDefFormat(virBufferPtr buf, case VIR_DOMAIN_DISK_TYPE_BLOCK: virBufferEscapeString(buf, " <source dev='%s'", def->src); - if (def->seclabel) { + if (def->seclabels && def->seclabels[0]) { virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); - virSecurityDeviceLabelDefFormat(buf, def->seclabel); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[0]); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -13189,9 +13219,11 @@ virDomainDefFormatInternal(virDomainDefPtr def, virBufferAddLit(buf, " </devices>\n"); - virBufferAdjustIndent(buf, 2); - virSecurityLabelDefFormat(buf, &def->seclabel); - virBufferAdjustIndent(buf, -2); + if (def->nseclabels && def->seclabels) { + virBufferAdjustIndent(buf, 2); + virSecurityLabelDefFormat(buf, def->seclabels[0]); + virBufferAdjustIndent(buf, -2); + } if (def->namespaceData && def->ns.format) { if ((def->ns.format)(buf, def->namespaceData) < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8d024b3..ac8da85 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -312,6 +312,7 @@ struct _virSecurityLabelDef { typedef struct _virSecurityDeviceLabelDef virSecurityDeviceLabelDef; typedef virSecurityDeviceLabelDef *virSecurityDeviceLabelDefPtr; struct _virSecurityDeviceLabelDef { + char *model; char *label; /* image label string */ bool norelabel; }; @@ -556,7 +557,6 @@ struct _virDomainDiskDef { int device; int bus; char *src; - virSecurityDeviceLabelDefPtr seclabel; char *dst; int tray_status; int protocol; @@ -596,6 +596,9 @@ struct _virDomainDiskDef { virStorageEncryptionPtr encryption; bool rawio_specified; int rawio; /* no = 0, yes = 1 */ + + size_t nseclabels; + virSecurityDeviceLabelDefPtr *seclabels; }; @@ -1672,8 +1675,10 @@ struct _virDomainDef { int nhubs; virDomainHubDefPtr *hubs; + size_t nseclabels; + virSecurityLabelDefPtr *seclabels; + /* Only 1 */ - virSecurityLabelDef seclabel; virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; virCPUDefPtr cpu; @@ -1940,7 +1945,7 @@ void virDomainRemoveInactive(virDomainObjListPtr doms, virDomainObjPtr dom); virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, - const virDomainDefPtr def, + virDomainDefPtr def, const char *xmlStr, unsigned int flags); virDomainDefPtr virDomainDefParseString(virCapsPtr caps, diff --git a/src/lxc/lxc_conf.c b/src/lxc/lxc_conf.c index a508f21..03340cf 100644 --- a/src/lxc/lxc_conf.c +++ b/src/lxc/lxc_conf.c @@ -134,9 +134,13 @@ virCapsPtr lxcCapsInit(virLXCDriverPtr driver) doi = virSecurityManagerGetDOI(driver->securityManager); model = virSecurityManagerGetModel(driver->securityManager); if (STRNEQ(model, "none")) { - if (!(caps->host.secModel.model = strdup(model))) + /* Allocate just the primary security driver for LXC. */ + if (VIR_ALLOC(caps->host.secModels) < 0) goto no_memory; - if (!(caps->host.secModel.doi = strdup(doi))) + caps->host.nsecModels = 1; + if (!(caps->host.secModels[0].model = strdup(model))) + goto no_memory; + if (!(caps->host.secModels[0].doi = strdup(doi))) goto no_memory; } diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index 4c3c17f..e5aea11 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -1602,10 +1602,10 @@ int main(int argc, char *argv[]) goto cleanup; VIR_DEBUG("Security model %s type %s label %s imagelabel %s", - NULLSTR(ctrl->def->seclabel.model), - virDomainSeclabelTypeToString(ctrl->def->seclabel.type), - NULLSTR(ctrl->def->seclabel.label), - NULLSTR(ctrl->def->seclabel.imagelabel)); + NULLSTR(ctrl->def->seclabels[0]->model), + virDomainSeclabelTypeToString(ctrl->def->seclabels[0]->type), + NULLSTR(ctrl->def->seclabels[0]->label), + NULLSTR(ctrl->def->seclabels[0]->imagelabel)); ctrl->veths = veths; ctrl->nveths = nveths; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 2b5707e..ff11c2c 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -964,7 +964,6 @@ cleanup: return ret; } - /** * lxcDomainStartWithFlags: * @dom: domain to start @@ -1182,12 +1181,12 @@ static int lxcNodeGetSecurityModel(virConnectPtr conn, lxcDriverLock(driver); memset(secmodel, 0, sizeof(*secmodel)); - /* NULL indicates no driver, which we treat as - * success, but simply return no data in *secmodel */ - if (driver->caps->host.secModel.model == NULL) + /* we treat no driver as success, but simply return no data in *secmodel */ + if (driver->caps->host.nsecModels == 0 + || driver->caps->host.secModels[0].model == NULL) goto cleanup; - if (!virStrcpy(secmodel->model, driver->caps->host.secModel.model, + if (!virStrcpy(secmodel->model, driver->caps->host.secModels[0].model, VIR_SECURITY_MODEL_BUFLEN)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security model string exceeds max %d bytes"), @@ -1196,7 +1195,7 @@ static int lxcNodeGetSecurityModel(virConnectPtr conn, goto cleanup; } - if (!virStrcpy(secmodel->doi, driver->caps->host.secModel.doi, + if (!virStrcpy(secmodel->doi, driver->caps->host.secModels[0].doi, VIR_SECURITY_DOI_BUFLEN)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security DOI string exceeds max %d bytes"), diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index 046ed86..0ba8a8e 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -706,10 +706,11 @@ int virLXCProcessStop(virLXCDriverPtr driver, vm->def, false); virSecurityManagerReleaseLabel(driver->securityManager, vm->def); /* Clear out dynamically assigned labels */ - if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) { - VIR_FREE(vm->def->seclabel.model); - VIR_FREE(vm->def->seclabel.label); - VIR_FREE(vm->def->seclabel.imagelabel); + if (vm->def->nseclabels && + vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { + VIR_FREE(vm->def->seclabels[0]->model); + VIR_FREE(vm->def->seclabels[0]->label); + VIR_FREE(vm->def->seclabels[0]->imagelabel); } if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) == 0) { @@ -1001,8 +1002,9 @@ int virLXCProcessStart(virConnectPtr conn, /* If you are using a SecurityDriver with dynamic labelling, then generate a security label for isolation */ VIR_DEBUG("Generating domain security label (if required)"); - if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DEFAULT) - vm->def->seclabel.type = VIR_DOMAIN_SECLABEL_NONE; + if (vm->def->nseclabels && + vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DEFAULT) + vm->def->seclabels[0]->type = VIR_DOMAIN_SECLABEL_NONE; if (virSecurityManagerGenLabel(driver->securityManager, vm->def) < 0) { virDomainAuditSecurityLabel(vm, false); @@ -1207,10 +1209,11 @@ cleanup: vm->def, false); virSecurityManagerReleaseLabel(driver->securityManager, vm->def); /* Clear out dynamically assigned labels */ - if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) { - VIR_FREE(vm->def->seclabel.model); - VIR_FREE(vm->def->seclabel.label); - VIR_FREE(vm->def->seclabel.imagelabel); + if (vm->def->nseclabels && + vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { + VIR_FREE(vm->def->seclabels[0]->model); + VIR_FREE(vm->def->seclabels[0]->label); + VIR_FREE(vm->def->seclabels[0]->imagelabel); } } for (i = 0 ; i < nttyFDs ; i++) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 839606e..e2384c0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -321,10 +321,15 @@ qemuCreateCapabilities(virCapsPtr oldcaps, doi = virSecurityManagerGetDOI(driver->securityManager); model = virSecurityManagerGetModel(driver->securityManager); + + if (VIR_ALLOC(caps->host.secModels) < 0) { + goto no_memory; + } + if (STRNEQ(model, "none")) { - if (!(caps->host.secModel.model = strdup(model))) + if (!(caps->host.secModels[0].model = strdup(model))) goto no_memory; - if (!(caps->host.secModel.doi = strdup(doi))) + if (!(caps->host.secModels[0].doi = strdup(doi))) goto no_memory; } @@ -4060,10 +4065,10 @@ static int qemudNodeGetSecurityModel(virConnectPtr conn, /* NULL indicates no driver, which we treat as * success, but simply return no data in *secmodel */ - if (driver->caps->host.secModel.model == NULL) + if (driver->caps->host.secModels[0].model == NULL) goto cleanup; - p = driver->caps->host.secModel.model; + p = driver->caps->host.secModels[0].model; if (strlen(p) >= VIR_SECURITY_MODEL_BUFLEN-1) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security model string exceeds max %d bytes"), @@ -4073,7 +4078,7 @@ static int qemudNodeGetSecurityModel(virConnectPtr conn, } strcpy(secmodel->model, p); - p = driver->caps->host.secModel.doi; + p = driver->caps->host.secModels[0].doi; if (strlen(p) >= VIR_SECURITY_DOI_BUFLEN-1) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security DOI string exceeds max %d bytes"), diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index df4a016..553bf90 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4079,12 +4079,12 @@ void qemuProcessStop(struct qemud_driver *driver, virSecurityManagerReleaseLabel(driver->securityManager, vm->def); /* Clear out dynamically assigned labels */ - if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) { - if (!vm->def->seclabel.baselabel) - VIR_FREE(vm->def->seclabel.model); - VIR_FREE(vm->def->seclabel.label); + if (vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { + if (!vm->def->seclabels[0]->baselabel) + VIR_FREE(vm->def->seclabels[0]->model); + VIR_FREE(vm->def->seclabels[0]->label); } - VIR_FREE(vm->def->seclabel.imagelabel); + VIR_FREE(vm->def->seclabels[0]->imagelabel); virDomainDefClearDeviceAliases(vm->def); if (!priv->persistentAddrs) { @@ -4227,16 +4227,16 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, goto no_memory; VIR_DEBUG("Detect security driver config"); - vm->def->seclabel.type = VIR_DOMAIN_SECLABEL_STATIC; + vm->def->seclabels[0]->type = VIR_DOMAIN_SECLABEL_STATIC; if (VIR_ALLOC(seclabel) < 0) goto no_memory; if (virSecurityManagerGetProcessLabel(driver->securityManager, vm->def, vm->pid, seclabel) < 0) goto cleanup; - if (driver->caps->host.secModel.model && - !(vm->def->seclabel.model = strdup(driver->caps->host.secModel.model))) + if (driver->caps->host.secModels[0].model && + !(vm->def->seclabels[0]->model = strdup(driver->caps->host.secModels[0].model))) goto no_memory; - if (!(vm->def->seclabel.label = strdup(seclabel->label))) + if (!(vm->def->seclabels[0]->label = strdup(seclabel->label))) goto no_memory; VIR_DEBUG("Creating domain log file"); diff --git a/src/security/security_manager.c b/src/security/security_manager.c index 44ab6fb..6b372b5 100644 --- a/src/security/security_manager.c +++ b/src/security/security_manager.c @@ -308,16 +308,16 @@ int virSecurityManagerRestoreSavedStateLabel(virSecurityManagerPtr mgr, int virSecurityManagerGenLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { - if (vm->seclabel.type == VIR_DOMAIN_SECLABEL_DEFAULT) { + if (vm->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DEFAULT) { if (mgr->defaultConfined) { - vm->seclabel.type = VIR_DOMAIN_SECLABEL_DYNAMIC; + vm->seclabels[0]->type = VIR_DOMAIN_SECLABEL_DYNAMIC; } else { - vm->seclabel.type = VIR_DOMAIN_SECLABEL_NONE; - vm->seclabel.norelabel = true; + vm->seclabels[0]->type = VIR_DOMAIN_SECLABEL_NONE; + vm->seclabels[0]->norelabel = true; } } - if ((vm->seclabel.type == VIR_DOMAIN_SECLABEL_NONE) && + if ((vm->seclabels[0]->type == VIR_DOMAIN_SECLABEL_NONE) && mgr->requireConfined) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unconfined guests are not allowed on this host")); @@ -399,7 +399,7 @@ int virSecurityManagerSetProcessLabel(virSecurityManagerPtr mgr, int virSecurityManagerVerify(virSecurityManagerPtr mgr, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; /* NULL model == dynamic labelling, with whatever driver * is active, so we can short circuit verify check to * avoid drivers de-referencing NULLs by accident diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 48fd78b..474ff66 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -363,43 +363,43 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); VIR_DEBUG("driver=%s", virSecurityManagerGetDriver(mgr)); - if ((def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) && - !def->seclabel.baselabel && - def->seclabel.model) { + if ((def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) && + !def->seclabels[0]->baselabel && + def->seclabels[0]->model) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("security model already defined for VM")); return rc; } - if (def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC && - def->seclabel.label) { + if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + def->seclabels[0]->label) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("security label already defined for VM")); return rc; } - if (def->seclabel.imagelabel) { + if (def->seclabels[0]->imagelabel) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("security image label already defined for VM")); return rc; } - if (def->seclabel.model && - STRNEQ(def->seclabel.model, SECURITY_SELINUX_NAME)) { + if (def->seclabels[0]->model && + STRNEQ(def->seclabels[0]->model, SECURITY_SELINUX_NAME)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security label model %s is not supported with selinux"), - def->seclabel.model); + def->seclabels[0]->model); return rc; } - VIR_DEBUG("type=%d", def->seclabel.type); + VIR_DEBUG("type%d", def->seclabels[0]->type); - switch (def->seclabel.type) { + switch (def->seclabels[0]->type) { case VIR_DOMAIN_SECLABEL_STATIC: - if (!(ctx = context_new(def->seclabel.label)) ) { + if (!(ctx = context_new(def->seclabels[0]->label)) ) { virReportSystemError(errno, _("unable to allocate socket security context '%s'"), - def->seclabel.label); + def->seclabels[0]->label); return rc; } @@ -418,11 +418,15 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, if (virSecuritySELinuxMCSAdd(mgr, mcs) < 0) goto cleanup; - if (!(def->seclabel.label = - virSecuritySELinuxGenNewContext(def->seclabel.baselabel ? - def->seclabel.baselabel : - data->domain_context, mcs))) + def->seclabels[0]->label = + virSecuritySELinuxGenNewContext(def->seclabels[0]->baselabel ? + def->seclabels[0]->baselabel : + data->domain_context, mcs); + if (! def->seclabels[0]->label) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot generate selinux context for %s"), mcs); goto cleanup; + } break; case VIR_DOMAIN_SECLABEL_NONE: @@ -432,18 +436,22 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected security label type '%s'"), - virDomainSeclabelTypeToString(def->seclabel.type)); + virDomainSeclabelTypeToString(def->seclabels[0]->type)); goto cleanup; } - if (!def->seclabel.norelabel) { - if (!(def->seclabel.imagelabel = - virSecuritySELinuxGenNewContext(data->file_context, mcs))) + if (!def->seclabels[0]->norelabel) { + def->seclabels[0]->imagelabel = virSecuritySELinuxGenNewContext( + data->file_context, mcs); + if (!def->seclabels[0]->imagelabel) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot generate selinux context for %s"), mcs); goto cleanup; + } } - if (!def->seclabel.model && - !(def->seclabel.model = strdup(SECURITY_SELINUX_NAME))) { + if (!def->seclabels[0]->model && + !(def->seclabels[0]->model = strdup(SECURITY_SELINUX_NAME))) { virReportOOMError(); goto cleanup; } @@ -452,12 +460,12 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, cleanup: if (rc != 0) { - if (def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) - VIR_FREE(def->seclabel.label); - VIR_FREE(def->seclabel.imagelabel); - if (def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC && - !def->seclabel.baselabel) - VIR_FREE(def->seclabel.model); + if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) + VIR_FREE(def->seclabels[0]->label); + VIR_FREE(def->seclabels[0]->imagelabel); + if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + !def->seclabels[0]->baselabel) + VIR_FREE(def->seclabels[0]->model); } if (ctx) @@ -466,16 +474,16 @@ cleanup: VIR_FREE(mcs); VIR_DEBUG("model=%s label=%s imagelabel=%s baselabel=%s", - NULLSTR(def->seclabel.model), - NULLSTR(def->seclabel.label), - NULLSTR(def->seclabel.imagelabel), - NULLSTR(def->seclabel.baselabel)); + NULLSTR(def->seclabels[0]->model), + NULLSTR(def->seclabels[0]->label), + NULLSTR(def->seclabels[0]->imagelabel), + NULLSTR(def->seclabels[0]->baselabel)); return rc; } static int -virSecuritySELinuxReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, +virSecuritySELinuxReserveSecurityLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, pid_t pid) { @@ -484,7 +492,7 @@ virSecuritySELinuxReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSE const char *mcs; int rv; - if (def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC) + if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_STATIC) return 0; if (getpidcon(pid, &pctx) == -1) { @@ -788,9 +796,9 @@ virSecuritySELinuxRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr ATTRIBU virDomainDiskDefPtr disk, int migrated) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; - if (secdef->norelabel || (disk->seclabel && disk->seclabel->norelabel)) + if (secdef->norelabel || (disk->seclabels[0] && disk->seclabels[0]->norelabel)) return 0; /* Don't restore labels on readoly/shared disks, because @@ -847,12 +855,12 @@ virSecuritySELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk, int ret; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(cbdata->manager); - if (disk->seclabel && disk->seclabel->norelabel) + if (disk->seclabels[0] && disk->seclabels[0]->norelabel) return 0; - if (disk->seclabel && !disk->seclabel->norelabel && - disk->seclabel->label) { - ret = virSecuritySELinuxSetFilecon(path, disk->seclabel->label); + if (disk->seclabels[0] && !disk->seclabels[0]->norelabel && + disk->seclabels[0]->label) { + ret = virSecuritySELinuxSetFilecon(path, disk->seclabels[0]->label); } else if (depth == 0) { if (disk->shared) { @@ -867,14 +875,14 @@ virSecuritySELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk, } else { ret = virSecuritySELinuxSetFileconOptional(path, data->content_context); } - if (ret == 1 && !disk->seclabel) { + if (ret == 1 && !disk->seclabels[0]) { /* If we failed to set a label, but virt_use_nfs let us * proceed anyway, then we don't need to relabel later. */ - if (VIR_ALLOC(disk->seclabel) < 0) { + if (VIR_ALLOC(disk->seclabels[0]) < 0) { virReportOOMError(); return -1; } - disk->seclabel->norelabel = true; + disk->seclabels[0]->norelabel = true; ret = 0; } return ret; @@ -887,7 +895,7 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, { virSecuritySELinuxCallbackData cbdata; - cbdata.secdef = &def->seclabel; + cbdata.secdef = def->seclabels[0]; cbdata.manager = mgr; bool allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr); @@ -918,7 +926,7 @@ virSecuritySELinuxSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { virDomainDefPtr def = opaque; - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; return virSecuritySELinuxSetFilecon(file, secdef->imagelabel); } @@ -928,7 +936,7 @@ virSecuritySELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { virDomainDefPtr def = opaque; - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; return virSecuritySELinuxSetFilecon(file, secdef->imagelabel); } @@ -939,7 +947,7 @@ virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN virDomainHostdevDefPtr dev) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; int ret = -1; if (secdef->norelabel) @@ -1008,7 +1016,7 @@ virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUT virDomainHostdevDefPtr dev) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; int ret = -1; if (secdef->norelabel) @@ -1061,7 +1069,7 @@ virSecuritySELinuxSetSecurityChardevLabel(virDomainDefPtr def, virDomainChrSourceDefPtr dev) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; char *in = NULL, *out = NULL; int ret = -1; @@ -1107,7 +1115,7 @@ virSecuritySELinuxRestoreSecurityChardevLabel(virDomainDefPtr def, virDomainChrSourceDefPtr dev) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; char *in = NULL, *out = NULL; int ret = -1; @@ -1200,7 +1208,7 @@ virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN virDomainDefPtr def, int migrated ATTRIBUTE_UNUSED) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; int i; int rc = 0; @@ -1250,7 +1258,7 @@ static int virSecuritySELinuxReleaseSecurityLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; if (secdef->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { if (secdef->label != NULL) { @@ -1275,7 +1283,7 @@ virSecuritySELinuxSetSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, const char *savefile) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; if (secdef->norelabel) return 0; @@ -1289,7 +1297,7 @@ virSecuritySELinuxRestoreSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNU virDomainDefPtr def, const char *savefile) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; if (secdef->norelabel) return 0; @@ -1302,7 +1310,7 @@ static int virSecuritySELinuxSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " @@ -1327,10 +1335,10 @@ virSecuritySELinuxSetSecurityProcessLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { /* TODO: verify DOI */ - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; VIR_DEBUG("label=%s", secdef->label); - if (def->seclabel.label == NULL) + if (def->seclabels[0]->label == NULL) return 0; if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { @@ -1359,13 +1367,13 @@ virSecuritySELinuxSetSecurityDaemonSocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { /* TODO: verify DOI */ - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; context_t execcon = NULL; context_t proccon = NULL; security_context_t scon = NULL; int rc = -1; - if (def->seclabel.label == NULL) + if (def->seclabels[0]->label == NULL) return 0; if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { @@ -1429,7 +1437,7 @@ static int virSecuritySELinuxSetSecuritySocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { - const virSecurityLabelDefPtr secdef = &vm->seclabel; + const virSecurityLabelDefPtr secdef = vm->seclabels[0]; int rc = -1; if (secdef->label == NULL) @@ -1467,9 +1475,9 @@ virSecuritySELinuxClearSecuritySocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { /* TODO: verify DOI */ - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; - if (def->seclabel.label == NULL) + if (def->seclabels[0]->label == NULL) return 0; if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { @@ -1546,7 +1554,7 @@ virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, const char *stdin_path) { virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; int i; if (secdef->norelabel) @@ -1607,7 +1615,7 @@ virSecuritySELinuxSetImageFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, int fd) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; if (secdef->imagelabel == NULL) return 0; @@ -1619,7 +1627,7 @@ static char * virSecuritySELinuxGenImageLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); const char *range; context_t ctx = NULL; @@ -1655,7 +1663,7 @@ virSecuritySELinuxGetSecurityMountOptions(virSecurityManagerPtr mgr, virDomainDefPtr def) { char *opts = NULL; - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; if (! secdef->imagelabel) secdef->imagelabel = virSecuritySELinuxGenImageLabel(mgr,def); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index a767e21..aa4418a 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -206,12 +206,15 @@ testBuildCapabilities(virConnectPtr conn) { caps->privateDataAllocFunc = testDomainObjPrivateAlloc; caps->privateDataFreeFunc = testDomainObjPrivateFree; - caps->host.secModel.model = strdup("testSecurity"); - if (!caps->host.secModel.model) + caps->host.nsecModels = 1; + if (VIR_ALLOC_N(caps->host.secModels, caps->host.nsecModels) < 0) + goto no_memory; + caps->host.secModels[0].model = strdup("testSecurity"); + if (!caps->host.secModels[0].model) goto no_memory; - caps->host.secModel.doi = strdup(""); - if (!caps->host.secModel.doi) + caps->host.secModels[0].doi = strdup(""); + if (!caps->host.secModels[0].doi) goto no_memory; return caps; -- 1.7.1

On 16.08.2012 00:10, Marcelo Cerri wrote:
This patch updates the structures that store information about each domain and each hypervisor to support multiple security labels and drivers. It also updates all the remaining code to use the new fields.
Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/conf/capabilities.c | 17 +++-- src/conf/capabilities.h | 6 ++- src/conf/domain_audit.c | 14 +++-- src/conf/domain_conf.c | 84 ++++++++++++++++------- src/conf/domain_conf.h | 11 ++- src/lxc/lxc_conf.c | 8 ++- src/lxc/lxc_controller.c | 8 +- src/lxc/lxc_driver.c | 11 ++-- src/lxc/lxc_process.c | 23 ++++--- src/qemu/qemu_driver.c | 15 +++-- src/qemu/qemu_process.c | 18 +++--- src/security/security_manager.c | 12 ++-- src/security/security_selinux.c | 142 ++++++++++++++++++++------------------ src/test/test_driver.c | 11 ++- 14 files changed, 227 insertions(+), 153 deletions(-)
ACK Michal

This patch updates the domain and capability XML parser and formatter to support more than one "seclabel" element for each domain and device. The RNG schema and the tests related to this are also updated by this patch. Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- docs/formatdomain.html.in | 11 +- docs/schemas/capability.rng | 18 +- docs/schemas/domaincommon.rng | 30 ++- src/conf/capabilities.c | 6 +- src/conf/domain_conf.c | 353 ++++++++++++++------ src/conf/domain_conf.h | 9 + src/libvirt_private.syms | 3 + .../qemuxml2argv-seclabel-dynamic-override.xml | 4 +- .../qemuxml2argv-seclabel-dynamic.xml | 2 +- 9 files changed, 304 insertions(+), 132 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index fb25cb8..85d2515 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1275,8 +1275,8 @@ path to the file holding the disk. If the disk <code>type</code> is "block", then the <code>dev</code> attribute specifies the path to the host device to serve as - the disk. With both "file" and "block", an optional - sub-element <code>seclabel</code>, <a href="#seclabel">described + the disk. With both "file" and "block", one or more optional + sub-elements <code>seclabel</code>, <a href="#seclabel">described below</a> (and <span class="since">since 0.9.9</span>), can be used to override the domain security labeling policy for just that source file. If the disk <code>type</code> is "dir", then the @@ -3880,6 +3880,13 @@ qemu-kvm -net nic,model=? /dev/null </p> <p> + If more than one security driver is used by libvirt, multiple + <code>seclabel</code> tags can be used, one for each driver and + the security driver referenced by each tag can be defined using + the attribute <code>model</code> + </p> + + <p> Valid input XML configurations for the top-level security label are: </p> diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index c392e44..8c928bc 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -44,20 +44,22 @@ <optional> <ref name='topology'/> </optional> - <optional> + <zeroOrMore> <ref name='secmodel'/> - </optional> + </zeroOrMore> </element> </define> <define name='secmodel'> <element name='secmodel'> - <element name='model'> - <text/> - </element> - <element name='doi'> - <text/> - </element> + <interleave> + <element name='model'> + <text/> + </element> + <element name='doi'> + <text/> + </element> + </interleave> </element> </define> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 1b94155..dd93f4d 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -55,9 +55,9 @@ <optional> <ref name="devices"/> </optional> - <optional> + <zeroOrMore> <ref name="seclabel"/> - </optional> + </zeroOrMore> <optional> <ref name='qemucmdline'/> </optional> @@ -148,18 +148,32 @@ <!-- A per-device seclabel override is more limited, either relabel=no or a <label> must be present. --> <choice> - <attribute name='relabel'> - <value>no</value> - </attribute> <group> <optional> + <attribute name='model'> + <text/> + </attribute> + </optional> + <attribute name='relabel'> + <value>no</value> + </attribute> + </group> + <group> + <optional> + <attribute name='model'> + <text/> + </attribute> + </optional> + <optional> <attribute name='relabel'> <value>yes</value> </attribute> </optional> - <element name='label'> - <text/> - </element> + <zeroOrMore> + <element name='label'> + <text/> + </element> + </zeroOrMore> </group> </choice> </element> diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 9a0bc3d..bc6266e 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -772,12 +772,12 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBufferAddLit(&xml, " </topology>\n"); } - if (caps->host.nsecModels) { + for (i = 0; i < caps->host.nsecModels; i++) { virBufferAddLit(&xml, " <secmodel>\n"); virBufferAsprintf(&xml, " <model>%s</model>\n", - caps->host.secModels[0].model); + caps->host.secModels[i].model); virBufferAsprintf(&xml, " <doi>%s</doi>\n", - caps->host.secModels[0].doi); + caps->host.secModels[i].doi); virBufferAddLit(&xml, " </secmodel>\n"); } diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 98daf32..2dea33e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3067,17 +3067,19 @@ virDomainDiskDefAssignAddress(virCapsPtr caps, virDomainDiskDefPtr def) return 0; } -static int -virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, - xmlXPathContextPtr ctxt, +static virSecurityLabelDefPtr +virSecurityLabelDefParseXML(xmlXPathContextPtr ctxt, unsigned int flags) { char *p; + virSecurityLabelDefPtr def = NULL; - if (virXPathNode("./seclabel[1]", ctxt) == NULL) - return 0; + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto error; + } - p = virXPathStringLimit("string(./seclabel[1]/@type)", + p = virXPathStringLimit("string(./@type)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { def->type = VIR_DOMAIN_SECLABEL_DYNAMIC; @@ -3091,7 +3093,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, } } - p = virXPathStringLimit("string(./seclabel[1]/@relabel)", + p = virXPathStringLimit("string(./@relabel)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p != NULL) { if (STREQ(p, "yes")) { @@ -3131,7 +3133,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, if (def->type == VIR_DOMAIN_SECLABEL_STATIC || (!(flags & VIR_DOMAIN_XML_INACTIVE) && def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel[1]/label[1])", + p = virXPathStringLimit("string(./label[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, @@ -3146,7 +3148,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, if (!def->norelabel && (!(flags & VIR_DOMAIN_XML_INACTIVE) && def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel[1]/imagelabel[1])", + p = virXPathStringLimit("string(./imagelabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, @@ -3158,93 +3160,179 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, /* Only parse baselabel for dynamic label type */ if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { - p = virXPathStringLimit("string(./seclabel[1]/baselabel[1])", + p = virXPathStringLimit("string(./baselabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); def->baselabel = p; } - /* Only parse model, if static labelling, or a base - * label is set, or doing active XML - */ - if (def->type == VIR_DOMAIN_SECLABEL_STATIC || - def->baselabel || - (!(flags & VIR_DOMAIN_XML_INACTIVE) && - def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel[1]/@model)", - VIR_SECURITY_MODEL_BUFLEN-1, ctxt); - if (p == NULL) { - virReportError(VIR_ERR_XML_ERROR, - "%s", _("missing security model")); - goto error; - } - def->model = p; + /* Always parse model */ + p = virXPathStringLimit("string(./@model)", + VIR_SECURITY_MODEL_BUFLEN-1, ctxt); + if (p == NULL && def->type != VIR_DOMAIN_SECLABEL_NONE) { + virReportError(VIR_ERR_XML_ERROR, + "%s", _("missing security model")); } + def->model = p; - return 0; + return def; error: virSecurityLabelDefFree(def); + return NULL; +} + +static int +virSecurityLabelDefsParseXML(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + int i, n; + xmlNodePtr *list = NULL, saved_node; + + /* Check args and save context */ + if (def == NULL || ctxt == NULL) + return 0; + saved_node = ctxt->node; + + /* Allocate a security labels based on XML */ + if ((n = virXPathNodeSet("./seclabel", ctxt, &list)) == 0) + return 0; + + if (VIR_ALLOC_N(def->seclabels, n) < 0) { + virReportOOMError(); + goto error; + } + + /* Parse each "seclabel" tag */ + for (i = 0; i < n; i++) { + ctxt->node = list[i]; + def->seclabels[i] = virSecurityLabelDefParseXML(ctxt, flags); + if (def->seclabels[i] == NULL) + goto error; + } + def->nseclabels = n; + ctxt->node = saved_node; + VIR_FREE(list); + + /* Checking missing model information + * when there is more than one seclabel */ + if (n > 1) { + for(; n; n--) { + if (def->seclabels[n - 1]->model == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing security model " + "when using multiple labels")); + goto error; + } + } + } + return 0; + +error: + ctxt->node = saved_node; + for (; i > 0; i--) { + virSecurityLabelDefFree(def->seclabels[i - 1]); + } + VIR_FREE(def->seclabels); + VIR_FREE(list); return -1; } - static int -virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr *def, - virSecurityLabelDefPtr vmDef, - xmlXPathContextPtr ctxt) +virSecurityDeviceLabelDefParseXML(virDomainDiskDefPtr def, + virSecurityLabelDefPtr *vmSeclabels, + int nvmSeclabels, xmlXPathContextPtr ctxt) { - char *p; + int n, i, j; + xmlNodePtr *list = NULL; + virSecurityLabelDefPtr vmDef = NULL; + char *model, *relabel, *label; - *def = NULL; - - if (virXPathNode("./seclabel[1]", ctxt) == NULL) + if (def == NULL) return 0; - /* Can't use overrides if top-level doesn't allow relabeling. */ - if (vmDef && vmDef->norelabel) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("label overrides require relabeling to be " - "enabled at the domain level")); - return -1; - } + if ((n = virXPathNodeSet("./seclabel", ctxt, &list)) == 0) + return 0; - if (VIR_ALLOC(*def) < 0) { + def->nseclabels = n; + if (VIR_ALLOC_N(def->seclabels, n) < 0) { virReportOOMError(); - return -1; + goto error; } + for (i = 0; i < n; i++) { + if (VIR_ALLOC(def->seclabels[i]) < 0) { + virReportOOMError(); + goto error; + } + } + + for (i = 0; i < n; i++) { + /* get model associated to this override */ + model = virXMLPropString(list[i], "model"); + if (model == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("invalid security model")); + goto error; + } else { + /* find the security label that it's being overriden */ + for (j = 0; j < nvmSeclabels; j++) { + if (STREQ(vmSeclabels[j]->model, model)) { + vmDef = vmSeclabels[j]; + break; + } + } + def->seclabels[i]->model = model; + } - p = virXPathStringLimit("string(./seclabel[1]/@relabel)", - VIR_SECURITY_LABEL_BUFLEN-1, ctxt); - if (p != NULL) { - if (STREQ(p, "yes")) { - (*def)->norelabel = false; - } else if (STREQ(p, "no")) { - (*def)->norelabel = true; + /* Can't use overrides if top-level doesn't allow relabeling. */ + if (vmDef && vmDef->norelabel) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("label overrides require relabeling to be " + "enabled at the domain level")); + goto error; + } + + relabel = virXMLPropString(list[i], "relabel"); + if (relabel != NULL) { + if (STREQ(relabel, "yes")) { + def->seclabels[i]->norelabel = false; + } else if (STREQ(relabel, "no")) { + def->seclabels[i]->norelabel = true; + } else { + virReportError(VIR_ERR_XML_ERROR, + _("invalid security relabel value %s"), + relabel); + VIR_FREE(relabel); + goto error; + } + VIR_FREE(relabel); } else { + def->seclabels[i]->norelabel = false; + } + + ctxt->node = list[i]; + label = virXPathStringLimit("string(./label)", + VIR_SECURITY_LABEL_BUFLEN-1, ctxt); + def->seclabels[i]->label = label; + + if (label && def->seclabels[i]->norelabel) { virReportError(VIR_ERR_XML_ERROR, - _("invalid security relabel value %s"), p); - VIR_FREE(p); - VIR_FREE(*def); - return -1; + _("Cannot specify a label if relabelling is " + "turned off. model=%s"), + def->seclabels[i]->model); + goto error; } - VIR_FREE(p); - } else { - (*def)->norelabel = false; } - - p = virXPathStringLimit("string(./seclabel[1]/label[1])", - VIR_SECURITY_LABEL_BUFLEN-1, ctxt); - (*def)->label = p; - - if ((*def)->label && (*def)->norelabel) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Cannot specify a label if relabelling is turned off")); - VIR_FREE((*def)->label); - VIR_FREE(*def); - return -1; - } - + VIR_FREE(list); return 0; + +error: + for (i = 0; i < n; i++) { + virSecurityDeviceLabelDefFree(def->seclabels[i]); + } + VIR_FREE(def->seclabels); + VIR_FREE(list); + return -1; } @@ -3328,7 +3416,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlNodePtr node, xmlXPathContextPtr ctxt, virBitmapPtr bootMap, - virSecurityLabelDefPtr vmSeclabel, + virSecurityLabelDefPtr* vmSeclabels, + int nvmSeclabels, unsigned int flags) { virDomainDiskDefPtr def; @@ -3666,15 +3755,10 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if ((VIR_ALLOC(def->seclabels) < 0)) { - virReportOOMError(); - goto error; - } - if (virSecurityDeviceLabelDefParseXML(&def->seclabels[0], - vmSeclabel, + if (virSecurityDeviceLabelDefParseXML(def, vmSeclabels, + nvmSeclabels, ctxt) < 0) goto error; - def->nseclabels = 1; ctxt->node = saved_node; } @@ -7138,18 +7222,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; } - if (!def->seclabels) { - if ((VIR_ALLOC(def->seclabels) < 0) || - (VIR_ALLOC(def->seclabels[0])) < 0 ) { - virReportOOMError(); - goto error; - } - } - if (xmlStrEqual(node->name, BAD_CAST "disk")) { dev->type = VIR_DOMAIN_DEVICE_DISK; if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node, ctxt, - NULL, def->seclabels[0], + NULL, def->seclabels, + def->nseclabels, flags))) goto error; } else if (xmlStrEqual(node->name, BAD_CAST "lease")) { @@ -8088,13 +8165,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, /* analysis of security label, done early even though we format it * late, so devices can refer to this for defaults */ - if ((VIR_ALLOC(def->seclabels) < 0) || - (VIR_ALLOC(def->seclabels[0]) < 0)) { - virReportOOMError(); - goto error; - } - def->nseclabels = 1; - if (virSecurityLabelDefParseXML(def->seclabels[0], ctxt, flags) == -1) + if (virSecurityLabelDefsParseXML(def, ctxt, flags) == -1) goto error; /* Extract domain memory */ @@ -8694,7 +8765,8 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - def->seclabels[0], + def->seclabels, + def->nseclabels, flags); if (!disk) goto error; @@ -11040,13 +11112,14 @@ virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def) virBufferAsprintf(buf, "<seclabel type='%s'", sectype); + if (def->model) + virBufferEscapeString(buf, " model='%s'", def->model); + if (def->type == VIR_DOMAIN_SECLABEL_NONE) { virBufferAddLit(buf, "/>\n"); return; } - virBufferEscapeString(buf, " model='%s'", def->model); - virBufferAsprintf(buf, " relabel='%s'", def->norelabel ? "no" : "yes"); @@ -11072,8 +11145,8 @@ static void virSecurityDeviceLabelDefFormat(virBufferPtr buf, virSecurityDeviceLabelDefPtr def) { - virBufferAsprintf(buf, "<seclabel relabel='%s'", - def->norelabel ? "no" : "yes"); + virBufferAsprintf(buf, "<seclabel model='%s' relabel='%s'", + def->model, def->norelabel ? "no" : "yes"); if (def->label) { virBufferAddLit(buf, ">\n"); virBufferEscapeString(buf, " <label>%s</label>\n", @@ -11118,6 +11191,7 @@ virDomainDiskDefFormat(virBufferPtr buf, const char *copy_on_read = virDomainVirtioEventIdxTypeToString(def->copy_on_read); const char *startupPolicy = virDomainStartupPolicyTypeToString(def->startupPolicy); + int n; char uuidstr[VIR_UUID_STRING_BUFLEN]; if (!type) { @@ -11213,10 +11287,11 @@ virDomainDiskDefFormat(virBufferPtr buf, if (def->startupPolicy) virBufferEscapeString(buf, " startupPolicy='%s'", startupPolicy); - if (def->seclabels && def->seclabels[0]) { + if (def->nseclabels) { virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); - virSecurityDeviceLabelDefFormat(buf, def->seclabels[0]); + for (n = 0; n < def->nseclabels; n++) + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -11226,10 +11301,11 @@ virDomainDiskDefFormat(virBufferPtr buf, case VIR_DOMAIN_DISK_TYPE_BLOCK: virBufferEscapeString(buf, " <source dev='%s'", def->src); - if (def->seclabels && def->seclabels[0]) { + if (def->nseclabels) { virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); - virSecurityDeviceLabelDefFormat(buf, def->seclabels[0]); + for (n = 0; n < def->nseclabels; n++) + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -13219,11 +13295,10 @@ virDomainDefFormatInternal(virDomainDefPtr def, virBufferAddLit(buf, " </devices>\n"); - if (def->nseclabels && def->seclabels) { - virBufferAdjustIndent(buf, 2); - virSecurityLabelDefFormat(buf, def->seclabels[0]); - virBufferAdjustIndent(buf, -2); - } + virBufferAdjustIndent(buf, 2); + for (n = 0; n < def->nseclabels; n++) + virSecurityLabelDefFormat(buf, def->seclabels[n]); + virBufferAdjustIndent(buf, -2); if (def->namespaceData && def->ns.format) { if ((def->ns.format)(buf, def->namespaceData) < 0) @@ -15579,3 +15654,65 @@ cleanup: } return ret; } + +virSecurityLabelDefPtr +virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model) +{ + int i; + + if (def == NULL || model == NULL) + return NULL; + + for (i = 0; i < def->nseclabels; i++) { + if (def->seclabels[i]->model == NULL) + continue; + if (STREQ(def->seclabels[i]->model, model)) + return def->seclabels[i]; + } + + return virDomainDefAddSecurityLabelDef(def, model); +} + +virSecurityDeviceLabelDefPtr +virDomainDiskDefGetSecurityLabelDef(virDomainDiskDefPtr def, const char *model) +{ + int i; + + if (def == NULL) + return NULL; + + for (i = 0; i < def->nseclabels; i++) { + if (STREQ(def->seclabels[i]->model, model)) + return def->seclabels[i]; + } + return NULL; +} + +virSecurityLabelDefPtr +virDomainDefAddSecurityLabelDef(virDomainDefPtr def, const char *model) +{ + virSecurityLabelDefPtr seclabel = NULL; + + if (VIR_ALLOC(seclabel) < 0) { + virReportOOMError(); + return NULL; + } + + if (model) { + seclabel->model = strdup(model); + if (seclabel->model == NULL) { + virReportOOMError(); + virSecurityLabelDefFree(seclabel); + return NULL; + } + } + + if (VIR_EXPAND_N(def->seclabels, def->nseclabels, 1) < 0) { + virReportOOMError(); + virSecurityLabelDefFree(seclabel); + return NULL; + } + def->seclabels[def->nseclabels - 1] = seclabel; + + return seclabel; +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ac8da85..e99cabc 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2157,6 +2157,15 @@ virDomainState virDomainObjGetState(virDomainObjPtr obj, int *reason) ATTRIBUTE_NONNULL(1); +virSecurityLabelDefPtr +virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model); + +virSecurityDeviceLabelDefPtr +virDomainDiskDefGetSecurityLabelDef(virDomainDiskDefPtr def, const char *model); + +virSecurityLabelDefPtr +virDomainDefAddSecurityLabelDef(virDomainDefPtr def, const char *model); + typedef const char* (*virLifecycleToStringFunc)(int type); typedef int (*virLifecycleFromStringFunc)(const char *type); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e5d3582..bc61668 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -292,6 +292,9 @@ virDomainDefCompatibleDevice; virDomainDefFormat; virDomainDefFormatInternal; virDomainDefFree; +virDomainDefGetSecurityLabelDef; +virDomainDiskDefGetSecurityLabelDef; +virDomainDefAddSecurityLabelDef; virDomainDefParseFile; virDomainDefParseNode; virDomainDefParseString; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.xml b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.xml index 4de435b..426b663 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.xml @@ -16,14 +16,14 @@ <emulator>/usr/bin/qemu</emulator> <disk type='block' device='disk'> <source dev='/dev/HostVG/QEMUGuest1'> - <seclabel relabel='no'/> + <seclabel model='selinux' relabel='no'/> </source> <target dev='hda' bus='ide'/> <address type='drive' controller='0' bus='0' target='0' unit='0'/> </disk> <disk type='block' device='disk'> <source dev='/dev/HostVG/QEMUGuest2'> - <seclabel relabel='yes'> + <seclabel model='selinux' relabel='yes'> <label>system_u:system_r:public_content_t:s0</label> </seclabel> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml index 78a6b6a..36df9d4 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml @@ -22,5 +22,5 @@ <controller type='ide' index='0'/> <memballoon model='virtio'/> </devices> - <seclabel type='dynamic' relabel='yes'/> + <seclabel model='selinux' type='dynamic' relabel='yes'/> </domain> -- 1.7.1

On 16.08.2012 00:10, Marcelo Cerri wrote:
This patch updates the domain and capability XML parser and formatter to support more than one "seclabel" element for each domain and device. The RNG schema and the tests related to this are also updated by this patch.
Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- docs/formatdomain.html.in | 11 +- docs/schemas/capability.rng | 18 +- docs/schemas/domaincommon.rng | 30 ++- src/conf/capabilities.c | 6 +- src/conf/domain_conf.c | 353 ++++++++++++++------ src/conf/domain_conf.h | 9 + src/libvirt_private.syms | 3 + .../qemuxml2argv-seclabel-dynamic-override.xml | 4 +- .../qemuxml2argv-seclabel-dynamic.xml | 2 +- 9 files changed, 304 insertions(+), 132 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index fb25cb8..85d2515 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1275,8 +1275,8 @@ path to the file holding the disk. If the disk <code>type</code> is "block", then the <code>dev</code> attribute specifies the path to the host device to serve as - the disk. With both "file" and "block", an optional - sub-element <code>seclabel</code>, <a href="#seclabel">described + the disk. With both "file" and "block", one or more optional + sub-elements <code>seclabel</code>, <a href="#seclabel">described below</a> (and <span class="since">since 0.9.9</span>), can be used to override the domain security labeling policy for just that source file. If the disk <code>type</code> is "dir", then the @@ -3880,6 +3880,13 @@ qemu-kvm -net nic,model=? /dev/null </p>
<p> + If more than one security driver is used by libvirt, multiple + <code>seclabel</code> tags can be used, one for each driver and + the security driver referenced by each tag can be defined using + the attribute <code>model</code> + </p> + + <p> Valid input XML configurations for the top-level security label are: </p> diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index c392e44..8c928bc 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -44,20 +44,22 @@ <optional> <ref name='topology'/> </optional> - <optional> + <zeroOrMore> <ref name='secmodel'/> - </optional> + </zeroOrMore> </element> </define>
<define name='secmodel'> <element name='secmodel'> - <element name='model'> - <text/> - </element> - <element name='doi'> - <text/> - </element> + <interleave> + <element name='model'> + <text/> + </element> + <element name='doi'> + <text/> + </element> + </interleave> </element> </define>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 1b94155..dd93f4d 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -55,9 +55,9 @@ <optional> <ref name="devices"/> </optional> - <optional> + <zeroOrMore> <ref name="seclabel"/> - </optional> + </zeroOrMore> <optional> <ref name='qemucmdline'/> </optional> @@ -148,18 +148,32 @@ <!-- A per-device seclabel override is more limited, either relabel=no or a <label> must be present. --> <choice> - <attribute name='relabel'> - <value>no</value> - </attribute> <group> <optional> + <attribute name='model'> + <text/> + </attribute> + </optional> + <attribute name='relabel'> + <value>no</value> + </attribute> + </group> + <group> + <optional> + <attribute name='model'> + <text/> + </attribute> + </optional> + <optional> <attribute name='relabel'> <value>yes</value> </attribute> </optional> - <element name='label'> - <text/> - </element> + <zeroOrMore> + <element name='label'> + <text/> + </element> + </zeroOrMore> </group> </choice> </element> diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 9a0bc3d..bc6266e 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -772,12 +772,12 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBufferAddLit(&xml, " </topology>\n"); }
- if (caps->host.nsecModels) { + for (i = 0; i < caps->host.nsecModels; i++) { virBufferAddLit(&xml, " <secmodel>\n"); virBufferAsprintf(&xml, " <model>%s</model>\n", - caps->host.secModels[0].model); + caps->host.secModels[i].model); virBufferAsprintf(&xml, " <doi>%s</doi>\n", - caps->host.secModels[0].doi); + caps->host.secModels[i].doi); virBufferAddLit(&xml, " </secmodel>\n"); }
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 98daf32..2dea33e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3067,17 +3067,19 @@ virDomainDiskDefAssignAddress(virCapsPtr caps, virDomainDiskDefPtr def) return 0; }
-static int -virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, - xmlXPathContextPtr ctxt, +static virSecurityLabelDefPtr +virSecurityLabelDefParseXML(xmlXPathContextPtr ctxt, unsigned int flags) { char *p; + virSecurityLabelDefPtr def = NULL;
- if (virXPathNode("./seclabel[1]", ctxt) == NULL) - return 0; + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto error; + }
- p = virXPathStringLimit("string(./seclabel[1]/@type)", + p = virXPathStringLimit("string(./@type)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { def->type = VIR_DOMAIN_SECLABEL_DYNAMIC; @@ -3091,7 +3093,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, } }
- p = virXPathStringLimit("string(./seclabel[1]/@relabel)", + p = virXPathStringLimit("string(./@relabel)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p != NULL) { if (STREQ(p, "yes")) { @@ -3131,7 +3133,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, if (def->type == VIR_DOMAIN_SECLABEL_STATIC || (!(flags & VIR_DOMAIN_XML_INACTIVE) && def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel[1]/label[1])", + p = virXPathStringLimit("string(./label[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, @@ -3146,7 +3148,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, if (!def->norelabel && (!(flags & VIR_DOMAIN_XML_INACTIVE) && def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel[1]/imagelabel[1])", + p = virXPathStringLimit("string(./imagelabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, @@ -3158,93 +3160,179 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def,
/* Only parse baselabel for dynamic label type */ if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { - p = virXPathStringLimit("string(./seclabel[1]/baselabel[1])", + p = virXPathStringLimit("string(./baselabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); def->baselabel = p; }
- /* Only parse model, if static labelling, or a base - * label is set, or doing active XML - */ - if (def->type == VIR_DOMAIN_SECLABEL_STATIC || - def->baselabel || - (!(flags & VIR_DOMAIN_XML_INACTIVE) && - def->type != VIR_DOMAIN_SECLABEL_NONE)) { - p = virXPathStringLimit("string(./seclabel[1]/@model)", - VIR_SECURITY_MODEL_BUFLEN-1, ctxt); - if (p == NULL) { - virReportError(VIR_ERR_XML_ERROR, - "%s", _("missing security model")); - goto error; - } - def->model = p; + /* Always parse model */ + p = virXPathStringLimit("string(./@model)", + VIR_SECURITY_MODEL_BUFLEN-1, ctxt); + if (p == NULL && def->type != VIR_DOMAIN_SECLABEL_NONE) { + virReportError(VIR_ERR_XML_ERROR, + "%s", _("missing security model")); } + def->model = p;
- return 0; + return def;
error: virSecurityLabelDefFree(def); + return NULL; +} + +static int +virSecurityLabelDefsParseXML(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + int i, n;
'i' needs to be initialized, otherwise ..
+ xmlNodePtr *list = NULL, saved_node; + + /* Check args and save context */ + if (def == NULL || ctxt == NULL) + return 0; + saved_node = ctxt->node; + + /* Allocate a security labels based on XML */ + if ((n = virXPathNodeSet("./seclabel", ctxt, &list)) == 0) + return 0; + + if (VIR_ALLOC_N(def->seclabels, n) < 0) { + virReportOOMError(); + goto error;
.. we might jump to error here ..
+ } + + /* Parse each "seclabel" tag */ + for (i = 0; i < n; i++) { + ctxt->node = list[i]; + def->seclabels[i] = virSecurityLabelDefParseXML(ctxt, flags); + if (def->seclabels[i] == NULL) + goto error; + } + def->nseclabels = n; + ctxt->node = saved_node; + VIR_FREE(list); + + /* Checking missing model information + * when there is more than one seclabel */ + if (n > 1) { + for(; n; n--) { + if (def->seclabels[n - 1]->model == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing security model " + "when using multiple labels")); + goto error; + } + } + } + return 0; + +error: + ctxt->node = saved_node; + for (; i > 0; i--) {
and use 'i' uninitialized. side note: It took me quite some time to identify this as I had hit gcc bug here. For some reason it was reporting uninitialized variable 'i' but in totally different function virDomainDefParseXML. I guess since virSecurityLabelDefsParseXML is static, gcc might have inlined it and hence the wrong function name.
+ virSecurityLabelDefFree(def->seclabels[i - 1]); + } + VIR_FREE(def->seclabels); + VIR_FREE(list); return -1; }
ACK with this squashed in: diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8f9f6de..c9f5a3c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3104,7 +3104,7 @@ virSecurityLabelDefsParseXML(virDomainDefPtr def, xmlXPathContextPtr ctxt, unsigned int flags) { - int i, n; + int i = 0, n; xmlNodePtr *list = NULL, saved_node; /* Check args and save context */

These changes make the security drivers able to find and handle the correct security label information when more than one label is available. They also update the DAC driver to be used as an usual security driver. Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 45 ++++-- src/qemu/qemu_process.c | 50 ++++-- src/security/security_apparmor.c | 118 ++++++++++---- src/security/security_dac.c | 324 ++++++++++++++++++++++++++++++++++---- src/security/security_manager.c | 101 +++++++++--- src/security/security_manager.h | 8 +- src/security/security_selinux.c | 257 +++++++++++++++++++++--------- src/security/security_stack.c | 237 +++++++++++++++++++--------- src/security/security_stack.h | 13 ++ 10 files changed, 880 insertions(+), 275 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bc61668..b3f4bf5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -981,6 +981,7 @@ virSecurityManagerFree; virSecurityManagerGenLabel; virSecurityManagerGetDOI; virSecurityManagerGetModel; +virSecurityManagerGetNested; virSecurityManagerGetProcessLabel; virSecurityManagerNew; virSecurityManagerNewStack; @@ -999,6 +1000,7 @@ virSecurityManagerSetHostdevLabel; virSecurityManagerSetProcessLabel; virSecurityManagerSetSavedStateLabel; virSecurityManagerSetSocketLabel; +virSecurityManagerStackAddNested; virSecurityManagerVerify; virSecurityManagerGetMountOptions; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e2384c0..48376c3 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -268,8 +268,8 @@ qemuSecurityInit(struct qemud_driver *driver) if (!dac) goto error; - if (!(driver->securityManager = virSecurityManagerNewStack(mgr, - dac))) { + if (!(driver->securityManager = virSecurityManagerNewStack(mgr)) || + !(virSecurityManagerStackAddNested(mgr, dac))) { virSecurityManagerFree(dac); goto error; @@ -291,7 +291,11 @@ static virCapsPtr qemuCreateCapabilities(virCapsPtr oldcaps, struct qemud_driver *driver) { + size_t i; virCapsPtr caps; + virSecurityManagerPtr *sec_managers = NULL; + /* Security driver data */ + const char *doi, *model; /* Basic host arch / guest machine capabilities */ if (!(caps = qemuCapsInit(oldcaps))) { @@ -316,31 +320,38 @@ qemuCreateCapabilities(virCapsPtr oldcaps, goto err_exit; } - /* Security driver data */ - const char *doi, *model; + /* access sec drivers and create a sec model for each one */ + sec_managers = virSecurityManagerGetNested(driver->securityManager); + if (sec_managers == NULL) { + goto err_exit; + } - doi = virSecurityManagerGetDOI(driver->securityManager); - model = virSecurityManagerGetModel(driver->securityManager); + /* calculate length */ + for (i = 0; sec_managers[i]; i++) + ; + caps->host.nsecModels = i; - if (VIR_ALLOC(caps->host.secModels) < 0) { + if (VIR_ALLOC_N(caps->host.secModels, caps->host.nsecModels) < 0) goto no_memory; - } - if (STRNEQ(model, "none")) { - if (!(caps->host.secModels[0].model = strdup(model))) + for (i = 0; sec_managers[i]; i++) { + doi = virSecurityManagerGetDOI(sec_managers[i]); + model = virSecurityManagerGetModel(sec_managers[i]); + if (!(caps->host.secModels[i].model = strdup(model))) goto no_memory; - if (!(caps->host.secModels[0].doi = strdup(doi))) + if (!(caps->host.secModels[i].doi = strdup(doi))) goto no_memory; + VIR_DEBUG("Initialized caps for security driver \"%s\" with " + "DOI \"%s\"", model, doi); } - - VIR_DEBUG("Initialized caps for security driver \"%s\" with " - "DOI \"%s\"", model, doi); + VIR_FREE(sec_managers); return caps; no_memory: virReportOOMError(); err_exit: + VIR_FREE(sec_managers); virCapabilitiesFree(caps); return NULL; } @@ -4063,9 +4074,9 @@ static int qemudNodeGetSecurityModel(virConnectPtr conn, qemuDriverLock(driver); memset(secmodel, 0, sizeof(*secmodel)); - /* NULL indicates no driver, which we treat as - * success, but simply return no data in *secmodel */ - if (driver->caps->host.secModels[0].model == NULL) + /* We treat no driver as success, but simply return no data in *secmodel */ + if (driver->caps->host.nsecModels == 0 || + driver->caps->host.secModels[0].model == NULL) goto cleanup; p = driver->caps->host.secModels[0].model; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 553bf90..8a9f995 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4079,12 +4079,12 @@ void qemuProcessStop(struct qemud_driver *driver, virSecurityManagerReleaseLabel(driver->securityManager, vm->def); /* Clear out dynamically assigned labels */ - if (vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { - if (!vm->def->seclabels[0]->baselabel) - VIR_FREE(vm->def->seclabels[0]->model); - VIR_FREE(vm->def->seclabels[0]->label); + for (i = 0; i < vm->def->nseclabels; i++) { + if (vm->def->seclabels[i]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { + VIR_FREE(vm->def->seclabels[i]->label); + } + VIR_FREE(vm->def->seclabels[i]->imagelabel); } - VIR_FREE(vm->def->seclabels[0]->imagelabel); virDomainDefClearDeviceAliases(vm->def); if (!priv->persistentAddrs) { @@ -4188,6 +4188,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainChrSourceDefPtr monConfig, bool monJSON) { + size_t i; char ebuf[1024]; int logfile = -1; char *timestamp; @@ -4195,6 +4196,9 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, bool running = true; virDomainPausedReason reason; virSecurityLabelPtr seclabel = NULL; + virSecurityLabelDefPtr seclabeldef = NULL; + virSecurityManagerPtr* sec_managers = NULL; + const char *model; VIR_DEBUG("Beginning VM attach process"); @@ -4227,17 +4231,31 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, goto no_memory; VIR_DEBUG("Detect security driver config"); - vm->def->seclabels[0]->type = VIR_DOMAIN_SECLABEL_STATIC; - if (VIR_ALLOC(seclabel) < 0) - goto no_memory; - if (virSecurityManagerGetProcessLabel(driver->securityManager, - vm->def, vm->pid, seclabel) < 0) + sec_managers = virSecurityManagerGetNested(driver->securityManager); + if (sec_managers == NULL) { goto cleanup; - if (driver->caps->host.secModels[0].model && - !(vm->def->seclabels[0]->model = strdup(driver->caps->host.secModels[0].model))) - goto no_memory; - if (!(vm->def->seclabels[0]->label = strdup(seclabel->label))) - goto no_memory; + } + + for (i = 0; sec_managers[i]; i++) { + model = virSecurityManagerGetModel(sec_managers[i]); + seclabeldef = virDomainDefGetSecurityLabelDef(vm->def, model); + if (seclabeldef == NULL) { + goto cleanup; + } + seclabeldef->type = VIR_DOMAIN_SECLABEL_STATIC; + if (VIR_ALLOC(seclabel) < 0) + goto no_memory; + if (virSecurityManagerGetProcessLabel(driver->securityManager, + vm->def, vm->pid, seclabel) < 0) + goto cleanup; + + if (!(seclabeldef->model = strdup(model))) + goto no_memory; + + if (!(seclabeldef->label = strdup(seclabel->label))) + goto no_memory; + VIR_FREE(seclabel); + } VIR_DEBUG("Creating domain log file"); if ((logfile = qemuDomainCreateLog(driver, vm, false)) < 0) @@ -4362,6 +4380,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_FORCE_CLOSE(logfile); VIR_FREE(seclabel); + VIR_FREE(sec_managers); return 0; @@ -4373,6 +4392,7 @@ cleanup: * pretend we never started it */ VIR_FORCE_CLOSE(logfile); VIR_FREE(seclabel); + VIR_FREE(sec_managers); virDomainChrSourceDefFree(monConfig); return -1; } diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index 3385232..299bba6 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -272,9 +272,13 @@ reload_profile(virSecurityManagerPtr mgr, const char *fn, bool append) { - const virSecurityLabelDefPtr secdef = &def->seclabel; int rc = -1; char *profile_name = NULL; + const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef( + def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return rc; if (secdef->norelabel) return 0; @@ -308,10 +312,14 @@ AppArmorSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, virDomainDefPtr def = ptr->def; if (reload_profile(ptr->mgr, def, file, true) < 0) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef( + def, SECURITY_APPARMOR_NAME); + if (!secdef) { + virReportOOMError(); + return -1; + } virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot update AppArmor profile " - "\'%s\'"), + _("cannot update AppArmor profile \'%s\'"), secdef->imagelabel); return -1; } @@ -326,10 +334,14 @@ AppArmorSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, virDomainDefPtr def = ptr->def; if (reload_profile(ptr->mgr, def, file, true) < 0) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef( + def, SECURITY_APPARMOR_NAME); + if (!secdef) { + virReportOOMError(); + return -1; + } virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot update AppArmor profile " - "\'%s\'"), + _("cannot update AppArmor profile \'%s\'"), secdef->imagelabel); return -1; } @@ -408,18 +420,23 @@ AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, { int rc = -1; char *profile_name = NULL; + virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(def, + SECURITY_APPARMOR_NAME); - if (def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC) + if (!secdef) + return -1; + + if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) return 0; - if (def->seclabel.baselabel) { + if (secdef->baselabel) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Cannot set a base label with AppArmour")); return rc; } - if ((def->seclabel.label) || - (def->seclabel.model) || (def->seclabel.imagelabel)) { + if ((secdef->label) || + (secdef->model) || (secdef->imagelabel)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("security label already defined for VM")); @@ -429,31 +446,31 @@ AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, if ((profile_name = get_profile_name(def)) == NULL) return rc; - def->seclabel.label = strndup(profile_name, strlen(profile_name)); - if (!def->seclabel.label) { + secdef->label = strndup(profile_name, strlen(profile_name)); + if (!secdef->label) { virReportOOMError(); goto clean; } /* set imagelabel the same as label (but we won't use it) */ - def->seclabel.imagelabel = strndup(profile_name, - strlen(profile_name)); - if (!def->seclabel.imagelabel) { + secdef->imagelabel = strndup(profile_name, + strlen(profile_name)); + if (!secdef->imagelabel) { virReportOOMError(); goto err; } - def->seclabel.model = strdup(SECURITY_APPARMOR_NAME); - if (!def->seclabel.model) { + secdef->model = strdup(SECURITY_APPARMOR_NAME); + if (!secdef->model) { virReportOOMError(); goto err; } /* Now that we have a label, load the profile into the kernel. */ - if (load_profile(mgr, def->seclabel.label, def, NULL, false) < 0) { + if (load_profile(mgr, secdef->label, def, NULL, false) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot load AppArmor profile " - "\'%s\'"), def->seclabel.label); + "\'%s\'"), secdef->label); goto err; } @@ -461,9 +478,9 @@ AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, goto clean; err: - VIR_FREE(def->seclabel.label); - VIR_FREE(def->seclabel.imagelabel); - VIR_FREE(def->seclabel.model); + VIR_FREE(secdef->label); + VIR_FREE(secdef->imagelabel); + VIR_FREE(secdef->model); clean: VIR_FREE(profile_name); @@ -475,7 +492,12 @@ static int AppArmorSetSecurityAllLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, const char *stdin_path) { - if (def->seclabel.norelabel) + virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(def, + SECURITY_APPARMOR_NAME); + if (!secdef) + return -1; + + if (secdef->norelabel) return 0; /* Reload the profile if stdin_path is specified. Note that @@ -528,7 +550,10 @@ static int AppArmorReleaseSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(def, + SECURITY_APPARMOR_NAME); + if (!secdef) + return -1; VIR_FREE(secdef->model); VIR_FREE(secdef->label); @@ -543,8 +568,12 @@ AppArmorRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, int migrated ATTRIBUTE_UNUSED) { - const virSecurityLabelDefPtr secdef = &def->seclabel; int rc = 0; + const virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return -1; if (secdef->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { if ((rc = remove_profile(secdef->label)) != 0) { @@ -562,9 +591,13 @@ AppArmorRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, static int AppArmorSetSecurityProcessLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = &def->seclabel; int rc = -1; char *profile_name = NULL; + const virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return -1; if ((profile_name = get_profile_name(def)) == NULL) return rc; @@ -631,9 +664,13 @@ static int AppArmorSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, virDomainDiskDefPtr disk) { - const virSecurityLabelDefPtr secdef = &def->seclabel; int rc = -1; char *profile_name; + const virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return -1; if (secdef->norelabel) return 0; @@ -676,7 +713,11 @@ static int AppArmorSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return -1; if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) { if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) { @@ -704,9 +745,13 @@ AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainHostdevDefPtr dev) { - const virSecurityLabelDefPtr secdef = &def->seclabel; struct SDPDOP *ptr; int ret = -1; + const virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return -1; if (secdef->norelabel) return 0; @@ -766,7 +811,12 @@ AppArmorRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return -1; + if (secdef->norelabel) return 0; @@ -799,7 +849,11 @@ AppArmorSetImageFDLabel(virSecurityManagerPtr mgr, char *proc = NULL; char *fd_path = NULL; - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); + + if (!secdef) + return -1; if (secdef->imagelabel == NULL) return 0; diff --git a/src/security/security_dac.c b/src/security/security_dac.c index f398c3f..e37b09e 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -33,6 +33,7 @@ #include "storage_file.h" #define VIR_FROM_THIS VIR_FROM_SECURITY +#define SECURITY_DAC_NAME "dac" typedef struct _virSecurityDACData virSecurityDACData; typedef virSecurityDACData *virSecurityDACDataPtr; @@ -64,6 +65,134 @@ void virSecurityDACSetDynamicOwnership(virSecurityManagerPtr mgr, priv->dynamicOwnership = dynamicOwnership; } +static +int parseIds(const char *label, uid_t *uidPtr, gid_t *gidPtr) +{ + uid_t uid; + gid_t gid; + char *endptr = NULL; + + if (label == NULL) + return -1; + + if (virStrToLong_ui(label, &endptr, 10, &uid) || + endptr == NULL || *endptr != ':') { + return -1; + } + + if (virStrToLong_ui(endptr + 1, NULL, 10, &gid)) + return -1; + + if (uidPtr) + *uidPtr = uid; + if (gidPtr) + *gidPtr = gid; + return 0; +} + +static +int virSecurityDACParseIds(virDomainDefPtr def, uid_t *uidPtr, gid_t *gidPtr) +{ + uid_t uid; + gid_t gid; + virSecurityLabelDefPtr seclabel; + + if (def == NULL) + return -1; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); + if (seclabel == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label for DAC not found in domain %s"), + def->name); + return -1; + } + + if (seclabel->label && parseIds(seclabel->label, &uid, &gid)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse uid and gid for DAC " + "security driver: %s"), seclabel->label); + return -1; + } + + if (uidPtr) + *uidPtr = uid; + if (gidPtr) + *gidPtr = gid; + + return 0; +} + +static +int virSecurityDACGetIds(virDomainDefPtr def, virSecurityDACDataPtr priv, + uid_t *uidPtr, gid_t *gidPtr) +{ + if (virSecurityDACParseIds(def, uidPtr, gidPtr) == 0) + return 0; + + if (priv) { + if (uidPtr) + *uidPtr = priv->user; + if (gidPtr) + *gidPtr = priv->group; + return 0; + } + return -1; +} + +static +int virSecurityDACParseImageIds(virDomainDefPtr def, + uid_t *uidPtr, gid_t *gidPtr) +{ + uid_t uid; + gid_t gid; + virSecurityLabelDefPtr seclabel; + + if (def == NULL) + return -1; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); + if (seclabel == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label for DAC not found in domain %s"), + def->name); + return -1; + } + + if (seclabel->imagelabel + && parseIds(seclabel->imagelabel, &uid, &gid)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse uid and gid for DAC " + "security driver: %s"), seclabel->label); + return -1; + } + + if (uidPtr) + *uidPtr = uid; + if (gidPtr) + *gidPtr = gid; + + return 0; +} + +static +int virSecurityDACGetImageIds(virDomainDefPtr def, virSecurityDACDataPtr priv, + uid_t *uidPtr, gid_t *gidPtr) +{ + if (virSecurityDACParseImageIds(def, uidPtr, gidPtr) == 0) + return 0; + + if (priv) { + if (uidPtr) + *uidPtr = priv->user; + if (gidPtr) + *gidPtr = priv->group; + return 0; + } + return -1; +} + + static virSecurityDriverStatus virSecurityDACProbe(const char *virtDriver ATTRIBUTE_UNUSED) { @@ -85,7 +214,7 @@ virSecurityDACClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) static const char * virSecurityDACGetModel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) { - return "dac"; + return SECURITY_DAC_NAME; } static const char * virSecurityDACGetDOI(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) @@ -167,10 +296,17 @@ virSecurityDACSetSecurityFileLabel(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, size_t depth ATTRIBUTE_UNUSED, void *opaque) { - virSecurityManagerPtr mgr = opaque; + void **params = opaque; + virSecurityManagerPtr mgr = params[0]; + virDomainDefPtr def = params[1]; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + uid_t user; + gid_t group; - return virSecurityDACSetOwnership(path, priv->user, priv->group); + if (virSecurityDACGetImageIds(def, priv, &user, &group)) + return -1; + + return virSecurityDACSetOwnership(path, user, group); } @@ -180,6 +316,9 @@ virSecurityDACSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk) { + uid_t user; + gid_t group; + void *params[2]; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); if (!priv->dynamicOwnership) @@ -188,12 +327,17 @@ virSecurityDACSetSecurityImageLabel(virSecurityManagerPtr mgr, if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0; + if (virSecurityDACGetImageIds(def, priv, &user, &group)) + return -1; + + params[0] = mgr; + params[1] = def; return virDomainDiskDefForeachPath(disk, virSecurityManagerGetAllowDiskFormatProbing(mgr), false, - priv->user, priv->group, + user, group, virSecurityDACSetSecurityFileLabel, - mgr); + params); } @@ -259,10 +403,17 @@ virSecurityDACSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { - virSecurityManagerPtr mgr = opaque; + void **params = opaque; + virSecurityManagerPtr mgr = params[0]; + virDomainDefPtr def = params[1]; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + uid_t user; + gid_t group; - return virSecurityDACSetOwnership(file, priv->user, priv->group); + if (virSecurityDACGetIds(def, priv, &user, &group)) + return -1; + + return virSecurityDACSetOwnership(file, user, group); } @@ -271,18 +422,26 @@ virSecurityDACSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { - virSecurityManagerPtr mgr = opaque; + void **params = opaque; + virSecurityManagerPtr mgr = params[0]; + virDomainDefPtr def = params[1]; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + uid_t user; + gid_t group; - return virSecurityDACSetOwnership(file, priv->user, priv->group); + if (virSecurityDACGetIds(def, priv, &user, &group)) + return -1; + + return virSecurityDACSetOwnership(file, user, group); } static int virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr, - virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDefPtr def, virDomainHostdevDefPtr dev) { + void *params[] = {mgr, def}; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); int ret = -1; @@ -300,7 +459,8 @@ virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr, if (!usb) goto done; - ret = usbDeviceFileIterate(usb, virSecurityDACSetSecurityUSBLabel, mgr); + ret = usbDeviceFileIterate(usb, virSecurityDACSetSecurityUSBLabel, + params); usbFreeDevice(usb); break; } @@ -314,7 +474,8 @@ virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr, if (!pci) goto done; - ret = pciDeviceFileIterate(pci, virSecurityDACSetSecurityPCILabel, mgr); + ret = pciDeviceFileIterate(pci, virSecurityDACSetSecurityPCILabel, + params); pciFreeDevice(pci); break; @@ -404,17 +565,23 @@ done: static int virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, virDomainChrSourceDefPtr dev) { virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); char *in = NULL, *out = NULL; int ret = -1; + uid_t user; + gid_t group; + + if (virSecurityDACGetIds(def, priv, &user, &group)) + return -1; switch (dev->type) { case VIR_DOMAIN_CHR_TYPE_DEV: case VIR_DOMAIN_CHR_TYPE_FILE: - ret = virSecurityDACSetOwnership(dev->data.file.path, priv->user, priv->group); + ret = virSecurityDACSetOwnership(dev->data.file.path, user, group); break; case VIR_DOMAIN_CHR_TYPE_PIPE: @@ -424,12 +591,12 @@ virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr, goto done; } if (virFileExists(in) && virFileExists(out)) { - if ((virSecurityDACSetOwnership(in, priv->user, priv->group) < 0) || - (virSecurityDACSetOwnership(out, priv->user, priv->group) < 0)) { + if ((virSecurityDACSetOwnership(in, user, group) < 0) || + (virSecurityDACSetOwnership(out, user, group) < 0)) { goto done; } } else if (virSecurityDACSetOwnership(dev->data.file.path, - priv->user, priv->group) < 0) { + user, group) < 0) { goto done; } ret = 0; @@ -554,7 +721,7 @@ virSecurityDACSetChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, { virSecurityManagerPtr mgr = opaque; - return virSecurityDACSetChardevLabel(mgr, &dev->source); + return virSecurityDACSetChardevLabel(mgr, def, &dev->source); } @@ -565,6 +732,8 @@ virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr, { virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); int i; + uid_t user; + gid_t group; if (!priv->dynamicOwnership) return 0; @@ -591,16 +760,15 @@ virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr, mgr) < 0) return -1; + if (virSecurityDACGetImageIds(def, priv, &user, &group)) + return -1; + if (def->os.kernel && - virSecurityDACSetOwnership(def->os.kernel, - priv->user, - priv->group) < 0) + virSecurityDACSetOwnership(def->os.kernel, user, group) < 0) return -1; if (def->os.initrd && - virSecurityDACSetOwnership(def->os.initrd, - priv->user, - priv->group) < 0) + virSecurityDACSetOwnership(def->os.initrd, user, group) < 0) return -1; return 0; @@ -609,12 +777,17 @@ virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr, static int virSecurityDACSetSavedStateLabel(virSecurityManagerPtr mgr, - virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDefPtr def, const char *savefile) { + uid_t user; + gid_t group; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); - return virSecurityDACSetOwnership(savefile, priv->user, priv->group); + if (virSecurityDACGetImageIds(def, priv, &user, &group)) + return -1; + + return virSecurityDACSetOwnership(savefile, user, group); } @@ -636,12 +809,16 @@ static int virSecurityDACSetProcessLabel(virSecurityManagerPtr mgr, virDomainDefPtr def ATTRIBUTE_UNUSED) { + uid_t user; + gid_t group; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); - VIR_DEBUG("Dropping privileges of DEF to %u:%u", - (unsigned int) priv->user, (unsigned int) priv->group); + if (virSecurityDACGetIds(def, priv, &user, &group)) + return -1; - if (virSetUIDGID(priv->user, priv->group) < 0) + VIR_DEBUG("Dropping privileges of DEF to %u:%u", user, group); + + if (virSetUIDGID(user, group) < 0) return -1; return 0; @@ -656,9 +833,85 @@ virSecurityDACVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, } static int -virSecurityDACGenLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, - virDomainDefPtr def ATTRIBUTE_UNUSED) +virSecurityDACGenLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def) { + int rc = -1; + virSecurityLabelDefPtr seclabel; + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + if (mgr == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("invalid security driver")); + return rc; + } + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); + if (seclabel == NULL) { + return rc; + } + + if (seclabel->imagelabel) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("security image label already " + "defined for VM")); + return rc; + } + + if (seclabel->model + && STRNEQ(seclabel->model, SECURITY_DAC_NAME)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label model %s is not supported " + "with selinux"), + seclabel->model); + return rc; + } + + switch(seclabel->type) { + case VIR_DOMAIN_SECLABEL_STATIC: + if (seclabel->label == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing label for static security " + "driver in domain %s"), def->name); + return rc; + } + break; + case VIR_DOMAIN_SECLABEL_DYNAMIC: + if (virAsprintf(&seclabel->label, "%d:%d", priv->user, priv->group) < 0) { + virReportOOMError(); + return rc; + } + if (seclabel->label == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot generate dac user and group id " + "for domain %s"), def->name); + return rc; + } + break; + case VIR_DOMAIN_SECLABEL_NONE: + /* no op */ + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected security label type '%s'"), + virDomainSeclabelTypeToString(seclabel->type)); + return rc; + } + + if (!seclabel->norelabel) { + if (seclabel->imagelabel == NULL) { + seclabel->imagelabel = strdup(seclabel->label); + if (seclabel->imagelabel == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot generate dac user and group id " + "for domain %s"), def->name); + VIR_FREE(seclabel->label); + seclabel->label = NULL; + return rc; + } + } + } + return 0; } @@ -683,6 +936,15 @@ virSecurityDACGetProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, pid_t pid ATTRIBUTE_UNUSED, virSecurityLabelPtr seclabel ATTRIBUTE_UNUSED) { + virSecurityLabelDefPtr secdef = + virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); + + if (!secdef || !seclabel) + return -1; + + if (secdef->label) + strcpy(seclabel->label, secdef->label); + return 0; } @@ -724,7 +986,7 @@ static char *virSecurityDACGetMountOptions(virSecurityManagerPtr mgr ATTRIBUTE_U virSecurityDriver virSecurityDriverDAC = { .privateDataLen = sizeof(virSecurityDACData), - .name = "virDAC", + .name = SECURITY_DAC_NAME, .probe = virSecurityDACProbe, .open = virSecurityDACOpen, .close = virSecurityDACClose, diff --git a/src/security/security_manager.c b/src/security/security_manager.c index 6b372b5..0e106d5 100644 --- a/src/security/security_manager.c +++ b/src/security/security_manager.c @@ -68,8 +68,7 @@ static virSecurityManagerPtr virSecurityManagerNewDriver(virSecurityDriverPtr dr return mgr; } -virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary, - virSecurityManagerPtr secondary) +virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary) { virSecurityManagerPtr mgr = virSecurityManagerNewDriver(&virSecurityDriverStack, @@ -81,12 +80,19 @@ virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary, if (!mgr) return NULL; - virSecurityStackSetPrimary(mgr, primary); - virSecurityStackSetSecondary(mgr, secondary); + virSecurityStackAddPrimary(mgr, primary); return mgr; } +int virSecurityManagerStackAddNested(virSecurityManagerPtr stack, + virSecurityManagerPtr nested) +{ + if (!STREQ("stack", stack->drv->name)) + return -1; + return virSecurityStackAddNested(stack, nested); +} + virSecurityManagerPtr virSecurityManagerNewDAC(const char *virtDriver, uid_t user, gid_t group, @@ -308,27 +314,52 @@ int virSecurityManagerRestoreSavedStateLabel(virSecurityManagerPtr mgr, int virSecurityManagerGenLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { - if (vm->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DEFAULT) { - if (mgr->defaultConfined) { - vm->seclabels[0]->type = VIR_DOMAIN_SECLABEL_DYNAMIC; + int rc = 0; + size_t i; + virSecurityManagerPtr* sec_managers = NULL; + virSecurityLabelDefPtr seclabel; + + if (mgr == NULL || mgr->drv == NULL) + return -1; + + if ((sec_managers = virSecurityManagerGetNested(mgr)) == NULL) + return -1; + + for (i = 0; sec_managers[i]; i++) { + seclabel = virDomainDefGetSecurityLabelDef(vm, + sec_managers[i]->drv->name); + if (seclabel == NULL) { + rc = -1; + goto cleanup; + } + + if (seclabel->type == VIR_DOMAIN_SECLABEL_DEFAULT) { + if (sec_managers[i]->defaultConfined) + seclabel->type = VIR_DOMAIN_SECLABEL_DYNAMIC; + else + seclabel->type = VIR_DOMAIN_SECLABEL_NONE; + } + + if ((seclabel->type == VIR_DOMAIN_SECLABEL_NONE) && + sec_managers[i]->requireConfined) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Unconfined guests are not allowed on this host")); + rc = -1; + goto cleanup; + } + + if (!sec_managers[i]->drv->domainGenSecurityLabel) { + virReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); } else { - vm->seclabels[0]->type = VIR_DOMAIN_SECLABEL_NONE; - vm->seclabels[0]->norelabel = true; + rc += sec_managers[i]->drv->domainGenSecurityLabel(sec_managers[i], vm); + if (rc) + goto cleanup; } } - if ((vm->seclabels[0]->type == VIR_DOMAIN_SECLABEL_NONE) && - mgr->requireConfined) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Unconfined guests are not allowed on this host")); - return -1; - } - - if (mgr->drv->domainGenSecurityLabel) - return mgr->drv->domainGenSecurityLabel(mgr, vm); - - virReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; +cleanup: + VIR_FREE(sec_managers); + return rc; } int virSecurityManagerReserveLabel(virSecurityManagerPtr mgr, @@ -399,12 +430,17 @@ int virSecurityManagerSetProcessLabel(virSecurityManagerPtr mgr, int virSecurityManagerVerify(virSecurityManagerPtr mgr, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + if (mgr == NULL || mgr->drv == NULL) + return 0; + /* NULL model == dynamic labelling, with whatever driver * is active, so we can short circuit verify check to * avoid drivers de-referencing NULLs by accident */ - if (!secdef->model) + secdef = virDomainDefGetSecurityLabelDef(def, mgr->drv->name); + if (secdef == NULL || secdef->model == NULL) return 0; if (mgr->drv->domainSecurityVerify) @@ -437,3 +473,22 @@ char *virSecurityManagerGetMountOptions(virSecurityManagerPtr mgr, */ return NULL; } + +virSecurityManagerPtr* +virSecurityManagerGetNested(virSecurityManagerPtr mgr) +{ + virSecurityManagerPtr* list = NULL; + + if (STREQ("stack", mgr->drv->name)) { + return virSecurityStackGetNested(mgr); + } + + if (VIR_ALLOC_N(list, 2) < 0) { + virReportOOMError(); + return NULL; + } + + list[0] = mgr; + list[1] = NULL; + return list; +} diff --git a/src/security/security_manager.h b/src/security/security_manager.h index 325fc1e..7d63806 100644 --- a/src/security/security_manager.h +++ b/src/security/security_manager.h @@ -34,8 +34,9 @@ virSecurityManagerPtr virSecurityManagerNew(const char *name, bool defaultConfined, bool requireConfined); -virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary, - virSecurityManagerPtr secondary); +virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary); +int virSecurityManagerStackAddNested(virSecurityManagerPtr stack, + virSecurityManagerPtr nested); virSecurityManagerPtr virSecurityManagerNewDAC(const char *virtDriver, uid_t user, @@ -106,4 +107,7 @@ int virSecurityManagerSetImageFDLabel(virSecurityManagerPtr mgr, int fd); char *virSecurityManagerGetMountOptions(virSecurityManagerPtr mgr, virDomainDefPtr vm); +virSecurityManagerPtr* +virSecurityManagerGetNested(virSecurityManagerPtr mgr); + #endif /* VIR_SECURITY_MANAGER_H__ */ diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 474ff66..d7beb1c 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -360,46 +360,52 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, char *scontext = NULL; context_t ctx = NULL; const char *range; - virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); + virSecurityLabelDefPtr seclabel; + virSecuritySELinuxDataPtr data; - VIR_DEBUG("driver=%s", virSecurityManagerGetDriver(mgr)); - if ((def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) && - !def->seclabels[0]->baselabel && - def->seclabels[0]->model) { + if (mgr == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("security model already defined for VM")); + "%s", _("invalid security driver")); return rc; } - if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC && - def->seclabels[0]->label) { + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (seclabel == NULL) { + return rc; + } + + data = virSecurityManagerGetPrivateData(mgr); + + VIR_DEBUG("label=%s", virSecurityManagerGetDriver(mgr)); + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + seclabel->label) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("security label already defined for VM")); return rc; } - if (def->seclabels[0]->imagelabel) { + if (seclabel->imagelabel) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("security image label already defined for VM")); return rc; } - if (def->seclabels[0]->model && - STRNEQ(def->seclabels[0]->model, SECURITY_SELINUX_NAME)) { + if (seclabel->model && + STRNEQ(seclabel->model, SECURITY_SELINUX_NAME)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security label model %s is not supported with selinux"), - def->seclabels[0]->model); + seclabel->model); return rc; } - VIR_DEBUG("type%d", def->seclabels[0]->type); + VIR_DEBUG("type=%d", seclabel->type); - switch (def->seclabels[0]->type) { + switch (seclabel->type) { case VIR_DOMAIN_SECLABEL_STATIC: - if (!(ctx = context_new(def->seclabels[0]->label)) ) { + if (!(ctx = context_new(seclabel->label)) ) { virReportSystemError(errno, _("unable to allocate socket security context '%s'"), - def->seclabels[0]->label); + seclabel->label); return rc; } @@ -418,11 +424,11 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, if (virSecuritySELinuxMCSAdd(mgr, mcs) < 0) goto cleanup; - def->seclabels[0]->label = - virSecuritySELinuxGenNewContext(def->seclabels[0]->baselabel ? - def->seclabels[0]->baselabel : - data->domain_context, mcs); - if (! def->seclabels[0]->label) { + seclabel->label = + virSecuritySELinuxGenNewContext(seclabel->baselabel ? + seclabel->baselabel : + data->domain_context, mcs); + if (!seclabel->label) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate selinux context for %s"), mcs); goto cleanup; @@ -436,22 +442,22 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected security label type '%s'"), - virDomainSeclabelTypeToString(def->seclabels[0]->type)); + virDomainSeclabelTypeToString(seclabel->type)); goto cleanup; } - if (!def->seclabels[0]->norelabel) { - def->seclabels[0]->imagelabel = virSecuritySELinuxGenNewContext( - data->file_context, mcs); - if (!def->seclabels[0]->imagelabel) { + if (!seclabel->norelabel) { + seclabel->imagelabel = virSecuritySELinuxGenNewContext( + data->domain_context, mcs); + if (!seclabel->imagelabel) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate selinux context for %s"), mcs); goto cleanup; } } - if (!def->seclabels[0]->model && - !(def->seclabels[0]->model = strdup(SECURITY_SELINUX_NAME))) { + if (!seclabel->model && + !(seclabel->model = strdup(SECURITY_SELINUX_NAME))) { virReportOOMError(); goto cleanup; } @@ -460,12 +466,12 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, cleanup: if (rc != 0) { - if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) - VIR_FREE(def->seclabels[0]->label); - VIR_FREE(def->seclabels[0]->imagelabel); - if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC && - !def->seclabels[0]->baselabel) - VIR_FREE(def->seclabels[0]->model); + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC) + VIR_FREE(seclabel->label); + VIR_FREE(seclabel->imagelabel); + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + !seclabel->baselabel) + VIR_FREE(seclabel->model); } if (ctx) @@ -474,10 +480,10 @@ cleanup: VIR_FREE(mcs); VIR_DEBUG("model=%s label=%s imagelabel=%s baselabel=%s", - NULLSTR(def->seclabels[0]->model), - NULLSTR(def->seclabels[0]->label), - NULLSTR(def->seclabels[0]->imagelabel), - NULLSTR(def->seclabels[0]->baselabel)); + NULLSTR(seclabel->model), + NULLSTR(seclabel->label), + NULLSTR(seclabel->imagelabel), + NULLSTR(seclabel->baselabel)); return rc; } @@ -491,8 +497,14 @@ virSecuritySELinuxReserveSecurityLabel(virSecurityManagerPtr mgr, context_t ctx = NULL; const char *mcs; int rv; + virSecurityLabelDefPtr seclabel; - if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_STATIC) + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (seclabel == NULL) { + return -1; + } + + if (seclabel->type == VIR_DOMAIN_SECLABEL_STATIC) return 0; if (getpidcon(pid, &pctx) == -1) { @@ -796,9 +808,16 @@ virSecuritySELinuxRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr ATTRIBU virDomainDiskDefPtr disk, int migrated) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr seclabel; + virSecurityDeviceLabelDefPtr disk_seclabel; - if (secdef->norelabel || (disk->seclabels[0] && disk->seclabels[0]->norelabel)) + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (seclabel == NULL) + return -1; + + disk_seclabel = virDomainDiskDefGetSecurityLabelDef(disk, + SECURITY_SELINUX_NAME); + if (seclabel->norelabel || (disk_seclabel && disk_seclabel->norelabel)) return 0; /* Don't restore labels on readoly/shared disks, because @@ -850,17 +869,21 @@ virSecuritySELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk, size_t depth, void *opaque) { + int ret; + virSecurityDeviceLabelDefPtr disk_seclabel; virSecuritySELinuxCallbackDataPtr cbdata = opaque; const virSecurityLabelDefPtr secdef = cbdata->secdef; - int ret; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(cbdata->manager); - if (disk->seclabels[0] && disk->seclabels[0]->norelabel) + disk_seclabel = virDomainDiskDefGetSecurityLabelDef(disk, + SECURITY_SELINUX_NAME); + + if (disk_seclabel && disk_seclabel->norelabel) return 0; - if (disk->seclabels[0] && !disk->seclabels[0]->norelabel && - disk->seclabels[0]->label) { - ret = virSecuritySELinuxSetFilecon(path, disk->seclabels[0]->label); + if (disk_seclabel && !disk_seclabel->norelabel && + disk_seclabel->label) { + ret = virSecuritySELinuxSetFilecon(path, disk_seclabel->label); } else if (depth == 0) { if (disk->shared) { @@ -875,14 +898,14 @@ virSecuritySELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk, } else { ret = virSecuritySELinuxSetFileconOptional(path, data->content_context); } - if (ret == 1 && !disk->seclabels[0]) { + if (ret == 1 && !disk_seclabel) { /* If we failed to set a label, but virt_use_nfs let us * proceed anyway, then we don't need to relabel later. */ - if (VIR_ALLOC(disk->seclabels[0]) < 0) { + if (VIR_ALLOC(disk_seclabel) < 0) { virReportOOMError(); return -1; } - disk->seclabels[0]->norelabel = true; + disk_seclabel->norelabel = true; ret = 0; } return ret; @@ -894,11 +917,15 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk) { + bool allowDiskFormatProbing; virSecuritySELinuxCallbackData cbdata; - cbdata.secdef = def->seclabels[0]; cbdata.manager = mgr; + cbdata.secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); - bool allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr); + allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr); + + if (cbdata.secdef == NULL) + return -1; if (cbdata.secdef->norelabel) return 0; @@ -925,9 +952,12 @@ static int virSecuritySELinuxSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { + virSecurityLabelDefPtr secdef; virDomainDefPtr def = opaque; - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; return virSecuritySELinuxSetFilecon(file, secdef->imagelabel); } @@ -935,8 +965,12 @@ static int virSecuritySELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { + virSecurityLabelDefPtr secdef; virDomainDefPtr def = opaque; - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; return virSecuritySELinuxSetFilecon(file, secdef->imagelabel); } @@ -947,9 +981,13 @@ virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN virDomainHostdevDefPtr dev) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; int ret = -1; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + if (secdef->norelabel) return 0; @@ -1016,9 +1054,13 @@ virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUT virDomainHostdevDefPtr dev) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; int ret = -1; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + if (secdef->norelabel) return 0; @@ -1069,10 +1111,14 @@ virSecuritySELinuxSetSecurityChardevLabel(virDomainDefPtr def, virDomainChrSourceDefPtr dev) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; char *in = NULL, *out = NULL; int ret = -1; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + if (secdef->norelabel) return 0; @@ -1115,10 +1161,14 @@ virSecuritySELinuxRestoreSecurityChardevLabel(virDomainDefPtr def, virDomainChrSourceDefPtr dev) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; char *in = NULL, *out = NULL; int ret = -1; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + if (secdef->norelabel) return 0; @@ -1208,12 +1258,16 @@ virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN virDomainDefPtr def, int migrated ATTRIBUTE_UNUSED) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; int i; int rc = 0; VIR_DEBUG("Restoring security label on %s", def->name); + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + if (secdef->norelabel) return 0; @@ -1258,7 +1312,11 @@ static int virSecuritySELinuxReleaseSecurityLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; if (secdef->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { if (secdef->label != NULL) { @@ -1283,7 +1341,11 @@ virSecuritySELinuxSetSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, const char *savefile) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; if (secdef->norelabel) return 0; @@ -1297,7 +1359,11 @@ virSecuritySELinuxRestoreSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNU virDomainDefPtr def, const char *savefile) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; if (secdef->norelabel) return 0; @@ -1310,7 +1376,12 @@ static int virSecuritySELinuxSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " @@ -1335,12 +1406,16 @@ virSecuritySELinuxSetSecurityProcessLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { /* TODO: verify DOI */ - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + if (secdef->label == NULL) + return 0; + VIR_DEBUG("label=%s", secdef->label); - - if (def->seclabels[0]->label == NULL) - return 0; - if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " @@ -1367,13 +1442,17 @@ virSecuritySELinuxSetSecurityDaemonSocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { /* TODO: verify DOI */ - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; context_t execcon = NULL; context_t proccon = NULL; security_context_t scon = NULL; int rc = -1; - if (def->seclabels[0]->label == NULL) + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + if (secdef->label == NULL) return 0; if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { @@ -1437,9 +1516,13 @@ static int virSecuritySELinuxSetSecuritySocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { - const virSecurityLabelDefPtr secdef = vm->seclabels[0]; + virSecurityLabelDefPtr secdef; int rc = -1; + secdef = virDomainDefGetSecurityLabelDef(vm, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + if (secdef->label == NULL) return 0; @@ -1475,9 +1558,13 @@ virSecuritySELinuxClearSecuritySocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { /* TODO: verify DOI */ - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; - if (def->seclabels[0]->label == NULL) + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + if (secdef->label == NULL) return 0; if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { @@ -1553,9 +1640,13 @@ virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, const char *stdin_path) { - virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); - const virSecurityLabelDefPtr secdef = def->seclabels[0]; int i; + virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; if (secdef->norelabel) return 0; @@ -1615,7 +1706,11 @@ virSecuritySELinuxSetImageFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, int fd) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; if (secdef->imagelabel == NULL) return 0; @@ -1627,13 +1722,17 @@ static char * virSecuritySELinuxGenImageLabel(virSecurityManagerPtr mgr, virDomainDefPtr def) { - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); const char *range; context_t ctx = NULL; char *label = NULL; const char *mcs = NULL; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + goto cleanup; + if (secdef->label) { ctx = context_new(secdef->label); if (!ctx) { @@ -1663,7 +1762,11 @@ virSecuritySELinuxGetSecurityMountOptions(virSecurityManagerPtr mgr, virDomainDefPtr def) { char *opts = NULL; - const virSecurityLabelDefPtr secdef = def->seclabels[0]; + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return NULL; if (! secdef->imagelabel) secdef->imagelabel = virSecuritySELinuxGenImageLabel(mgr,def); diff --git a/src/security/security_stack.c b/src/security/security_stack.c index 65d9dd7..7dcd626 100644 --- a/src/security/security_stack.c +++ b/src/security/security_stack.c @@ -23,29 +23,70 @@ #include "security_stack.h" #include "virterror_internal.h" +#include "memory.h" #define VIR_FROM_THIS VIR_FROM_SECURITY typedef struct _virSecurityStackData virSecurityStackData; typedef virSecurityStackData *virSecurityStackDataPtr; +typedef struct _virSecurityStackItem virSecurityStackItem; +typedef virSecurityStackItem* virSecurityStackItemPtr; + +struct _virSecurityStackItem { + virSecurityManagerPtr securityManager; + virSecurityStackItemPtr next; +}; struct _virSecurityStackData { virSecurityManagerPtr primary; - virSecurityManagerPtr secondary; + virSecurityStackItemPtr itemsHead; }; +int +virSecurityStackAddPrimary(virSecurityManagerPtr mgr, + virSecurityManagerPtr primary) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + if (virSecurityStackAddNested(mgr, primary) < 0) + return -1; + priv->primary = primary; + return 0; +} + +int +virSecurityStackAddNested(virSecurityManagerPtr mgr, + virSecurityManagerPtr nested) +{ + virSecurityStackItemPtr item = NULL; + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + if (VIR_ALLOC(item) < 0) { + virReportOOMError(); + return -1; + } + item->securityManager = nested; + item->next = priv->itemsHead; + priv->itemsHead = item; + return 0; +} + +virSecurityManagerPtr +virSecurityStackGetPrimary(virSecurityManagerPtr mgr) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + return (priv->primary) ? priv->primary : priv->itemsHead->securityManager; +} + void virSecurityStackSetPrimary(virSecurityManagerPtr mgr, virSecurityManagerPtr primary) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); - priv->primary = primary; + virSecurityStackAddPrimary(mgr, primary); } void virSecurityStackSetSecondary(virSecurityManagerPtr mgr, virSecurityManagerPtr secondary) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); - priv->secondary = secondary; + virSecurityStackAddNested(mgr, secondary); } static virSecurityDriverStatus @@ -64,9 +105,14 @@ static int virSecurityStackClose(virSecurityManagerPtr mgr) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr next, item = priv->itemsHead; - virSecurityManagerFree(priv->primary); - virSecurityManagerFree(priv->secondary); + while (item) { + next = item->next; + virSecurityManagerFree(item->securityManager); + VIR_FREE(item); + item = next; + } return 0; } @@ -74,17 +120,13 @@ virSecurityStackClose(virSecurityManagerPtr mgr) static const char * virSecurityStackGetModel(virSecurityManagerPtr mgr) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); - - return virSecurityManagerGetModel(priv->primary); + return virSecurityManagerGetModel(virSecurityStackGetPrimary(mgr)); } static const char * virSecurityStackGetDOI(virSecurityManagerPtr mgr) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); - - return virSecurityManagerGetDOI(priv->primary); + return virSecurityManagerGetDOI(virSecurityStackGetPrimary(mgr)); } static int @@ -92,13 +134,15 @@ virSecurityStackVerify(virSecurityManagerPtr mgr, virDomainDefPtr def) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerVerify(priv->primary, def) < 0) - rc = -1; - - if (virSecurityManagerVerify(priv->secondary, def) < 0) - rc = -1; + for(; item; item = item->next) { + if (virSecurityManagerVerify(item->securityManager, def) < 0) { + rc = -1; + break; + } + } return rc; } @@ -108,12 +152,12 @@ static int virSecurityStackGenLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); int rc = 0; - if (virSecurityManagerGenLabel(priv->primary, vm) < 0) + if (virSecurityManagerGenLabel(virSecurityStackGetPrimary(mgr), vm) < 0) rc = -1; +// TODO #if 0 /* We don't allow secondary drivers to generate labels. * This may have to change in the future, but requires @@ -133,11 +177,12 @@ static int virSecurityStackReleaseLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); int rc = 0; - if (virSecurityManagerReleaseLabel(priv->primary, vm) < 0) + if (virSecurityManagerReleaseLabel(virSecurityStackGetPrimary(mgr), vm) < 0) rc = -1; + +// TODO #if 0 /* XXX See note in GenLabel */ if (virSecurityManagerReleaseLabel(priv->secondary, vm) < 0) @@ -153,11 +198,11 @@ virSecurityStackReserveLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, pid_t pid) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); int rc = 0; - if (virSecurityManagerReserveLabel(priv->primary, vm, pid) < 0) + if (virSecurityManagerReserveLabel(virSecurityStackGetPrimary(mgr), vm, pid) < 0) rc = -1; +// TODO #if 0 /* XXX See note in GenLabel */ if (virSecurityManagerReserveLabel(priv->secondary, vm, pid) < 0) @@ -174,12 +219,13 @@ virSecurityStackSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetImageLabel(priv->secondary, vm, disk) < 0) - rc = -1; - if (virSecurityManagerSetImageLabel(priv->primary, vm, disk) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetImageLabel(item->securityManager, vm, disk) < 0) + rc = -1; + } return rc; } @@ -191,12 +237,13 @@ virSecurityStackRestoreSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerRestoreImageLabel(priv->secondary, vm, disk) < 0) - rc = -1; - if (virSecurityManagerRestoreImageLabel(priv->primary, vm, disk) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerRestoreImageLabel(item->securityManager, vm, disk) < 0) + rc = -1; + } return rc; } @@ -209,12 +256,13 @@ virSecurityStackSetSecurityHostdevLabel(virSecurityManagerPtr mgr, { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetHostdevLabel(priv->secondary, vm, dev) < 0) - rc = -1; - if (virSecurityManagerSetHostdevLabel(priv->primary, vm, dev) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetHostdevLabel(item->securityManager, vm, dev) < 0) + rc = -1; + } return rc; } @@ -226,12 +274,13 @@ virSecurityStackRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainHostdevDefPtr dev) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerRestoreHostdevLabel(priv->secondary, vm, dev) < 0) - rc = -1; - if (virSecurityManagerRestoreHostdevLabel(priv->primary, vm, dev) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerRestoreHostdevLabel(item->securityManager, vm, dev) < 0) + rc = -1; + } return rc; } @@ -243,12 +292,13 @@ virSecurityStackSetSecurityAllLabel(virSecurityManagerPtr mgr, const char *stdin_path) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetAllLabel(priv->secondary, vm, stdin_path) < 0) - rc = -1; - if (virSecurityManagerSetAllLabel(priv->primary, vm, stdin_path) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetAllLabel(item->securityManager, vm, stdin_path) < 0) + rc = -1; + } return rc; } @@ -260,12 +310,13 @@ virSecurityStackRestoreSecurityAllLabel(virSecurityManagerPtr mgr, int migrated) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerRestoreAllLabel(priv->secondary, vm, migrated) < 0) - rc = -1; - if (virSecurityManagerRestoreAllLabel(priv->primary, vm, migrated) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerRestoreAllLabel(item->securityManager, vm, migrated) < 0) + rc = -1; + } return rc; } @@ -277,12 +328,13 @@ virSecurityStackSetSavedStateLabel(virSecurityManagerPtr mgr, const char *savefile) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetSavedStateLabel(priv->secondary, vm, savefile) < 0) - rc = -1; - if (virSecurityManagerSetSavedStateLabel(priv->primary, vm, savefile) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetSavedStateLabel(item->securityManager, vm, savefile) < 0) + rc = -1; + } return rc; } @@ -294,12 +346,13 @@ virSecurityStackRestoreSavedStateLabel(virSecurityManagerPtr mgr, const char *savefile) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerRestoreSavedStateLabel(priv->secondary, vm, savefile) < 0) - rc = -1; - if (virSecurityManagerRestoreSavedStateLabel(priv->primary, vm, savefile) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerRestoreSavedStateLabel(item->securityManager, vm, savefile) < 0) + rc = -1; + } return rc; } @@ -310,12 +363,13 @@ virSecurityStackSetProcessLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetProcessLabel(priv->secondary, vm) < 0) - rc = -1; - if (virSecurityManagerSetProcessLabel(priv->primary, vm) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetProcessLabel(item->securityManager, vm) < 0) + rc = -1; + } return rc; } @@ -326,14 +380,14 @@ virSecurityStackGetProcessLabel(virSecurityManagerPtr mgr, pid_t pid, virSecurityLabelPtr seclabel) { - virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); int rc = 0; +// TODO #if 0 if (virSecurityManagerGetProcessLabel(priv->secondary, vm, pid, seclabel) < 0) rc = -1; #endif - if (virSecurityManagerGetProcessLabel(priv->primary, vm, pid, seclabel) < 0) + if (virSecurityManagerGetProcessLabel(virSecurityStackGetPrimary(mgr), vm, pid, seclabel) < 0) rc = -1; return rc; @@ -345,12 +399,13 @@ virSecurityStackSetDaemonSocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetDaemonSocketLabel(priv->secondary, vm) < 0) - rc = -1; - if (virSecurityManagerSetDaemonSocketLabel(priv->primary, vm) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetDaemonSocketLabel(item->securityManager, vm) < 0) + rc = -1; + } return rc; } @@ -361,12 +416,13 @@ virSecurityStackSetSocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetSocketLabel(priv->secondary, vm) < 0) - rc = -1; - if (virSecurityManagerSetSocketLabel(priv->primary, vm) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetSocketLabel(item->securityManager, vm) < 0) + rc = -1; + } return rc; } @@ -377,12 +433,13 @@ virSecurityStackClearSocketLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerClearSocketLabel(priv->secondary, vm) < 0) - rc = -1; - if (virSecurityManagerClearSocketLabel(priv->primary, vm) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerClearSocketLabel(item->securityManager, vm) < 0) + rc = -1; + } return rc; } @@ -393,12 +450,13 @@ virSecurityStackSetImageFDLabel(virSecurityManagerPtr mgr, int fd) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; - if (virSecurityManagerSetImageFDLabel(priv->secondary, vm, fd) < 0) - rc = -1; - if (virSecurityManagerSetImageFDLabel(priv->primary, vm, fd) < 0) - rc = -1; + for (; item; item = item->next) { + if (virSecurityManagerSetImageFDLabel(item->securityManager, vm, fd) < 0) + rc = -1; + } return rc; } @@ -408,6 +466,29 @@ static char *virSecurityStackGetMountOptions(virSecurityManagerPtr mgr ATTRIBUTE return NULL; } +virSecurityManagerPtr* +virSecurityStackGetNested(virSecurityManagerPtr mgr) +{ + virSecurityManagerPtr *list = NULL; + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item; + int len = 0, i = 0; + + for (item = priv->itemsHead; item; item = item->next) + len++; + + if (VIR_ALLOC_N(list, len + 1) < 0) { + virReportOOMError(); + return NULL; + } + + for (item = priv->itemsHead; item; item = item->next, i++) + list[i] = item->securityManager; + list[len] = NULL; + + return list; +} + virSecurityDriver virSecurityDriverStack = { .privateDataLen = sizeof(virSecurityStackData), .name = "stack", diff --git a/src/security/security_stack.h b/src/security/security_stack.h index fd0d26f..6898c03 100644 --- a/src/security/security_stack.h +++ b/src/security/security_stack.h @@ -25,9 +25,22 @@ extern virSecurityDriver virSecurityDriverStack; + +int +virSecurityStackAddPrimary(virSecurityManagerPtr mgr, + virSecurityManagerPtr primary); +int +virSecurityStackAddNested(virSecurityManagerPtr mgr, + virSecurityManagerPtr nested); +virSecurityManagerPtr +virSecurityStackGetPrimary(virSecurityManagerPtr mgr); + void virSecurityStackSetPrimary(virSecurityManagerPtr mgr, virSecurityManagerPtr primary); void virSecurityStackSetSecondary(virSecurityManagerPtr mgr, virSecurityManagerPtr secondary); +virSecurityManagerPtr* +virSecurityStackGetNested(virSecurityManagerPtr mgr); + #endif /* __VIR_SECURITY_DAC */ -- 1.7.1

On 16.08.2012 00:10, Marcelo Cerri wrote:
These changes make the security drivers able to find and handle the correct security label information when more than one label is available. They also update the DAC driver to be used as an usual security driver.
Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 45 ++++-- src/qemu/qemu_process.c | 50 ++++-- src/security/security_apparmor.c | 118 ++++++++++---- src/security/security_dac.c | 324 ++++++++++++++++++++++++++++++++++---- src/security/security_manager.c | 101 +++++++++--- src/security/security_manager.h | 8 +- src/security/security_selinux.c | 257 +++++++++++++++++++++--------- src/security/security_stack.c | 237 +++++++++++++++++++--------- src/security/security_stack.h | 13 ++ 10 files changed, 880 insertions(+), 275 deletions(-)
ACK Michal

On 08/16/2012 12:10 AM, Marcelo Cerri wrote:
- if (!def->seclabels[0]->norelabel) { - def->seclabels[0]->imagelabel = virSecuritySELinuxGenNewContext( - data->file_context, mcs); - if (!def->seclabels[0]->imagelabel) { + if (!seclabel->norelabel) { + seclabel->imagelabel = virSecuritySELinuxGenNewContext( + data->domain_context, mcs); + if (!seclabel->imagelabel) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate selinux context for %s"), mcs); goto cleanup; } }
- if (!def->seclabels[0]->model && - !(def->seclabels[0]->model = strdup(SECURITY_SELINUX_NAME))) { + if (!seclabel->model && + !(seclabel->model = strdup(SECURITY_SELINUX_NAME))) { virReportOOMError(); goto cleanup; }
the patch breaks the object labelling again ... please apply the following patch to fix, thanks! Image context must always use data->file_context. Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> --- src/security/security_selinux.c | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index eea8fbd..da2a9c4 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -450,9 +450,10 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, } if (!seclabel->norelabel) { - seclabel->imagelabel = virSecuritySELinuxGenNewContext(data->domain_context, - mcs, - true); + seclabel->imagelabel = + virSecuritySELinuxGenNewContext(data->file_context, + mcs, + true); if (!seclabel->imagelabel) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate selinux context for %s"), mcs); -- 1.7.0.4 -- Mit freundlichen Grüßen/Kind Regards Viktor Mihajlovski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Martin Jetter Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

On Tue, Aug 21, 2012 at 12:45:47PM +0200, Viktor Mihajlovski wrote:
On 08/16/2012 12:10 AM, Marcelo Cerri wrote:
- if (!def->seclabels[0]->norelabel) { - def->seclabels[0]->imagelabel = virSecuritySELinuxGenNewContext( - data->file_context, mcs); - if (!def->seclabels[0]->imagelabel) { + if (!seclabel->norelabel) { + seclabel->imagelabel = virSecuritySELinuxGenNewContext( + data->domain_context, mcs); + if (!seclabel->imagelabel) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate selinux context for %s"), mcs); goto cleanup; } }
- if (!def->seclabels[0]->model && - !(def->seclabels[0]->model = strdup(SECURITY_SELINUX_NAME))) { + if (!seclabel->model && + !(seclabel->model = strdup(SECURITY_SELINUX_NAME))) { virReportOOMError(); goto cleanup; }
the patch breaks the object labelling again ... please apply the following patch to fix, thanks!
Image context must always use data->file_context.
Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> --- src/security/security_selinux.c | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index eea8fbd..da2a9c4 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -450,9 +450,10 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, }
if (!seclabel->norelabel) { - seclabel->imagelabel = virSecuritySELinuxGenNewContext(data->domain_context, - mcs, - true); + seclabel->imagelabel = + virSecuritySELinuxGenNewContext(data->file_context, + mcs, + true); if (!seclabel->imagelabel) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate selinux context for %s"), mcs);
I've already pushed the same fix - I noticed it when i ran my selinux label test case Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This patch updates the key "security_driver" in QEMU config to suport both a sigle default driver or a list of default drivers. This ensures that it will remain compatible with older versions of the config file. Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/qemu/qemu.conf | 6 ++- src/qemu/qemu_conf.c | 38 +++++++++++++++++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 99 +++++++++++++++++++++++++++++++++++++----------- 4 files changed, 118 insertions(+), 27 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index ed4683c..257a477 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -144,7 +144,11 @@ # on the host, then the security driver will automatically disable # itself. If you wish to disable QEMU SELinux security driver while # leaving SELinux enabled for the host in general, then set this -# to 'none' instead. +# to 'none' instead. It's also possible to use more than one security +# driver at the same time, for this use a list of names separated by +# comma and delimited by square brackets. For example: +# +# security_driver = [ "selinux", "dac" ] # #security_driver = "selinux" diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b7db277..ed6d832 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -193,13 +193,45 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, } p = virConfGetValue (conf, "security_driver"); - CHECK_TYPE ("security_driver", VIR_CONF_STRING); - if (p && p->str) { - if (!(driver->securityDriverName = strdup(p->str))) { + if (p && p->type == VIR_CONF_LIST) { + size_t len; + virConfValuePtr pp; + + /* Calc lenght and check items */ + for (len = 0, pp = p->list; pp; len++, pp = pp->next) { + if (pp->type != VIR_CONF_STRING) { + VIR_ERROR(_("security_driver be a list of strings")); + virConfFree(conf); + return -1; + } + } + + if (VIR_ALLOC_N(driver->securityDriverNames, len + 1) < 0) { virReportOOMError(); virConfFree(conf); return -1; } + + for (i = 0, pp = p->list; pp; i++, pp = pp->next) { + driver->securityDriverNames[i] = strdup(pp->str); + if (driver->securityDriverNames == NULL) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + driver->securityDriverNames[len] = NULL; + } else { + CHECK_TYPE ("security_driver", VIR_CONF_STRING); + if (p && p->str) { + if (VIR_ALLOC_N(driver->securityDriverNames, 2) < 0 || + !(driver->securityDriverNames[0] = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + driver->securityDriverNames[1] = NULL; + } } p = virConfGetValue (conf, "security_default_confined"); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 92e4968..8a51471 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -116,7 +116,7 @@ struct qemud_driver { virDomainEventStatePtr domainEventState; - char *securityDriverName; + char **securityDriverNames; bool securityDefaultConfined; bool securityRequireConfined; virSecurityManagerPtr securityManager; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 48376c3..0c88310 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -248,36 +248,91 @@ qemuAutostartDomains(struct qemud_driver *driver) static int qemuSecurityInit(struct qemud_driver *driver) { - virSecurityManagerPtr mgr = virSecurityManagerNew(driver->securityDriverName, - QEMU_DRIVER_NAME, - driver->allowDiskFormatProbing, - driver->securityDefaultConfined, - driver->securityRequireConfined); + char **names; + char *primary; + virSecurityManagerPtr mgr, nested, stack; + if (driver->securityDriverNames == NULL) + primary = NULL; + else + primary = driver->securityDriverNames[0]; + + /* Create primary driver */ + mgr = virSecurityManagerNew(primary, + QEMU_DRIVER_NAME, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined); if (!mgr) goto error; + /* If a DAC driver is required or additional drivers are provived, a stack + * driver should be create to group them all */ + if (driver->privileged || + (driver->securityDriverNames && driver->securityDriverNames[1])) { + stack = virSecurityManagerNewStack(mgr); + if (!stack) + goto error; + mgr = stack; + } + + /* Loop through additional driver names and add a secudary driver to each + * one */ + if (driver->securityDriverNames) { + names = driver->securityDriverNames + 1; + while (names && *names) { + if (STREQ("dac", *names)) { + /* A DAC driver has specific parameters */ + nested = virSecurityManagerNewDAC(QEMU_DRIVER_NAME, + driver->user, + driver->group, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined, + driver->dynamicOwnership); + } else { + nested = virSecurityManagerNew(*names, + QEMU_DRIVER_NAME, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined); + } + if (nested == NULL) + goto error; + if (virSecurityManagerStackAddNested(stack, nested)) + goto error; + names++; + } + } + if (driver->privileged) { - virSecurityManagerPtr dac = virSecurityManagerNewDAC(QEMU_DRIVER_NAME, - driver->user, - driver->group, - driver->allowDiskFormatProbing, - driver->securityDefaultConfined, - driver->securityRequireConfined, - driver->dynamicOwnership); - if (!dac) - goto error; - - if (!(driver->securityManager = virSecurityManagerNewStack(mgr)) || - !(virSecurityManagerStackAddNested(mgr, dac))) { - - virSecurityManagerFree(dac); - goto error; + /* When a DAC driver is required, check if there is already one in the + * additional drivers */ + names = driver->securityDriverNames; + while (names && *names) { + if (STREQ("dac", *names)) { + break; + } + names++; + } + /* If there is no DAC driver, create a new one and add it to the stack + * manager */ + if (names == NULL || *names == NULL) { + nested = virSecurityManagerNewDAC(QEMU_DRIVER_NAME, + driver->user, + driver->group, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined, + driver->dynamicOwnership); + if (nested == NULL) + goto error; + if (virSecurityManagerStackAddNested(stack, nested)) + goto error; } - } else { - driver->securityManager = mgr; } + driver->securityManager = mgr; return 0; error: -- 1.7.1

On 16.08.2012 00:10, Marcelo Cerri wrote:
This patch updates the key "security_driver" in QEMU config to suport both a sigle default driver or a list of default drivers. This ensures that it will remain compatible with older versions of the config file.
Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/qemu/qemu.conf | 6 ++- src/qemu/qemu_conf.c | 38 +++++++++++++++++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 99 +++++++++++++++++++++++++++++++++++++----------- 4 files changed, 118 insertions(+), 27 deletions(-)
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index ed4683c..257a477 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -144,7 +144,11 @@ # on the host, then the security driver will automatically disable # itself. If you wish to disable QEMU SELinux security driver while # leaving SELinux enabled for the host in general, then set this -# to 'none' instead. +# to 'none' instead. It's also possible to use more than one security +# driver at the same time, for this use a list of names separated by +# comma and delimited by square brackets. For example: +# +# security_driver = [ "selinux", "dac" ] # #security_driver = "selinux"
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b7db277..ed6d832 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -193,13 +193,45 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, }
p = virConfGetValue (conf, "security_driver"); - CHECK_TYPE ("security_driver", VIR_CONF_STRING); - if (p && p->str) { - if (!(driver->securityDriverName = strdup(p->str))) { + if (p && p->type == VIR_CONF_LIST) { + size_t len; + virConfValuePtr pp; + + /* Calc lenght and check items */ + for (len = 0, pp = p->list; pp; len++, pp = pp->next) { + if (pp->type != VIR_CONF_STRING) { + VIR_ERROR(_("security_driver be a list of strings")); + virConfFree(conf); + return -1; + } + } + + if (VIR_ALLOC_N(driver->securityDriverNames, len + 1) < 0) { virReportOOMError(); virConfFree(conf); return -1; } + + for (i = 0, pp = p->list; pp; i++, pp = pp->next) { + driver->securityDriverNames[i] = strdup(pp->str); + if (driver->securityDriverNames == NULL) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + driver->securityDriverNames[len] = NULL; + } else { + CHECK_TYPE ("security_driver", VIR_CONF_STRING); + if (p && p->str) { + if (VIR_ALLOC_N(driver->securityDriverNames, 2) < 0 || + !(driver->securityDriverNames[0] = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + driver->securityDriverNames[1] = NULL; + } }
p = virConfGetValue (conf, "security_default_confined"); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 92e4968..8a51471 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -116,7 +116,7 @@ struct qemud_driver {
virDomainEventStatePtr domainEventState;
- char *securityDriverName; + char **securityDriverNames; bool securityDefaultConfined; bool securityRequireConfined; virSecurityManagerPtr securityManager; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 48376c3..0c88310 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -248,36 +248,91 @@ qemuAutostartDomains(struct qemud_driver *driver) static int qemuSecurityInit(struct qemud_driver *driver) { - virSecurityManagerPtr mgr = virSecurityManagerNew(driver->securityDriverName, - QEMU_DRIVER_NAME, - driver->allowDiskFormatProbing, - driver->securityDefaultConfined, - driver->securityRequireConfined); + char **names; + char *primary; + virSecurityManagerPtr mgr, nested, stack;
Again, gcc thinks 'stack' may be used uninitialized ...
+ if (driver->securityDriverNames == NULL) + primary = NULL; + else + primary = driver->securityDriverNames[0]; + + /* Create primary driver */ + mgr = virSecurityManagerNew(primary, + QEMU_DRIVER_NAME, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined); if (!mgr) goto error;
+ /* If a DAC driver is required or additional drivers are provived, a stack + * driver should be create to group them all */ + if (driver->privileged || + (driver->securityDriverNames && driver->securityDriverNames[1])) { + stack = virSecurityManagerNewStack(mgr); + if (!stack) + goto error; + mgr = stack; + } + + /* Loop through additional driver names and add a secudary driver to each + * one */ + if (driver->securityDriverNames) { + names = driver->securityDriverNames + 1; + while (names && *names) { + if (STREQ("dac", *names)) { + /* A DAC driver has specific parameters */ + nested = virSecurityManagerNewDAC(QEMU_DRIVER_NAME, + driver->user, + driver->group, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined, + driver->dynamicOwnership); + } else { + nested = virSecurityManagerNew(*names, + QEMU_DRIVER_NAME, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined); + } + if (nested == NULL) + goto error; + if (virSecurityManagerStackAddNested(stack, nested))
... here. ACK with this squashed in: diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a70ca92..116d447 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -250,7 +250,7 @@ qemuSecurityInit(struct qemud_driver *driver) { char **names; char *primary; - virSecurityManagerPtr mgr, nested, stack; + virSecurityManagerPtr mgr, nested, stack = NULL; if (driver->securityDriverNames == NULL) primary = NULL;

On 16.08.2012 00:10, Marcelo Cerri wrote:
This patch updates the key "security_driver" in QEMU config to suport both a sigle default driver or a list of default drivers. This ensures that it will remain compatible with older versions of the config file.
Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/qemu/qemu.conf | 6 ++- src/qemu/qemu_conf.c | 38 +++++++++++++++++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 99 +++++++++++++++++++++++++++++++++++++----------- 4 files changed, 118 insertions(+), 27 deletions(-)
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index ed4683c..257a477 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -144,7 +144,11 @@ # on the host, then the security driver will automatically disable # itself. If you wish to disable QEMU SELinux security driver while # leaving SELinux enabled for the host in general, then set this -# to 'none' instead. +# to 'none' instead. It's also possible to use more than one security +# driver at the same time, for this use a list of names separated by +# comma and delimited by square brackets. For example: +# +# security_driver = [ "selinux", "dac" ] # #security_driver = "selinux"
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b7db277..ed6d832 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -193,13 +193,45 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, }
p = virConfGetValue (conf, "security_driver"); - CHECK_TYPE ("security_driver", VIR_CONF_STRING); - if (p && p->str) { - if (!(driver->securityDriverName = strdup(p->str))) { + if (p && p->type == VIR_CONF_LIST) { + size_t len; + virConfValuePtr pp; + + /* Calc lenght and check items */ + for (len = 0, pp = p->list; pp; len++, pp = pp->next) { + if (pp->type != VIR_CONF_STRING) { + VIR_ERROR(_("security_driver be a list of strings")); + virConfFree(conf); + return -1; + } + } + + if (VIR_ALLOC_N(driver->securityDriverNames, len + 1) < 0) { virReportOOMError(); virConfFree(conf); return -1; } + + for (i = 0, pp = p->list; pp; i++, pp = pp->next) { + driver->securityDriverNames[i] = strdup(pp->str); + if (driver->securityDriverNames == NULL) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + driver->securityDriverNames[len] = NULL; + } else { + CHECK_TYPE ("security_driver", VIR_CONF_STRING); + if (p && p->str) { + if (VIR_ALLOC_N(driver->securityDriverNames, 2) < 0 || + !(driver->securityDriverNames[0] = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + driver->securityDriverNames[1] = NULL; + } }
p = virConfGetValue (conf, "security_default_confined"); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 92e4968..8a51471 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -116,7 +116,7 @@ struct qemud_driver {
virDomainEventStatePtr domainEventState;
- char *securityDriverName; + char **securityDriverNames; bool securityDefaultConfined; bool securityRequireConfined; virSecurityManagerPtr securityManager; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 48376c3..0c88310 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -248,36 +248,91 @@ qemuAutostartDomains(struct qemud_driver *driver) static int qemuSecurityInit(struct qemud_driver *driver) { - virSecurityManagerPtr mgr = virSecurityManagerNew(driver->securityDriverName, - QEMU_DRIVER_NAME, - driver->allowDiskFormatProbing, - driver->securityDefaultConfined, - driver->securityRequireConfined); + char **names; + char *primary; + virSecurityManagerPtr mgr, nested, stack;
Again, gcc thinks 'stack' may be used uninitialized ...
+ if (driver->securityDriverNames == NULL) + primary = NULL; + else + primary = driver->securityDriverNames[0]; + + /* Create primary driver */ + mgr = virSecurityManagerNew(primary, + QEMU_DRIVER_NAME, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined); if (!mgr) goto error;
+ /* If a DAC driver is required or additional drivers are provived, a stack + * driver should be create to group them all */ + if (driver->privileged || + (driver->securityDriverNames && driver->securityDriverNames[1])) { + stack = virSecurityManagerNewStack(mgr); + if (!stack) + goto error; + mgr = stack; + } + + /* Loop through additional driver names and add a secudary driver to each + * one */ + if (driver->securityDriverNames) { + names = driver->securityDriverNames + 1; + while (names && *names) { + if (STREQ("dac", *names)) { + /* A DAC driver has specific parameters */ + nested = virSecurityManagerNewDAC(QEMU_DRIVER_NAME, + driver->user, + driver->group, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined, + driver->dynamicOwnership); + } else { + nested = virSecurityManagerNew(*names, + QEMU_DRIVER_NAME, + driver->allowDiskFormatProbing, + driver->securityDefaultConfined, + driver->securityRequireConfined); + } + if (nested == NULL) + goto error; + if (virSecurityManagerStackAddNested(stack, nested))
... here. ACK with this squashed in: diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a70ca92..116d447 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -250,7 +250,7 @@ qemuSecurityInit(struct qemud_driver *driver) { char **names; char *primary; - virSecurityManagerPtr mgr, nested, stack; + virSecurityManagerPtr mgr, nested, stack = NULL; if (driver->securityDriverNames == NULL) primary = NULL;

This patch updates libvirt's API to allow applications to inspect the full list of security labels of a domain. Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- daemon/remote.c | 63 ++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 2 + python/generator.py | 1 + src/driver.h | 4 ++ src/libvirt.c | 47 +++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + src/qemu/qemu_driver.c | 73 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 46 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 17 +++++++++- src/remote_protocol-structs | 11 ++++++ 10 files changed, 264 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 851bcc1..f82af86 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1417,6 +1417,69 @@ cleanup: } static int +remoteDispatchDomainGetSecurityLabelList(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_security_label_list_args *args, + remote_domain_get_security_label_list_ret *ret) +{ + virDomainPtr dom = NULL; + virSecurityLabelPtr seclabels = NULL; + int i, len, rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if ((len = virDomainGetSecurityLabelList(dom, &seclabels)) < 0) { + ret->ret = len; + ret->labels.labels_len = 0; + ret->labels.labels_val = NULL; + goto done; + } + + if (VIR_ALLOC_N(ret->labels.labels_val, len) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < len; i++) { + size_t label_len = strlen(seclabels[i].label) + 1; + remote_domain_get_security_label_ret *cur = &ret->labels.labels_val[i]; + if (VIR_ALLOC_N(cur->label.label_val, label_len) < 0) { + virReportOOMError(); + goto cleanup; + } + if (virStrcpy(cur->label.label_val, seclabels[i].label, label_len) == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to copy security label")); + goto cleanup; + } + cur->label.label_len = label_len; + cur->enforcing = seclabels[i].enforcing; + } + ret->labels.labels_len = ret->ret = len; + +done: + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + VIR_FREE(seclabels); + return rv; +} + +static int remoteDispatchNodeGetSecurityModel(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, virNetMessagePtr msg ATTRIBUTE_UNUSED, diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index d21d029..0985dd3 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1593,6 +1593,8 @@ int virDomainGetSecurityLabel (virDomainPtr domain, virSecurityLabelPtr seclabel); char * virDomainGetHostname (virDomainPtr domain, unsigned int flags); +int virDomainGetSecurityLabelList (virDomainPtr domain, + virSecurityLabelPtr* seclabels); typedef enum { VIR_DOMAIN_METADATA_DESCRIPTION = 0, /* Operate on <description> */ diff --git a/python/generator.py b/python/generator.py index 6559ece..1f87195 100755 --- a/python/generator.py +++ b/python/generator.py @@ -448,6 +448,7 @@ skip_function = ( 'virConnectOpenAuth', # Python C code is manually written 'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C 'virDomainGetSecurityLabel', # Needs investigation... + 'virDomainGetSecurityLabelList', # Needs investigation... 'virNodeGetSecurityModel', # Needs investigation... 'virConnectDomainEventRegister', # overridden in virConnect.py 'virConnectDomainEventDeregister', # overridden in virConnect.py diff --git a/src/driver.h b/src/driver.h index aab9766..203497d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -320,6 +320,9 @@ typedef int (*virDrvDomainGetSecurityLabel) (virDomainPtr domain, virSecurityLabelPtr seclabel); typedef int + (*virDrvDomainGetSecurityLabelList) (virDomainPtr domain, + virSecurityLabelPtr* seclabels); +typedef int (*virDrvNodeGetSecurityModel) (virConnectPtr conn, virSecurityModelPtr secmodel); typedef int @@ -941,6 +944,7 @@ struct _virDriver { virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; virDrvDomainGetSecurityLabel domainGetSecurityLabel; + virDrvDomainGetSecurityLabelList domainGetSecurityLabelList; virDrvNodeGetSecurityModel nodeGetSecurityModel; virDrvDomainGetXMLDesc domainGetXMLDesc; virDrvConnectDomainXMLFromNative domainXMLFromNative; diff --git a/src/libvirt.c b/src/libvirt.c index 893d380..b3fc8a8 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9021,6 +9021,53 @@ error: } /** + * virDomainGetSecurityLabelList: + * @domain: a domain object + * @seclabels: will be auto-allocated and filled with domains' security labels. + * Caller must free memory on return. + * + * Extract the security labels of an active domain. The 'label' field + * in the @seclabels argument will be initialized to the empty + * string if the domain is not running under a security model. + * + * Returns number of elemnets in @seclabels on success, -1 in case of failure. + */ +int +virDomainGetSecurityLabelList(virDomainPtr domain, + virSecurityLabelPtr* seclabels) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "seclabels=%p", seclabels); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (seclabels == NULL) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainGetSecurityLabelList) { + int ret; + ret = conn->driver->domainGetSecurityLabelList(domain, seclabels); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} +/** * virDomainSetMetadata: * @domain: a domain object * @type: type of description, from virDomainMetadataType diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e3ba119..43f2cf2 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -549,6 +549,7 @@ LIBVIRT_0.10.0 { virDomainGetHostname; virConnectRegisterCloseCallback; virConnectUnregisterCloseCallback; + virDomainGetSecurityLabelList; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0c88310..f95feec 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4119,6 +4119,78 @@ cleanup: return ret; } +static int qemuDomainGetSecurityLabelList(virDomainPtr dom, + virSecurityLabelPtr* seclabels) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int i, ret = -1; + + /* Protect domain data with qemu lock */ + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainVirtTypeToString(vm->def->virtType)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown virt type in domain definition '%d'"), + vm->def->virtType); + goto cleanup; + } + + /* + * Check the comment in qemudDomainGetSecurityLabel function. + */ + if (!virDomainObjIsActive(vm)) { + /* No seclabels */ + *seclabels = NULL; + ret = 0; + } else { + int len = 0; + virSecurityManagerPtr* mgrs = virSecurityManagerGetNested( + driver->securityManager); + if (!mgrs) + goto cleanup; + + /* Allocate seclabels array */ + for (i = 0; mgrs[i]; i++) + len++; + + if (VIR_ALLOC_N((*seclabels), len) < 0) { + virReportOOMError(); + VIR_FREE(mgrs); + goto cleanup; + } + memset(*seclabels, 0, sizeof(**seclabels) * len); + + /* Fill the array */ + for (i = 0; i < len; i++) { + if (virSecurityManagerGetProcessLabel(mgrs[i], vm->def, vm->pid, + &(*seclabels)[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to get security label")); + VIR_FREE(mgrs); + VIR_FREE(*seclabels); + goto cleanup; + } + } + ret = len; + VIR_FREE(mgrs); + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} static int qemudNodeGetSecurityModel(virConnectPtr conn, virSecurityModelPtr secmodel) { @@ -13362,6 +13434,7 @@ static virDriver qemuDriver = { .domainGetVcpus = qemudDomainGetVcpus, /* 0.4.4 */ .domainGetMaxVcpus = qemudDomainGetMaxVcpus, /* 0.4.4 */ .domainGetSecurityLabel = qemudDomainGetSecurityLabel, /* 0.6.1 */ + .domainGetSecurityLabelList = qemuDomainGetSecurityLabelList, /* 0.10.0 */ .nodeGetSecurityModel = qemudNodeGetSecurityModel, /* 0.6.1 */ .domainGetXMLDesc = qemuDomainGetXMLDesc, /* 0.2.0 */ .domainXMLFromNative = qemuDomainXMLFromNative, /* 0.6.4 */ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c4941c5..51f89af 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1958,6 +1958,51 @@ done: } static int +remoteDomainGetSecurityLabelList (virDomainPtr domain, virSecurityLabelPtr* seclabels) +{ + remote_domain_get_security_label_list_args args; + remote_domain_get_security_label_list_ret ret; + struct private_data *priv = domain->conn->privateData; + int i, rv = -1; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + memset(&ret, 0, sizeof(ret)); + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL_LIST, + (xdrproc_t) xdr_remote_domain_get_security_label_list_args, (char *)&args, + (xdrproc_t) xdr_remote_domain_get_security_label_list_ret, (char *)&ret) == -1) { + goto done; + } + + if (VIR_ALLOC_N(*seclabels, ret.labels.labels_len) < 0) + goto cleanup; + + for (i = 0; i < ret.labels.labels_len; i++) { + remote_domain_get_security_label_ret *cur = &ret.labels.labels_val[i]; + if (cur->label.label_val != NULL) { + if (strlen(cur->label.label_val) >= sizeof((*seclabels)->label)) { + virReportError(VIR_ERR_RPC, _("security label exceeds maximum: %zd"), + sizeof((*seclabels)->label) - 1); + VIR_FREE(*seclabels); + goto cleanup; + } + strcpy((*seclabels)[i].label, cur->label.label_val); + (*seclabels)[i].enforcing = cur->enforcing; + } + } + rv = ret.ret; + +cleanup: + xdr_free((xdrproc_t) xdr_remote_domain_get_security_label_list_ret, (char *)&ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteDomainGetState(virDomainPtr domain, int *state, int *reason, @@ -5260,6 +5305,7 @@ static virDriver remote_driver = { .domainGetVcpus = remoteDomainGetVcpus, /* 0.3.0 */ .domainGetMaxVcpus = remoteDomainGetMaxVcpus, /* 0.3.0 */ .domainGetSecurityLabel = remoteDomainGetSecurityLabel, /* 0.6.1 */ + .domainGetSecurityLabelList = remoteDomainGetSecurityLabelList, /* 0.10.0*/ .nodeGetSecurityModel = remoteNodeGetSecurityModel, /* 0.6.1 */ .domainGetXMLDesc = remoteDomainGetXMLDesc, /* 0.3.0 */ .domainXMLFromNative = remoteDomainXMLFromNative, /* 0.6.4 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 200fe75..3be15f5 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -169,6 +169,11 @@ const REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX = 1048576; const REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX = 1048576; /* + * Maximum length of a security label list. + */ +const REMOTE_SECURITY_LABEL_LIST_MAX=64; + +/* * Maximum length of a security model field. */ const REMOTE_SECURITY_MODEL_MAX = VIR_SECURITY_MODEL_BUFLEN; @@ -1082,6 +1087,15 @@ struct remote_domain_get_security_label_ret { int enforcing; }; +struct remote_domain_get_security_label_list_args { + remote_nonnull_domain dom; +}; + +struct remote_domain_get_security_label_list_ret { + remote_domain_get_security_label_ret labels<REMOTE_SECURITY_LABEL_LIST_MAX>; + int ret; +}; + struct remote_node_get_security_model_ret { char model<REMOTE_SECURITY_MODEL_MAX>; char doi<REMOTE_SECURITY_DOI_MAX>; @@ -2854,7 +2868,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, /* autogen autogen */ - REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277 /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL_LIST = 278 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 8d09138..c180e6e 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -749,6 +749,16 @@ struct remote_domain_get_security_label_ret { } label; int enforcing; }; +struct remote_domain_get_security_label_list_args { + remote_nonnull_domain dom; +}; +struct remote_domain_get_security_label_list_ret { + struct { + u_int labels_len; + remote_domain_get_security_label_ret * labels_val; + } labels; + int ret; +}; struct remote_node_get_security_model_ret { struct { u_int model_len; @@ -2259,4 +2269,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, + REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL_LIST = 278, }; -- 1.7.1

On 16.08.2012 00:10, Marcelo Cerri wrote:
This patch updates libvirt's API to allow applications to inspect the full list of security labels of a domain.
Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- daemon/remote.c | 63 ++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 2 + python/generator.py | 1 + src/driver.h | 4 ++ src/libvirt.c | 47 +++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + src/qemu/qemu_driver.c | 73 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 46 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 17 +++++++++- src/remote_protocol-structs | 11 ++++++ 10 files changed, 264 insertions(+), 1 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c index 851bcc1..f82af86 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1417,6 +1417,69 @@ cleanup: }
static int +remoteDispatchDomainGetSecurityLabelList(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_security_label_list_args *args, + remote_domain_get_security_label_list_ret *ret) +{ + virDomainPtr dom = NULL; + virSecurityLabelPtr seclabels = NULL; + int i, len, rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if ((len = virDomainGetSecurityLabelList(dom, &seclabels)) < 0) { + ret->ret = len; + ret->labels.labels_len = 0; + ret->labels.labels_val = NULL; + goto done; + } + + if (VIR_ALLOC_N(ret->labels.labels_val, len) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < len; i++) { + size_t label_len = strlen(seclabels[i].label) + 1; + remote_domain_get_security_label_ret *cur = &ret->labels.labels_val[i]; + if (VIR_ALLOC_N(cur->label.label_val, label_len) < 0) { + virReportOOMError(); + goto cleanup; + } + if (virStrcpy(cur->label.label_val, seclabels[i].label, label_len) == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to copy security label")); + goto cleanup; + } + cur->label.label_len = label_len; + cur->enforcing = seclabels[i].enforcing; + } + ret->labels.labels_len = ret->ret = len; + +done: + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + VIR_FREE(seclabels); + return rv; +} + +static int remoteDispatchNodeGetSecurityModel(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, virNetMessagePtr msg ATTRIBUTE_UNUSED, diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index d21d029..0985dd3 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1593,6 +1593,8 @@ int virDomainGetSecurityLabel (virDomainPtr domain, virSecurityLabelPtr seclabel); char * virDomainGetHostname (virDomainPtr domain, unsigned int flags); +int virDomainGetSecurityLabelList (virDomainPtr domain, + virSecurityLabelPtr* seclabels);
typedef enum { VIR_DOMAIN_METADATA_DESCRIPTION = 0, /* Operate on <description> */ diff --git a/python/generator.py b/python/generator.py index 6559ece..1f87195 100755 --- a/python/generator.py +++ b/python/generator.py @@ -448,6 +448,7 @@ skip_function = ( 'virConnectOpenAuth', # Python C code is manually written 'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C 'virDomainGetSecurityLabel', # Needs investigation... + 'virDomainGetSecurityLabelList', # Needs investigation... 'virNodeGetSecurityModel', # Needs investigation... 'virConnectDomainEventRegister', # overridden in virConnect.py 'virConnectDomainEventDeregister', # overridden in virConnect.py diff --git a/src/driver.h b/src/driver.h index aab9766..203497d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -320,6 +320,9 @@ typedef int (*virDrvDomainGetSecurityLabel) (virDomainPtr domain, virSecurityLabelPtr seclabel); typedef int + (*virDrvDomainGetSecurityLabelList) (virDomainPtr domain, + virSecurityLabelPtr* seclabels); +typedef int (*virDrvNodeGetSecurityModel) (virConnectPtr conn, virSecurityModelPtr secmodel); typedef int @@ -941,6 +944,7 @@ struct _virDriver { virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; virDrvDomainGetSecurityLabel domainGetSecurityLabel; + virDrvDomainGetSecurityLabelList domainGetSecurityLabelList; virDrvNodeGetSecurityModel nodeGetSecurityModel; virDrvDomainGetXMLDesc domainGetXMLDesc; virDrvConnectDomainXMLFromNative domainXMLFromNative; diff --git a/src/libvirt.c b/src/libvirt.c index 893d380..b3fc8a8 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9021,6 +9021,53 @@ error: }
/** + * virDomainGetSecurityLabelList: + * @domain: a domain object + * @seclabels: will be auto-allocated and filled with domains' security labels. + * Caller must free memory on return. + * + * Extract the security labels of an active domain. The 'label' field + * in the @seclabels argument will be initialized to the empty + * string if the domain is not running under a security model. + * + * Returns number of elemnets in @seclabels on success, -1 in case of failure. + */ +int +virDomainGetSecurityLabelList(virDomainPtr domain, + virSecurityLabelPtr* seclabels) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "seclabels=%p", seclabels); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (seclabels == NULL) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainGetSecurityLabelList) { + int ret; + ret = conn->driver->domainGetSecurityLabelList(domain, seclabels); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} +/** * virDomainSetMetadata: * @domain: a domain object * @type: type of description, from virDomainMetadataType diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e3ba119..43f2cf2 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -549,6 +549,7 @@ LIBVIRT_0.10.0 { virDomainGetHostname; virConnectRegisterCloseCallback; virConnectUnregisterCloseCallback; + virDomainGetSecurityLabelList; } LIBVIRT_0.9.13;
# .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0c88310..f95feec 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4119,6 +4119,78 @@ cleanup: return ret; }
+static int qemuDomainGetSecurityLabelList(virDomainPtr dom, + virSecurityLabelPtr* seclabels) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int i, ret = -1; + + /* Protect domain data with qemu lock */ + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainVirtTypeToString(vm->def->virtType)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown virt type in domain definition '%d'"), + vm->def->virtType); + goto cleanup; + } + + /* + * Check the comment in qemudDomainGetSecurityLabel function. + */ + if (!virDomainObjIsActive(vm)) { + /* No seclabels */ + *seclabels = NULL; + ret = 0; + } else { + int len = 0; + virSecurityManagerPtr* mgrs = virSecurityManagerGetNested( + driver->securityManager); + if (!mgrs) + goto cleanup; + + /* Allocate seclabels array */ + for (i = 0; mgrs[i]; i++) + len++; + + if (VIR_ALLOC_N((*seclabels), len) < 0) { + virReportOOMError(); + VIR_FREE(mgrs); + goto cleanup; + } + memset(*seclabels, 0, sizeof(**seclabels) * len); + + /* Fill the array */ + for (i = 0; i < len; i++) { + if (virSecurityManagerGetProcessLabel(mgrs[i], vm->def, vm->pid, + &(*seclabels)[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to get security label")); + VIR_FREE(mgrs); + VIR_FREE(*seclabels); + goto cleanup; + } + } + ret = len; + VIR_FREE(mgrs); + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} static int qemudNodeGetSecurityModel(virConnectPtr conn, virSecurityModelPtr secmodel) { @@ -13362,6 +13434,7 @@ static virDriver qemuDriver = { .domainGetVcpus = qemudDomainGetVcpus, /* 0.4.4 */ .domainGetMaxVcpus = qemudDomainGetMaxVcpus, /* 0.4.4 */ .domainGetSecurityLabel = qemudDomainGetSecurityLabel, /* 0.6.1 */ + .domainGetSecurityLabelList = qemuDomainGetSecurityLabelList, /* 0.10.0 */ .nodeGetSecurityModel = qemudNodeGetSecurityModel, /* 0.6.1 */ .domainGetXMLDesc = qemuDomainGetXMLDesc, /* 0.2.0 */ .domainXMLFromNative = qemuDomainXMLFromNative, /* 0.6.4 */ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c4941c5..51f89af 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1958,6 +1958,51 @@ done: }
static int +remoteDomainGetSecurityLabelList (virDomainPtr domain, virSecurityLabelPtr* seclabels) +{ + remote_domain_get_security_label_list_args args; + remote_domain_get_security_label_list_ret ret; + struct private_data *priv = domain->conn->privateData; + int i, rv = -1; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + memset(&ret, 0, sizeof(ret)); + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL_LIST, + (xdrproc_t) xdr_remote_domain_get_security_label_list_args, (char *)&args, + (xdrproc_t) xdr_remote_domain_get_security_label_list_ret, (char *)&ret) == -1) { + goto done; + } + + if (VIR_ALLOC_N(*seclabels, ret.labels.labels_len) < 0) + goto cleanup; + + for (i = 0; i < ret.labels.labels_len; i++) { + remote_domain_get_security_label_ret *cur = &ret.labels.labels_val[i]; + if (cur->label.label_val != NULL) { + if (strlen(cur->label.label_val) >= sizeof((*seclabels)->label)) { + virReportError(VIR_ERR_RPC, _("security label exceeds maximum: %zd"), + sizeof((*seclabels)->label) - 1); + VIR_FREE(*seclabels); + goto cleanup; + } + strcpy((*seclabels)[i].label, cur->label.label_val); + (*seclabels)[i].enforcing = cur->enforcing; + } + } + rv = ret.ret; + +cleanup: + xdr_free((xdrproc_t) xdr_remote_domain_get_security_label_list_ret, (char *)&ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteDomainGetState(virDomainPtr domain, int *state, int *reason, @@ -5260,6 +5305,7 @@ static virDriver remote_driver = { .domainGetVcpus = remoteDomainGetVcpus, /* 0.3.0 */ .domainGetMaxVcpus = remoteDomainGetMaxVcpus, /* 0.3.0 */ .domainGetSecurityLabel = remoteDomainGetSecurityLabel, /* 0.6.1 */ + .domainGetSecurityLabelList = remoteDomainGetSecurityLabelList, /* 0.10.0*/
s/0\*/0 \*/ ACK Michal

On 16.08.2012 00:10, Marcelo Cerri wrote:
This is a v4 patch series that updates the libvirt's security driver mechanism to support per-guest configurable user and group for QEMU processes running together with other security drivers, such as SELinux and AppArmor.
Marcelo Cerri (5): Internal refactory of data structures Multiple security drivers in XML data Update security layer to handle many security labels Support for multiple default security drivers in QEMU config Update the remote API
daemon/remote.c | 63 ++++ docs/formatdomain.html.in | 11 +- docs/schemas/capability.rng | 18 +- docs/schemas/domaincommon.rng | 30 ++- include/libvirt/libvirt.h.in | 2 + python/generator.py | 1 + src/conf/capabilities.c | 17 +- src/conf/capabilities.h | 6 +- src/conf/domain_audit.c | 14 +- src/conf/domain_conf.c | 343 +++++++++++++++----- src/conf/domain_conf.h | 20 +- src/driver.h | 4 + src/libvirt.c | 47 +++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 1 + src/lxc/lxc_conf.c | 8 +- src/lxc/lxc_controller.c | 8 +- src/lxc/lxc_driver.c | 11 +- src/lxc/lxc_process.c | 23 +- src/qemu/qemu.conf | 6 +- src/qemu/qemu_conf.c | 38 ++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 218 +++++++++++--- src/qemu/qemu_process.c | 50 ++- src/remote/remote_driver.c | 46 +++ src/remote/remote_protocol.x | 17 +- src/remote_protocol-structs | 11 + src/security/security_apparmor.c | 118 +++++-- src/security/security_dac.c | 324 +++++++++++++++++-- src/security/security_manager.c | 101 +++++-- src/security/security_manager.h | 8 +- src/security/security_selinux.c | 263 +++++++++++----- src/security/security_stack.c | 237 +++++++++----- src/security/security_stack.h | 13 + src/test/test_driver.c | 11 +- .../qemuxml2argv-seclabel-dynamic-override.xml | 4 +- .../qemuxml2argv-seclabel-dynamic.xml | 2 +- 37 files changed, 1653 insertions(+), 448 deletions(-)
Okay, I've went ahead, squashed in nits I've pointed out and pushed the whole series. Thanks for your patches! Michal

Are there any other requirements that need to be taken care of to enable execution of QEMU guests under separate unprivileged user IDs (ie. DAC isolation)? At this point, this patch series (Per-guest configurable user/group for QEMU processes) is upstream, allowing libvirt to execute guests under separate unprivileged user IDs. Additionally, the QEMU bridge helper series is upstream, allowing QEMU to allocate a tap device and attach it to a bridge when run under an unprivileged user ID (http://www.redhat.com/archives/libvir-list/2012-August/msg00277.html). Is there any other feature in QEMU that requires QEMU to be run as root? -- Regards, Corey On 08/15/2012 06:10 PM, Marcelo Cerri wrote:
This is a v4 patch series that updates the libvirt's security driver mechanism to support per-guest configurable user and group for QEMU processes running together with other security drivers, such as SELinux and AppArmor.
Marcelo Cerri (5): Internal refactory of data structures Multiple security drivers in XML data Update security layer to handle many security labels Support for multiple default security drivers in QEMU config Update the remote API
daemon/remote.c | 63 ++++ docs/formatdomain.html.in | 11 +- docs/schemas/capability.rng | 18 +- docs/schemas/domaincommon.rng | 30 ++- include/libvirt/libvirt.h.in | 2 + python/generator.py | 1 + src/conf/capabilities.c | 17 +- src/conf/capabilities.h | 6 +- src/conf/domain_audit.c | 14 +- src/conf/domain_conf.c | 343 +++++++++++++++----- src/conf/domain_conf.h | 20 +- src/driver.h | 4 + src/libvirt.c | 47 +++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 1 + src/lxc/lxc_conf.c | 8 +- src/lxc/lxc_controller.c | 8 +- src/lxc/lxc_driver.c | 11 +- src/lxc/lxc_process.c | 23 +- src/qemu/qemu.conf | 6 +- src/qemu/qemu_conf.c | 38 ++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 218 +++++++++++--- src/qemu/qemu_process.c | 50 ++- src/remote/remote_driver.c | 46 +++ src/remote/remote_protocol.x | 17 +- src/remote_protocol-structs | 11 + src/security/security_apparmor.c | 118 +++++-- src/security/security_dac.c | 324 +++++++++++++++++-- src/security/security_manager.c | 101 +++++-- src/security/security_manager.h | 8 +- src/security/security_selinux.c | 263 +++++++++++----- src/security/security_stack.c | 237 +++++++++----- src/security/security_stack.h | 13 + src/test/test_driver.c | 11 +- .../qemuxml2argv-seclabel-dynamic-override.xml | 4 +- .../qemuxml2argv-seclabel-dynamic.xml | 2 +- 37 files changed, 1653 insertions(+), 448 deletions(-)
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

Hi, Any comments about that? Regards, Marcelo On Tue, Sep 11, 2012 at 02:13:38PM -0400, Corey Bryant wrote:
Are there any other requirements that need to be taken care of to enable execution of QEMU guests under separate unprivileged user IDs (ie. DAC isolation)?
At this point, this patch series (Per-guest configurable user/group for QEMU processes) is upstream, allowing libvirt to execute guests under separate unprivileged user IDs. Additionally, the QEMU bridge helper series is upstream, allowing QEMU to allocate a tap device and attach it to a bridge when run under an unprivileged user ID (http://www.redhat.com/archives/libvir-list/2012-August/msg00277.html).
Is there any other feature in QEMU that requires QEMU to be run as root?
-- Regards, Corey
On 08/15/2012 06:10 PM, Marcelo Cerri wrote:
This is a v4 patch series that updates the libvirt's security driver mechanism to support per-guest configurable user and group for QEMU processes running together with other security drivers, such as SELinux and AppArmor.
Marcelo Cerri (5): Internal refactory of data structures Multiple security drivers in XML data Update security layer to handle many security labels Support for multiple default security drivers in QEMU config Update the remote API
daemon/remote.c | 63 ++++ docs/formatdomain.html.in | 11 +- docs/schemas/capability.rng | 18 +- docs/schemas/domaincommon.rng | 30 ++- include/libvirt/libvirt.h.in | 2 + python/generator.py | 1 + src/conf/capabilities.c | 17 +- src/conf/capabilities.h | 6 +- src/conf/domain_audit.c | 14 +- src/conf/domain_conf.c | 343 +++++++++++++++----- src/conf/domain_conf.h | 20 +- src/driver.h | 4 + src/libvirt.c | 47 +++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 1 + src/lxc/lxc_conf.c | 8 +- src/lxc/lxc_controller.c | 8 +- src/lxc/lxc_driver.c | 11 +- src/lxc/lxc_process.c | 23 +- src/qemu/qemu.conf | 6 +- src/qemu/qemu_conf.c | 38 ++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 218 +++++++++++--- src/qemu/qemu_process.c | 50 ++- src/remote/remote_driver.c | 46 +++ src/remote/remote_protocol.x | 17 +- src/remote_protocol-structs | 11 + src/security/security_apparmor.c | 118 +++++-- src/security/security_dac.c | 324 +++++++++++++++++-- src/security/security_manager.c | 101 +++++-- src/security/security_manager.h | 8 +- src/security/security_selinux.c | 263 +++++++++++----- src/security/security_stack.c | 237 +++++++++----- src/security/security_stack.h | 13 + src/test/test_driver.c | 11 +- .../qemuxml2argv-seclabel-dynamic-override.xml | 4 +- .../qemuxml2argv-seclabel-dynamic.xml | 2 +- 37 files changed, 1653 insertions(+), 448 deletions(-)
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Tue, Sep 11, 2012 at 02:13:38PM -0400, Corey Bryant wrote:
Are there any other requirements that need to be taken care of to enable execution of QEMU guests under separate unprivileged user IDs (ie. DAC isolation)?
At this point, this patch series (Per-guest configurable user/group for QEMU processes) is upstream, allowing libvirt to execute guests under separate unprivileged user IDs. Additionally, the QEMU bridge helper series is upstream, allowing QEMU to allocate a tap device and attach it to a bridge when run under an unprivileged user ID (http://www.redhat.com/archives/libvir-list/2012-August/msg00277.html).
Is there any other feature in QEMU that requires QEMU to be run as root?
Well those features you mention are for two separate issues. When running libvirt privileged (qemu:///system), QEMU was already run as non-root (qemu:qemu). The per-guest user/group was just making sure that QEMU VMs were isolated from each other using user IDs. Since libvirtd is running privileged, it can either set permissions or open things on QEMU's behalf. All this side of things really works already. The TAP device bridge helper is something that's needed when running libvirtd itself unprivileged (eg the per user qemu:///session libvirtd). In this case libvirtd can't access privileged resources at all, hence the setuid TAP helper was required. So I guess this is a roundabout way of saying that I'm not really clear what you're asking about ? If you're using qemu:///system there has never been any problem with running QEMU unprivileged. When using qemu:///session you're obviously limited to whatever resources the user is allowed to access. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 09/14/2012 04:40 AM, Daniel P. Berrange wrote:
On Tue, Sep 11, 2012 at 02:13:38PM -0400, Corey Bryant wrote:
Are there any other requirements that need to be taken care of to enable execution of QEMU guests under separate unprivileged user IDs (ie. DAC isolation)?
At this point, this patch series (Per-guest configurable user/group for QEMU processes) is upstream, allowing libvirt to execute guests under separate unprivileged user IDs. Additionally, the QEMU bridge helper series is upstream, allowing QEMU to allocate a tap device and attach it to a bridge when run under an unprivileged user ID (http://www.redhat.com/archives/libvir-list/2012-August/msg00277.html).
Is there any other feature in QEMU that requires QEMU to be run as root?
Well those features you mention are for two separate issues. When running libvirt privileged (qemu:///system), QEMU was already run as non-root (qemu:qemu). The per-guest user/group was just making sure that QEMU VMs were isolated from each other using user IDs. Since libvirtd is running privileged, it can either set permissions or open things on QEMU's behalf. All this side of things really works already.
Ok good. This is really what I was getting at and you answered my question. So we now have DAC isolation of QEMU guests when running with the qemu:///system URI and there shouldn't be any issues running unprivileged guests from a privileged libvirt.
The TAP device bridge helper is something that's needed when running libvirtd itself unprivileged (eg the per user qemu:///session libvirtd). In this case libvirtd can't access privileged resources at all, hence the setuid TAP helper was required.
Ah, that's right, the bridge helper is really only benefiting libvirt when running with the qemu:///session URI. Is there a desire to get to a point where libvirt can do everything under a session URI that it can do today under a system URI? Then libvirt and guests could all run unprivileged. I'm sure it's a lot of work.. I'm just asking. :)
So I guess this is a roundabout way of saying that I'm not really clear what you're asking about ? If you're using qemu:///system there has never been any problem with running QEMU unprivileged. When using qemu:///session you're obviously limited to whatever resources the user is allowed to access.
-- Regards, Corey Bryant

On Fri, Sep 14, 2012 at 09:31:26AM -0400, Corey Bryant wrote:
On 09/14/2012 04:40 AM, Daniel P. Berrange wrote:
On Tue, Sep 11, 2012 at 02:13:38PM -0400, Corey Bryant wrote:
Are there any other requirements that need to be taken care of to enable execution of QEMU guests under separate unprivileged user IDs (ie. DAC isolation)?
At this point, this patch series (Per-guest configurable user/group for QEMU processes) is upstream, allowing libvirt to execute guests under separate unprivileged user IDs. Additionally, the QEMU bridge helper series is upstream, allowing QEMU to allocate a tap device and attach it to a bridge when run under an unprivileged user ID (http://www.redhat.com/archives/libvir-list/2012-August/msg00277.html).
Is there any other feature in QEMU that requires QEMU to be run as root?
Well those features you mention are for two separate issues. When running libvirt privileged (qemu:///system), QEMU was already run as non-root (qemu:qemu). The per-guest user/group was just making sure that QEMU VMs were isolated from each other using user IDs. Since libvirtd is running privileged, it can either set permissions or open things on QEMU's behalf. All this side of things really works already.
Ok good. This is really what I was getting at and you answered my question. So we now have DAC isolation of QEMU guests when running with the qemu:///system URI and there shouldn't be any issues running unprivileged guests from a privileged libvirt.
The TAP device bridge helper is something that's needed when running libvirtd itself unprivileged (eg the per user qemu:///session libvirtd). In this case libvirtd can't access privileged resources at all, hence the setuid TAP helper was required.
Ah, that's right, the bridge helper is really only benefiting libvirt when running with the qemu:///session URI.
Is there a desire to get to a point where libvirt can do everything under a session URI that it can do today under a system URI? Then libvirt and guests could all run unprivileged. I'm sure it's a lot of work.. I'm just asking. :)
Well if you want to give a VM a raw block device someone/thing needs to be running privileged to set an ACL on the device to le the unprivileged VM use it. Similarly for PCI device passthrough. Traditionally in the qemu:///system case, libvirt can deal with this. In a qemu:///session case the sysadmin would have had to setup ACLs/permissions on the devices / files ahead of time. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 09/14/2012 09:51 AM, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 09:31:26AM -0400, Corey Bryant wrote:
On 09/14/2012 04:40 AM, Daniel P. Berrange wrote:
On Tue, Sep 11, 2012 at 02:13:38PM -0400, Corey Bryant wrote:
Are there any other requirements that need to be taken care of to enable execution of QEMU guests under separate unprivileged user IDs (ie. DAC isolation)?
At this point, this patch series (Per-guest configurable user/group for QEMU processes) is upstream, allowing libvirt to execute guests under separate unprivileged user IDs. Additionally, the QEMU bridge helper series is upstream, allowing QEMU to allocate a tap device and attach it to a bridge when run under an unprivileged user ID (http://www.redhat.com/archives/libvir-list/2012-August/msg00277.html).
Is there any other feature in QEMU that requires QEMU to be run as root?
Well those features you mention are for two separate issues. When running libvirt privileged (qemu:///system), QEMU was already run as non-root (qemu:qemu). The per-guest user/group was just making sure that QEMU VMs were isolated from each other using user IDs. Since libvirtd is running privileged, it can either set permissions or open things on QEMU's behalf. All this side of things really works already.
Ok good. This is really what I was getting at and you answered my question. So we now have DAC isolation of QEMU guests when running with the qemu:///system URI and there shouldn't be any issues running unprivileged guests from a privileged libvirt.
The TAP device bridge helper is something that's needed when running libvirtd itself unprivileged (eg the per user qemu:///session libvirtd). In this case libvirtd can't access privileged resources at all, hence the setuid TAP helper was required.
Ah, that's right, the bridge helper is really only benefiting libvirt when running with the qemu:///session URI.
Is there a desire to get to a point where libvirt can do everything under a session URI that it can do today under a system URI? Then libvirt and guests could all run unprivileged. I'm sure it's a lot of work.. I'm just asking. :)
Well if you want to give a VM a raw block device someone/thing needs to be running privileged to set an ACL on the device to le the unprivileged VM use it. Similarly for PCI device passthrough. Traditionally in the qemu:///system case, libvirt can deal with this. In a qemu:///session case the sysadmin would have had to setup ACLs/permissions on the devices / files ahead of time.
Perhaps these are things that could eventually be taken care of by a setuid root helper with reduced capabilities, allowing libvirt to run unprivileged. -- Regards, Corey Bryant
participants (5)
-
Corey Bryant
-
Daniel P. Berrange
-
Marcelo Cerri
-
Michal Privoznik
-
Viktor Mihajlovski