/*
* 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
* .
*/
#include
#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(virQEMUDriverPtr driver,
virDomainObjPtr vm,
const char *incomingPath,
bool migrated)
{
int ret = -1;
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerSetAllLabel(driver->securityManager,
vm->def,
incomingPath,
priv->chardevStdioLogd,
migrated) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
void
qemuSecurityRestoreAllLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
bool migrated)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
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) >= 0)
transactionStarted = true;
virSecurityManagerRestoreAllLabel(driver->securityManager,
vm->def,
migrated,
priv->chardevStdioLogd);
if (transactionStarted &&
virSecurityManagerTransactionCommit(driver->securityManager,
-1, priv->rememberOwner) < 0)
VIR_WARN("Unable to run security manager transaction");
virSecurityManagerTransactionAbort(driver->securityManager);
}
int
qemuSecuritySetImageLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virStorageSourcePtr src,
bool backingChain,
bool chainTop)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
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) < 0)
goto cleanup;
if (virSecurityManagerSetImageLabel(driver->securityManager,
vm->def, src, labelFlags) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreImageLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virStorageSourcePtr src,
bool backingChain)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
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) < 0)
goto cleanup;
if (virSecurityManagerRestoreImageLabel(driver->securityManager,
vm->def, src, labelFlags) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityMoveImageMetadata(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virStorageSourcePtr src,
virStorageSourcePtr dst)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
if (!priv->rememberOwner)
return 0;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
return virSecurityManagerMoveImageMetadata(driver->securityManager, pid, src, dst);
}
int
qemuSecuritySetHostdevLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerSetHostdevLabel(driver->securityManager,
vm->def,
hostdev,
NULL) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreHostdevLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerRestoreHostdevLabel(driver->securityManager,
vm->def,
hostdev,
NULL) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetMemoryLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainMemoryDefPtr mem)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerSetMemoryLabel(driver->securityManager,
vm->def,
mem) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreMemoryLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainMemoryDefPtr mem)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerRestoreMemoryLabel(driver->securityManager,
vm->def,
mem) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetInputLabel(virDomainObjPtr vm,
virDomainInputDefPtr input)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
virQEMUDriverPtr driver = priv->driver;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerSetInputLabel(driver->securityManager,
vm->def,
input) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreInputLabel(virDomainObjPtr vm,
virDomainInputDefPtr input)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
virQEMUDriverPtr driver = priv->driver;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerRestoreInputLabel(driver->securityManager,
vm->def,
input) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecuritySetChardevLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainChrDefPtr chr)
{
int ret = -1;
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerSetChardevLabel(driver->securityManager,
vm->def,
chr->source,
priv->chardevStdioLogd) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityRestoreChardevLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainChrDefPtr chr)
{
int ret = -1;
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerRestoreChardevLabel(driver->securityManager,
vm->def,
chr->source,
priv->chardevStdioLogd) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
/*
* qemuSecurityStartVhostUserGPU:
*
* @driver: the QEMU driver
* @vm: the domain object
* @cmd: the command to run
* @existstatus: pointer to int returning exit status of process
* @cmdret: pointer to int returning result of virCommandRun
*
* Start the vhost-user-gpu process with appropriate labels.
* This function returns -1 on security setup error, 0 if all the
* setup was done properly. In case the virCommand failed to run
* 0 is returned but cmdret is set appropriately with the process
* exitstatus also set.
*/
int
qemuSecurityStartVhostUserGPU(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virCommandPtr cmd,
int *exitstatus,
int *cmdret)
{
if (virSecurityManagerSetChildProcessLabel(driver->securityManager,
vm->def, cmd) < 0)
return -1;
if (virSecurityManagerPreFork(driver->securityManager) < 0)
return -1;
*cmdret = virCommandRun(cmd, exitstatus);
virSecurityManagerPostFork(driver->securityManager);
if (*cmdret < 0)
return -1;
return 0;
}
/*
* qemuSecurityStartTPMEmulator:
*
* @driver: the QEMU driver
* @vm: the domain object
* @cmd: the command to run
* @uid: the uid to run the emulator
* @gid: the gid to run the emulator
* @existstatus: pointer to int returning exit status of process
* @cmdret: pointer to int returning result of virCommandRun
*
* Start the TPM emulator with appropriate labels. Apply security
* labels to files first.
* This function returns -1 on security setup error, 0 if all the
* setup was done properly. In case the virCommand failed to run
* 0 is returned but cmdret is set appropriately with the process
* exitstatus also set.
*/
int
qemuSecurityStartTPMEmulator(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virCommandPtr cmd,
uid_t uid,
gid_t gid,
int *exitstatus,
int *cmdret)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
int ret = -1;
bool transactionStarted = false;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
return -1;
transactionStarted = true;
if (virSecurityManagerSetTPMLabels(driver->securityManager,
vm->def) < 0) {
virSecurityManagerTransactionAbort(driver->securityManager);
return -1;
}
if (virSecurityManagerTransactionCommit(driver->securityManager,
-1, priv->rememberOwner) < 0)
goto cleanup_abort;
transactionStarted = false;
if (qemuSecurityCommandRun(driver, vm, cmd, uid, gid, exitstatus, cmdret) < 0)
goto cleanup;
ret = 0;
if (*cmdret < 0)
goto cleanup;
return 0;
cleanup:
if (!transactionStarted &&
virSecurityManagerTransactionStart(driver->securityManager) >= 0)
transactionStarted = true;
virSecurityManagerRestoreTPMLabels(driver->securityManager, vm->def);
if (transactionStarted &&
virSecurityManagerTransactionCommit(driver->securityManager,
-1, priv->rememberOwner) < 0)
VIR_WARN("Unable to run security manager transaction");
cleanup_abort:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
void
qemuSecurityCleanupTPMEmulator(virQEMUDriverPtr driver,
virDomainObjPtr vm)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
bool transactionStarted = false;
if (virSecurityManagerTransactionStart(driver->securityManager) >= 0)
transactionStarted = true;
virSecurityManagerRestoreTPMLabels(driver->securityManager, vm->def);
if (transactionStarted &&
virSecurityManagerTransactionCommit(driver->securityManager,
-1, priv->rememberOwner) < 0)
VIR_WARN("Unable to run security manager transaction");
virSecurityManagerTransactionAbort(driver->securityManager);
}
int
qemuSecurityDomainSetPathLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
const char *path,
bool allowSubtree)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerDomainSetPathLabel(driver->securityManager,
vm->def,
path,
allowSubtree) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 0)
goto cleanup;
ret = 0;
cleanup:
virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
int
qemuSecurityDomainRestorePathLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
const char *path)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pid_t pid = -1;
int ret = -1;
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
pid = vm->pid;
if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
if (virSecurityManagerDomainRestorePathLabel(driver->securityManager,
vm->def,
path) < 0)
goto cleanup;
if (virSecurityManagerTransactionCommit(driver->securityManager,
pid, priv->rememberOwner) < 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: pointer to int returning exit status of process
* @cmdret: pointer to int returning result of virCommandRun
*
* 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.
*/
int
qemuSecurityCommandRun(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virCommandPtr cmd,
uid_t uid,
gid_t gid,
int *exitstatus,
int *cmdret)
{
if (virSecurityManagerSetChildProcessLabel(driver->securityManager,
vm->def, cmd) < 0)
return -1;
if (uid != (uid_t) -1)
virCommandSetUID(cmd, uid);
if (gid != (gid_t) -1)
virCommandSetGID(cmd, gid);
if (virSecurityManagerPreFork(driver->securityManager) < 0)
return -1;
*cmdret = virCommandRun(cmd, exitstatus);
virSecurityManagerPostFork(driver->securityManager);
return 0;
}