libvirt/tests/qemusecuritytest.c

267 lines
8.1 KiB
C
Raw Normal View History

/*
* Copyright (C) 2018 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "qemusecuritytest.h"
#include "testutils.h"
#include "testutilsqemu.h"
#include "security/security_manager.h"
#include "security/security_util.h"
#include "conf/domain_conf.h"
#include "qemu/qemu_domain.h"
#include "qemu/qemu_security.h"
#define VIR_FROM_THIS VIR_FROM_NONE
struct testData {
virQEMUDriver *driver;
const char *file; /* file name to load VM def XML from; qemuxml2argvdata/ */
};
static int
prepareObjects(virQEMUDriver *driver,
const char *xmlname,
virDomainObj **vm_ret)
{
qemuDomainObjPrivate *priv;
g_autoptr(virDomainObj) vm = NULL;
g_autofree char *filename = NULL;
g_autofree char *domxml = NULL;
g_autofree char *latestCapsFile = NULL;
filename = g_strdup_printf("%s/qemuxml2argvdata/%s.xml", abs_srcdir, xmlname);
if (virTestLoadFile(filename, &domxml) < 0)
return -1;
if (!(vm = virDomainObjNew(driver->xmlopt)))
return -1;
priv = vm->privateData;
priv->chardevStdioLogd = false;
priv->rememberOwner = true;
if (!(latestCapsFile = testQemuGetLatestCapsForArch("x86_64", "xml")))
return -1;
if (!(priv->qemuCaps = qemuTestParseCapabilitiesArch(VIR_ARCH_X86_64, latestCapsFile)))
return -1;
virFileCacheClear(driver->qemuCapsCache);
if (qemuTestCapsCacheInsert(driver->qemuCapsCache, priv->qemuCaps) < 0)
return -1;
if (!(vm->def = virDomainDefParseString(domxml,
driver->xmlopt,
NULL,
0)))
return -1;
if (virSecurityManagerGenLabel(driver->securityManager, vm->def) < 0)
return -1;
*vm_ret = g_steal_pointer(&vm);
return 0;
}
static int
testDomain(const void *opaque)
{
const struct testData *data = opaque;
g_autoptr(virDomainObj) vm = NULL;
g_autoptr(GHashTable) notRestored = virHashNew(NULL);
security: Don't remember owner for shared resources This effectively reverts d7420430ce6 and adds new code. Here is the problem: Imagine a file X that is to be shared between two domains as a disk. Let the first domain (vm1) have seclabel remembering turned on and the other (vm2) has it turned off. Assume that both domains will run under the same user, but the original owner of X is different (i.e. trying to access X without relabelling leads to EPERM). Let's start vm1 first. This will cause X to be relabelled and to gain new attributes: trusted.libvirt.security.ref_dac="1" trusted.libvirt.security.dac="$originalOwner" When vm2 is started, X will again be relabelled, but since the new label is the same as X already has (because of vm1) nothing changes and vm1 and vm2 can access X just fine. Note that no XATTR is changed (especially the refcounter keeps its value of 1) because the vm2 domain has the feature turned off. Now, vm1 is shut off and vm2 continues running. In seclabel restore process we would get to X and since its refcounter is 1 we would restore the $originalOwner on it. But this is unsafe to do because vm2 is still using X (remember the assumption that $originalOwner and vm2's seclabel are distinct?). The problem is that refcounter stored in XATTRs doesn't reflect the actual times a resource is in use. Since I don't see any easy way around it let's just not store original owner on shared resources. Shared resource in world of domain disks is: - whole backing chain but the top layer, - read only disk (we don't require CDROM to be explicitly marked as shareable), - disk marked as shareable. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2019-02-15 10:37:03 +00:00
size_t i;
int ret = -1;
if (prepareObjects(data->driver, data->file, &vm) < 0)
return -1;
security: Don't remember owner for shared resources This effectively reverts d7420430ce6 and adds new code. Here is the problem: Imagine a file X that is to be shared between two domains as a disk. Let the first domain (vm1) have seclabel remembering turned on and the other (vm2) has it turned off. Assume that both domains will run under the same user, but the original owner of X is different (i.e. trying to access X without relabelling leads to EPERM). Let's start vm1 first. This will cause X to be relabelled and to gain new attributes: trusted.libvirt.security.ref_dac="1" trusted.libvirt.security.dac="$originalOwner" When vm2 is started, X will again be relabelled, but since the new label is the same as X already has (because of vm1) nothing changes and vm1 and vm2 can access X just fine. Note that no XATTR is changed (especially the refcounter keeps its value of 1) because the vm2 domain has the feature turned off. Now, vm1 is shut off and vm2 continues running. In seclabel restore process we would get to X and since its refcounter is 1 we would restore the $originalOwner on it. But this is unsafe to do because vm2 is still using X (remember the assumption that $originalOwner and vm2's seclabel are distinct?). The problem is that refcounter stored in XATTRs doesn't reflect the actual times a resource is in use. Since I don't see any easy way around it let's just not store original owner on shared resources. Shared resource in world of domain disks is: - whole backing chain but the top layer, - read only disk (we don't require CDROM to be explicitly marked as shareable), - disk marked as shareable. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2019-02-15 10:37:03 +00:00
for (i = 0; i < vm->def->ndisks; i++) {
virStorageSource *src = vm->def->disks[i]->src;
virStorageSource *n;
security: Don't remember owner for shared resources This effectively reverts d7420430ce6 and adds new code. Here is the problem: Imagine a file X that is to be shared between two domains as a disk. Let the first domain (vm1) have seclabel remembering turned on and the other (vm2) has it turned off. Assume that both domains will run under the same user, but the original owner of X is different (i.e. trying to access X without relabelling leads to EPERM). Let's start vm1 first. This will cause X to be relabelled and to gain new attributes: trusted.libvirt.security.ref_dac="1" trusted.libvirt.security.dac="$originalOwner" When vm2 is started, X will again be relabelled, but since the new label is the same as X already has (because of vm1) nothing changes and vm1 and vm2 can access X just fine. Note that no XATTR is changed (especially the refcounter keeps its value of 1) because the vm2 domain has the feature turned off. Now, vm1 is shut off and vm2 continues running. In seclabel restore process we would get to X and since its refcounter is 1 we would restore the $originalOwner on it. But this is unsafe to do because vm2 is still using X (remember the assumption that $originalOwner and vm2's seclabel are distinct?). The problem is that refcounter stored in XATTRs doesn't reflect the actual times a resource is in use. Since I don't see any easy way around it let's just not store original owner on shared resources. Shared resource in world of domain disks is: - whole backing chain but the top layer, - read only disk (we don't require CDROM to be explicitly marked as shareable), - disk marked as shareable. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2019-02-15 10:37:03 +00:00
if (!src)
continue;
if (virStorageSourceIsLocalStorage(src) && src->path &&
(src->shared || src->readonly))
g_hash_table_insert(notRestored, g_strdup(src->path), NULL);
security: Don't remember owner for shared resources This effectively reverts d7420430ce6 and adds new code. Here is the problem: Imagine a file X that is to be shared between two domains as a disk. Let the first domain (vm1) have seclabel remembering turned on and the other (vm2) has it turned off. Assume that both domains will run under the same user, but the original owner of X is different (i.e. trying to access X without relabelling leads to EPERM). Let's start vm1 first. This will cause X to be relabelled and to gain new attributes: trusted.libvirt.security.ref_dac="1" trusted.libvirt.security.dac="$originalOwner" When vm2 is started, X will again be relabelled, but since the new label is the same as X already has (because of vm1) nothing changes and vm1 and vm2 can access X just fine. Note that no XATTR is changed (especially the refcounter keeps its value of 1) because the vm2 domain has the feature turned off. Now, vm1 is shut off and vm2 continues running. In seclabel restore process we would get to X and since its refcounter is 1 we would restore the $originalOwner on it. But this is unsafe to do because vm2 is still using X (remember the assumption that $originalOwner and vm2's seclabel are distinct?). The problem is that refcounter stored in XATTRs doesn't reflect the actual times a resource is in use. Since I don't see any easy way around it let's just not store original owner on shared resources. Shared resource in world of domain disks is: - whole backing chain but the top layer, - read only disk (we don't require CDROM to be explicitly marked as shareable), - disk marked as shareable. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2019-02-15 10:37:03 +00:00
for (n = src->backingStore; virStorageSourceIsBacking(n); n = n->backingStore) {
if (virStorageSourceIsLocalStorage(n) && n->path)
g_hash_table_insert(notRestored, g_strdup(n->path), NULL);
security: Don't remember owner for shared resources This effectively reverts d7420430ce6 and adds new code. Here is the problem: Imagine a file X that is to be shared between two domains as a disk. Let the first domain (vm1) have seclabel remembering turned on and the other (vm2) has it turned off. Assume that both domains will run under the same user, but the original owner of X is different (i.e. trying to access X without relabelling leads to EPERM). Let's start vm1 first. This will cause X to be relabelled and to gain new attributes: trusted.libvirt.security.ref_dac="1" trusted.libvirt.security.dac="$originalOwner" When vm2 is started, X will again be relabelled, but since the new label is the same as X already has (because of vm1) nothing changes and vm1 and vm2 can access X just fine. Note that no XATTR is changed (especially the refcounter keeps its value of 1) because the vm2 domain has the feature turned off. Now, vm1 is shut off and vm2 continues running. In seclabel restore process we would get to X and since its refcounter is 1 we would restore the $originalOwner on it. But this is unsafe to do because vm2 is still using X (remember the assumption that $originalOwner and vm2's seclabel are distinct?). The problem is that refcounter stored in XATTRs doesn't reflect the actual times a resource is in use. Since I don't see any easy way around it let's just not store original owner on shared resources. Shared resource in world of domain disks is: - whole backing chain but the top layer, - read only disk (we don't require CDROM to be explicitly marked as shareable), - disk marked as shareable. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2019-02-15 10:37:03 +00:00
}
}
/* Mocking is enabled only when this env variable is set.
* See mock code for explanation. */
if (g_setenv(ENVVAR, "1", FALSE) == FALSE)
return -1;
if (qemuSecuritySetAllLabel(data->driver, vm, NULL, false) < 0)
goto cleanup;
qemuSecurityRestoreAllLabel(data->driver, vm, false);
if (checkPaths(notRestored) < 0)
goto cleanup;
ret = 0;
cleanup:
g_unsetenv(ENVVAR);
freePaths();
return ret;
}
static int
mymain(void)
{
virQEMUDriver driver;
virSecurityManager *stack = NULL;
virSecurityManager *dac = NULL;
#ifdef WITH_SELINUX
virSecurityManager *selinux = NULL;
#endif
int ret = 0;
if (!virSecurityXATTRNamespaceDefined())
return EXIT_AM_SKIP;
if (virInitialize() < 0 ||
qemuTestDriverInit(&driver) < 0)
return -1;
/* Now fix the secdriver */
virObjectUnref(driver.securityManager);
if (!(dac = virSecurityManagerNewDAC("test", 1000, 1000,
VIR_SECURITY_MANAGER_PRIVILEGED |
VIR_SECURITY_MANAGER_DYNAMIC_OWNERSHIP,
NULL))) {
fprintf(stderr, "Cannot initialize DAC security driver");
ret = -1;
goto cleanup;
}
if (!(stack = virSecurityManagerNewStack(dac))) {
fprintf(stderr, "Cannot initialize stack security driver");
ret = -1;
goto cleanup;
}
dac = NULL;
#if WITH_SELINUX
selinux = virSecurityManagerNew("selinux", "test",
VIR_SECURITY_MANAGER_PRIVILEGED |
VIR_SECURITY_MANAGER_DEFAULT_CONFINED |
VIR_SECURITY_MANAGER_REQUIRE_CONFINED);
if (!selinux) {
fprintf(stderr, "Cannot initialize selinux security driver");
ret = -1;
goto cleanup;
}
if (virSecurityManagerStackAddNested(stack, selinux) < 0) {
fprintf(stderr, "Cannot add selinux security driver onto stack");
ret = -1;
goto cleanup;
}
selinux = NULL;
#endif
driver.securityManager = g_steal_pointer(&stack);
#define DO_TEST_DOMAIN(f) \
do { \
struct testData data = {.driver = &driver, .file = f}; \
if (virTestRun(f, testDomain, &data) < 0) \
ret = -1; \
} while (0)
DO_TEST_DOMAIN("acpi-table");
DO_TEST_DOMAIN("channel-unix-guestfwd");
DO_TEST_DOMAIN("console-virtio-unix");
DO_TEST_DOMAIN("controller-virtio-scsi");
DO_TEST_DOMAIN("disk-aio");
security: Don't remember owner for shared resources This effectively reverts d7420430ce6 and adds new code. Here is the problem: Imagine a file X that is to be shared between two domains as a disk. Let the first domain (vm1) have seclabel remembering turned on and the other (vm2) has it turned off. Assume that both domains will run under the same user, but the original owner of X is different (i.e. trying to access X without relabelling leads to EPERM). Let's start vm1 first. This will cause X to be relabelled and to gain new attributes: trusted.libvirt.security.ref_dac="1" trusted.libvirt.security.dac="$originalOwner" When vm2 is started, X will again be relabelled, but since the new label is the same as X already has (because of vm1) nothing changes and vm1 and vm2 can access X just fine. Note that no XATTR is changed (especially the refcounter keeps its value of 1) because the vm2 domain has the feature turned off. Now, vm1 is shut off and vm2 continues running. In seclabel restore process we would get to X and since its refcounter is 1 we would restore the $originalOwner on it. But this is unsafe to do because vm2 is still using X (remember the assumption that $originalOwner and vm2's seclabel are distinct?). The problem is that refcounter stored in XATTRs doesn't reflect the actual times a resource is in use. Since I don't see any easy way around it let's just not store original owner on shared resources. Shared resource in world of domain disks is: - whole backing chain but the top layer, - read only disk (we don't require CDROM to be explicitly marked as shareable), - disk marked as shareable. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2019-02-15 10:37:03 +00:00
DO_TEST_DOMAIN("disk-backing-chains-noindex");
DO_TEST_DOMAIN("disk-cache");
DO_TEST_DOMAIN("disk-cdrom");
DO_TEST_DOMAIN("disk-cdrom-bus-other");
DO_TEST_DOMAIN("disk-cdrom-network");
DO_TEST_DOMAIN("disk-cdrom-tray");
DO_TEST_DOMAIN("disk-copy_on_read");
DO_TEST_DOMAIN("disk-detect-zeroes");
DO_TEST_DOMAIN("disk-error-policy");
DO_TEST_DOMAIN("disk-floppy");
DO_TEST_DOMAIN("disk-floppy-q35");
DO_TEST_DOMAIN("disk-network-gluster");
DO_TEST_DOMAIN("disk-network-iscsi");
DO_TEST_DOMAIN("disk-network-nbd");
DO_TEST_DOMAIN("disk-network-rbd");
DO_TEST_DOMAIN("disk-network-sheepdog");
DO_TEST_DOMAIN("disk-network-source-auth");
DO_TEST_DOMAIN("disk-network-tlsx509-nbd");
DO_TEST_DOMAIN("disk-network-tlsx509-vxhs");
DO_TEST_DOMAIN("disk-readonly-disk");
DO_TEST_DOMAIN("disk-scsi");
DO_TEST_DOMAIN("disk-scsi-device-auto");
DO_TEST_DOMAIN("disk-shared");
DO_TEST_DOMAIN("disk-virtio");
DO_TEST_DOMAIN("disk-virtio-scsi-reservations");
DO_TEST_DOMAIN("graphics-vnc-tls-secret");
DO_TEST_DOMAIN("hugepages-nvdimm");
DO_TEST_DOMAIN("iothreads-virtio-scsi-pci");
DO_TEST_DOMAIN("memory-hotplug-nvdimm");
DO_TEST_DOMAIN("memory-hotplug-nvdimm-access");
DO_TEST_DOMAIN("memory-hotplug-nvdimm-align");
DO_TEST_DOMAIN("memory-hotplug-nvdimm-label");
DO_TEST_DOMAIN("memory-hotplug-nvdimm-pmem");
DO_TEST_DOMAIN("memory-hotplug-nvdimm-readonly");
DO_TEST_DOMAIN("net-vhostuser");
DO_TEST_DOMAIN("firmware-auto-bios");
DO_TEST_DOMAIN("firmware-auto-efi");
DO_TEST_DOMAIN("firmware-auto-efi-loader-secure");
DO_TEST_DOMAIN("pci-bridge-many-disks");
DO_TEST_DOMAIN("tseg-explicit-size");
DO_TEST_DOMAIN("usb-redir-unix");
DO_TEST_DOMAIN("virtio-non-transitional");
DO_TEST_DOMAIN("virtio-transitional");
DO_TEST_DOMAIN("x86_64-pc-graphics");
DO_TEST_DOMAIN("x86_64-pc-headless");
DO_TEST_DOMAIN("x86_64-q35-graphics");
DO_TEST_DOMAIN("x86_64-q35-headless");
cleanup:
qemuTestDriverFree(&driver);
#ifdef WITH_SELINUX
virObjectUnref(selinux);
#endif
virObjectUnref(dac);
virObjectUnref(stack);
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIR_TEST_MAIN_PRELOAD(mymain,
VIR_TEST_MOCK("domaincaps"))