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

This is a v3 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. Comments and feedbacks are welcome. 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 | 342 +++++++++++++++----- src/conf/domain_conf.h | 18 +- src/driver.h | 4 + src/libvirt.c | 47 +++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 5 + 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.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 | 1 + 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 | 256 ++++++++++----- 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 +- 36 files changed, 1634 insertions(+), 445 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 | 81 ++++++++++++++++------- src/conf/domain_conf.h | 9 ++- 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 | 133 ++++++++++++++++++++------------------- src/test/test_driver.c | 11 ++- 14 files changed, 216 insertions(+), 150 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 58603a3..1c318a0 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -855,12 +855,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); } @@ -869,6 +872,7 @@ virSecurityDeviceLabelDefFree(virSecurityDeviceLabelDefPtr def) { if (!def) return; + VIR_FREE(def->model); VIR_FREE(def->label); VIR_FREE(def); } @@ -954,7 +958,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]); @@ -1620,7 +1628,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); @@ -3075,10 +3085,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; @@ -3092,7 +3102,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")) { @@ -3132,7 +3142,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, @@ -3147,7 +3157,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, @@ -3159,7 +3169,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; } @@ -3171,7 +3181,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, @@ -3184,7 +3194,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, return 0; error: - virSecurityLabelDefClear(def); + virSecurityLabelDefFree(def); return -1; } @@ -3198,7 +3208,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. */ @@ -3214,7 +3224,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")) { @@ -3233,7 +3243,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; @@ -3667,10 +3677,16 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if (virSecurityDeviceLabelDefParseXML(&def->seclabel, + if ((VIR_ALLOC(def->seclabels) < 0) || + (VIR_ALLOC(def->seclabels[0]) < 0)) { + virReportOOMError(); + goto error; + } + if (virSecurityDeviceLabelDefParseXML(&def->seclabels[0], vmSeclabel, ctxt) < 0) goto error; + def->nseclabels = 1; ctxt->node = saved_node; } @@ -7120,10 +7136,17 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; } + 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; @@ -8061,7 +8084,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 */ @@ -8661,7 +8690,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - &def->seclabel, + def->seclabels[0], flags); if (!disk) goto error; @@ -11181,10 +11210,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 { @@ -11194,10 +11223,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 { @@ -13223,9 +13252,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 f4c43c6..db6ef6c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -311,6 +311,7 @@ struct _virSecurityLabelDef { typedef struct _virSecurityDeviceLabelDef virSecurityDeviceLabelDef; typedef virSecurityDeviceLabelDef *virSecurityDeviceLabelDefPtr; struct _virSecurityDeviceLabelDef { + char *model; char *label; /* image label string */ bool norelabel; }; @@ -555,7 +556,6 @@ struct _virDomainDiskDef { int device; int bus; char *src; - virSecurityDeviceLabelDefPtr seclabel; char *dst; int tray_status; int protocol; @@ -595,6 +595,9 @@ struct _virDomainDiskDef { virStorageEncryptionPtr encryption; bool rawio_specified; int rawio; /* no = 0, yes = 1 */ + + size_t nseclabels; + virSecurityDeviceLabelDefPtr *seclabels; }; @@ -1675,8 +1678,10 @@ struct _virDomainDef { int nhubs; virDomainHubDefPtr *hubs; + size_t nseclabels; + virSecurityLabelDefPtr *seclabels; + /* Only 1 */ - virSecurityLabelDef seclabel; virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; virCPUDefPtr cpu; 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 56ed7d3..7f2176b 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -1592,10 +1592,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 d118dd2..bc808e0 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -936,7 +936,6 @@ cleanup: return ret; } - /** * lxcDomainStartWithFlags: * @dom: domain to start @@ -1154,12 +1153,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"), @@ -1168,7 +1167,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 65b463f..bd924f0 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 369e8ed..1061484 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -293,10 +293,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; } @@ -4030,10 +4035,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"), @@ -4043,7 +4048,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 685ea7c..5fc0edc 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4081,12 +4081,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) { @@ -4229,16 +4229,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 ca19b70..9d6e7df 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -272,43 +272,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; } @@ -348,11 +348,11 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, break; } - def->seclabel.label = - virSecuritySELinuxGenNewContext(def->seclabel.baselabel ? - def->seclabel.baselabel : + def->seclabels[0]->label = + virSecuritySELinuxGenNewContext(def->seclabels[0]->baselabel ? + def->seclabels[0]->baselabel : data->domain_context, mcs); - if (! def->seclabel.label) { + if (! def->seclabels[0]->label) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate selinux context for %s"), mcs); goto cleanup; @@ -366,21 +366,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) { - def->seclabel.imagelabel = virSecuritySELinuxGenNewContext(data->file_context, mcs); - if (!def->seclabel.imagelabel) { + 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; } @@ -389,12 +390,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) @@ -403,10 +404,10 @@ 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; } @@ -421,7 +422,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) { @@ -725,9 +726,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 @@ -784,12 +785,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) { @@ -804,14 +805,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; @@ -824,7 +825,7 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, { virSecuritySELinuxCallbackData cbdata; - cbdata.secdef = &def->seclabel; + cbdata.secdef = def->seclabels[0]; cbdata.manager = mgr; bool allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr); @@ -855,7 +856,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); } @@ -865,7 +866,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); } @@ -876,7 +877,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) @@ -945,7 +946,7 @@ virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUT virDomainHostdevDefPtr dev) { - const virSecurityLabelDefPtr secdef = &def->seclabel; + const virSecurityLabelDefPtr secdef = def->seclabels[0]; int ret = -1; if (secdef->norelabel) @@ -998,7 +999,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; @@ -1044,7 +1045,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; @@ -1137,7 +1138,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; @@ -1187,7 +1188,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) { @@ -1212,7 +1213,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; @@ -1226,7 +1227,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; @@ -1239,7 +1240,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: " @@ -1264,10 +1265,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)) { @@ -1296,13 +1297,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)) { @@ -1366,7 +1367,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) @@ -1404,9 +1405,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)) { @@ -1483,7 +1484,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) @@ -1544,7 +1545,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; @@ -1556,7 +1557,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; @@ -1595,7 +1596,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 01e515a..9c3759f 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -207,12 +207,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 03.08.2012 16:18, 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 | 81 ++++++++++++++++------- src/conf/domain_conf.h | 9 ++- 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 | 133 ++++++++++++++++++++------------------- src/test/test_driver.c | 11 ++- 14 files changed, 216 insertions(+), 150 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 58603a3..1c318a0 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -855,12 +855,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); }
@@ -869,6 +872,7 @@ virSecurityDeviceLabelDefFree(virSecurityDeviceLabelDefPtr def) { if (!def) return; + VIR_FREE(def->model); VIR_FREE(def->label); VIR_FREE(def); } @@ -954,7 +958,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]); @@ -1620,7 +1628,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);
@@ -3075,10 +3085,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; @@ -3092,7 +3102,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")) { @@ -3132,7 +3142,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, @@ -3147,7 +3157,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, @@ -3159,7 +3169,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; } @@ -3171,7 +3181,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, @@ -3184,7 +3194,7 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, return 0;
error: - virSecurityLabelDefClear(def); + virSecurityLabelDefFree(def); return -1; }
@@ -3198,7 +3208,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. */ @@ -3214,7 +3224,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")) { @@ -3233,7 +3243,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;
@@ -3667,10 +3677,16 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if (virSecurityDeviceLabelDefParseXML(&def->seclabel, + if ((VIR_ALLOC(def->seclabels) < 0) || + (VIR_ALLOC(def->seclabels[0]) < 0)) { + virReportOOMError(); + goto error; + } + if (virSecurityDeviceLabelDefParseXML(&def->seclabels[0], vmSeclabel, ctxt) < 0) goto error; + def->nseclabels = 1; ctxt->node = saved_node; }
@@ -7120,10 +7136,17 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; }
+ if ((VIR_ALLOC(def->seclabels) < 0) || + (VIR_ALLOC(def->seclabels[0])) < 0 ) { + virReportOOMError(); + goto error; + } +
'def' is passed as 'const virDomainDefPtr def' here. I guess we could preserve const correctness here. Moreover, are we guaranteed that these haven't been allocated yet? I mean, virDomainDeviceDefParse is called from virDomainDeviceDefCopy() too which is called when doing live+config device attach 'virsh attach-devoce --persistent device.xml'
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; @@ -8061,7 +8084,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 */ @@ -8661,7 +8690,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - &def->seclabel, + def->seclabels[0], flags); if (!disk) goto error; @@ -11181,10 +11210,10 @@ virDomainDiskDefFormat(virBufferPtr buf, if (def->startupPolicy) virBufferEscapeString(buf, " startupPolicy='%s'", startupPolicy); - if (def->seclabel) { + if (def->seclabels && def->seclabels[0]) {
I guess def->nseclabels would be sufficient here.
virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); - virSecurityDeviceLabelDefFormat(buf, def->seclabel); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[0]); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -11194,10 +11223,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]) {
ditto.
virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); - virSecurityDeviceLabelDefFormat(buf, def->seclabel); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[0]); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else {
Otherwise I haven't spotted anything wrong. ACK modulo problem with 'def' within virDomainDeviceDefParse(). Michal

On 08/06/2012 10:30 AM, Michal Privoznik wrote:
On 03.08.2012 16:18, Marcelo Cerri wrote:
... @@ -11181,10 +11210,10 @@ virDomainDiskDefFormat(virBufferPtr buf, if (def->startupPolicy) virBufferEscapeString(buf, " startupPolicy='%s'", startupPolicy); - if (def->seclabel) { + if (def->seclabels && def->seclabels[0]) {
I guess def->nseclabels would be sufficient here.
I will keep this check because when a security label is not provide in XML `def->seclabels[0]` is set to NULL. Anyway, this behavior is replaced in the next commits. Regards, Marcelo

On Fri, Aug 03, 2012 at 11:18:54AM -0300, 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 | 81 ++++++++++++++++------- src/conf/domain_conf.h | 9 ++- 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 | 133 ++++++++++++++++++++------------------- src/test/test_driver.c | 11 ++- 14 files changed, 216 insertions(+), 150 deletions(-)
ACK 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 domain and capability XML parser and formatter to support morethan 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, 305 insertions(+), 131 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f97c630..88e0418 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1273,8 +1273,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 @@ -3828,6 +3828,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 c85d763..d1c1683 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 1c318a0..866df12 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3078,17 +3078,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; @@ -3102,7 +3104,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")) { @@ -3142,7 +3144,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, @@ -3157,7 +3159,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, @@ -3169,93 +3171,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; } @@ -3339,7 +3427,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlNodePtr node, xmlXPathContextPtr ctxt, virBitmapPtr bootMap, - virSecurityLabelDefPtr vmSeclabel, + virSecurityLabelDefPtr* vmSeclabels, + int nvmSeclabels, unsigned int flags) { virDomainDiskDefPtr def; @@ -3677,16 +3766,10 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if ((VIR_ALLOC(def->seclabels) < 0) || - (VIR_ALLOC(def->seclabels[0]) < 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; } @@ -7136,16 +7219,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; } - 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")) { @@ -8084,13 +8162,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 */ @@ -8690,7 +8762,8 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - def->seclabels[0], + def->seclabels, + def->nseclabels, flags); if (!disk) goto error; @@ -11037,13 +11110,15 @@ 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"); @@ -11069,8 +11144,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", @@ -11115,6 +11190,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) { @@ -11210,10 +11286,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 { @@ -11223,10 +11300,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 { @@ -13252,11 +13330,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) @@ -15433,3 +15510,65 @@ cleanup: VIR_FREE(xmlStr); 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 db6ef6c..dda6d24 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2161,6 +2161,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 75997fe..be8b610 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -287,6 +287,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

Just a note: I will provide a test case for multiple seclabels in XML in a separated patch. On 08/03/2012 11:18 AM, Marcelo Cerri wrote:
This patch updates the domain and capability XML parser and formatter to support morethan 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, 305 insertions(+), 131 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f97c630..88e0418 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1273,8 +1273,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 @@ -3828,6 +3828,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 c85d763..d1c1683 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 1c318a0..866df12 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3078,17 +3078,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; @@ -3102,7 +3104,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")) { @@ -3142,7 +3144,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, @@ -3157,7 +3159,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, @@ -3169,93 +3171,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; }
@@ -3339,7 +3427,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlNodePtr node, xmlXPathContextPtr ctxt, virBitmapPtr bootMap, - virSecurityLabelDefPtr vmSeclabel, + virSecurityLabelDefPtr* vmSeclabels, + int nvmSeclabels, unsigned int flags) { virDomainDiskDefPtr def; @@ -3677,16 +3766,10 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if ((VIR_ALLOC(def->seclabels) < 0) || - (VIR_ALLOC(def->seclabels[0]) < 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; }
@@ -7136,16 +7219,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; }
- 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")) { @@ -8084,13 +8162,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 */ @@ -8690,7 +8762,8 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - def->seclabels[0], + def->seclabels, + def->nseclabels, flags); if (!disk) goto error; @@ -11037,13 +11110,15 @@ 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");
@@ -11069,8 +11144,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", @@ -11115,6 +11190,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) { @@ -11210,10 +11286,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 { @@ -11223,10 +11300,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 { @@ -13252,11 +13330,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) @@ -15433,3 +15510,65 @@ cleanup: VIR_FREE(xmlStr); 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 db6ef6c..dda6d24 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2161,6 +2161,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 75997fe..be8b610 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -287,6 +287,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>

On 03.08.2012 16:18, Marcelo Cerri wrote:
This patch updates the domain and capability XML parser and formatter to support morethan 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, 305 insertions(+), 131 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f97c630..88e0418 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1273,8 +1273,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 @@ -3828,6 +3828,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 c85d763..d1c1683 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>
IIUC, both 'model' and 'relabel' are purely optional. If that is the case, the first <group> can be left out.
<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 1c318a0..866df12 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3078,17 +3078,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; @@ -3102,7 +3104,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")) { @@ -3142,7 +3144,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, @@ -3157,7 +3159,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, @@ -3169,93 +3171,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; }
@@ -3339,7 +3427,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlNodePtr node, xmlXPathContextPtr ctxt, virBitmapPtr bootMap, - virSecurityLabelDefPtr vmSeclabel, + virSecurityLabelDefPtr* vmSeclabels, + int nvmSeclabels, unsigned int flags) { virDomainDiskDefPtr def; @@ -3677,16 +3766,10 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if ((VIR_ALLOC(def->seclabels) < 0) || - (VIR_ALLOC(def->seclabels[0]) < 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; }
@@ -7136,16 +7219,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; }
- 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")) { @@ -8084,13 +8162,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 */ @@ -8690,7 +8762,8 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - def->seclabels[0], + def->seclabels, + def->nseclabels, flags); if (!disk) goto error; @@ -11037,13 +11110,15 @@ 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");
@@ -11069,8 +11144,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", @@ -11115,6 +11190,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) { @@ -11210,10 +11286,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 { @@ -11223,10 +11300,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 { @@ -13252,11 +13330,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) @@ -15433,3 +15510,65 @@ cleanup: VIR_FREE(xmlStr); 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 db6ef6c..dda6d24 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2161,6 +2161,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 75997fe..be8b610 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -287,6 +287,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>
ACK otherwise. Michal

Hi Michal, Comments below. On 08/06/2012 10:30 AM, Michal Privoznik wrote:
On 03.08.2012 16:18, Marcelo Cerri wrote: ...
@@ -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>
IIUC, both 'model' and 'relabel' are purely optional. If that is the case, the first <group> can be left out.
The idea here is to not allow "label" elements when relabel="no".
<attribute name='relabel'> <value>yes</value> </attribute> </optional> - <element name='label'> - <text/> - </element> + <zeroOrMore> + <element name='label'> + <text/> + </element> + </zeroOrMore>
Regards, Marcelo

On Fri, Aug 03, 2012 at 11:18:55AM -0300, Marcelo Cerri wrote:
This patch updates the domain and capability XML parser and formatter to support morethan one "seclabel" element for each domain and device. The
s/morethan/more than/
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, 305 insertions(+), 131 deletions(-) @@ -11037,13 +11110,15 @@ virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def) virBufferAsprintf(buf, "<seclabel type='%s'", sectype);
+ if (def->model) { + virBufferEscapeString(buf, " model='%s'", def->model); + }
Nit-pick, no need to use {} in this single line 'if' ACK, 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 :|

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 be8b610..13eaf4b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -976,6 +976,7 @@ virSecurityManagerFree; virSecurityManagerGenLabel; virSecurityManagerGetDOI; virSecurityManagerGetModel; +virSecurityManagerGetNested; virSecurityManagerGetProcessLabel; virSecurityManagerNew; virSecurityManagerNewStack; @@ -994,6 +995,7 @@ virSecurityManagerSetHostdevLabel; virSecurityManagerSetProcessLabel; virSecurityManagerSetSavedStateLabel; virSecurityManagerSetSocketLabel; +virSecurityManagerStackAddNested; virSecurityManagerVerify; virSecurityManagerGetMountOptions; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1061484..e3f71c3 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -240,8 +240,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; @@ -263,7 +263,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))) { @@ -288,31 +292,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; } @@ -4033,9 +4044,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 5fc0edc..915a74b 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4081,12 +4081,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) { @@ -4190,6 +4190,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainChrSourceDefPtr monConfig, bool monJSON) { + size_t i; char ebuf[1024]; int logfile = -1; char *timestamp; @@ -4197,6 +4198,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"); @@ -4229,17 +4233,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) @@ -4363,6 +4381,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_FORCE_CLOSE(logfile); VIR_FREE(seclabel); + VIR_FREE(sec_managers); return 0; @@ -4374,6 +4393,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 36d801b..7ac7c6f 100644 --- a/src/security/security_manager.h +++ b/src/security/security_manager.h @@ -32,8 +32,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, @@ -104,4 +105,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 9d6e7df..e02ec13 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -269,46 +269,52 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, int c2 = 0; 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; } @@ -348,11 +354,11 @@ virSecuritySELinuxGenSecurityLabel(virSecurityManagerPtr mgr, break; } - 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; @@ -366,22 +372,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; } @@ -390,12 +396,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) @@ -404,10 +410,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; } @@ -421,8 +427,14 @@ virSecuritySELinuxReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSE 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) { @@ -726,9 +738,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 @@ -780,17 +799,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) { @@ -805,14 +828,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; @@ -824,11 +847,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; @@ -855,9 +882,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); } @@ -865,8 +895,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); } @@ -877,9 +911,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; @@ -946,9 +984,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; @@ -999,10 +1041,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; @@ -1045,10 +1091,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; @@ -1138,12 +1188,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; @@ -1188,7 +1242,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) { @@ -1213,7 +1271,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; @@ -1227,7 +1289,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; @@ -1240,7 +1306,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: " @@ -1265,12 +1336,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: " @@ -1297,13 +1372,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)) { @@ -1367,9 +1446,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; @@ -1405,9 +1488,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)) { @@ -1483,9 +1570,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; @@ -1545,7 +1636,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; @@ -1557,13 +1652,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) { @@ -1596,7 +1695,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 03.08.2012 16:18, 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 Fri, Aug 03, 2012 at 11:18:56AM -0300, 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>
ACK 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 replaces the key "security_driver" in QEMU config by "security_drivers", which accepts a list of default drivers. If "security_drivers" can't be found, libvirt will use "security_driver" to ensure that it will remain compatible with older version of the config file. Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/qemu/qemu_conf.c | 38 +++++++++++++++++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 99 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 113 insertions(+), 26 deletions(-) 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 e3f71c3..ec0f02b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -220,36 +220,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 03.08.2012 16:18, Marcelo Cerri wrote:
This patch replaces the key "security_driver" in QEMU config by "security_drivers", which accepts a list of default drivers. If "security_drivers" can't be found, libvirt will use "security_driver" to ensure that it will remain compatible with older version of the config file.
Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> --- src/qemu/qemu_conf.c | 38 +++++++++++++++++- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 99 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 113 insertions(+), 26 deletions(-)
Maybe it's worth mentioning in qemu.conf that multiple drivers per security_driver variable are supported.
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 e3f71c3..ec0f02b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -220,36 +220,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:
ACK with qemu.conf updated. Michal

On Fri, Aug 03, 2012 at 11:18:57AM -0300, Marcelo Cerri wrote:
This patch replaces the key "security_driver" in QEMU config by "security_drivers", which accepts a list of default drivers. If "security_drivers" can't be found, libvirt will use "security_driver" to ensure that it will remain compatible with older version of the config file.
This description doesn't match the code - the code just uses 'security_driver', but allows it to be a list or a string ACK if commit message is fixed 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 :|

Thist 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 | 5 +++ src/qemu/qemu_driver.c | 73 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 46 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 17 +++++++++- src/remote_protocol-structs | 1 + 10 files changed, 258 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index d25717c..367180a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1420,6 +1420,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 3c4bf8c..dfe49eb 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9027,6 +9027,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 0 in case of 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..06ef08a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -551,4 +551,9 @@ LIBVIRT_0.10.0 { virConnectUnregisterCloseCallback; } LIBVIRT_0.9.13; +LIBVIRT_0.10.1{ + global: + virDomainGetSecurityLabelList; +} LIBVIRT_0.10.0; + # .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ec0f02b..7d95fef 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4089,6 +4089,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) { @@ -13332,6 +13404,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 afd367b..6f56ee6 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1956,6 +1956,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, @@ -5258,6 +5303,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..9cfd285 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2259,4 +2259,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 03.08.2012 16:18, Marcelo Cerri wrote:
Thist 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 | 5 +++ src/qemu/qemu_driver.c | 73 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 46 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 17 +++++++++- src/remote_protocol-structs | 1 + 10 files changed, 258 insertions(+), 1 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c index d25717c..367180a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1420,6 +1420,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 3c4bf8c..dfe49eb 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9027,6 +9027,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 0 in case of 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..06ef08a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -551,4 +551,9 @@ LIBVIRT_0.10.0 { virConnectUnregisterCloseCallback; } LIBVIRT_0.9.13;
+LIBVIRT_0.10.1{ + global: + virDomainGetSecurityLabelList; +} LIBVIRT_0.10.0; +
Since 0.10.0 is not out yet, I guess this can be squashed into 0.10.0 (okay, we've release -rc0 but that's because patch flow is lower making our release prolonged about twice).
# .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ec0f02b..7d95fef 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4089,6 +4089,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) { @@ -13332,6 +13404,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 afd367b..6f56ee6 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1956,6 +1956,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, @@ -5258,6 +5303,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..9cfd285 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2259,4 +2259,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, };
ACK with this squashed in: --- remote_protocol-structs 2012-08-06 14:47:40.813120178 +0200 +++ remote_protocol-struct-t3 2012-08-06 15:22:39.223650145 +0200 @@ -749,6 +749,16 @@ } 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; and libvirt_public.syms updated. Michal

Hi Michal, I'm updating the patch series, but I have a few questions below: On 08/06/2012 10:29 AM, Michal Privoznik wrote:
On 03.08.2012 16:18, Marcelo Cerri wrote:
...
+LIBVIRT_0.10.1{ + global: + virDomainGetSecurityLabelList; +} LIBVIRT_0.10.0; + Since 0.10.0 is not out yet, I guess this can be squashed into 0.10.0 (okay, we've release -rc0 but that's because patch flow is lower making our release prolonged about twice). Can you tell me in which version I can include it now? ...
ACK with this squashed in:
--- remote_protocol-structs 2012-08-06 14:47:40.813120178 +0200 +++ remote_protocol-struct-t3 2012-08-06 15:22:39.223650145 +0200 @@ -749,6 +749,16 @@ } 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; Why did you want to change this?
Regards, Marcelo

On 08/14/2012 02:19 PM, Marcelo Cerri wrote:
Hi Michal,
I'm updating the patch series, but I have a few questions below:
On 08/06/2012 10:29 AM, Michal Privoznik wrote:
On 03.08.2012 16:18, Marcelo Cerri wrote:
... +LIBVIRT_0.10.1{ + global: + virDomainGetSecurityLabelList; +} LIBVIRT_0.10.0; + Since 0.10.0 is not out yet, I guess this can be squashed into 0.10.0 (okay, we've release -rc0 but that's because patch flow is lower making our release prolonged about twice). Can you tell me in which version I can include it now?
Include it in the existing LIBVIRT_0.10.0{} chunk.
--- remote_protocol-structs 2012-08-06 14:47:40.813120178 +0200 +++ remote_protocol-struct-t3 2012-08-06 15:22:39.223650145 +0200 @@ -749,6 +749,16 @@ } label; int enforcing; }; +struct remote_domain_get_security_label_list_args {
Why did you want to change this?
This file is generated by 'make check' _if_ you have 'pdwtags' installed (part of the 'dwarves' package on Fedora), and keeping it in version control forces reviewers to notice if we are breaking RPC ABI compatibility (strict addition of new structs is okay, changing of existing structs is generally not okay). Apparently you didn't have pdwtags available, so Michal is showing what you'd have to change to pass 'make check' for those of us that do have it installed. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Fri, Aug 03, 2012 at 11:18:58AM -0300, Marcelo Cerri wrote:
Thist 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 | 5 +++ src/qemu/qemu_driver.c | 73 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 46 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 17 +++++++++- src/remote_protocol-structs | 1 + 10 files changed, 258 insertions(+), 1 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c index d25717c..367180a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1420,6 +1420,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 3c4bf8c..dfe49eb 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9027,6 +9027,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 0 in case of success, -1 in case of failure
Should this be: 'Returns number of elemnets in @seclabels on success, -1 in case of failure' eg see the QEMU impl:
+ 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; +}
ACK with comment fixed & the issue Michael pointed out 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 03.08.2012 16:18, Marcelo Cerri wrote:
This is a v3 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.
Comments and feedbacks are welcome.
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 | 342 +++++++++++++++----- src/conf/domain_conf.h | 18 +- src/driver.h | 4 + src/libvirt.c | 47 +++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 5 + 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.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 | 1 + 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 | 256 ++++++++++----- 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 +- 36 files changed, 1634 insertions(+), 445 deletions(-)
Okay, I've done basic review. Basically, I've not spotted something obviously wrong so I've ACKed all the patches (but see my notes on each of them because some ACKs are conditional). However, I am not pushing this for now as I'd like to give others some time to say their opinions. If nobody complains I think this is in good shape to be pushed in. Michal

Daniel, Any comments or feedback? Regards, Marcelo On 08/03/2012 11:18 AM, Marcelo Cerri wrote:
This is a v3 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.
Comments and feedbacks are welcome.
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 | 342 +++++++++++++++----- src/conf/domain_conf.h | 18 +- src/driver.h | 4 + src/libvirt.c | 47 +++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 5 + 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.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 | 1 + 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 | 256 ++++++++++----- 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 +- 36 files changed, 1634 insertions(+), 445 deletions(-)
participants (4)
-
Daniel P. Berrange
-
Eric Blake
-
Marcelo Cerri
-
Michal Privoznik