From: Michal Privoznik <[email protected]>

When starting a domain with a disk, or when hotplugging one, the
seclabel on corresponding file is set (the original seclabel is
recorded in XATTRs) and then on hotunplug or VM shutdown the
original seclabel is restored. This works so well, that even if
QEMU rejects hotplug seclabel of the file is the same before and
after hotplug attempt. Except for read only and shared disks. For
these restore is explicitly skipped.

Now imagine a user, trying to hotplug a disk with the source as
say /root and QEMU denying the hotplug request (this is in fact
fairly easy to achieve - hotplug a cdrom with a directory as its
source). We'd set seclabels on the directory (/root), tell QEMU
to hotplug it (or change media in a cdrom, doesn't really matter)
which fails. But there's no seclabel restore leaving the
directory with incorrect seclabels.

Check for this specific case in our DAC and SELinux secdrivers.
AppArmor is not necessary because it doesn't actually touch the
directory.

NB, one might expect qemusecuritytest to fail after this commit,
but in fact, our qemusecuritymock re-implements stat() and
pretends everything is a file. This is fixed in next commits.

Resolves: https://redhat.atlassian.net/browse/RHEL-262704
Signed-off-by: Michal Privoznik <[email protected]>
---
 src/security/security_dac.c     | 37 +++++++++++++++++++++++++++++++++
 src/security/security_selinux.c | 37 +++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index e7cffa8e59..32e3d20b6e 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -947,6 +947,43 @@ virSecurityDACSetImageLabelInternal(virSecurityManager 
*mgr,
     if (src->seclabelSkipRemember)
         remember = false;
 
+    /* If we don't remember the label, do an extra check if the
+     * storage source is actually of expected type. Ideally, if
+     * hypervisor rejects incorrect type then we'd just restore
+     * the remembered label, BUT if we do not remember it in the
+     * first place, then there's no restore. */
+    if (!remember && src->path) {
+        struct stat sb;
+
+        switch (src->type) {
+        case VIR_STORAGE_TYPE_FILE:
+        case VIR_STORAGE_TYPE_BLOCK:
+        case VIR_STORAGE_TYPE_DIR:
+        case VIR_STORAGE_TYPE_CTL:
+            if (stat(src->path, &sb) < 0) {
+                virReportSystemError(errno, _("Failed to stat %1$s"), 
src->path);
+                return -1;
+            }
+
+            if ((src->type == VIR_STORAGE_TYPE_DIR) != !!S_ISDIR(sb.st_mode)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("Invalid file type for disk source %1$s"),
+                               src->path);
+                return -1;
+            }
+            break;
+
+        case VIR_STORAGE_TYPE_NONE:
+        case VIR_STORAGE_TYPE_NETWORK:
+        case VIR_STORAGE_TYPE_VOLUME:
+        case VIR_STORAGE_TYPE_NVME:
+        case VIR_STORAGE_TYPE_VHOST_USER:
+        case VIR_STORAGE_TYPE_VHOST_VDPA:
+        case VIR_STORAGE_TYPE_LAST:
+            break;
+        }
+    }
+
     return virSecurityDACSetOwnership(mgr, src, NULL, user, group, remember);
 }
 
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 423dcd0bd9..9dc6242cfa 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -2025,6 +2025,43 @@ 
virSecuritySELinuxSetImageLabelInternal(virSecurityManager *mgr,
         if (src->seclabelSkipRemember)
             remember = false;
 
+        /* If we don't remember the label, do an extra check if the
+         * storage source is actually of expected type. Ideally, if
+         * hypervisor rejects incorrect type then we'd just restore
+         * the remembered label, BUT if we do not remember it in the
+         * first place, then there's no restore. */
+        if (!remember) {
+            struct stat sb;
+
+            switch (src->type) {
+            case VIR_STORAGE_TYPE_FILE:
+            case VIR_STORAGE_TYPE_BLOCK:
+            case VIR_STORAGE_TYPE_DIR:
+            case VIR_STORAGE_TYPE_CTL:
+                if (stat(path, &sb) < 0) {
+                    virReportSystemError(errno, _("Failed to stat %1$s"), 
path);
+                    return -1;
+                }
+
+                if ((src->type == VIR_STORAGE_TYPE_DIR) != 
!!S_ISDIR(sb.st_mode)) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("Invalid file type for disk source %1$s"),
+                                   path);
+                    return -1;
+                }
+                break;
+
+            case VIR_STORAGE_TYPE_NONE:
+            case VIR_STORAGE_TYPE_NETWORK:
+            case VIR_STORAGE_TYPE_VOLUME:
+            case VIR_STORAGE_TYPE_NVME:
+            case VIR_STORAGE_TYPE_VHOST_USER:
+            case VIR_STORAGE_TYPE_VHOST_VDPA:
+            case VIR_STORAGE_TYPE_LAST:
+                break;
+            }
+        }
+
         ret = virSecuritySELinuxSetFilecon(mgr, path, use_label, remember);
     }
 
-- 
2.56.0

Reply via email to