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, " \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 @@
-
+