From e9377dda367b847b5a15dac1403bcdf19f05438a Mon Sep 17 00:00:00 2001 From: Marcelo Cerri Date: Wed, 15 Aug 2012 19:10:36 -0300 Subject: [PATCH] Multiple security drivers in XML data This patch updates the domain and capability XML parser and formatter to support more than one "seclabel" element for each domain and device. The RNG schema and the tests related to this are also updated by this patch. Signed-off-by: Marcelo Cerri --- docs/formatdomain.html.in | 11 +- docs/schemas/capability.rng | 18 +- docs/schemas/domaincommon.rng | 30 +- src/conf/capabilities.c | 6 +- src/conf/domain_conf.c | 353 ++++++++++++------ src/conf/domain_conf.h | 9 + src/libvirt_private.syms | 3 + ...qemuxml2argv-seclabel-dynamic-override.xml | 4 +- .../qemuxml2argv-seclabel-dynamic.xml | 2 +- 9 files changed, 304 insertions(+), 132 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 2c5c456320..8e0748977f 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1275,8 +1275,8 @@ path to the file holding the disk. If the disk type is "block", then the dev attribute specifies the path to the host device to serve as - the disk. With both "file" and "block", an optional - sub-element seclabel, described + the disk. With both "file" and "block", one or more optional + sub-elements seclabel, described below (and since 0.9.9), can be used to override the domain security labeling policy for just that source file. If the disk type is "dir", then the @@ -3919,6 +3919,13 @@ qemu-kvm -net nic,model=? /dev/null since 0.6.2, and 'none' since 0.9.10.

+

+ If more than one security driver is used by libvirt, multiple + seclabel tags can be used, one for each driver and + the security driver referenced by each tag can be defined using + the attribute model +

+

Valid input XML configurations for the top-level security label are: diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index c392e44351..8c928bcf0e 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -44,20 +44,22 @@ - + - + - - - - - - + + + + + + + + diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 35e9f82565..401b76ba86 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -55,9 +55,9 @@ - + - + @@ -148,18 +148,32 @@ - - no - + + + + + + + no + + + + + + + + yes - - - + + + + + diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 9a0bc3da55..bc6266ea29 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -772,12 +772,12 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBufferAddLit(&xml, " \n"); } - if (caps->host.nsecModels) { + for (i = 0; i < caps->host.nsecModels; i++) { virBufferAddLit(&xml, " \n"); virBufferAsprintf(&xml, " %s\n", - caps->host.secModels[0].model); + caps->host.secModels[i].model); virBufferAsprintf(&xml, " %s\n", - caps->host.secModels[0].doi); + caps->host.secModels[i].doi); virBufferAddLit(&xml, " \n"); } diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4234416c0d..c9f5a3c0ea 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2985,17 +2985,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; @@ -3009,7 +3011,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")) { @@ -3049,7 +3051,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, @@ -3064,7 +3066,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, @@ -3076,93 +3078,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 = 0, 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; } - - 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; - } else { - virReportError(VIR_ERR_XML_ERROR, - _("invalid security relabel value %s"), p); - VIR_FREE(p); - VIR_FREE(*def); - return -1; + for (i = 0; i < n; i++) { + if (VIR_ALLOC(def->seclabels[i]) < 0) { + virReportOOMError(); + 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; + 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; + } - 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; + /* 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, + _("Cannot specify a label if relabelling is " + "turned off. model=%s"), + def->seclabels[i]->model); + goto error; + } } - + 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; } @@ -3246,7 +3334,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlNodePtr node, xmlXPathContextPtr ctxt, virBitmapPtr bootMap, - virSecurityLabelDefPtr vmSeclabel, + virSecurityLabelDefPtr* vmSeclabels, + int nvmSeclabels, unsigned int flags) { virDomainDiskDefPtr def; @@ -3584,15 +3673,10 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; - if ((VIR_ALLOC(def->seclabels) < 0)) { - virReportOOMError(); - goto error; - } - if (virSecurityDeviceLabelDefParseXML(&def->seclabels[0], - vmSeclabel, + if (virSecurityDeviceLabelDefParseXML(def, vmSeclabels, + nvmSeclabels, ctxt) < 0) goto error; - def->nseclabels = 1; ctxt->node = saved_node; } @@ -7064,18 +7148,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, goto error; } - if (!def->seclabels) { - if ((VIR_ALLOC(def->seclabels) < 0) || - (VIR_ALLOC(def->seclabels[0])) < 0 ) { - virReportOOMError(); - goto error; - } - } - if (xmlStrEqual(node->name, BAD_CAST "disk")) { dev->type = VIR_DOMAIN_DEVICE_DISK; if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node, ctxt, - NULL, def->seclabels[0], + NULL, def->seclabels, + def->nseclabels, flags))) goto error; } else if (xmlStrEqual(node->name, BAD_CAST "lease")) { @@ -8014,13 +8091,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 */ @@ -8620,7 +8691,8 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, - def->seclabels[0], + def->seclabels, + def->nseclabels, flags); if (!disk) goto error; @@ -10975,13 +11047,14 @@ virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def) virBufferAsprintf(buf, "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"); @@ -11007,8 +11080,8 @@ static void virSecurityDeviceLabelDefFormat(virBufferPtr buf, virSecurityDeviceLabelDefPtr def) { - virBufferAsprintf(buf, "norelabel ? "no" : "yes"); + virBufferAsprintf(buf, "model, def->norelabel ? "no" : "yes"); if (def->label) { virBufferAddLit(buf, ">\n"); virBufferEscapeString(buf, " \n", @@ -11053,6 +11126,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) { @@ -11148,10 +11222,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, " \n"); } else { @@ -11161,10 +11236,11 @@ virDomainDiskDefFormat(virBufferPtr buf, case VIR_DOMAIN_DISK_TYPE_BLOCK: virBufferEscapeString(buf, " 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, " \n"); } else { @@ -13158,11 +13234,10 @@ virDomainDefFormatInternal(virDomainDefPtr def, virBufferAddLit(buf, " \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) @@ -15529,3 +15604,65 @@ cleanup: } return ret; } + +virSecurityLabelDefPtr +virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model) +{ + int i; + + if (def == NULL || model == NULL) + return NULL; + + for (i = 0; i < def->nseclabels; i++) { + if (def->seclabels[i]->model == NULL) + continue; + if (STREQ(def->seclabels[i]->model, model)) + return def->seclabels[i]; + } + + return virDomainDefAddSecurityLabelDef(def, model); +} + +virSecurityDeviceLabelDefPtr +virDomainDiskDefGetSecurityLabelDef(virDomainDiskDefPtr def, const char *model) +{ + int i; + + if (def == NULL) + return NULL; + + for (i = 0; i < def->nseclabels; i++) { + if (STREQ(def->seclabels[i]->model, model)) + return def->seclabels[i]; + } + return NULL; +} + +virSecurityLabelDefPtr +virDomainDefAddSecurityLabelDef(virDomainDefPtr def, const char *model) +{ + virSecurityLabelDefPtr seclabel = NULL; + + if (VIR_ALLOC(seclabel) < 0) { + virReportOOMError(); + return NULL; + } + + if (model) { + seclabel->model = strdup(model); + if (seclabel->model == NULL) { + virReportOOMError(); + virSecurityLabelDefFree(seclabel); + return NULL; + } + } + + if (VIR_EXPAND_N(def->seclabels, def->nseclabels, 1) < 0) { + virReportOOMError(); + virSecurityLabelDefFree(seclabel); + return NULL; + } + def->seclabels[def->nseclabels - 1] = seclabel; + + return seclabel; +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 05a8bee7d0..74abe6c71f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2143,6 +2143,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 7bc513fb3a..4d28607ff8 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -300,6 +300,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 4de435b9d2..426b663ce2 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.xml @@ -16,14 +16,14 @@ /usr/bin/qemu - +

- + diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml index 78a6b6a8a1..36df9d4891 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic.xml @@ -22,5 +22,5 @@ - +