mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-31 18:33:11 +00:00
19d74fdf0e
A TPM Proxy device can coexist with a regular TPM, but the current domain definition supports only a single TPM device in the 'tpm' pointer. This patch replaces this existing pointer in the domain definition to an array of TPM devices. All files that references the old pointer were adapted to handle the new array instead. virDomainDefParseXML() TPM related code was adapted to handle the parsing of an extra TPM device. TPM validations after this new scenario will be updated in the next patch. Tested-by: Satheesh Rajendran <sathnaga@linux.vnet.ibm.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> Signed-off-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
2627 lines
78 KiB
C
2627 lines
78 KiB
C
/*
|
|
* Copyright (C) 2010-2014 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/>.
|
|
*
|
|
* POSIX DAC security driver
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef __FreeBSD__
|
|
# include <sys/sysctl.h>
|
|
# include <sys/user.h>
|
|
#endif
|
|
|
|
#include "security_dac.h"
|
|
#include "security_util.h"
|
|
#include "virerror.h"
|
|
#include "virfile.h"
|
|
#include "viralloc.h"
|
|
#include "virlog.h"
|
|
#include "virmdev.h"
|
|
#include "virpci.h"
|
|
#include "virusb.h"
|
|
#include "virscsi.h"
|
|
#include "virscsivhost.h"
|
|
#include "virstoragefile.h"
|
|
#include "virstring.h"
|
|
#include "virutil.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_SECURITY
|
|
|
|
VIR_LOG_INIT("security.security_dac");
|
|
|
|
#define SECURITY_DAC_NAME "dac"
|
|
#define DEV_SEV "/dev/sev"
|
|
|
|
typedef struct _virSecurityDACData virSecurityDACData;
|
|
typedef virSecurityDACData *virSecurityDACDataPtr;
|
|
|
|
struct _virSecurityDACData {
|
|
uid_t user;
|
|
gid_t group;
|
|
gid_t *groups;
|
|
int ngroups;
|
|
bool dynamicOwnership;
|
|
bool mountNamespace;
|
|
char *baselabel;
|
|
virSecurityManagerDACChownCallback chownCallback;
|
|
};
|
|
|
|
typedef struct _virSecurityDACCallbackData virSecurityDACCallbackData;
|
|
typedef virSecurityDACCallbackData *virSecurityDACCallbackDataPtr;
|
|
|
|
struct _virSecurityDACCallbackData {
|
|
virSecurityManagerPtr manager;
|
|
virSecurityLabelDefPtr secdef;
|
|
};
|
|
|
|
typedef struct _virSecurityDACChownItem virSecurityDACChownItem;
|
|
typedef virSecurityDACChownItem *virSecurityDACChownItemPtr;
|
|
struct _virSecurityDACChownItem {
|
|
char *path;
|
|
const virStorageSource *src;
|
|
uid_t uid;
|
|
gid_t gid;
|
|
bool remember; /* Whether owner remembering should be done for @path/@src */
|
|
bool restore; /* Whether current operation is 'set' or 'restore' */
|
|
};
|
|
|
|
typedef struct _virSecurityDACChownList virSecurityDACChownList;
|
|
typedef virSecurityDACChownList *virSecurityDACChownListPtr;
|
|
struct _virSecurityDACChownList {
|
|
virSecurityManagerPtr manager;
|
|
virSecurityDACChownItemPtr *items;
|
|
size_t nItems;
|
|
bool lock;
|
|
};
|
|
|
|
|
|
virThreadLocal chownList;
|
|
|
|
static int
|
|
virSecurityDACChownListAppend(virSecurityDACChownListPtr list,
|
|
const char *path,
|
|
const virStorageSource *src,
|
|
uid_t uid,
|
|
gid_t gid,
|
|
bool remember,
|
|
bool restore)
|
|
{
|
|
int ret = -1;
|
|
char *tmp = NULL;
|
|
virSecurityDACChownItemPtr item = NULL;
|
|
|
|
if (VIR_ALLOC(item) < 0)
|
|
return -1;
|
|
|
|
tmp = g_strdup(path);
|
|
|
|
item->path = tmp;
|
|
item->src = src;
|
|
item->uid = uid;
|
|
item->gid = gid;
|
|
item->remember = remember;
|
|
item->restore = restore;
|
|
|
|
if (VIR_APPEND_ELEMENT(list->items, list->nItems, item) < 0)
|
|
goto cleanup;
|
|
|
|
tmp = NULL;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
VIR_FREE(tmp);
|
|
VIR_FREE(item);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
virSecurityDACChownListFree(void *opaque)
|
|
{
|
|
virSecurityDACChownListPtr list = opaque;
|
|
size_t i;
|
|
|
|
if (!list)
|
|
return;
|
|
|
|
for (i = 0; i < list->nItems; i++) {
|
|
VIR_FREE(list->items[i]->path);
|
|
VIR_FREE(list->items[i]);
|
|
}
|
|
VIR_FREE(list->items);
|
|
virObjectUnref(list->manager);
|
|
VIR_FREE(list);
|
|
}
|
|
|
|
|
|
/**
|
|
* virSecurityDACTransactionAppend:
|
|
* @path: Path to chown
|
|
* @src: disk source to chown
|
|
* @uid: user ID
|
|
* @gid: group ID
|
|
* @remember: if the original owner should be recorded/recalled
|
|
* @restore: if current operation is set or restore
|
|
*
|
|
* Appends an entry onto transaction list.
|
|
* The @remember should be true if caller wishes to record/recall
|
|
* the original owner of @path/@src.
|
|
* The @restore should be true if the operation is restoring
|
|
* seclabel and false otherwise.
|
|
*
|
|
* Returns: 1 in case of successful append
|
|
* 0 if there is no transaction enabled
|
|
* -1 otherwise.
|
|
*/
|
|
static int
|
|
virSecurityDACTransactionAppend(const char *path,
|
|
const virStorageSource *src,
|
|
uid_t uid,
|
|
gid_t gid,
|
|
bool remember,
|
|
bool restore)
|
|
{
|
|
virSecurityDACChownListPtr list = virThreadLocalGet(&chownList);
|
|
if (!list)
|
|
return 0;
|
|
|
|
if (virSecurityDACChownListAppend(list, path, src,
|
|
uid, gid, remember, restore) < 0)
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
|
|
const virStorageSource *src,
|
|
const char *path,
|
|
uid_t uid,
|
|
gid_t gid,
|
|
bool remember);
|
|
|
|
static int virSecurityDACRestoreFileLabelInternal(virSecurityManagerPtr mgr,
|
|
const virStorageSource *src,
|
|
const char *path,
|
|
bool recall);
|
|
/**
|
|
* virSecurityDACTransactionRun:
|
|
* @pid: process pid
|
|
* @opaque: opaque data
|
|
*
|
|
* This is the callback that runs in the same namespace as the domain we are
|
|
* relabelling. For given transaction (@opaque) it relabels all the paths on
|
|
* the list. Depending on security manager configuration it might lock paths
|
|
* we will relabel.
|
|
*
|
|
* Returns: 0 on success
|
|
* -1 otherwise.
|
|
*/
|
|
static int
|
|
virSecurityDACTransactionRun(pid_t pid G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virSecurityDACChownListPtr list = opaque;
|
|
virSecurityManagerMetadataLockStatePtr state;
|
|
const char **paths = NULL;
|
|
size_t npaths = 0;
|
|
size_t i;
|
|
int rv = 0;
|
|
int ret = -1;
|
|
|
|
if (list->lock) {
|
|
if (VIR_ALLOC_N(paths, list->nItems) < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < list->nItems; i++) {
|
|
virSecurityDACChownItemPtr item = list->items[i];
|
|
const char *p = item->path;
|
|
|
|
if (item->remember)
|
|
VIR_APPEND_ELEMENT_COPY_INPLACE(paths, npaths, p);
|
|
}
|
|
|
|
if (!(state = virSecurityManagerMetadataLock(list->manager, paths, npaths)))
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < list->nItems; i++) {
|
|
virSecurityDACChownItemPtr item = list->items[i];
|
|
size_t j;
|
|
|
|
for (j = 0; j < state->nfds; j++) {
|
|
if (STREQ_NULLABLE(item->path, state->paths[j]))
|
|
break;
|
|
}
|
|
|
|
/* If path wasn't locked, don't try to remember its label. */
|
|
if (j == state->nfds)
|
|
item->remember = false;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < list->nItems; i++) {
|
|
virSecurityDACChownItemPtr item = list->items[i];
|
|
const bool remember = item->remember && list->lock;
|
|
|
|
if (!item->restore) {
|
|
rv = virSecurityDACSetOwnership(list->manager,
|
|
item->src,
|
|
item->path,
|
|
item->uid,
|
|
item->gid,
|
|
remember);
|
|
} else {
|
|
rv = virSecurityDACRestoreFileLabelInternal(list->manager,
|
|
item->src,
|
|
item->path,
|
|
remember);
|
|
}
|
|
|
|
if (rv < 0)
|
|
break;
|
|
}
|
|
|
|
for (; rv < 0 && i > 0; i--) {
|
|
virSecurityDACChownItemPtr item = list->items[i - 1];
|
|
const bool remember = item->remember && list->lock;
|
|
|
|
if (!item->restore) {
|
|
virSecurityDACRestoreFileLabelInternal(list->manager,
|
|
item->src,
|
|
item->path,
|
|
remember);
|
|
} else {
|
|
VIR_WARN("Ignoring failed restore attempt on %s",
|
|
NULLSTR(item->src ? item->src->path : item->path));
|
|
}
|
|
}
|
|
|
|
if (list->lock)
|
|
virSecurityManagerMetadataUnlock(list->manager, &state);
|
|
|
|
if (rv < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
VIR_FREE(paths);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* returns -1 on error, 0 on success */
|
|
int
|
|
virSecurityDACSetUserAndGroup(virSecurityManagerPtr mgr,
|
|
uid_t user,
|
|
gid_t group)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
priv->user = user;
|
|
priv->group = group;
|
|
|
|
priv->baselabel = g_strdup_printf("+%u:+%u", (unsigned int)user,
|
|
(unsigned int)group);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
virSecurityDACSetDynamicOwnership(virSecurityManagerPtr mgr,
|
|
bool dynamicOwnership)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
priv->dynamicOwnership = dynamicOwnership;
|
|
}
|
|
|
|
void
|
|
virSecurityDACSetMountNamespace(virSecurityManagerPtr mgr,
|
|
bool mountNamespace)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
priv->mountNamespace = mountNamespace;
|
|
}
|
|
|
|
|
|
void
|
|
virSecurityDACSetChownCallback(virSecurityManagerPtr mgr,
|
|
virSecurityManagerDACChownCallback chownCallback)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
priv->chownCallback = chownCallback;
|
|
}
|
|
|
|
/* returns 1 if label isn't found, 0 on success, -1 on error */
|
|
static int
|
|
ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
|
|
virSecurityDACParseIds(virSecurityLabelDefPtr seclabel,
|
|
uid_t *uidPtr, gid_t *gidPtr)
|
|
{
|
|
if (!seclabel || !seclabel->label)
|
|
return 1;
|
|
|
|
if (virParseOwnershipIds(seclabel->label, uidPtr, gidPtr) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
|
|
virSecurityDACGetIds(virSecurityLabelDefPtr seclabel,
|
|
virSecurityDACDataPtr priv,
|
|
uid_t *uidPtr, gid_t *gidPtr,
|
|
gid_t **groups, int *ngroups)
|
|
{
|
|
int ret;
|
|
|
|
if (groups)
|
|
*groups = priv ? priv->groups : NULL;
|
|
if (ngroups)
|
|
*ngroups = priv ? priv->ngroups : 0;
|
|
|
|
if ((ret = virSecurityDACParseIds(seclabel, uidPtr, gidPtr)) <= 0)
|
|
return ret;
|
|
|
|
if (!priv) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("DAC seclabel couldn't be determined"));
|
|
return -1;
|
|
}
|
|
|
|
*uidPtr = priv->user;
|
|
*gidPtr = priv->group;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* returns 1 if label isn't found, 0 on success, -1 on error */
|
|
static int
|
|
ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
|
|
virSecurityDACParseImageIds(virSecurityLabelDefPtr seclabel,
|
|
uid_t *uidPtr, gid_t *gidPtr)
|
|
{
|
|
if (!seclabel || !seclabel->imagelabel)
|
|
return 1;
|
|
|
|
if (virParseOwnershipIds(seclabel->imagelabel, uidPtr, gidPtr) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
|
|
virSecurityDACGetImageIds(virSecurityLabelDefPtr seclabel,
|
|
virSecurityDACDataPtr priv,
|
|
uid_t *uidPtr, gid_t *gidPtr)
|
|
{
|
|
int ret;
|
|
|
|
if ((ret = virSecurityDACParseImageIds(seclabel, uidPtr, gidPtr)) <= 0)
|
|
return ret;
|
|
|
|
if (!priv) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("DAC imagelabel couldn't be determined"));
|
|
return -1;
|
|
}
|
|
|
|
*uidPtr = priv->user;
|
|
*gidPtr = priv->group;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* virSecurityDACRememberLabel:
|
|
* @priv: driver's private data
|
|
* @path: path to the file
|
|
* @uid: user owning the @path
|
|
* @gid: group owning the @path
|
|
*
|
|
* Remember the owner of @path (represented by @uid:@gid).
|
|
*
|
|
* Returns: the @path refcount, or
|
|
* -1 on failure
|
|
*/
|
|
static int
|
|
virSecurityDACRememberLabel(virSecurityDACDataPtr priv G_GNUC_UNUSED,
|
|
const char *path,
|
|
uid_t uid,
|
|
gid_t gid)
|
|
{
|
|
char *label = NULL;
|
|
int ret = -1;
|
|
|
|
label = g_strdup_printf("+%u:+%u", (unsigned int)uid, (unsigned int)gid);
|
|
|
|
ret = virSecuritySetRememberedLabel(SECURITY_DAC_NAME, path, label);
|
|
VIR_FREE(label);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* virSecurityDACRecallLabel:
|
|
* @priv: driver's private data
|
|
* @path: path to the file
|
|
* @uid: user owning the @path
|
|
* @gid: group owning the @path
|
|
*
|
|
* Recall the previously recorded owner for the @path. However, it may happen
|
|
* that @path is still in use (e.g. by another domain). In that case, 1 is
|
|
* returned and caller should not relabel the @path.
|
|
*
|
|
* Returns: 1 if @path is still in use (@uid and @gid not touched)
|
|
* 0 if @path should be restored (@uid and @gid set)
|
|
* -1 on failure (@uid and @gid not touched)
|
|
*/
|
|
static int
|
|
virSecurityDACRecallLabel(virSecurityDACDataPtr priv G_GNUC_UNUSED,
|
|
const char *path,
|
|
uid_t *uid,
|
|
gid_t *gid)
|
|
{
|
|
char *label;
|
|
int ret = -1;
|
|
int rv;
|
|
|
|
rv = virSecurityGetRememberedLabel(SECURITY_DAC_NAME, path, &label);
|
|
if (rv < 0)
|
|
return rv;
|
|
|
|
if (!label)
|
|
return 1;
|
|
|
|
if (virParseOwnershipIds(label, uid, gid) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
VIR_FREE(label);
|
|
return ret;
|
|
}
|
|
|
|
static virSecurityDriverStatus
|
|
virSecurityDACProbe(const char *virtDriver G_GNUC_UNUSED)
|
|
{
|
|
return SECURITY_DRIVER_ENABLE;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACOpen(virSecurityManagerPtr mgr G_GNUC_UNUSED)
|
|
{
|
|
if (virThreadLocalInit(&chownList,
|
|
virSecurityDACChownListFree) < 0) {
|
|
virReportSystemError(errno, "%s",
|
|
_("Unable to initialize thread local variable"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACClose(virSecurityManagerPtr mgr)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
VIR_FREE(priv->groups);
|
|
VIR_FREE(priv->baselabel);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const char *
|
|
virSecurityDACGetModel(virSecurityManagerPtr mgr G_GNUC_UNUSED)
|
|
{
|
|
return SECURITY_DAC_NAME;
|
|
}
|
|
|
|
static const char *
|
|
virSecurityDACGetDOI(virSecurityManagerPtr mgr G_GNUC_UNUSED)
|
|
{
|
|
return "0";
|
|
}
|
|
|
|
static int
|
|
virSecurityDACPreFork(virSecurityManagerPtr mgr)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
int ngroups;
|
|
|
|
VIR_FREE(priv->groups);
|
|
priv->ngroups = 0;
|
|
if ((ngroups = virGetGroupList(priv->user, priv->group,
|
|
&priv->groups)) < 0)
|
|
return -1;
|
|
priv->ngroups = ngroups;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* virSecurityDACTransactionStart:
|
|
* @mgr: security manager
|
|
*
|
|
* Starts a new transaction. In transaction nothing is chown()-ed until
|
|
* TransactionCommit() is called. This is implemented as a list that is
|
|
* appended to whenever chown() would be called. Since secdriver APIs
|
|
* can be called from multiple threads (to work over different domains)
|
|
* the pointer to the list is stored in thread local variable.
|
|
*
|
|
* Returns 0 on success,
|
|
* -1 otherwise.
|
|
*/
|
|
static int
|
|
virSecurityDACTransactionStart(virSecurityManagerPtr mgr)
|
|
{
|
|
virSecurityDACChownListPtr list;
|
|
|
|
list = virThreadLocalGet(&chownList);
|
|
if (list) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Another relabel transaction is already started"));
|
|
return -1;
|
|
}
|
|
|
|
if (VIR_ALLOC(list) < 0)
|
|
return -1;
|
|
|
|
list->manager = virObjectRef(mgr);
|
|
|
|
if (virThreadLocalSet(&chownList, list) < 0) {
|
|
virReportSystemError(errno, "%s",
|
|
_("Unable to set thread local variable"));
|
|
virSecurityDACChownListFree(list);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* virSecurityDACTransactionCommit:
|
|
* @mgr: security manager
|
|
* @pid: domain's PID
|
|
* @lock: lock and unlock paths that are relabeled
|
|
*
|
|
* If @pid is not -1 then enter the @pid namespace (usually @pid refers
|
|
* to a domain) and perform all the chown()-s on the list. If @pid is -1
|
|
* then the transaction is performed in the namespace of the caller.
|
|
*
|
|
* If @lock is true then all the paths that transaction would
|
|
* touch are locked before and unlocked after it is done so.
|
|
*
|
|
* Note that the transaction is also freed, therefore new one has to be
|
|
* started after successful return from this function. Also it is
|
|
* considered as error if there's no transaction set and this function
|
|
* is called.
|
|
*
|
|
* Returns: 0 on success,
|
|
* -1 otherwise.
|
|
*/
|
|
static int
|
|
virSecurityDACTransactionCommit(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
pid_t pid,
|
|
bool lock)
|
|
{
|
|
virSecurityDACChownListPtr list;
|
|
int rc;
|
|
int ret = -1;
|
|
|
|
list = virThreadLocalGet(&chownList);
|
|
if (!list) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("No transaction is set"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virThreadLocalSet(&chownList, NULL) < 0) {
|
|
virReportSystemError(errno, "%s",
|
|
_("Unable to clear thread local variable"));
|
|
goto cleanup;
|
|
}
|
|
|
|
list->lock = lock;
|
|
|
|
if (pid != -1) {
|
|
rc = virProcessRunInMountNamespace(pid,
|
|
virSecurityDACTransactionRun,
|
|
list);
|
|
if (rc < 0) {
|
|
if (virGetLastErrorCode() == VIR_ERR_SYSTEM_ERROR)
|
|
pid = -1;
|
|
else
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (pid == -1) {
|
|
if (lock)
|
|
rc = virProcessRunInFork(virSecurityDACTransactionRun, list);
|
|
else
|
|
rc = virSecurityDACTransactionRun(pid, list);
|
|
}
|
|
|
|
if (rc < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virSecurityDACChownListFree(list);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* virSecurityDACTransactionAbort:
|
|
* @mgr: security manager
|
|
*
|
|
* Cancels and frees any out standing transaction.
|
|
*/
|
|
static void
|
|
virSecurityDACTransactionAbort(virSecurityManagerPtr mgr G_GNUC_UNUSED)
|
|
{
|
|
virSecurityDACChownListPtr list;
|
|
|
|
list = virThreadLocalGet(&chownList);
|
|
if (!list)
|
|
return;
|
|
|
|
if (virThreadLocalSet(&chownList, NULL) < 0)
|
|
VIR_DEBUG("Unable to clear thread local variable");
|
|
virSecurityDACChownListFree(list);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetOwnershipInternal(const virSecurityDACData *priv,
|
|
const virStorageSource *src,
|
|
const char *path,
|
|
uid_t uid,
|
|
gid_t gid)
|
|
{
|
|
int rc;
|
|
|
|
/* Be aware that this function might run in a separate process.
|
|
* Therefore, any driver state changes would be thrown away. */
|
|
|
|
if (priv && src && priv->chownCallback) {
|
|
rc = priv->chownCallback(src, uid, gid);
|
|
/* here path is used only for error messages */
|
|
path = NULLSTR(src->path);
|
|
|
|
/* on -2 returned an error was already reported */
|
|
if (rc == -2)
|
|
return -1;
|
|
} else {
|
|
struct stat sb;
|
|
|
|
if (!path) {
|
|
if (!src || !src->path)
|
|
return 0;
|
|
|
|
if (!virStorageSourceIsLocalStorage(src))
|
|
return 0;
|
|
|
|
path = src->path;
|
|
}
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
virReportSystemError(errno, _("unable to stat: %s"), path);
|
|
return -1;
|
|
}
|
|
|
|
if (sb.st_uid == uid && sb.st_gid == gid) {
|
|
/* nothing to chown */
|
|
return 0;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
rc = ENOSYS;
|
|
#else /* !WIN32 */
|
|
rc = chown(path, uid, gid);
|
|
#endif /* !WIN32 */
|
|
}
|
|
|
|
if (rc < 0) {
|
|
if (errno == EOPNOTSUPP || errno == EINVAL) {
|
|
VIR_INFO("Setting user and group to '%ld:%ld' on '%s' not "
|
|
"supported by filesystem",
|
|
(long)uid, (long)gid, path);
|
|
} else if (errno == EPERM) {
|
|
VIR_INFO("Setting user and group to '%ld:%ld' on '%s' not "
|
|
"permitted",
|
|
(long)uid, (long)gid, path);
|
|
} else if (errno == EROFS) {
|
|
VIR_INFO("Setting user and group to '%ld:%ld' on '%s' not "
|
|
"possible on readonly filesystem",
|
|
(long)uid, (long)gid, path);
|
|
} else {
|
|
virReportSystemError(errno,
|
|
_("unable to set user and group to '%ld:%ld' "
|
|
"on '%s'"),
|
|
(long)uid, (long)gid, path);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
|
|
const virStorageSource *src,
|
|
const char *path,
|
|
uid_t uid,
|
|
gid_t gid,
|
|
bool remember)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virErrorPtr origerr;
|
|
struct stat sb;
|
|
int refcount;
|
|
int rc;
|
|
|
|
if (!path && src && src->path &&
|
|
virStorageSourceIsLocalStorage(src))
|
|
path = src->path;
|
|
|
|
/* Be aware that this function might run in a separate process.
|
|
* Therefore, any driver state changes would be thrown away. */
|
|
|
|
if ((rc = virSecurityDACTransactionAppend(path, src,
|
|
uid, gid, remember, false)) < 0)
|
|
return -1;
|
|
else if (rc > 0)
|
|
return 0;
|
|
|
|
if (remember && path) {
|
|
if (stat(path, &sb) < 0) {
|
|
virReportSystemError(errno, _("unable to stat: %s"), path);
|
|
return -1;
|
|
}
|
|
|
|
refcount = virSecurityDACRememberLabel(priv, path, sb.st_uid, sb.st_gid);
|
|
if (refcount == -2) {
|
|
/* Not supported. Don't error though. */
|
|
} else if (refcount < 0) {
|
|
return -1;
|
|
} else if (refcount > 1) {
|
|
/* Refcount is greater than 1 which means that there
|
|
* is @refcount domains using the @path. Do not
|
|
* change the label (as it would almost certainly
|
|
* cause the other domains to lose access to the
|
|
* @path). However, the refcounter was incremented in
|
|
* XATTRs so decrease it. */
|
|
if (sb.st_uid != uid || sb.st_gid != gid) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("Setting different DAC user or group on %s "
|
|
"which is already in use"), path);
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
VIR_INFO("Setting DAC user and group on '%s' to '%ld:%ld'",
|
|
NULLSTR(src ? src->path : path), (long)uid, (long)gid);
|
|
|
|
if (virSecurityDACSetOwnershipInternal(priv, src, path, uid, gid) < 0)
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
virErrorPreserveLast(&origerr);
|
|
/* Try to restore the label. This is done so that XATTRs
|
|
* are left in the same state as when the control entered
|
|
* this function. However, if our attempt fails, there's
|
|
* not much we can do. XATTRs refcounting is fubar'ed and
|
|
* the only option we have is warn users. */
|
|
if (virSecurityDACRestoreFileLabelInternal(mgr, src, path, remember) < 0)
|
|
VIR_WARN("Unable to restore label on '%s'. "
|
|
"XATTRs might have been left in inconsistent state.",
|
|
NULLSTR(src ? src->path : path));
|
|
|
|
virErrorRestore(&origerr);
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreFileLabelInternal(virSecurityManagerPtr mgr,
|
|
const virStorageSource *src,
|
|
const char *path,
|
|
bool recall)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
int rv;
|
|
uid_t uid = 0; /* By default return to root:root */
|
|
gid_t gid = 0;
|
|
|
|
if (!path && src && src->path &&
|
|
virStorageSourceIsLocalStorage(src))
|
|
path = src->path;
|
|
|
|
/* Be aware that this function might run in a separate process.
|
|
* Therefore, any driver state changes would be thrown away. */
|
|
|
|
if ((rv = virSecurityDACTransactionAppend(path, src, uid, gid, recall, true)) < 0)
|
|
return -1;
|
|
else if (rv > 0)
|
|
return 0;
|
|
|
|
if (recall && path) {
|
|
rv = virSecurityDACRecallLabel(priv, path, &uid, &gid);
|
|
if (rv == -2) {
|
|
/* Not supported. Don't error though. */
|
|
} else if (rv < 0) {
|
|
return -1;
|
|
} else if (rv > 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
VIR_INFO("Restoring DAC user and group on '%s' to %ld:%ld",
|
|
NULLSTR(src ? src->path : path), (long)uid, (long)gid);
|
|
|
|
return virSecurityDACSetOwnershipInternal(priv, src, path, uid, gid);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreFileLabel(virSecurityManagerPtr mgr,
|
|
const char *path)
|
|
{
|
|
return virSecurityDACRestoreFileLabelInternal(mgr, NULL, path, true);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetImageLabelInternal(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virStorageSourcePtr src,
|
|
virStorageSourcePtr parent,
|
|
bool isChainTop)
|
|
{
|
|
virSecurityLabelDefPtr secdef;
|
|
virSecurityDeviceLabelDefPtr disk_seclabel;
|
|
virSecurityDeviceLabelDefPtr parent_seclabel = NULL;
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
bool remember;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
if (!priv->dynamicOwnership)
|
|
return 0;
|
|
|
|
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
if (secdef && !secdef->relabel)
|
|
return 0;
|
|
|
|
disk_seclabel = virStorageSourceGetSecurityLabelDef(src, SECURITY_DAC_NAME);
|
|
parent_seclabel = virStorageSourceGetSecurityLabelDef(parent,
|
|
SECURITY_DAC_NAME);
|
|
|
|
if (disk_seclabel && (!disk_seclabel->relabel || disk_seclabel->label)) {
|
|
if (!disk_seclabel->relabel)
|
|
return 0;
|
|
|
|
if (virParseOwnershipIds(disk_seclabel->label, &user, &group) < 0)
|
|
return -1;
|
|
} else if (parent_seclabel &&
|
|
(!parent_seclabel->relabel || parent_seclabel->label)) {
|
|
if (!parent_seclabel->relabel)
|
|
return 0;
|
|
|
|
if (virParseOwnershipIds(parent_seclabel->label, &user, &group) < 0)
|
|
return -1;
|
|
} else {
|
|
if (virSecurityDACGetImageIds(secdef, priv, &user, &group))
|
|
return -1;
|
|
}
|
|
|
|
/* This is not very clean. But so far we don't have NVMe
|
|
* storage pool backend so that its chownCallback would be
|
|
* called. And this place looks least offensive. */
|
|
if (src->type == VIR_STORAGE_TYPE_NVME) {
|
|
const virStorageSourceNVMeDef *nvme = src->nvme;
|
|
g_autofree char *vfioGroupDev = NULL;
|
|
|
|
if (!(vfioGroupDev = virPCIDeviceAddressGetIOMMUGroupDev(&nvme->pciAddr)))
|
|
return -1;
|
|
|
|
return virSecurityDACSetOwnership(mgr, NULL, vfioGroupDev, user, group, false);
|
|
}
|
|
|
|
/* We can't do restore on shared resources safely. Not even
|
|
* with refcounting implemented in XATTRs because if there
|
|
* was a domain running with the feature turned off the
|
|
* refcounter in XATTRs would not reflect the actual number
|
|
* of times the resource is in use and thus the last restore
|
|
* on the resource (which actually restores the original
|
|
* owner) might cut off access to the domain with the feature
|
|
* disabled.
|
|
* For disks, a shared resource is the whole backing chain
|
|
* but the top layer, or read only image, or disk explicitly
|
|
* marked as shared.
|
|
*/
|
|
remember = isChainTop && !src->readonly && !src->shared;
|
|
|
|
return virSecurityDACSetOwnership(mgr, src, NULL, user, group, remember);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetImageLabelRelative(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virStorageSourcePtr src,
|
|
virStorageSourcePtr parent,
|
|
virSecurityDomainImageLabelFlags flags)
|
|
{
|
|
virStorageSourcePtr n;
|
|
|
|
for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) {
|
|
const bool isChainTop = flags & VIR_SECURITY_DOMAIN_IMAGE_PARENT_CHAIN_TOP;
|
|
|
|
if (virSecurityDACSetImageLabelInternal(mgr, def, n, parent, isChainTop) < 0)
|
|
return -1;
|
|
|
|
if (!(flags & VIR_SECURITY_DOMAIN_IMAGE_LABEL_BACKING_CHAIN))
|
|
break;
|
|
|
|
flags &= ~VIR_SECURITY_DOMAIN_IMAGE_PARENT_CHAIN_TOP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACSetImageLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virStorageSourcePtr src,
|
|
virSecurityDomainImageLabelFlags flags)
|
|
{
|
|
return virSecurityDACSetImageLabelRelative(mgr, def, src, src, flags);
|
|
}
|
|
|
|
static int
|
|
virSecurityDACRestoreImageLabelSingle(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virStorageSourcePtr src,
|
|
bool migrated)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr secdef;
|
|
virSecurityDeviceLabelDefPtr disk_seclabel;
|
|
|
|
if (!priv->dynamicOwnership)
|
|
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 domains and try to figure out if it is in use, but this would
|
|
* not work for clustered filesystems, since we can't see running VMs using
|
|
* the file on other nodes. Safest bet is thus to skip the restore step. */
|
|
if (src->readonly || src->shared)
|
|
return 0;
|
|
|
|
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
if (secdef && !secdef->relabel)
|
|
return 0;
|
|
|
|
disk_seclabel = virStorageSourceGetSecurityLabelDef(src,
|
|
SECURITY_DAC_NAME);
|
|
if (disk_seclabel && !disk_seclabel->relabel)
|
|
return 0;
|
|
|
|
/* If we have a shared FS and are doing migration, we must not change
|
|
* ownership, because that kills access on the destination host which is
|
|
* sub-optimal for the guest VM's I/O attempts :-) */
|
|
if (migrated) {
|
|
int rc = 1;
|
|
|
|
if (virStorageSourceIsLocalStorage(src)) {
|
|
if (!src->path)
|
|
return 0;
|
|
|
|
if ((rc = virFileIsSharedFS(src->path)) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (rc == 1) {
|
|
VIR_DEBUG("Skipping image label restore on %s because FS is shared",
|
|
src->path);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* This is not very clean. But so far we don't have NVMe
|
|
* storage pool backend so that its chownCallback would be
|
|
* called. And this place looks least offensive. */
|
|
if (src->type == VIR_STORAGE_TYPE_NVME) {
|
|
const virStorageSourceNVMeDef *nvme = src->nvme;
|
|
g_autofree char *vfioGroupDev = NULL;
|
|
|
|
if (!(vfioGroupDev = virPCIDeviceAddressGetIOMMUGroupDev(&nvme->pciAddr)))
|
|
return -1;
|
|
|
|
/* Ideally, we would check if there is not another PCI
|
|
* device within domain def that is in the same IOMMU
|
|
* group. But we're not doing that for hostdevs yet. */
|
|
|
|
return virSecurityDACRestoreFileLabelInternal(mgr, NULL, vfioGroupDev, false);
|
|
}
|
|
|
|
return virSecurityDACRestoreFileLabelInternal(mgr, src, NULL, true);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreImageLabelInt(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virStorageSourcePtr src,
|
|
bool migrated)
|
|
{
|
|
if (virSecurityDACRestoreImageLabelSingle(mgr, def, src, migrated) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreImageLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virStorageSourcePtr src,
|
|
virSecurityDomainImageLabelFlags flags G_GNUC_UNUSED)
|
|
{
|
|
return virSecurityDACRestoreImageLabelInt(mgr, def, src, false);
|
|
}
|
|
|
|
|
|
struct virSecurityDACMoveImageMetadataData {
|
|
virSecurityManagerPtr mgr;
|
|
const char *src;
|
|
const char *dst;
|
|
};
|
|
|
|
|
|
static int
|
|
virSecurityDACMoveImageMetadataHelper(pid_t pid G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
struct virSecurityDACMoveImageMetadataData *data = opaque;
|
|
const char *paths[2] = { data->src, data->dst };
|
|
virSecurityManagerMetadataLockStatePtr state;
|
|
int ret;
|
|
|
|
if (!(state = virSecurityManagerMetadataLock(data->mgr, paths, G_N_ELEMENTS(paths))))
|
|
return -1;
|
|
|
|
ret = virSecurityMoveRememberedLabel(SECURITY_DAC_NAME, data->src, data->dst);
|
|
virSecurityManagerMetadataUnlock(data->mgr, &state);
|
|
|
|
if (ret == -2) {
|
|
/* Libvirt built without XATTRS */
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACMoveImageMetadata(virSecurityManagerPtr mgr,
|
|
pid_t pid,
|
|
virStorageSourcePtr src,
|
|
virStorageSourcePtr dst)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
struct virSecurityDACMoveImageMetadataData data = { .mgr = mgr, 0 };
|
|
int rc;
|
|
|
|
/* If dynamicOwnership is turned off, or owner remembering is
|
|
* not enabled there's nothing for us to do. */
|
|
if (!priv->dynamicOwnership)
|
|
return 0;
|
|
|
|
if (src && virStorageSourceIsLocalStorage(src))
|
|
data.src = src->path;
|
|
|
|
if (dst && virStorageSourceIsLocalStorage(dst))
|
|
data.dst = dst->path;
|
|
|
|
if (!data.src)
|
|
return 0;
|
|
|
|
if (pid == -1) {
|
|
rc = virProcessRunInFork(virSecurityDACMoveImageMetadataHelper, &data);
|
|
} else {
|
|
rc = virProcessRunInMountNamespace(pid,
|
|
virSecurityDACMoveImageMetadataHelper,
|
|
&data);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetHostdevLabelHelper(const char *file,
|
|
bool remember,
|
|
void *opaque)
|
|
{
|
|
virSecurityDACCallbackDataPtr cbdata = opaque;
|
|
virSecurityManagerPtr mgr = cbdata->manager;
|
|
virSecurityLabelDefPtr secdef = cbdata->secdef;
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
if (virSecurityDACGetIds(secdef, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
|
|
return virSecurityDACSetOwnership(mgr, NULL, file, user, group, remember);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetPCILabel(virPCIDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
return virSecurityDACSetHostdevLabelHelper(file, true, opaque);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetUSBLabel(virUSBDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
return virSecurityDACSetHostdevLabelHelper(file, true, opaque);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetSCSILabel(virSCSIDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
return virSecurityDACSetHostdevLabelHelper(file, true, opaque);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetHostLabel(virSCSIVHostDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
return virSecurityDACSetHostdevLabelHelper(file, true, opaque);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetHostdevLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainHostdevDefPtr dev,
|
|
const char *vroot)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityDACCallbackData cbdata;
|
|
virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
|
|
virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
|
|
virDomainHostdevSubsysSCSIVHostPtr hostsrc = &dev->source.subsys.u.scsi_host;
|
|
virDomainHostdevSubsysMediatedDevPtr mdevsrc = &dev->source.subsys.u.mdev;
|
|
int ret = -1;
|
|
|
|
if (!priv->dynamicOwnership)
|
|
return 0;
|
|
|
|
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
return 0;
|
|
|
|
/* Like virSecurityDACSetImageLabel() for a networked disk,
|
|
* do nothing for an iSCSI hostdev
|
|
*/
|
|
if (dev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI &&
|
|
scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI)
|
|
return 0;
|
|
|
|
cbdata.manager = mgr;
|
|
cbdata.secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (cbdata.secdef && !cbdata.secdef->relabel)
|
|
return 0;
|
|
|
|
switch ((virDomainHostdevSubsysType)dev->source.subsys.type) {
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
|
|
virUSBDevicePtr usb;
|
|
|
|
if (dev->missing)
|
|
return 0;
|
|
|
|
if (!(usb = virUSBDeviceNew(usbsrc->bus, usbsrc->device, vroot)))
|
|
return -1;
|
|
|
|
ret = virUSBDeviceFileIterate(usb,
|
|
virSecurityDACSetUSBLabel,
|
|
&cbdata);
|
|
virUSBDeviceFree(usb);
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
|
|
virPCIDevicePtr pci =
|
|
virPCIDeviceNew(pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
|
|
if (!pci)
|
|
return -1;
|
|
|
|
if (pcisrc->backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
|
|
char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
|
|
|
|
if (!vfioGroupDev) {
|
|
virPCIDeviceFree(pci);
|
|
return -1;
|
|
}
|
|
ret = virSecurityDACSetHostdevLabelHelper(vfioGroupDev,
|
|
false,
|
|
&cbdata);
|
|
VIR_FREE(vfioGroupDev);
|
|
} else {
|
|
ret = virPCIDeviceFileIterate(pci,
|
|
virSecurityDACSetPCILabel,
|
|
&cbdata);
|
|
}
|
|
|
|
virPCIDeviceFree(pci);
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: {
|
|
virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host;
|
|
virSCSIDevicePtr scsi =
|
|
virSCSIDeviceNew(NULL,
|
|
scsihostsrc->adapter, scsihostsrc->bus,
|
|
scsihostsrc->target, scsihostsrc->unit,
|
|
dev->readonly, dev->shareable);
|
|
|
|
if (!scsi)
|
|
return -1;
|
|
|
|
ret = virSCSIDeviceFileIterate(scsi,
|
|
virSecurityDACSetSCSILabel,
|
|
&cbdata);
|
|
virSCSIDeviceFree(scsi);
|
|
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: {
|
|
virSCSIVHostDevicePtr host = virSCSIVHostDeviceNew(hostsrc->wwpn);
|
|
|
|
if (!host)
|
|
return -1;
|
|
|
|
ret = virSCSIVHostDeviceFileIterate(host,
|
|
virSecurityDACSetHostLabel,
|
|
&cbdata);
|
|
virSCSIVHostDeviceFree(host);
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: {
|
|
char *vfiodev = NULL;
|
|
|
|
if (!(vfiodev = virMediatedDeviceGetIOMMUGroupDev(mdevsrc->uuidstr)))
|
|
return -1;
|
|
|
|
ret = virSecurityDACSetHostdevLabelHelper(vfiodev, true, &cbdata);
|
|
|
|
VIR_FREE(vfiodev);
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestorePCILabel(virPCIDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
virSecurityManagerPtr mgr = opaque;
|
|
return virSecurityDACRestoreFileLabel(mgr, file);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreUSBLabel(virUSBDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
virSecurityManagerPtr mgr = opaque;
|
|
return virSecurityDACRestoreFileLabel(mgr, file);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreSCSILabel(virSCSIDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
virSecurityManagerPtr mgr = opaque;
|
|
return virSecurityDACRestoreFileLabel(mgr, file);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreHostLabel(virSCSIVHostDevicePtr dev G_GNUC_UNUSED,
|
|
const char *file,
|
|
void *opaque)
|
|
{
|
|
virSecurityManagerPtr mgr = opaque;
|
|
return virSecurityDACRestoreFileLabel(mgr, file);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreHostdevLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainHostdevDefPtr dev,
|
|
const char *vroot)
|
|
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr secdef;
|
|
virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
|
|
virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
|
|
virDomainHostdevSubsysSCSIVHostPtr hostsrc = &dev->source.subsys.u.scsi_host;
|
|
virDomainHostdevSubsysMediatedDevPtr mdevsrc = &dev->source.subsys.u.mdev;
|
|
int ret = -1;
|
|
|
|
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (!priv->dynamicOwnership || (secdef && !secdef->relabel))
|
|
return 0;
|
|
|
|
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
return 0;
|
|
|
|
/* Like virSecurityDACRestoreImageLabelInt() for a networked disk,
|
|
* do nothing for an iSCSI hostdev
|
|
*/
|
|
if (dev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI &&
|
|
scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI)
|
|
return 0;
|
|
|
|
switch ((virDomainHostdevSubsysType)dev->source.subsys.type) {
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
|
|
virUSBDevicePtr usb;
|
|
|
|
if (dev->missing)
|
|
return 0;
|
|
|
|
if (!(usb = virUSBDeviceNew(usbsrc->bus, usbsrc->device, vroot)))
|
|
return -1;
|
|
|
|
ret = virUSBDeviceFileIterate(usb, virSecurityDACRestoreUSBLabel, mgr);
|
|
virUSBDeviceFree(usb);
|
|
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
|
|
virPCIDevicePtr pci =
|
|
virPCIDeviceNew(pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
|
|
if (!pci)
|
|
return -1;
|
|
|
|
if (pcisrc->backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
|
|
char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
|
|
|
|
if (!vfioGroupDev) {
|
|
virPCIDeviceFree(pci);
|
|
return -1;
|
|
}
|
|
ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
|
|
vfioGroupDev, false);
|
|
VIR_FREE(vfioGroupDev);
|
|
} else {
|
|
ret = virPCIDeviceFileIterate(pci, virSecurityDACRestorePCILabel, mgr);
|
|
}
|
|
virPCIDeviceFree(pci);
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: {
|
|
virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host;
|
|
virSCSIDevicePtr scsi =
|
|
virSCSIDeviceNew(NULL,
|
|
scsihostsrc->adapter, scsihostsrc->bus,
|
|
scsihostsrc->target, scsihostsrc->unit,
|
|
dev->readonly, dev->shareable);
|
|
|
|
if (!scsi)
|
|
return -1;
|
|
|
|
ret = virSCSIDeviceFileIterate(scsi, virSecurityDACRestoreSCSILabel, mgr);
|
|
virSCSIDeviceFree(scsi);
|
|
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: {
|
|
virSCSIVHostDevicePtr host = virSCSIVHostDeviceNew(hostsrc->wwpn);
|
|
|
|
if (!host)
|
|
return -1;
|
|
|
|
ret = virSCSIVHostDeviceFileIterate(host,
|
|
virSecurityDACRestoreHostLabel,
|
|
mgr);
|
|
virSCSIVHostDeviceFree(host);
|
|
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: {
|
|
char *vfiodev = NULL;
|
|
|
|
if (!(vfiodev = virMediatedDeviceGetIOMMUGroupDev(mdevsrc->uuidstr)))
|
|
return -1;
|
|
|
|
ret = virSecurityDACRestoreFileLabel(mgr, vfiodev);
|
|
VIR_FREE(vfiodev);
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetChardevLabelHelper(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainChrSourceDefPtr dev_source,
|
|
bool chardevStdioLogd,
|
|
bool remember)
|
|
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr seclabel;
|
|
virSecurityDeviceLabelDefPtr chr_seclabel = NULL;
|
|
char *in = NULL, *out = NULL;
|
|
int ret = -1;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
chr_seclabel = virDomainChrSourceDefGetSecurityLabelDef(dev_source,
|
|
SECURITY_DAC_NAME);
|
|
|
|
if (chr_seclabel && !chr_seclabel->relabel)
|
|
return 0;
|
|
|
|
if (!chr_seclabel &&
|
|
dev_source->type == VIR_DOMAIN_CHR_TYPE_FILE &&
|
|
chardevStdioLogd)
|
|
return 0;
|
|
|
|
if (chr_seclabel && chr_seclabel->label) {
|
|
if (virParseOwnershipIds(chr_seclabel->label, &user, &group) < 0)
|
|
return -1;
|
|
} else {
|
|
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
}
|
|
|
|
switch ((virDomainChrType)dev_source->type) {
|
|
case VIR_DOMAIN_CHR_TYPE_DEV:
|
|
case VIR_DOMAIN_CHR_TYPE_FILE:
|
|
ret = virSecurityDACSetOwnership(mgr, NULL,
|
|
dev_source->data.file.path,
|
|
user, group, remember);
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PIPE:
|
|
in = g_strdup_printf("%s.in", dev_source->data.file.path);
|
|
out = g_strdup_printf("%s.out", dev_source->data.file.path);
|
|
if (virFileExists(in) && virFileExists(out)) {
|
|
if (virSecurityDACSetOwnership(mgr, NULL, in, user, group, remember) < 0 ||
|
|
virSecurityDACSetOwnership(mgr, NULL, out, user, group, remember) < 0)
|
|
goto done;
|
|
} else if (virSecurityDACSetOwnership(mgr, NULL,
|
|
dev_source->data.file.path,
|
|
user, group, remember) < 0) {
|
|
goto done;
|
|
}
|
|
ret = 0;
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
|
if (!dev_source->data.nix.listen ||
|
|
(dev_source->data.nix.path &&
|
|
virFileExists(dev_source->data.nix.path))) {
|
|
/* Also label mode='bind' sockets if they exist,
|
|
* e.g. because they were created by libvirt
|
|
* and passed via FD */
|
|
if (virSecurityDACSetOwnership(mgr, NULL,
|
|
dev_source->data.nix.path,
|
|
user, group, remember) < 0)
|
|
goto done;
|
|
}
|
|
ret = 0;
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
|
|
case VIR_DOMAIN_CHR_TYPE_NULL:
|
|
case VIR_DOMAIN_CHR_TYPE_VC:
|
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
|
case VIR_DOMAIN_CHR_TYPE_STDIO:
|
|
case VIR_DOMAIN_CHR_TYPE_UDP:
|
|
case VIR_DOMAIN_CHR_TYPE_TCP:
|
|
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
|
|
case VIR_DOMAIN_CHR_TYPE_NMDM:
|
|
case VIR_DOMAIN_CHR_TYPE_LAST:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
VIR_FREE(in);
|
|
VIR_FREE(out);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainChrSourceDefPtr dev_source,
|
|
bool chardevStdioLogd)
|
|
{
|
|
return virSecurityDACSetChardevLabelHelper(mgr, def, dev_source,
|
|
chardevStdioLogd, true);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreChardevLabelHelper(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
virDomainChrSourceDefPtr dev_source,
|
|
bool chardevStdioLogd,
|
|
bool recall)
|
|
{
|
|
virSecurityDeviceLabelDefPtr chr_seclabel = NULL;
|
|
char *in = NULL, *out = NULL;
|
|
int ret = -1;
|
|
|
|
chr_seclabel = virDomainChrSourceDefGetSecurityLabelDef(dev_source,
|
|
SECURITY_DAC_NAME);
|
|
|
|
if (chr_seclabel && !chr_seclabel->relabel)
|
|
return 0;
|
|
|
|
if (!chr_seclabel &&
|
|
dev_source->type == VIR_DOMAIN_CHR_TYPE_FILE &&
|
|
chardevStdioLogd)
|
|
return 0;
|
|
|
|
switch ((virDomainChrType)dev_source->type) {
|
|
case VIR_DOMAIN_CHR_TYPE_DEV:
|
|
case VIR_DOMAIN_CHR_TYPE_FILE:
|
|
ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
|
|
dev_source->data.file.path,
|
|
recall);
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PIPE:
|
|
out = g_strdup_printf("%s.out", dev_source->data.file.path);
|
|
in = g_strdup_printf("%s.in", dev_source->data.file.path);
|
|
if (virFileExists(in) && virFileExists(out)) {
|
|
if (virSecurityDACRestoreFileLabelInternal(mgr, NULL, out, recall) < 0 ||
|
|
virSecurityDACRestoreFileLabelInternal(mgr, NULL, in, recall) < 0)
|
|
goto done;
|
|
} else if (virSecurityDACRestoreFileLabelInternal(mgr, NULL,
|
|
dev_source->data.file.path,
|
|
recall) < 0) {
|
|
goto done;
|
|
}
|
|
ret = 0;
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
|
if (!dev_source->data.nix.listen &&
|
|
virSecurityDACRestoreFileLabelInternal(mgr, NULL,
|
|
dev_source->data.nix.path,
|
|
recall) < 0) {
|
|
goto done;
|
|
}
|
|
ret = 0;
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_NULL:
|
|
case VIR_DOMAIN_CHR_TYPE_VC:
|
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
|
case VIR_DOMAIN_CHR_TYPE_STDIO:
|
|
case VIR_DOMAIN_CHR_TYPE_UDP:
|
|
case VIR_DOMAIN_CHR_TYPE_TCP:
|
|
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
|
|
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
|
|
case VIR_DOMAIN_CHR_TYPE_NMDM:
|
|
case VIR_DOMAIN_CHR_TYPE_LAST:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
VIR_FREE(in);
|
|
VIR_FREE(out);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreChardevLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainChrSourceDefPtr dev_source,
|
|
bool chardevStdioLogd)
|
|
{
|
|
return virSecurityDACRestoreChardevLabelHelper(mgr, def, dev_source,
|
|
chardevStdioLogd, true);
|
|
}
|
|
|
|
|
|
struct _virSecuritySELinuxChardevCallbackData {
|
|
virSecurityManagerPtr mgr;
|
|
bool chardevStdioLogd;
|
|
};
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreChardevCallback(virDomainDefPtr def,
|
|
virDomainChrDefPtr dev G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
struct _virSecuritySELinuxChardevCallbackData *data = opaque;
|
|
|
|
return virSecurityDACRestoreChardevLabel(data->mgr, def, dev->source,
|
|
data->chardevStdioLogd);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetTPMFileLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainTPMDefPtr tpm)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (tpm->type) {
|
|
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
|
|
ret = virSecurityDACSetChardevLabelHelper(mgr, def,
|
|
&tpm->data.passthrough.source,
|
|
false, false);
|
|
break;
|
|
case VIR_DOMAIN_TPM_TYPE_EMULATOR:
|
|
ret = virSecurityDACSetChardevLabelHelper(mgr, def,
|
|
&tpm->data.emulator.source,
|
|
false, false);
|
|
break;
|
|
case VIR_DOMAIN_TPM_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreTPMFileLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainTPMDefPtr tpm)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (tpm->type) {
|
|
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
|
|
ret = virSecurityDACRestoreChardevLabelHelper(mgr, def,
|
|
&tpm->data.passthrough.source,
|
|
false, false);
|
|
break;
|
|
case VIR_DOMAIN_TPM_TYPE_EMULATOR:
|
|
/* swtpm will have removed the Unix socket upon termination */
|
|
case VIR_DOMAIN_TPM_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetGraphicsLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainGraphicsDefPtr gfx)
|
|
|
|
{
|
|
const char *rendernode = virDomainGraphicsGetRenderNode(gfx);
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr seclabel;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
/* There's nothing to relabel */
|
|
if (!rendernode)
|
|
return 0;
|
|
|
|
/* Skip chowning the shared render file if namespaces are disabled */
|
|
if (!priv->mountNamespace)
|
|
return 0;
|
|
|
|
seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
if (seclabel && !seclabel->relabel)
|
|
return 0;
|
|
|
|
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
|
|
if (virSecurityDACSetOwnership(mgr, NULL, rendernode, user, group, true) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreGraphicsLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
virDomainGraphicsDefPtr gfx G_GNUC_UNUSED)
|
|
|
|
{
|
|
/* The only graphics labelling we do is dependent on mountNamespaces,
|
|
in which case 'restoring' the label doesn't actually accomplish
|
|
anything, so there's nothing to do here */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetInputLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainInputDefPtr input)
|
|
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr seclabel;
|
|
int ret = -1;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
if (seclabel && !seclabel->relabel)
|
|
return 0;
|
|
|
|
switch ((virDomainInputType)input->type) {
|
|
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
|
|
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
|
|
ret = virSecurityDACSetOwnership(mgr, NULL,
|
|
input->source.evdev,
|
|
user, group, true);
|
|
break;
|
|
|
|
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
|
|
case VIR_DOMAIN_INPUT_TYPE_TABLET:
|
|
case VIR_DOMAIN_INPUT_TYPE_KBD:
|
|
case VIR_DOMAIN_INPUT_TYPE_LAST:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACRestoreInputLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
virDomainInputDefPtr input)
|
|
{
|
|
int ret = -1;
|
|
|
|
switch ((virDomainInputType)input->type) {
|
|
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
|
|
ret = virSecurityDACRestoreFileLabel(mgr, input->source.evdev);
|
|
break;
|
|
|
|
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
|
|
case VIR_DOMAIN_INPUT_TYPE_TABLET:
|
|
case VIR_DOMAIN_INPUT_TYPE_KBD:
|
|
case VIR_DOMAIN_INPUT_TYPE_LAST:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreMemoryLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
virDomainMemoryDefPtr mem)
|
|
{
|
|
int ret = -1;
|
|
|
|
switch ((virDomainMemoryModel) mem->model) {
|
|
case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
|
|
ret = virSecurityDACRestoreFileLabel(mgr, mem->nvdimmPath);
|
|
break;
|
|
|
|
case VIR_DOMAIN_MEMORY_MODEL_DIMM:
|
|
case VIR_DOMAIN_MEMORY_MODEL_LAST:
|
|
case VIR_DOMAIN_MEMORY_MODEL_NONE:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreSEVLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED)
|
|
{
|
|
/* we only label /dev/sev when running with namespaces, so we don't need to
|
|
* restore anything */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreSysinfoLabel(virSecurityManagerPtr mgr,
|
|
virSysinfoDefPtr def)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < def->nfw_cfgs; i++) {
|
|
virSysinfoFWCfgDefPtr f = &def->fw_cfgs[i];
|
|
|
|
if (f->file &&
|
|
virSecurityDACRestoreFileLabel(mgr, f->file) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACRestoreAllLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
bool migrated,
|
|
bool chardevStdioLogd)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr secdef;
|
|
size_t i;
|
|
int rc = 0;
|
|
|
|
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (!priv->dynamicOwnership || (secdef && !secdef->relabel))
|
|
return 0;
|
|
|
|
VIR_DEBUG("Restoring security label on %s migrated=%d",
|
|
def->name, migrated);
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
if (virSecurityDACRestoreImageLabelInt(mgr,
|
|
def,
|
|
def->disks[i]->src,
|
|
migrated) < 0)
|
|
rc = -1;
|
|
}
|
|
|
|
for (i = 0; i < def->ngraphics; i++) {
|
|
if (virSecurityDACRestoreGraphicsLabel(mgr, def, def->graphics[i]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < def->ninputs; i++) {
|
|
if (virSecurityDACRestoreInputLabel(mgr, def, def->inputs[i]) < 0)
|
|
rc = -1;
|
|
}
|
|
|
|
for (i = 0; i < def->nhostdevs; i++) {
|
|
if (virSecurityDACRestoreHostdevLabel(mgr,
|
|
def,
|
|
def->hostdevs[i],
|
|
NULL) < 0)
|
|
rc = -1;
|
|
}
|
|
|
|
for (i = 0; i < def->nmems; i++) {
|
|
if (virSecurityDACRestoreMemoryLabel(mgr,
|
|
def,
|
|
def->mems[i]) < 0)
|
|
rc = -1;
|
|
}
|
|
|
|
struct _virSecuritySELinuxChardevCallbackData chardevData = {
|
|
.mgr = mgr,
|
|
.chardevStdioLogd = chardevStdioLogd,
|
|
};
|
|
|
|
if (virDomainChrDefForeach(def,
|
|
false,
|
|
virSecurityDACRestoreChardevCallback,
|
|
&chardevData) < 0)
|
|
rc = -1;
|
|
|
|
for (i = 0; i < def->ntpms; i++) {
|
|
if (virSecurityDACRestoreTPMFileLabel(mgr,
|
|
def,
|
|
def->tpms[i]) < 0)
|
|
rc = -1;
|
|
}
|
|
|
|
if (def->sev) {
|
|
if (virSecurityDACRestoreSEVLabel(mgr, def) < 0)
|
|
rc = -1;
|
|
}
|
|
|
|
for (i = 0; i < def->nsysinfo; i++) {
|
|
if (virSecurityDACRestoreSysinfoLabel(mgr,
|
|
def->sysinfo[i]) < 0)
|
|
rc = -1;
|
|
}
|
|
|
|
if (def->os.loader && def->os.loader->nvram &&
|
|
virSecurityDACRestoreFileLabel(mgr, def->os.loader->nvram) < 0)
|
|
rc = -1;
|
|
|
|
if (def->os.kernel &&
|
|
virSecurityDACRestoreFileLabel(mgr, def->os.kernel) < 0)
|
|
rc = -1;
|
|
|
|
if (def->os.initrd &&
|
|
virSecurityDACRestoreFileLabel(mgr, def->os.initrd) < 0)
|
|
rc = -1;
|
|
|
|
if (def->os.dtb &&
|
|
virSecurityDACRestoreFileLabel(mgr, def->os.dtb) < 0)
|
|
rc = -1;
|
|
|
|
if (def->os.slic_table &&
|
|
virSecurityDACRestoreFileLabel(mgr, def->os.slic_table) < 0)
|
|
rc = -1;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetChardevCallback(virDomainDefPtr def,
|
|
virDomainChrDefPtr dev G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
struct _virSecuritySELinuxChardevCallbackData *data = opaque;
|
|
|
|
return virSecurityDACSetChardevLabel(data->mgr, def, dev->source,
|
|
data->chardevStdioLogd);
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetMemoryLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virDomainMemoryDefPtr mem)
|
|
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr seclabel;
|
|
int ret = -1;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
switch ((virDomainMemoryModel) mem->model) {
|
|
case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
|
|
seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
if (seclabel && !seclabel->relabel)
|
|
return 0;
|
|
|
|
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
|
|
ret = virSecurityDACSetOwnership(mgr, NULL,
|
|
mem->nvdimmPath,
|
|
user, group, true);
|
|
break;
|
|
|
|
case VIR_DOMAIN_MEMORY_MODEL_DIMM:
|
|
case VIR_DOMAIN_MEMORY_MODEL_LAST:
|
|
case VIR_DOMAIN_MEMORY_MODEL_NONE:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetSEVLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr seclabel;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
/* Skip chowning /dev/sev if namespaces are disabled as we'd significantly
|
|
* increase the chance of a DOS attack on SEV
|
|
*/
|
|
if (!priv->mountNamespace)
|
|
return 0;
|
|
|
|
seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
if (seclabel && !seclabel->relabel)
|
|
return 0;
|
|
|
|
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
|
|
if (virSecurityDACSetOwnership(mgr, NULL, DEV_SEV,
|
|
user, group, true) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetSysinfoLabel(virSecurityManagerPtr mgr,
|
|
uid_t user,
|
|
gid_t group,
|
|
virSysinfoDefPtr def)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < def->nfw_cfgs; i++) {
|
|
virSysinfoFWCfgDefPtr f = &def->fw_cfgs[i];
|
|
|
|
if (f->file &&
|
|
virSecurityDACSetOwnership(mgr, NULL, f->file,
|
|
user, group, true) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetAllLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
const char *stdin_path G_GNUC_UNUSED,
|
|
bool chardevStdioLogd,
|
|
bool migrated G_GNUC_UNUSED)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr secdef;
|
|
size_t i;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (!priv->dynamicOwnership || (secdef && !secdef->relabel))
|
|
return 0;
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
/* XXX fixme - we need to recursively label the entire tree :-( */
|
|
if (virDomainDiskGetType(def->disks[i]) == VIR_STORAGE_TYPE_DIR)
|
|
continue;
|
|
if (virSecurityDACSetImageLabel(mgr, def, def->disks[i]->src,
|
|
VIR_SECURITY_DOMAIN_IMAGE_LABEL_BACKING_CHAIN |
|
|
VIR_SECURITY_DOMAIN_IMAGE_PARENT_CHAIN_TOP) < 0)
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < def->ngraphics; i++) {
|
|
if (virSecurityDACSetGraphicsLabel(mgr, def, def->graphics[i]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < def->ninputs; i++) {
|
|
if (virSecurityDACSetInputLabel(mgr, def, def->inputs[i]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < def->nhostdevs; i++) {
|
|
if (virSecurityDACSetHostdevLabel(mgr,
|
|
def,
|
|
def->hostdevs[i],
|
|
NULL) < 0)
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < def->nmems; i++) {
|
|
if (virSecurityDACSetMemoryLabel(mgr,
|
|
def,
|
|
def->mems[i]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
struct _virSecuritySELinuxChardevCallbackData chardevData = {
|
|
.mgr = mgr,
|
|
.chardevStdioLogd = chardevStdioLogd,
|
|
};
|
|
|
|
if (virDomainChrDefForeach(def,
|
|
true,
|
|
virSecurityDACSetChardevCallback,
|
|
&chardevData) < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < def->ntpms; i++) {
|
|
if (virSecurityDACSetTPMFileLabel(mgr,
|
|
def,
|
|
def->tpms[i]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (def->sev) {
|
|
if (virSecurityDACSetSEVLabel(mgr, def) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (virSecurityDACGetImageIds(secdef, priv, &user, &group))
|
|
return -1;
|
|
|
|
for (i = 0; i < def->nsysinfo; i++) {
|
|
if (virSecurityDACSetSysinfoLabel(mgr, user, group, def->sysinfo[i]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (def->os.loader && def->os.loader->nvram &&
|
|
virSecurityDACSetOwnership(mgr, NULL,
|
|
def->os.loader->nvram,
|
|
user, group, true) < 0)
|
|
return -1;
|
|
|
|
if (def->os.kernel &&
|
|
virSecurityDACSetOwnership(mgr, NULL,
|
|
def->os.kernel,
|
|
user, group, true) < 0)
|
|
return -1;
|
|
|
|
if (def->os.initrd &&
|
|
virSecurityDACSetOwnership(mgr, NULL,
|
|
def->os.initrd,
|
|
user, group, true) < 0)
|
|
return -1;
|
|
|
|
if (def->os.dtb &&
|
|
virSecurityDACSetOwnership(mgr, NULL,
|
|
def->os.dtb,
|
|
user, group, true) < 0)
|
|
return -1;
|
|
|
|
if (def->os.slic_table &&
|
|
virSecurityDACSetOwnership(mgr, NULL,
|
|
def->os.slic_table,
|
|
user, group, true) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetProcessLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr secdef;
|
|
uid_t user;
|
|
gid_t group;
|
|
gid_t *groups;
|
|
int ngroups;
|
|
|
|
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (virSecurityDACGetIds(secdef, priv, &user, &group, &groups, &ngroups) < 0)
|
|
return -1;
|
|
|
|
VIR_DEBUG("Dropping privileges to %u:%u, %d supplemental groups",
|
|
(unsigned int)user, (unsigned int)group, ngroups);
|
|
|
|
if (virSetUIDGID(user, group, groups, ngroups) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetChildProcessLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
virCommandPtr cmd)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr secdef;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (virSecurityDACGetIds(secdef, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
|
|
VIR_DEBUG("Setting child to drop privileges to %u:%u",
|
|
(unsigned int)user, (unsigned int)group);
|
|
|
|
virCommandSetUID(cmd, user);
|
|
virCommandSetGID(cmd, group);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACVerify(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACGenLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def)
|
|
{
|
|
int rc = -1;
|
|
virSecurityLabelDefPtr seclabel;
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
|
|
seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
if (seclabel == NULL)
|
|
return rc;
|
|
|
|
if (seclabel->imagelabel) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("security image label already "
|
|
"defined for VM"));
|
|
return rc;
|
|
}
|
|
|
|
if (seclabel->model
|
|
&& STRNEQ(seclabel->model, SECURITY_DAC_NAME)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("security label model %s is not supported "
|
|
"with selinux"),
|
|
seclabel->model);
|
|
return rc;
|
|
}
|
|
|
|
switch ((virDomainSeclabelType)seclabel->type) {
|
|
case VIR_DOMAIN_SECLABEL_STATIC:
|
|
if (seclabel->label == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("missing label for static security "
|
|
"driver in domain %s"), def->name);
|
|
return rc;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_SECLABEL_DYNAMIC:
|
|
seclabel->label = g_strdup_printf("+%u:+%u", (unsigned int)priv->user,
|
|
(unsigned int)priv->group);
|
|
if (seclabel->label == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot generate dac user and group id "
|
|
"for domain %s"), def->name);
|
|
return rc;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_SECLABEL_NONE:
|
|
/* no op */
|
|
return 0;
|
|
case VIR_DOMAIN_SECLABEL_DEFAULT:
|
|
case VIR_DOMAIN_SECLABEL_LAST:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected security label type '%s'"),
|
|
virDomainSeclabelTypeToString(seclabel->type));
|
|
return rc;
|
|
}
|
|
|
|
if (seclabel->relabel && !seclabel->imagelabel)
|
|
seclabel->imagelabel = g_strdup(seclabel->label);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACReleaseLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACReserveLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
pid_t pid G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
static int
|
|
virSecurityDACGetProcessLabelInternal(pid_t pid,
|
|
virSecurityLabelPtr seclabel)
|
|
{
|
|
struct stat sb;
|
|
char *path = NULL;
|
|
int ret = -1;
|
|
|
|
VIR_DEBUG("Getting DAC user and group on process '%d'", pid);
|
|
|
|
path = g_strdup_printf("/proc/%d", (int)pid);
|
|
|
|
if (g_lstat(path, &sb) < 0) {
|
|
virReportSystemError(errno,
|
|
_("unable to get uid and gid for PID %d via procfs"),
|
|
pid);
|
|
goto cleanup;
|
|
}
|
|
|
|
g_snprintf(seclabel->label, VIR_SECURITY_LABEL_BUFLEN,
|
|
"+%u:+%u", (unsigned int)sb.st_uid, (unsigned int)sb.st_gid);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(path);
|
|
return ret;
|
|
}
|
|
#elif defined(__FreeBSD__)
|
|
static int
|
|
virSecurityDACGetProcessLabelInternal(pid_t pid,
|
|
virSecurityLabelPtr seclabel)
|
|
{
|
|
struct kinfo_proc p;
|
|
int mib[4];
|
|
size_t len = 4;
|
|
|
|
sysctlnametomib("kern.proc.pid", mib, &len);
|
|
|
|
len = sizeof(struct kinfo_proc);
|
|
mib[3] = pid;
|
|
|
|
if (sysctl(mib, 4, &p, &len, NULL, 0) < 0) {
|
|
virReportSystemError(errno,
|
|
_("unable to get PID %d uid and gid via sysctl"),
|
|
pid);
|
|
return -1;
|
|
}
|
|
|
|
g_snprintf(seclabel->label, VIR_SECURITY_LABEL_BUFLEN,
|
|
"+%u:+%u", (unsigned int)p.ki_uid, (unsigned int)p.ki_groups[0]);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int
|
|
virSecurityDACGetProcessLabelInternal(pid_t pid G_GNUC_UNUSED,
|
|
virSecurityLabelPtr seclabel G_GNUC_UNUSED)
|
|
{
|
|
virReportSystemError(ENOSYS, "%s",
|
|
_("Cannot get process uid and gid on this platform"));
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
virSecurityDACGetProcessLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def,
|
|
pid_t pid,
|
|
virSecurityLabelPtr seclabel)
|
|
{
|
|
virSecurityLabelDefPtr secdef =
|
|
virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (secdef == NULL) {
|
|
VIR_DEBUG("missing label for DAC security "
|
|
"driver in domain %s", def->name);
|
|
|
|
if (virSecurityDACGetProcessLabelInternal(pid, seclabel) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
if (secdef->label)
|
|
ignore_value(virStrcpy(seclabel->label, secdef->label,
|
|
VIR_SECURITY_LABEL_BUFLEN));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACSetDaemonSocketLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr vm G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACSetSocketLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virSecurityDACClearSocketLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACSetImageFDLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
int fd G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACSetTapFDLabel(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
int fd G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
virSecurityDACGetMountOptions(virSecurityManagerPtr mgr G_GNUC_UNUSED,
|
|
virDomainDefPtr vm G_GNUC_UNUSED)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static const char *
|
|
virSecurityDACGetBaseLabel(virSecurityManagerPtr mgr,
|
|
int virt G_GNUC_UNUSED)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
return priv->baselabel;
|
|
}
|
|
|
|
static int
|
|
virSecurityDACDomainSetPathLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def,
|
|
const char *path,
|
|
bool allowSubtree G_GNUC_UNUSED)
|
|
{
|
|
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
|
|
virSecurityLabelDefPtr seclabel;
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
|
|
|
|
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
|
|
return -1;
|
|
|
|
return virSecurityDACSetOwnership(mgr, NULL, path, user, group, true);
|
|
}
|
|
|
|
static int
|
|
virSecurityDACDomainRestorePathLabel(virSecurityManagerPtr mgr,
|
|
virDomainDefPtr def G_GNUC_UNUSED,
|
|
const char *path)
|
|
{
|
|
return virSecurityDACRestoreFileLabel(mgr, path);
|
|
}
|
|
|
|
|
|
virSecurityDriver virSecurityDriverDAC = {
|
|
.privateDataLen = sizeof(virSecurityDACData),
|
|
.name = SECURITY_DAC_NAME,
|
|
.probe = virSecurityDACProbe,
|
|
.open = virSecurityDACOpen,
|
|
.close = virSecurityDACClose,
|
|
|
|
.getModel = virSecurityDACGetModel,
|
|
.getDOI = virSecurityDACGetDOI,
|
|
|
|
.preFork = virSecurityDACPreFork,
|
|
|
|
.transactionStart = virSecurityDACTransactionStart,
|
|
.transactionCommit = virSecurityDACTransactionCommit,
|
|
.transactionAbort = virSecurityDACTransactionAbort,
|
|
|
|
.domainSecurityVerify = virSecurityDACVerify,
|
|
|
|
.domainSetSecurityImageLabel = virSecurityDACSetImageLabel,
|
|
.domainRestoreSecurityImageLabel = virSecurityDACRestoreImageLabel,
|
|
.domainMoveImageMetadata = virSecurityDACMoveImageMetadata,
|
|
|
|
.domainSetSecurityMemoryLabel = virSecurityDACSetMemoryLabel,
|
|
.domainRestoreSecurityMemoryLabel = virSecurityDACRestoreMemoryLabel,
|
|
|
|
.domainSetSecurityInputLabel = virSecurityDACSetInputLabel,
|
|
.domainRestoreSecurityInputLabel = virSecurityDACRestoreInputLabel,
|
|
|
|
.domainSetSecurityDaemonSocketLabel = virSecurityDACSetDaemonSocketLabel,
|
|
.domainSetSecuritySocketLabel = virSecurityDACSetSocketLabel,
|
|
.domainClearSecuritySocketLabel = virSecurityDACClearSocketLabel,
|
|
|
|
.domainGenSecurityLabel = virSecurityDACGenLabel,
|
|
.domainReserveSecurityLabel = virSecurityDACReserveLabel,
|
|
.domainReleaseSecurityLabel = virSecurityDACReleaseLabel,
|
|
|
|
.domainGetSecurityProcessLabel = virSecurityDACGetProcessLabel,
|
|
.domainSetSecurityProcessLabel = virSecurityDACSetProcessLabel,
|
|
.domainSetSecurityChildProcessLabel = virSecurityDACSetChildProcessLabel,
|
|
|
|
.domainSetSecurityAllLabel = virSecurityDACSetAllLabel,
|
|
.domainRestoreSecurityAllLabel = virSecurityDACRestoreAllLabel,
|
|
|
|
.domainSetSecurityHostdevLabel = virSecurityDACSetHostdevLabel,
|
|
.domainRestoreSecurityHostdevLabel = virSecurityDACRestoreHostdevLabel,
|
|
|
|
.domainSetSecurityImageFDLabel = virSecurityDACSetImageFDLabel,
|
|
.domainSetSecurityTapFDLabel = virSecurityDACSetTapFDLabel,
|
|
|
|
.domainGetSecurityMountOptions = virSecurityDACGetMountOptions,
|
|
|
|
.getBaseLabel = virSecurityDACGetBaseLabel,
|
|
|
|
.domainSetPathLabel = virSecurityDACDomainSetPathLabel,
|
|
.domainRestorePathLabel = virSecurityDACDomainRestorePathLabel,
|
|
|
|
.domainSetSecurityChardevLabel = virSecurityDACSetChardevLabel,
|
|
.domainRestoreSecurityChardevLabel = virSecurityDACRestoreChardevLabel,
|
|
};
|