libvirt/src/qemu/qemu_security.c
Andrea Bolognani 7d6759135e qemu: Handle locking of TPM state directory for incoming migration
By not attempting to lock the lock file, which would fail.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
2024-10-03 14:50:06 +02:00

784 lines
25 KiB
C

/*
* qemu_security.c: QEMU security management
*
* Copyright (C) 2016 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 "qemu_domain.h"
#include "qemu_namespace.h"
#include "qemu_security.h"
#include "virlog.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_security");
int
qemuSecuritySetAllLabel(virQEMUDriver *driver,
virDomainObj *vm,
const char *incomingPath,
bool migrated)
{
int ret = -1;
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetAllLabel(driver->securityManager,
cfg->sharedFilesystems,
vm->def,
incomingPath,
priv->chardevStdioLogd,
migrated) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
void
qemuSecurityRestoreAllLabel(virQEMUDriver *driver,
virDomainObj *vm,
bool migrated)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
bool transactionStarted = false;
/* In contrast to qemuSecuritySetAllLabel, do not use vm->pid
* here. This function is called from qemuProcessStop() which
* is meant to do cleanup after qemu process died. The
* domain's namespace is gone as qemu was the only process
* running there. We would not succeed in entering the
* namespace then. */
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) >= 0)
transactionStarted = true;
virSecurityManagerRestoreAllLabel(driver->securityManager,
cfg->sharedFilesystems,
vm->def,
migrated,
priv->chardevStdioLogd);
if (transactionStarted &&
virSecurityManagerTransactionCommit(driver->securityManager,
-1, priv->rememberOwner,
false) < 0)
VIR_WARN("Unable to run security manager transaction");
virSecurityManagerTransactionAbort(driver->securityManager);
}
int
qemuSecuritySetImageLabel(virQEMUDriver *driver,
virDomainObj *vm,
virStorageSource *src,
bool backingChain,
bool chainTop)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
virSecurityDomainImageLabelFlags labelFlags = 0;
if (backingChain)
labelFlags |= VIR_SECURITY_DOMAIN_IMAGE_LABEL_BACKING_CHAIN;
if (chainTop)
labelFlags |= VIR_SECURITY_DOMAIN_IMAGE_PARENT_CHAIN_TOP;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetImageLabel(driver->securityManager,
cfg->sharedFilesystems,
vm->def, src, labelFlags) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreImageLabel(virQEMUDriver *driver,
virDomainObj *vm,
virStorageSource *src,
bool backingChain)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
virSecurityDomainImageLabelFlags labelFlags = 0;
if (backingChain)
labelFlags |= VIR_SECURITY_DOMAIN_IMAGE_LABEL_BACKING_CHAIN;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerRestoreImageLabel(driver->securityManager,
cfg->sharedFilesystems,
vm->def, src, labelFlags) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityMoveImageMetadata(virQEMUDriver *driver,
virDomainObj *vm,
virStorageSource *src,
virStorageSource *dst)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
if (!priv->rememberOwner)
return 0;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
return virSecurityManagerMoveImageMetadata(driver->securityManager,
cfg->sharedFilesystems,
pid, src, dst);
}
int
qemuSecuritySetHostdevLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainHostdevDef *hostdev)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetHostdevLabel(driver->securityManager,
vm->def,
hostdev,
NULL) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreHostdevLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainHostdevDef *hostdev)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerRestoreHostdevLabel(driver->securityManager,
vm->def,
hostdev,
NULL) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetMemoryLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainMemoryDef *mem)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetMemoryLabel(driver->securityManager,
vm->def,
mem) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreMemoryLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainMemoryDef *mem)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerRestoreMemoryLabel(driver->securityManager,
vm->def,
mem) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetInputLabel(virDomainObj *vm,
virDomainInputDef *input)
{
qemuDomainObjPrivate *priv = vm->privateData;
virQEMUDriver *driver = priv->driver;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetInputLabel(driver->securityManager,
vm->def,
input) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreInputLabel(virDomainObj *vm,
virDomainInputDef *input)
{
qemuDomainObjPrivate *priv = vm->privateData;
virQEMUDriver *driver = priv->driver;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerRestoreInputLabel(driver->securityManager,
vm->def,
input) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetChardevLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainChrDef *chr)
{
int ret = -1;
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetChardevLabel(driver->securityManager,
vm->def,
chr->source,
priv->chardevStdioLogd) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreChardevLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainChrDef *chr)
{
int ret = -1;
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerRestoreChardevLabel(driver->securityManager,
vm->def,
chr->source,
priv->chardevStdioLogd) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetNetdevLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainNetDef *net)
{
int ret = -1;
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetNetdevLabel(driver->securityManager,
vm->def, net) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreNetdevLabel(virQEMUDriver *driver,
virDomainObj *vm,
virDomainNetDef *net)
{
int ret = -1;
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerRestoreNetdevLabel(driver->securityManager,
vm->def, net) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetTPMLabels(virQEMUDriver *driver,
virDomainObj *vm,
bool setTPMStateLabel,
bool lockMetadataException)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
int ret = -1;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerSetTPMLabels(driver->securityManager,
vm->def, setTPMStateLabel) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
-1, priv->rememberOwner,
lockMetadataException) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreTPMLabels(virQEMUDriver *driver,
virDomainObj *vm,
bool restoreTPMStateLabel,
bool lockMetadataException)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
int ret = -1;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerRestoreTPMLabels(driver->securityManager,
vm->def, restoreTPMStateLabel) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
-1, priv->rememberOwner,
lockMetadataException) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityDomainSetPathLabel(virQEMUDriver *driver,
virDomainObj *vm,
const char *path,
bool allowSubtree)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerDomainSetPathLabel(driver->securityManager,
vm->def,
path,
allowSubtree) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityDomainRestorePathLabel(virQEMUDriver *driver,
virDomainObj *vm,
const char *path)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerDomainRestorePathLabel(driver->securityManager,
vm->def,
path) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner,
false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
/**
* qemuSecurityDomainSetMountNSPathLabel:
*
* Label given path in mount namespace. If mount namespace is not enabled,
* nothing is labeled at all.
*
* Because the label is only applied in mount namespace, there's no need to
* restore it.
*
* Returns 0 on success,
* 1 when mount namespace is not enabled,
* -1 on error.
*/
int
qemuSecurityDomainSetMountNSPathLabel(virQEMUDriver *driver,
virDomainObj *vm,
const char *path)
{
int ret = -1;
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT)) {
VIR_DEBUG("Not labeling '%s': mount namespace disabled for domain '%s'",
path, vm->def->name);
return 1;
}
if (virSecurityManagerTransactionStart(driver->securityManager,
cfg->sharedFilesystems) < 0)
goto cleanup;
if (virSecurityManagerDomainSetPathLabel(driver->securityManager,
vm->def, path, false) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
vm->pid, false, false) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
/**
* qemuSecurityCommandRun:
* @driver: the QEMU driver
* @vm: the domain object
* @cmd: the command to run
* @uid: the uid to force
* @gid: the gid to force
* @existstatus: optional pointer to int returning exit status of process
*
* Run @cmd with seclabels set on it. If @uid and/or @gid are not
* -1 then their value is enforced.
*
* Returns: 0 on success,
* -1 otherwise (with error reported).
*/
int
qemuSecurityCommandRun(virQEMUDriver *driver,
virDomainObj *vm,
virCommand *cmd,
uid_t uid,
gid_t gid,
bool useBinarySpecificLabel,
int *exitstatus)
{
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
qemuDomainObjPrivate *priv = vm->privateData;
int ret = -1;
if (virSecurityManagerSetChildProcessLabel(driver->securityManager,
vm->def, useBinarySpecificLabel,
cmd) < 0) {
return -1;
}
if (uid != (uid_t) -1)
virCommandSetUID(cmd, uid);
if (gid != (gid_t) -1)
virCommandSetGID(cmd, gid);
if (cfg->schedCore == QEMU_SCHED_CORE_FULL) {
pid_t pid = vm->pid;
if (pid <= 0)
pid = priv->schedCoreChildPID;
virCommandSetRunAmong(cmd, pid);
}
if (virSecurityManagerPreFork(driver->securityManager) < 0)
return -1;
ret = virCommandRun(cmd, exitstatus);
virSecurityManagerPostFork(driver->securityManager);
return ret;
}