[libvirt] [PATCH] selinux: distinguish failure to label from request to avoid label

https://bugzilla.redhat.com/show_bug.cgi?id=924153 Commit 904e05a2 (v0.9.9) added a per-<disk> seclabel element with an attribute relabel='no' in order to try and minimize the impact of shutdown delays when an NFS server disappears. The idea was that if a disk is on NFS and can't be labeled in the first place, there is no need to attempt the (no-op) relabel on domain shutdown. Unfortunately, the way this was implemented was by modifying the domain XML so that the optimization would survive libvirtd restart, but in a way that is indistinguishable from an explicit user setting. Furthermore, once the setting is turned on, libvirt avoids attempts at labeling, even for operations like snapshot or blockcopy where the chain is being extended or pivoted onto non-NFS, where SELinux labeling is once again possible. As a result, it was impossible to do a blockcopy to pivot from an NFS image file onto a local file. The solution is to separate the semantics of a chain that must not be labeled (which the user can set even on persistent domains) vs. the optimization of not attempting a relabel on cleanup (a live-only annotation), and using only the user's explicit notation rather than the optimization as the decision on whether to skip a label attempt in the first place. When upgrading an older libvirtd to a newer, an NFS volume will still attempt the relabel; but as the avoidance of a relabel was only an optimization, this shouldn't cause any problems. In the ideal future, libvirt will eventually have XML describing EVERY file in the backing chain, with each file having a separate <seclabel> element. At that point, libvirt will be able to track more closely which files need a relabel attempt at shutdown. But until we reach that point, the single <seclabel> for the entire <disk> chain is treated as a hint - when a chain has only one file, then we know it is accurate; but if the chain has more than one file, we have to attempt relabel in spite of the attribute, in case part of the chain is local and SELinux mattered for that portion of the chain. * src/conf/domain_conf.h (_virSecurityDeviceLabelDef): Add new member. * src/conf/domain_conf.c (virSecurityDeviceLabelDefParseXML): Parse it, for live images only. (virSecurityDeviceLabelDefFormat): Output it. (virDomainDiskDefParseXML, virDomainChrSourceDefParseXML) (virDomainDiskSourceDefFormat, virDomainChrDefFormat) (virDomainDiskDefFormat): Pass flags on through. * src/security/security_selinux.c (virSecuritySELinuxRestoreSecurityImageLabelInt): Honor labelskip when possible. (virSecuritySELinuxSetSecurityFileLabel): Set labelskip, not norelabel, if labeling fails. * docs/formatdomain.html.in (seclabel): Document new xml. * docs/schemas/domaincommon.rng (devSeclabel): Allow it in RNG. * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.xml: * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.args: * tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-*-skiplabel.xml: New test files. * tests/qemuxml2argvtest.c (mymain): Run the new tests. * tests/qemuxml2xmltest.c (mymain): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com> --- docs/formatdomain.html.in | 6 ++- docs/schemas/domaincommon.rng | 27 +++++++------ src/conf/domain_conf.c | 47 ++++++++++++++++------ src/conf/domain_conf.h | 3 +- src/security/security_selinux.c | 10 ++++- .../qemuxml2argv-seclabel-dynamic-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-dynamic-skiplabel.xml | 32 +++++++++++++++ .../qemuxml2argv-seclabel-static-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-static-skiplabel.xml | 33 +++++++++++++++ tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-seclabel-dynamic-skiplabel.xml | 31 ++++++++++++++ tests/qemuxml2xmltest.c | 8 ++-- 12 files changed, 178 insertions(+), 31 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 83d551a..cafa03f 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -5024,7 +5024,11 @@ qemu-kvm -net nic,model=? /dev/null a <code>seclabel</code> element is attached to a specific path rather than the top-level domain assignment, only the attribute <code>relabel</code> or the - sub-element <code>label</code> are supported. + sub-element <code>label</code> are supported. Additionally, + <span class="since">since 1.1.2</span>, an output-only + element <code>labelskip</code> will be present for active + domains on disks where labeling was skipped due to the image + being on a file system that lacks security labeling. </p> <h2><a name="examples">Example configs</a></h2> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index ac807e6..dfcd61c 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -152,34 +152,35 @@ <define name="devSeclabel"> <element name="seclabel"> <!-- A per-device seclabel override is more limited, either - relabel=no or a <label> must be present. --> + relabel=no or a <label> must be present on input; + output also can include labelskip=yes. --> + <optional> + <attribute name='model'> + <text/> + </attribute> + </optional> <choice> <group> - <optional> - <attribute name='model'> - <text/> - </attribute> - </optional> <attribute name='relabel'> <value>no</value> </attribute> </group> <group> - <optional> - <attribute name='model'> - <text/> - </attribute> - </optional> + <attribute name='labelskip'> + <value>yes</value> + </attribute> + </group> + <group> <optional> <attribute name='relabel'> <value>yes</value> </attribute> </optional> - <zeroOrMore> + <oneOrMore> <element name='label'> <text/> </element> - </zeroOrMore> + </oneOrMore> </group> </choice> </element> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7309877..759f686 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4484,7 +4484,8 @@ static int virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, size_t *nseclabels_rtn, virSecurityLabelDefPtr *vmSeclabels, - int nvmSeclabels, xmlXPathContextPtr ctxt) + int nvmSeclabels, xmlXPathContextPtr ctxt, + unsigned int flags) { virSecurityDeviceLabelDefPtr *seclabels; size_t nseclabels = 0; @@ -4492,7 +4493,7 @@ virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, size_t i, j; xmlNodePtr *list = NULL; virSecurityLabelDefPtr vmDef = NULL; - char *model, *relabel, *label; + char *model, *relabel, *label, *labelskip; if ((n = virXPathNodeSet("./seclabel", ctxt, &list)) < 0) goto error; @@ -4547,6 +4548,13 @@ virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, seclabels[i]->norelabel = false; } + /* labelskip is only parsed on live images */ + labelskip = virXMLPropString(list[i], "labelskip"); + seclabels[i]->labelskip = false; + if (labelskip && !(flags & VIR_DOMAIN_XML_INACTIVE)) + seclabels[i]->labelskip = STREQ(labelskip, "yes"); + VIR_FREE(labelskip); + ctxt->node = list[i]; label = virXPathStringLimit("string(./label)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); @@ -5208,7 +5216,8 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt, &def->nseclabels, vmSeclabels, nvmSeclabels, - ctxt) < 0) + ctxt, + flags) < 0) goto error; ctxt->node = saved_node; } @@ -6884,7 +6893,8 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, &chr_def->nseclabels, vmSeclabels, nvmSeclabels, - ctxt) < 0) { + ctxt, + flags) < 0) { ctxt->node = saved_node; goto error; } @@ -14018,14 +14028,23 @@ virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def) static void virSecurityDeviceLabelDefFormat(virBufferPtr buf, - virSecurityDeviceLabelDefPtr def) + virSecurityDeviceLabelDefPtr def, + unsigned int flags) { + /* For offline output, skip elements that allow labels but have no + * label specified (possible if labelskip was ignored on input). */ + if ((flags & VIR_DOMAIN_XML_INACTIVE) && !def->label && !def->norelabel) + return; + virBufferAddLit(buf, "<seclabel"); if (def->model) virBufferAsprintf(buf, " model='%s'", def->model); - virBufferAsprintf(buf, " relabel='%s'", def->norelabel ? "no" : "yes"); + if (def->labelskip) + virBufferAddLit(buf, " labelskip='yes'"); + else + virBufferAsprintf(buf, " relabel='%s'", def->norelabel ? "no" : "yes"); if (def->label) { virBufferAddLit(buf, ">\n"); @@ -14100,7 +14119,8 @@ virDomainDiskBlockIoDefFormat(virBufferPtr buf, static int virDomainDiskSourceDefFormat(virBufferPtr buf, - virDomainDiskDefPtr def) + virDomainDiskDefPtr def, + unsigned int flags) { int n; const char *startupPolicy = virDomainStartupPolicyTypeToString(def->startupPolicy); @@ -14119,7 +14139,8 @@ virDomainDiskSourceDefFormat(virBufferPtr buf, virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], + flags); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -14136,7 +14157,8 @@ virDomainDiskSourceDefFormat(virBufferPtr buf, virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], + flags); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -14201,7 +14223,8 @@ virDomainDiskSourceDefFormat(virBufferPtr buf, virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], + flags); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -14337,7 +14360,7 @@ virDomainDiskDefFormat(virBufferPtr buf, virBufferAddLit(buf, " </auth>\n"); } - if (virDomainDiskSourceDefFormat(buf, def) < 0) + if (virDomainDiskSourceDefFormat(buf, def, flags) < 0) return -1; virDomainDiskGeometryDefFormat(buf, def); virDomainDiskBlockIoDefFormat(buf, def); @@ -15189,7 +15212,7 @@ virDomainChrDefFormat(virBufferPtr buf, if (def->seclabels && def->nseclabels > 0) { virBufferAdjustIndent(buf, 2); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], flags); virBufferAdjustIndent(buf, -2); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3e118d6..500a5be 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -343,7 +343,8 @@ typedef virSecurityDeviceLabelDef *virSecurityDeviceLabelDefPtr; struct _virSecurityDeviceLabelDef { char *model; char *label; /* image label string */ - bool norelabel; + bool norelabel; /* true to skip label attempts */ + bool labelskip; /* live-only; true if skipping failed label attempt */ }; diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index e3dce66a..b1372e6 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1135,6 +1135,14 @@ virSecuritySELinuxRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr, if (seclabel->norelabel || (disk_seclabel && disk_seclabel->norelabel)) return 0; + /* If labelskip is true and there are no backing files, then we + * know it is safe to skip the restore. FIXME - backing files should + * be tracked in domain XML, at which point labelskip should be a + * per-file attribute instead of a disk attribute. */ + if (disk_seclabel && disk_seclabel->labelskip && + !disk->backingChain) + return 0; + /* Don't restore labels on readoly/shared disks, because * other VMs may still be accessing these * Alternatively we could iterate over all running @@ -1219,7 +1227,7 @@ virSecuritySELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk, disk_seclabel = virDomainDiskDefGenSecurityLabelDef(SECURITY_SELINUX_NAME); if (!disk_seclabel) return -1; - disk_seclabel->norelabel = true; + disk_seclabel->labelskip = true; if (VIR_APPEND_ELEMENT(disk->seclabels, disk->nseclabels, disk_seclabel) < 0) { virSecurityDeviceLabelDefFree(disk_seclabel); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args new file mode 100644 index 0000000..892c6b5 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args @@ -0,0 +1,5 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu \ +-name QEMUGuest1 -S -M pc -m 214 -smp 1 -nographic -monitor \ +unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \ +-hda /dev/HostVG/QEMUGuest1 \ +-net none -serial none -parallel none diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml new file mode 100644 index 0000000..e3bc700 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml @@ -0,0 +1,32 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'> + <seclabel model='selinux' labelskip='yes'/> + </source> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <baselabel>system_u:system_r:svirt_custom_t:s0</baselabel> + </seclabel> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args new file mode 100644 index 0000000..892c6b5 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args @@ -0,0 +1,5 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu \ +-name QEMUGuest1 -S -M pc -m 214 -smp 1 -nographic -monitor \ +unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \ +-hda /dev/HostVG/QEMUGuest1 \ +-net none -serial none -parallel none diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml new file mode 100644 index 0000000..a743448 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml @@ -0,0 +1,33 @@ +<domain type='qemu' id='1'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'> + <seclabel model='selinux' labelskip='yes'/> + </source> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> + <seclabel type='static' model='selinux' relabel='yes'> + <label>system_u:system_r:svirt_custom_t:s0:c192,c392</label> + <imagelabel>system_u:system_r:svirt_custom_t:s0:c192,c392</imagelabel> + </seclabel> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 679124e..09cc516 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -923,8 +923,10 @@ mymain(void) DO_TEST("seclabel-dynamic", QEMU_CAPS_NAME); DO_TEST("seclabel-dynamic-baselabel", QEMU_CAPS_NAME); DO_TEST("seclabel-dynamic-override", QEMU_CAPS_NAME); + DO_TEST("seclabel-dynamic-skiplabel", QEMU_CAPS_NAME); DO_TEST("seclabel-static", QEMU_CAPS_NAME); DO_TEST("seclabel-static-relabel", QEMU_CAPS_NAME); + DO_TEST("seclabel-static-skiplabel", QEMU_CAPS_NAME); DO_TEST("seclabel-none", QEMU_CAPS_NAME); DO_TEST("pseries-basic", diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml new file mode 100644 index 0000000..0764691 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml @@ -0,0 +1,31 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'> + </source> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <baselabel>system_u:system_r:svirt_custom_t:s0</baselabel> + </seclabel> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 5c6730d..68fbdc5 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -30,6 +30,7 @@ testCompareXMLToXMLFiles(const char *inxml, const char *outxml, bool live) char *actual = NULL; int ret = -1; virDomainDefPtr def = NULL; + unsigned int flags = live ? 0 : VIR_DOMAIN_XML_INACTIVE; if (virtTestLoadFile(inxml, &inXmlData) < 0) goto fail; @@ -37,11 +38,10 @@ testCompareXMLToXMLFiles(const char *inxml, const char *outxml, bool live) goto fail; if (!(def = virDomainDefParseString(inXmlData, driver.caps, driver.xmlopt, - QEMU_EXPECTED_VIRT_TYPES, - live ? 0 : VIR_DOMAIN_XML_INACTIVE))) + QEMU_EXPECTED_VIRT_TYPES, flags))) goto fail; - if (!(actual = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE))) + if (!(actual = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE | flags))) goto fail; if (STRNEQ(outXmlData, actual)) { @@ -257,7 +257,9 @@ mymain(void) DO_TEST_FULL("seclabel-dynamic-baselabel", false, WHEN_INACTIVE); DO_TEST_FULL("seclabel-dynamic-override", false, WHEN_INACTIVE); + DO_TEST_FULL("seclabel-dynamic-skiplabel", true, WHEN_INACTIVE); DO_TEST("seclabel-static"); + DO_TEST_FULL("seclabel-static-skiplabel", false, WHEN_ACTIVE); DO_TEST("seclabel-none"); DO_TEST("numad-static-vcpu-no-numatune"); DO_TEST("disk-scsi-lun-passthrough-sgio"); -- 1.8.3.1

On Mon, Aug 12, 2013 at 10:19:47PM -0600, Eric Blake wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=924153
Commit 904e05a2 (v0.9.9) added a per-<disk> seclabel element with an attribute relabel='no' in order to try and minimize the impact of shutdown delays when an NFS server disappears. The idea was that if a disk is on NFS and can't be labeled in the first place, there is no need to attempt the (no-op) relabel on domain shutdown. Unfortunately, the way this was implemented was by modifying the domain XML so that the optimization would survive libvirtd restart, but in a way that is indistinguishable from an explicit user setting. Furthermore, once the setting is turned on, libvirt avoids attempts at labeling, even for operations like snapshot or blockcopy where the chain is being extended or pivoted onto non-NFS, where SELinux labeling is once again possible. As a result, it was impossible to do a blockcopy to pivot from an NFS image file onto a local file.
The solution is to separate the semantics of a chain that must not be labeled (which the user can set even on persistent domains) vs. the optimization of not attempting a relabel on cleanup (a live-only annotation), and using only the user's explicit notation rather than the optimization as the decision on whether to skip a label attempt in the first place. When upgrading an older libvirtd to a newer, an NFS volume will still attempt the relabel; but as the avoidance of a relabel was only an optimization, this shouldn't cause any problems.
In the ideal future, libvirt will eventually have XML describing EVERY file in the backing chain, with each file having a separate <seclabel> element. At that point, libvirt will be able to track more closely which files need a relabel attempt at shutdown. But until we reach that point, the single <seclabel> for the entire <disk> chain is treated as a hint - when a chain has only one file, then we know it is accurate; but if the chain has more than one file, we have to attempt relabel in spite of the attribute, in case part of the chain is local and SELinux mattered for that portion of the chain.
* src/conf/domain_conf.h (_virSecurityDeviceLabelDef): Add new member. * src/conf/domain_conf.c (virSecurityDeviceLabelDefParseXML): Parse it, for live images only. (virSecurityDeviceLabelDefFormat): Output it. (virDomainDiskDefParseXML, virDomainChrSourceDefParseXML) (virDomainDiskSourceDefFormat, virDomainChrDefFormat) (virDomainDiskDefFormat): Pass flags on through. * src/security/security_selinux.c (virSecuritySELinuxRestoreSecurityImageLabelInt): Honor labelskip when possible. (virSecuritySELinuxSetSecurityFileLabel): Set labelskip, not norelabel, if labeling fails. * docs/formatdomain.html.in (seclabel): Document new xml. * docs/schemas/domaincommon.rng (devSeclabel): Allow it in RNG. * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.xml: * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.args: * tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-*-skiplabel.xml: New test files. * tests/qemuxml2argvtest.c (mymain): Run the new tests. * tests/qemuxml2xmltest.c (mymain): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com> ---
docs/formatdomain.html.in | 6 ++- docs/schemas/domaincommon.rng | 27 +++++++------ src/conf/domain_conf.c | 47 ++++++++++++++++------ src/conf/domain_conf.h | 3 +- src/security/security_selinux.c | 10 ++++- .../qemuxml2argv-seclabel-dynamic-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-dynamic-skiplabel.xml | 32 +++++++++++++++ .../qemuxml2argv-seclabel-static-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-static-skiplabel.xml | 33 +++++++++++++++ tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-seclabel-dynamic-skiplabel.xml | 31 ++++++++++++++ tests/qemuxml2xmltest.c | 8 ++-- 12 files changed, 178 insertions(+), 31 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml
The changes look reasonable, but I'd be alot happier if the securityselinuxlabeltest.c was covering this scenario. We already have that test using an LD_PRELOAD hack to mock the selinux APIs. It ought to be possible to extend it to return the same errno conditions you'd see on NFS, when given certain filenames, to allow this code to be validated. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 08/13/2013 08:11 AM, Daniel P. Berrange wrote:
On Mon, Aug 12, 2013 at 10:19:47PM -0600, Eric Blake wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=924153
Commit 904e05a2 (v0.9.9) added a per-<disk> seclabel element with an attribute relabel='no' in order to try and minimize the impact of shutdown delays when an NFS server disappears. The idea was that if a disk is on NFS and can't be labeled in the first place, there is no need to attempt the (no-op) relabel on domain shutdown. Unfortunately, the way this was implemented was by modifying the domain XML so that the optimization would survive libvirtd restart, but in a way that is indistinguishable from an explicit user setting. Furthermore, once the setting is turned on, libvirt avoids attempts at labeling, even for operations like snapshot or blockcopy where the chain is being extended or pivoted onto non-NFS, where SELinux labeling is once again possible. As a result, it was impossible to do a blockcopy to pivot from an NFS image file onto a local file.
The changes look reasonable, but I'd be alot happier if the securityselinuxlabeltest.c was covering this scenario. We already have that test using an LD_PRELOAD hack to mock the selinux APIs. It ought to be possible to extend it to return the same errno conditions you'd see on NFS, when given certain filenames, to allow this code to be validated.
Okay, I'll work on a followup patch to do that. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Daniel Berrange (correctly) pointed out that we should do a better job of testing selinux labeling fallbacks on NFS disks that lack labeling support. * tests/securityselinuxhelper.c (includes): Makefile already guaranteed xattr support. Add additional headers. (init_syms): New function, borrowing from vircgroupmock.c. (setfilecon_raw, getfilecon_raw): Fake NFS failure. (statfs): Fake an NFS mount point. (security_getenforce, security_get_boolean_active): Don't let host environment affect test. * tests/securityselinuxlabeldata/nfs.data: New file. * tests/securityselinuxlabeldata/nfs.xml: New file. * tests/securityselinuxlabeltest.c (testSELinuxCreateDisks) (testSELinuxDeleteDisks): Setup and cleanup for fake NFS mount. (testSELinuxCheckLabels): Test handling of SELinux NFS denial. Fix memory leak. (testSELinuxLabeling): Avoid infinite loop on dirty tree. (mymain): Add new test. --- tests/securityselinuxhelper.c | 84 ++++++++++++++++++++++++++++++---- tests/securityselinuxlabeldata/nfs.txt | 1 + tests/securityselinuxlabeldata/nfs.xml | 24 ++++++++++ tests/securityselinuxlabeltest.c | 17 +++++-- 4 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 tests/securityselinuxlabeldata/nfs.txt create mode 100644 tests/securityselinuxlabeldata/nfs.xml diff --git a/tests/securityselinuxhelper.c b/tests/securityselinuxhelper.c index a82ca6d..d7aae26 100644 --- a/tests/securityselinuxhelper.c +++ b/tests/securityselinuxhelper.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Red Hat, Inc. + * Copyright (C) 2011-2013 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,22 +19,51 @@ #include <config.h> +/* This file is only compiled on Linux, and only if xattr support was + * detected. */ + +#include <dlfcn.h> +#include <errno.h> +#include <linux/magic.h> #include <selinux/selinux.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/vfs.h> #include <unistd.h> -#include <errno.h> -#if WITH_ATTR -# include <attr/xattr.h> -#endif +#include <attr/xattr.h> #include "virstring.h" +static int (*realstatfs)(const char *path, struct statfs *buf); +static int (*realsecurity_get_boolean_active)(const char *name); + +static void init_syms(void) +{ + if (realstatfs) + return; + +# define LOAD_SYM(name) \ + do { \ + if (!(real ## name = dlsym(RTLD_NEXT, #name))) { \ + fprintf(stderr, "Cannot find real '%s' symbol\n", #name); \ + abort(); \ + } \ + } while (0) + + LOAD_SYM(statfs); + LOAD_SYM(security_get_boolean_active); +} + + /* * The kernel policy will not allow us to arbitrarily change * test process context. This helper is used as an LD_PRELOAD * so that the libvirt code /thinks/ it is changing/reading - * the process context, where as in fact we're faking it all + * the process context, where as in fact we're faking it all. + * Furthermore, we fake out that we are using an nfs subdirectory, + * where we control whether selinux is enforcing and whether + * the virt_use_nfs bool is set. */ int getcon_raw(security_context_t *context) @@ -83,10 +112,13 @@ int setcon(security_context_t context) } -#if WITH_ATTR int setfilecon_raw(const char *path, security_context_t con) { const char *constr = con; + if (STRPREFIX(path, abs_builddir "/securityselinuxlabeldata/nfs/")) { + errno = EOPNOTSUPP; + return -1; + } return setxattr(path, "user.libvirt.selinux", constr, strlen(constr), 0); } @@ -101,6 +133,10 @@ int getfilecon_raw(const char *path, security_context_t *con) char *constr = NULL; ssize_t len = getxattr(path, "user.libvirt.selinux", NULL, 0); + if (STRPREFIX(path, abs_builddir "/securityselinuxlabeldata/nfs/")) { + errno = EOPNOTSUPP; + return -1; + } if (len < 0) return -1; if (!(constr = malloc(len+1))) @@ -114,8 +150,40 @@ int getfilecon_raw(const char *path, security_context_t *con) constr[len] = '\0'; return 0; } + + int getfilecon(const char *path, security_context_t *con) { return getfilecon_raw(path, con); } -#endif + + +int statfs(const char *path, struct statfs *buf) +{ + int ret; + + init_syms(); + + ret = realstatfs(path, buf); + if (!ret && STREQ(path, abs_builddir "/securityselinuxlabeldata/nfs")) + buf->f_type = NFS_SUPER_MAGIC; + return ret; +} + + +int security_getenforce(void) +{ + /* For the purpose of our test, we are enforcing. */ + return 1; +} + + +int security_get_boolean_active(const char *name) +{ + /* For the purpose of our test, nfs is not permitted. */ + if (STREQ(name, "virt_use_nfs")) + return 0; + + init_syms(); + return realsecurity_get_boolean_active(name); +} diff --git a/tests/securityselinuxlabeldata/nfs.txt b/tests/securityselinuxlabeldata/nfs.txt new file mode 100644 index 0000000..4c1698e --- /dev/null +++ b/tests/securityselinuxlabeldata/nfs.txt @@ -0,0 +1 @@ +/nfs/plain.raw;EOPNOTSUPP diff --git a/tests/securityselinuxlabeldata/nfs.xml b/tests/securityselinuxlabeldata/nfs.xml new file mode 100644 index 0000000..46a1440 --- /dev/null +++ b/tests/securityselinuxlabeldata/nfs.xml @@ -0,0 +1,24 @@ +<domain type='kvm'> + <name>vm1</name> + <uuid>c7b3edbd-edaf-9455-926a-d65c16db1800</uuid> + <memory unit='KiB'>219200</memory> + <os> + <type arch='i686' machine='pc-1.0'>hvm</type> + <boot dev='cdrom'/> + </os> + <devices> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='/nfs/plain.raw'/> + <target dev='vda' bus='virtio'/> + </disk> + <input type='mouse' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'> + <listen type='address' address='0.0.0.0'/> + </graphics> + </devices> + <seclabel model="selinux" type="dynamic" relabel="yes"> + <label>system_u:system_r:svirt_t:s0:c41,c264</label> + <imagelabel>system_u:object_r:svirt_image_t:s0:c41,c264</imagelabel> + </seclabel> +</domain> diff --git a/tests/securityselinuxlabeltest.c b/tests/securityselinuxlabeltest.c index efe825a..fa99f99 100644 --- a/tests/securityselinuxlabeltest.c +++ b/tests/securityselinuxlabeltest.c @@ -209,7 +209,7 @@ testSELinuxCreateDisks(testSELinuxFile *files, size_t nfiles) { size_t i; - if (virFileMakePath(abs_builddir "/securityselinuxlabeldata") < 0) + if (virFileMakePath(abs_builddir "/securityselinuxlabeldata/nfs") < 0) return -1; for (i = 0; i < nfiles; i++) { @@ -228,6 +228,10 @@ testSELinuxDeleteDisks(testSELinuxFile *files, size_t nfiles) if (unlink(files[i].file) < 0) return -1; } + if (rmdir(abs_builddir "/securityselinuxlabeldata/nfs") < 0) + return -1; + /* Ignore failure to remove non-empty directory with in-tree build */ + rmdir(abs_builddir "/securityselinuxlabeldata"); return 0; } @@ -238,9 +242,13 @@ testSELinuxCheckLabels(testSELinuxFile *files, size_t nfiles) security_context_t ctx; for (i = 0; i < nfiles; i++) { + ctx = NULL; if (getfilecon(files[i].file, &ctx) < 0) { if (errno == ENODATA) { - ctx = NULL; + /* nothing to do */ + } else if (errno == EOPNOTSUPP) { + if (VIR_STRDUP(ctx, "EOPNOTSUPP") < 0) + return -1; } else { virReportSystemError(errno, "Cannot read label on %s", @@ -252,8 +260,10 @@ testSELinuxCheckLabels(testSELinuxFile *files, size_t nfiles) virReportError(VIR_ERR_INTERNAL_ERROR, "File %s context '%s' did not match epected '%s'", files[i].file, ctx, files[i].context); + VIR_FREE(ctx); return -1; } + VIR_FREE(ctx); } return 0; } @@ -287,7 +297,7 @@ testSELinuxLabeling(const void *opaque) cleanup: if (testSELinuxDeleteDisks(files, nfiles) < 0) - goto cleanup; + VIR_WARN("unable to fully clean up"); virDomainDefFree(def); for (i = 0; i < nfiles; i++) { @@ -334,6 +344,7 @@ mymain(void) DO_TEST_LABELING("disks"); DO_TEST_LABELING("kernel"); DO_TEST_LABELING("chardev"); + DO_TEST_LABELING("nfs"); return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } -- 1.8.3.1

Eric Blake wrote:
Daniel Berrange (correctly) pointed out that we should do a better job of testing selinux labeling fallbacks on NFS disks that lack labeling support.
* tests/securityselinuxhelper.c (includes): Makefile already guaranteed xattr support. Add additional headers. (init_syms): New function, borrowing from vircgroupmock.c. (setfilecon_raw, getfilecon_raw): Fake NFS failure. (statfs): Fake an NFS mount point. (security_getenforce, security_get_boolean_active): Don't let host environment affect test. * tests/securityselinuxlabeldata/nfs.data: New file. * tests/securityselinuxlabeldata/nfs.xml: New file. * tests/securityselinuxlabeltest.c (testSELinuxCreateDisks) (testSELinuxDeleteDisks): Setup and cleanup for fake NFS mount. (testSELinuxCheckLabels): Test handling of SELinux NFS denial. Fix memory leak. (testSELinuxLabeling): Avoid infinite loop on dirty tree. (mymain): Add new test. --- tests/securityselinuxhelper.c | 84 ++++++++++++++++++++++++++++++---- tests/securityselinuxlabeldata/nfs.txt | 1 + tests/securityselinuxlabeldata/nfs.xml | 24 ++++++++++ tests/securityselinuxlabeltest.c | 17 +++++-- 4 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 tests/securityselinuxlabeldata/nfs.txt create mode 100644 tests/securityselinuxlabeldata/nfs.xml
FYI, this is causing a build failure on a host without libattr-devel make[2]: Entering directory `/home/jfehlig/virt/upstream/libvirt/tests' CCLD libshunload.la CC securityselinuxhelper.lo securityselinuxhelper.c:34:24: fatal error: attr/xattr.h: No such file or directory compilation terminated. make[2]: *** [securityselinuxhelper.lo] Error 1 I need to go now but should have time to take a look later. Regards, Jim
diff --git a/tests/securityselinuxhelper.c b/tests/securityselinuxhelper.c index a82ca6d..d7aae26 100644 --- a/tests/securityselinuxhelper.c +++ b/tests/securityselinuxhelper.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Red Hat, Inc. + * Copyright (C) 2011-2013 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,22 +19,51 @@
#include <config.h>
+/* This file is only compiled on Linux, and only if xattr support was + * detected. */ + +#include <dlfcn.h> +#include <errno.h> +#include <linux/magic.h> #include <selinux/selinux.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/vfs.h> #include <unistd.h> -#include <errno.h> -#if WITH_ATTR -# include <attr/xattr.h> -#endif +#include <attr/xattr.h>
#include "virstring.h"
+static int (*realstatfs)(const char *path, struct statfs *buf); +static int (*realsecurity_get_boolean_active)(const char *name); + +static void init_syms(void) +{ + if (realstatfs) + return; + +# define LOAD_SYM(name) \ + do { \ + if (!(real ## name = dlsym(RTLD_NEXT, #name))) { \ + fprintf(stderr, "Cannot find real '%s' symbol\n", #name); \ + abort(); \ + } \ + } while (0) + + LOAD_SYM(statfs); + LOAD_SYM(security_get_boolean_active); +} + + /* * The kernel policy will not allow us to arbitrarily change * test process context. This helper is used as an LD_PRELOAD * so that the libvirt code /thinks/ it is changing/reading - * the process context, where as in fact we're faking it all + * the process context, where as in fact we're faking it all. + * Furthermore, we fake out that we are using an nfs subdirectory, + * where we control whether selinux is enforcing and whether + * the virt_use_nfs bool is set. */
int getcon_raw(security_context_t *context) @@ -83,10 +112,13 @@ int setcon(security_context_t context) }
-#if WITH_ATTR int setfilecon_raw(const char *path, security_context_t con) { const char *constr = con; + if (STRPREFIX(path, abs_builddir "/securityselinuxlabeldata/nfs/")) { + errno = EOPNOTSUPP; + return -1; + } return setxattr(path, "user.libvirt.selinux", constr, strlen(constr), 0); } @@ -101,6 +133,10 @@ int getfilecon_raw(const char *path, security_context_t *con) char *constr = NULL; ssize_t len = getxattr(path, "user.libvirt.selinux", NULL, 0); + if (STRPREFIX(path, abs_builddir "/securityselinuxlabeldata/nfs/")) { + errno = EOPNOTSUPP; + return -1; + } if (len < 0) return -1; if (!(constr = malloc(len+1))) @@ -114,8 +150,40 @@ int getfilecon_raw(const char *path, security_context_t *con) constr[len] = '\0'; return 0; } + + int getfilecon(const char *path, security_context_t *con) { return getfilecon_raw(path, con); } -#endif + + +int statfs(const char *path, struct statfs *buf) +{ + int ret; + + init_syms(); + + ret = realstatfs(path, buf); + if (!ret && STREQ(path, abs_builddir "/securityselinuxlabeldata/nfs")) + buf->f_type = NFS_SUPER_MAGIC; + return ret; +} + + +int security_getenforce(void) +{ + /* For the purpose of our test, we are enforcing. */ + return 1; +} + + +int security_get_boolean_active(const char *name) +{ + /* For the purpose of our test, nfs is not permitted. */ + if (STREQ(name, "virt_use_nfs")) + return 0; + + init_syms(); + return realsecurity_get_boolean_active(name); +} diff --git a/tests/securityselinuxlabeldata/nfs.txt b/tests/securityselinuxlabeldata/nfs.txt new file mode 100644 index 0000000..4c1698e --- /dev/null +++ b/tests/securityselinuxlabeldata/nfs.txt @@ -0,0 +1 @@ +/nfs/plain.raw;EOPNOTSUPP diff --git a/tests/securityselinuxlabeldata/nfs.xml b/tests/securityselinuxlabeldata/nfs.xml new file mode 100644 index 0000000..46a1440 --- /dev/null +++ b/tests/securityselinuxlabeldata/nfs.xml @@ -0,0 +1,24 @@ +<domain type='kvm'> + <name>vm1</name> + <uuid>c7b3edbd-edaf-9455-926a-d65c16db1800</uuid> + <memory unit='KiB'>219200</memory> + <os> + <type arch='i686' machine='pc-1.0'>hvm</type> + <boot dev='cdrom'/> + </os> + <devices> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='/nfs/plain.raw'/> + <target dev='vda' bus='virtio'/> + </disk> + <input type='mouse' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'> + <listen type='address' address='0.0.0.0'/> + </graphics> + </devices> + <seclabel model="selinux" type="dynamic" relabel="yes"> + <label>system_u:system_r:svirt_t:s0:c41,c264</label> + <imagelabel>system_u:object_r:svirt_image_t:s0:c41,c264</imagelabel> + </seclabel> +</domain> diff --git a/tests/securityselinuxlabeltest.c b/tests/securityselinuxlabeltest.c index efe825a..fa99f99 100644 --- a/tests/securityselinuxlabeltest.c +++ b/tests/securityselinuxlabeltest.c @@ -209,7 +209,7 @@ testSELinuxCreateDisks(testSELinuxFile *files, size_t nfiles) { size_t i;
- if (virFileMakePath(abs_builddir "/securityselinuxlabeldata") < 0) + if (virFileMakePath(abs_builddir "/securityselinuxlabeldata/nfs") < 0) return -1;
for (i = 0; i < nfiles; i++) { @@ -228,6 +228,10 @@ testSELinuxDeleteDisks(testSELinuxFile *files, size_t nfiles) if (unlink(files[i].file) < 0) return -1; } + if (rmdir(abs_builddir "/securityselinuxlabeldata/nfs") < 0) + return -1; + /* Ignore failure to remove non-empty directory with in-tree build */ + rmdir(abs_builddir "/securityselinuxlabeldata"); return 0; }
@@ -238,9 +242,13 @@ testSELinuxCheckLabels(testSELinuxFile *files, size_t nfiles) security_context_t ctx;
for (i = 0; i < nfiles; i++) { + ctx = NULL; if (getfilecon(files[i].file, &ctx) < 0) { if (errno == ENODATA) { - ctx = NULL; + /* nothing to do */ + } else if (errno == EOPNOTSUPP) { + if (VIR_STRDUP(ctx, "EOPNOTSUPP") < 0) + return -1; } else { virReportSystemError(errno, "Cannot read label on %s", @@ -252,8 +260,10 @@ testSELinuxCheckLabels(testSELinuxFile *files, size_t nfiles) virReportError(VIR_ERR_INTERNAL_ERROR, "File %s context '%s' did not match epected '%s'", files[i].file, ctx, files[i].context); + VIR_FREE(ctx); return -1; } + VIR_FREE(ctx); } return 0; } @@ -287,7 +297,7 @@ testSELinuxLabeling(const void *opaque)
cleanup: if (testSELinuxDeleteDisks(files, nfiles) < 0) - goto cleanup; + VIR_WARN("unable to fully clean up");
virDomainDefFree(def); for (i = 0; i < nfiles; i++) { @@ -334,6 +344,7 @@ mymain(void) DO_TEST_LABELING("disks"); DO_TEST_LABELING("kernel"); DO_TEST_LABELING("chardev"); + DO_TEST_LABELING("nfs");
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }

On 08/20/2013 04:58 PM, Jim Fehlig wrote:
Eric Blake wrote:
Daniel Berrange (correctly) pointed out that we should do a better job of testing selinux labeling fallbacks on NFS disks that lack labeling support.
FYI, this is causing a build failure on a host without libattr-devel
make[2]: Entering directory `/home/jfehlig/virt/upstream/libvirt/tests' CCLD libshunload.la CC securityselinuxhelper.lo securityselinuxhelper.c:34:24: fatal error: attr/xattr.h: No such file or directory compilation terminated. make[2]: *** [securityselinuxhelper.lo] Error 1
+/* This file is only compiled on Linux, and only if xattr support was + * detected. */ + +#include <dlfcn.h> +#include <errno.h> +#include <linux/magic.h> #include <selinux/selinux.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/vfs.h> #include <unistd.h> -#include <errno.h> -#if WITH_ATTR -# include <attr/xattr.h> -#endif +#include <attr/xattr.h>
Bummer - that means the makefile is not correctly avoiding the build of this file when attr/xattr.h does not exist. I don't know where the disconnect is between WITH_ATTR in the config.h setup, vs. WITH_ATTR as the automake variable used in tests/Makefile.am. I'm also out of time today, but if you don't beat me to it, I'll have a fix tomorrow. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

ping On 08/12/2013 10:19 PM, Eric Blake wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=924153
Commit 904e05a2 (v0.9.9) added a per-<disk> seclabel element with an attribute relabel='no' in order to try and minimize the impact of shutdown delays when an NFS server disappears. The idea was that if a disk is on NFS and can't be labeled in the first place, there is no need to attempt the (no-op) relabel on domain shutdown. Unfortunately, the way this was implemented was by modifying the domain XML so that the optimization would survive libvirtd restart, but in a way that is indistinguishable from an explicit user setting. Furthermore, once the setting is turned on, libvirt avoids attempts at labeling, even for operations like snapshot or blockcopy where the chain is being extended or pivoted onto non-NFS, where SELinux labeling is once again possible. As a result, it was impossible to do a blockcopy to pivot from an NFS image file onto a local file.
The solution is to separate the semantics of a chain that must not be labeled (which the user can set even on persistent domains) vs. the optimization of not attempting a relabel on cleanup (a live-only annotation), and using only the user's explicit notation rather than the optimization as the decision on whether to skip a label attempt in the first place. When upgrading an older libvirtd to a newer, an NFS volume will still attempt the relabel; but as the avoidance of a relabel was only an optimization, this shouldn't cause any problems.
In the ideal future, libvirt will eventually have XML describing EVERY file in the backing chain, with each file having a separate <seclabel> element. At that point, libvirt will be able to track more closely which files need a relabel attempt at shutdown. But until we reach that point, the single <seclabel> for the entire <disk> chain is treated as a hint - when a chain has only one file, then we know it is accurate; but if the chain has more than one file, we have to attempt relabel in spite of the attribute, in case part of the chain is local and SELinux mattered for that portion of the chain.
* src/conf/domain_conf.h (_virSecurityDeviceLabelDef): Add new member. * src/conf/domain_conf.c (virSecurityDeviceLabelDefParseXML): Parse it, for live images only. (virSecurityDeviceLabelDefFormat): Output it. (virDomainDiskDefParseXML, virDomainChrSourceDefParseXML) (virDomainDiskSourceDefFormat, virDomainChrDefFormat) (virDomainDiskDefFormat): Pass flags on through. * src/security/security_selinux.c (virSecuritySELinuxRestoreSecurityImageLabelInt): Honor labelskip when possible. (virSecuritySELinuxSetSecurityFileLabel): Set labelskip, not norelabel, if labeling fails. * docs/formatdomain.html.in (seclabel): Document new xml. * docs/schemas/domaincommon.rng (devSeclabel): Allow it in RNG. * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.xml: * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.args: * tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-*-skiplabel.xml: New test files. * tests/qemuxml2argvtest.c (mymain): Run the new tests. * tests/qemuxml2xmltest.c (mymain): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com> ---
docs/formatdomain.html.in | 6 ++- docs/schemas/domaincommon.rng | 27 +++++++------ src/conf/domain_conf.c | 47 ++++++++++++++++------ src/conf/domain_conf.h | 3 +- src/security/security_selinux.c | 10 ++++- .../qemuxml2argv-seclabel-dynamic-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-dynamic-skiplabel.xml | 32 +++++++++++++++ .../qemuxml2argv-seclabel-static-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-static-skiplabel.xml | 33 +++++++++++++++ tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-seclabel-dynamic-skiplabel.xml | 31 ++++++++++++++ tests/qemuxml2xmltest.c | 8 ++-- 12 files changed, 178 insertions(+), 31 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 83d551a..cafa03f 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -5024,7 +5024,11 @@ qemu-kvm -net nic,model=? /dev/null a <code>seclabel</code> element is attached to a specific path rather than the top-level domain assignment, only the attribute <code>relabel</code> or the - sub-element <code>label</code> are supported. + sub-element <code>label</code> are supported. Additionally, + <span class="since">since 1.1.2</span>, an output-only + element <code>labelskip</code> will be present for active + domains on disks where labeling was skipped due to the image + being on a file system that lacks security labeling. </p>
<h2><a name="examples">Example configs</a></h2> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index ac807e6..dfcd61c 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -152,34 +152,35 @@ <define name="devSeclabel"> <element name="seclabel"> <!-- A per-device seclabel override is more limited, either - relabel=no or a <label> must be present. --> + relabel=no or a <label> must be present on input; + output also can include labelskip=yes. --> + <optional> + <attribute name='model'> + <text/> + </attribute> + </optional> <choice> <group> - <optional> - <attribute name='model'> - <text/> - </attribute> - </optional> <attribute name='relabel'> <value>no</value> </attribute> </group> <group> - <optional> - <attribute name='model'> - <text/> - </attribute> - </optional> + <attribute name='labelskip'> + <value>yes</value> + </attribute> + </group> + <group> <optional> <attribute name='relabel'> <value>yes</value> </attribute> </optional> - <zeroOrMore> + <oneOrMore> <element name='label'> <text/> </element> - </zeroOrMore> + </oneOrMore> </group> </choice> </element> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7309877..759f686 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4484,7 +4484,8 @@ static int virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, size_t *nseclabels_rtn, virSecurityLabelDefPtr *vmSeclabels, - int nvmSeclabels, xmlXPathContextPtr ctxt) + int nvmSeclabels, xmlXPathContextPtr ctxt, + unsigned int flags) { virSecurityDeviceLabelDefPtr *seclabels; size_t nseclabels = 0; @@ -4492,7 +4493,7 @@ virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, size_t i, j; xmlNodePtr *list = NULL; virSecurityLabelDefPtr vmDef = NULL; - char *model, *relabel, *label; + char *model, *relabel, *label, *labelskip;
if ((n = virXPathNodeSet("./seclabel", ctxt, &list)) < 0) goto error; @@ -4547,6 +4548,13 @@ virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, seclabels[i]->norelabel = false; }
+ /* labelskip is only parsed on live images */ + labelskip = virXMLPropString(list[i], "labelskip"); + seclabels[i]->labelskip = false; + if (labelskip && !(flags & VIR_DOMAIN_XML_INACTIVE)) + seclabels[i]->labelskip = STREQ(labelskip, "yes"); + VIR_FREE(labelskip); + ctxt->node = list[i]; label = virXPathStringLimit("string(./label)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); @@ -5208,7 +5216,8 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt, &def->nseclabels, vmSeclabels, nvmSeclabels, - ctxt) < 0) + ctxt, + flags) < 0) goto error; ctxt->node = saved_node; } @@ -6884,7 +6893,8 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, &chr_def->nseclabels, vmSeclabels, nvmSeclabels, - ctxt) < 0) { + ctxt, + flags) < 0) { ctxt->node = saved_node; goto error; } @@ -14018,14 +14028,23 @@ virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def)
static void virSecurityDeviceLabelDefFormat(virBufferPtr buf, - virSecurityDeviceLabelDefPtr def) + virSecurityDeviceLabelDefPtr def, + unsigned int flags) { + /* For offline output, skip elements that allow labels but have no + * label specified (possible if labelskip was ignored on input). */ + if ((flags & VIR_DOMAIN_XML_INACTIVE) && !def->label && !def->norelabel) + return; + virBufferAddLit(buf, "<seclabel");
if (def->model) virBufferAsprintf(buf, " model='%s'", def->model);
- virBufferAsprintf(buf, " relabel='%s'", def->norelabel ? "no" : "yes"); + if (def->labelskip) + virBufferAddLit(buf, " labelskip='yes'"); + else + virBufferAsprintf(buf, " relabel='%s'", def->norelabel ? "no" : "yes");
if (def->label) { virBufferAddLit(buf, ">\n"); @@ -14100,7 +14119,8 @@ virDomainDiskBlockIoDefFormat(virBufferPtr buf,
static int virDomainDiskSourceDefFormat(virBufferPtr buf, - virDomainDiskDefPtr def) + virDomainDiskDefPtr def, + unsigned int flags) { int n; const char *startupPolicy = virDomainStartupPolicyTypeToString(def->startupPolicy); @@ -14119,7 +14139,8 @@ virDomainDiskSourceDefFormat(virBufferPtr buf, virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], + flags); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -14136,7 +14157,8 @@ virDomainDiskSourceDefFormat(virBufferPtr buf, virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], + flags); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -14201,7 +14223,8 @@ virDomainDiskSourceDefFormat(virBufferPtr buf, virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 8); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], + flags); virBufferAdjustIndent(buf, -8); virBufferAddLit(buf, " </source>\n"); } else { @@ -14337,7 +14360,7 @@ virDomainDiskDefFormat(virBufferPtr buf, virBufferAddLit(buf, " </auth>\n"); }
- if (virDomainDiskSourceDefFormat(buf, def) < 0) + if (virDomainDiskSourceDefFormat(buf, def, flags) < 0) return -1; virDomainDiskGeometryDefFormat(buf, def); virDomainDiskBlockIoDefFormat(buf, def); @@ -15189,7 +15212,7 @@ virDomainChrDefFormat(virBufferPtr buf, if (def->seclabels && def->nseclabels > 0) { virBufferAdjustIndent(buf, 2); for (n = 0; n < def->nseclabels; n++) - virSecurityDeviceLabelDefFormat(buf, def->seclabels[n]); + virSecurityDeviceLabelDefFormat(buf, def->seclabels[n], flags); virBufferAdjustIndent(buf, -2); }
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3e118d6..500a5be 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -343,7 +343,8 @@ typedef virSecurityDeviceLabelDef *virSecurityDeviceLabelDefPtr; struct _virSecurityDeviceLabelDef { char *model; char *label; /* image label string */ - bool norelabel; + bool norelabel; /* true to skip label attempts */ + bool labelskip; /* live-only; true if skipping failed label attempt */ };
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index e3dce66a..b1372e6 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1135,6 +1135,14 @@ virSecuritySELinuxRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr, if (seclabel->norelabel || (disk_seclabel && disk_seclabel->norelabel)) return 0;
+ /* If labelskip is true and there are no backing files, then we + * know it is safe to skip the restore. FIXME - backing files should + * be tracked in domain XML, at which point labelskip should be a + * per-file attribute instead of a disk attribute. */ + if (disk_seclabel && disk_seclabel->labelskip && + !disk->backingChain) + return 0; + /* Don't restore labels on readoly/shared disks, because * other VMs may still be accessing these * Alternatively we could iterate over all running @@ -1219,7 +1227,7 @@ virSecuritySELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk, disk_seclabel = virDomainDiskDefGenSecurityLabelDef(SECURITY_SELINUX_NAME); if (!disk_seclabel) return -1; - disk_seclabel->norelabel = true; + disk_seclabel->labelskip = true; if (VIR_APPEND_ELEMENT(disk->seclabels, disk->nseclabels, disk_seclabel) < 0) { virSecurityDeviceLabelDefFree(disk_seclabel); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args new file mode 100644 index 0000000..892c6b5 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args @@ -0,0 +1,5 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu \ +-name QEMUGuest1 -S -M pc -m 214 -smp 1 -nographic -monitor \ +unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \ +-hda /dev/HostVG/QEMUGuest1 \ +-net none -serial none -parallel none diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml new file mode 100644 index 0000000..e3bc700 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml @@ -0,0 +1,32 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'> + <seclabel model='selinux' labelskip='yes'/> + </source> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <baselabel>system_u:system_r:svirt_custom_t:s0</baselabel> + </seclabel> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args new file mode 100644 index 0000000..892c6b5 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args @@ -0,0 +1,5 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu \ +-name QEMUGuest1 -S -M pc -m 214 -smp 1 -nographic -monitor \ +unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \ +-hda /dev/HostVG/QEMUGuest1 \ +-net none -serial none -parallel none diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml new file mode 100644 index 0000000..a743448 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml @@ -0,0 +1,33 @@ +<domain type='qemu' id='1'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'> + <seclabel model='selinux' labelskip='yes'/> + </source> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> + <seclabel type='static' model='selinux' relabel='yes'> + <label>system_u:system_r:svirt_custom_t:s0:c192,c392</label> + <imagelabel>system_u:system_r:svirt_custom_t:s0:c192,c392</imagelabel> + </seclabel> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 679124e..09cc516 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -923,8 +923,10 @@ mymain(void) DO_TEST("seclabel-dynamic", QEMU_CAPS_NAME); DO_TEST("seclabel-dynamic-baselabel", QEMU_CAPS_NAME); DO_TEST("seclabel-dynamic-override", QEMU_CAPS_NAME); + DO_TEST("seclabel-dynamic-skiplabel", QEMU_CAPS_NAME); DO_TEST("seclabel-static", QEMU_CAPS_NAME); DO_TEST("seclabel-static-relabel", QEMU_CAPS_NAME); + DO_TEST("seclabel-static-skiplabel", QEMU_CAPS_NAME); DO_TEST("seclabel-none", QEMU_CAPS_NAME);
DO_TEST("pseries-basic", diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml new file mode 100644 index 0000000..0764691 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml @@ -0,0 +1,31 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'> + </source> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <baselabel>system_u:system_r:svirt_custom_t:s0</baselabel> + </seclabel> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 5c6730d..68fbdc5 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -30,6 +30,7 @@ testCompareXMLToXMLFiles(const char *inxml, const char *outxml, bool live) char *actual = NULL; int ret = -1; virDomainDefPtr def = NULL; + unsigned int flags = live ? 0 : VIR_DOMAIN_XML_INACTIVE;
if (virtTestLoadFile(inxml, &inXmlData) < 0) goto fail; @@ -37,11 +38,10 @@ testCompareXMLToXMLFiles(const char *inxml, const char *outxml, bool live) goto fail;
if (!(def = virDomainDefParseString(inXmlData, driver.caps, driver.xmlopt, - QEMU_EXPECTED_VIRT_TYPES, - live ? 0 : VIR_DOMAIN_XML_INACTIVE))) + QEMU_EXPECTED_VIRT_TYPES, flags))) goto fail;
- if (!(actual = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE))) + if (!(actual = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE | flags))) goto fail;
if (STRNEQ(outXmlData, actual)) { @@ -257,7 +257,9 @@ mymain(void)
DO_TEST_FULL("seclabel-dynamic-baselabel", false, WHEN_INACTIVE); DO_TEST_FULL("seclabel-dynamic-override", false, WHEN_INACTIVE); + DO_TEST_FULL("seclabel-dynamic-skiplabel", true, WHEN_INACTIVE); DO_TEST("seclabel-static"); + DO_TEST_FULL("seclabel-static-skiplabel", false, WHEN_ACTIVE); DO_TEST("seclabel-none"); DO_TEST("numad-static-vcpu-no-numatune"); DO_TEST("disk-scsi-lun-passthrough-sgio");
-- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Mon, Aug 12, 2013 at 10:19:47PM -0600, Eric Blake wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=924153
Commit 904e05a2 (v0.9.9) added a per-<disk> seclabel element with an attribute relabel='no' in order to try and minimize the impact of shutdown delays when an NFS server disappears. The idea was that if a disk is on NFS and can't be labeled in the first place, there is no need to attempt the (no-op) relabel on domain shutdown. Unfortunately, the way this was implemented was by modifying the domain XML so that the optimization would survive libvirtd restart, but in a way that is indistinguishable from an explicit user setting. Furthermore, once the setting is turned on, libvirt avoids attempts at labeling, even for operations like snapshot or blockcopy where the chain is being extended or pivoted onto non-NFS, where SELinux labeling is once again possible. As a result, it was impossible to do a blockcopy to pivot from an NFS image file onto a local file.
The solution is to separate the semantics of a chain that must not be labeled (which the user can set even on persistent domains) vs. the optimization of not attempting a relabel on cleanup (a live-only annotation), and using only the user's explicit notation rather than the optimization as the decision on whether to skip a label attempt in the first place. When upgrading an older libvirtd to a newer, an NFS volume will still attempt the relabel; but as the avoidance of a relabel was only an optimization, this shouldn't cause any problems.
In the ideal future, libvirt will eventually have XML describing EVERY file in the backing chain, with each file having a separate <seclabel> element. At that point, libvirt will be able to track more closely which files need a relabel attempt at shutdown. But until we reach that point, the single <seclabel> for the entire <disk> chain is treated as a hint - when a chain has only one file, then we know it is accurate; but if the chain has more than one file, we have to attempt relabel in spite of the attribute, in case part of the chain is local and SELinux mattered for that portion of the chain.
* src/conf/domain_conf.h (_virSecurityDeviceLabelDef): Add new member. * src/conf/domain_conf.c (virSecurityDeviceLabelDefParseXML): Parse it, for live images only. (virSecurityDeviceLabelDefFormat): Output it. (virDomainDiskDefParseXML, virDomainChrSourceDefParseXML) (virDomainDiskSourceDefFormat, virDomainChrDefFormat) (virDomainDiskDefFormat): Pass flags on through. * src/security/security_selinux.c (virSecuritySELinuxRestoreSecurityImageLabelInt): Honor labelskip when possible. (virSecuritySELinuxSetSecurityFileLabel): Set labelskip, not norelabel, if labeling fails. * docs/formatdomain.html.in (seclabel): Document new xml. * docs/schemas/domaincommon.rng (devSeclabel): Allow it in RNG. * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.xml: * tests/qemuxml2argvdata/qemuxml2argv-seclabel-*-skiplabel.args: * tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-*-skiplabel.xml: New test files. * tests/qemuxml2argvtest.c (mymain): Run the new tests. * tests/qemuxml2xmltest.c (mymain): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com> ---
docs/formatdomain.html.in | 6 ++- docs/schemas/domaincommon.rng | 27 +++++++------ src/conf/domain_conf.c | 47 ++++++++++++++++------ src/conf/domain_conf.h | 3 +- src/security/security_selinux.c | 10 ++++- .../qemuxml2argv-seclabel-dynamic-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-dynamic-skiplabel.xml | 32 +++++++++++++++ .../qemuxml2argv-seclabel-static-skiplabel.args | 5 +++ .../qemuxml2argv-seclabel-static-skiplabel.xml | 33 +++++++++++++++ tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-seclabel-dynamic-skiplabel.xml | 31 ++++++++++++++ tests/qemuxml2xmltest.c | 8 ++-- 12 files changed, 178 insertions(+), 31 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-skiplabel.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-static-skiplabel.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-seclabel-dynamic-skiplabel.xml
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 :|

On 08/20/2013 07:04 AM, Daniel P. Berrange wrote:
On Mon, Aug 12, 2013 at 10:19:47PM -0600, Eric Blake wrote:
The solution is to separate the semantics of a chain that must not be labeled (which the user can set even on persistent domains) vs. the optimization of not attempting a relabel on cleanup (a live-only annotation), and using only the user's explicit notation rather than the optimization as the decision on whether to skip a label attempt in the first place. When upgrading an older libvirtd to a newer, an NFS volume will still attempt the relabel; but as the avoidance of a relabel was only an optimization, this shouldn't cause any problems.
ACK
I've pushed this and the followup testsuite securityselinuxlabeltest enhancement. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
participants (3)
-
Daniel P. Berrange
-
Eric Blake
-
Jim Fehlig