mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-28 00:25:23 +00:00
9c3748d3c2
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
677 lines
17 KiB
C
677 lines
17 KiB
C
/*
|
|
* secret_driver.c: local driver for secret manipulation API
|
|
*
|
|
* Copyright (C) 2009-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 <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "internal.h"
|
|
#include "datatypes.h"
|
|
#include "driver.h"
|
|
#include "virlog.h"
|
|
#include "viralloc.h"
|
|
#include "secret_conf.h"
|
|
#include "virsecretobj.h"
|
|
#include "secret_driver.h"
|
|
#include "virthread.h"
|
|
#include "viruuid.h"
|
|
#include "virerror.h"
|
|
#include "virfile.h"
|
|
#include "virpidfile.h"
|
|
#include "configmake.h"
|
|
#include "virstring.h"
|
|
#include "viraccessapicheck.h"
|
|
#include "secret_event.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_SECRET
|
|
|
|
VIR_LOG_INIT("secret.secret_driver");
|
|
|
|
enum { SECRET_MAX_XML_FILE = 10*1024*1024 };
|
|
|
|
/* Internal driver state */
|
|
|
|
typedef struct _virSecretDriverState virSecretDriverState;
|
|
typedef virSecretDriverState *virSecretDriverStatePtr;
|
|
struct _virSecretDriverState {
|
|
virMutex lock;
|
|
bool privileged; /* readonly */
|
|
virSecretObjListPtr secrets;
|
|
char *stateDir;
|
|
char *configDir;
|
|
|
|
/* pid file FD, ensures two copies of the driver can't use the same root */
|
|
int lockFD;
|
|
|
|
/* Immutable pointer, self-locking APIs */
|
|
virObjectEventStatePtr secretEventState;
|
|
};
|
|
|
|
static virSecretDriverStatePtr driver;
|
|
|
|
static void
|
|
secretDriverLock(void)
|
|
{
|
|
virMutexLock(&driver->lock);
|
|
}
|
|
|
|
|
|
static void
|
|
secretDriverUnlock(void)
|
|
{
|
|
virMutexUnlock(&driver->lock);
|
|
}
|
|
|
|
|
|
static virSecretObjPtr
|
|
secretObjFromSecret(virSecretPtr secret)
|
|
{
|
|
virSecretObjPtr obj;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virUUIDFormat(secret->uuid, uuidstr);
|
|
if (!(obj = virSecretObjListFindByUUID(driver->secrets, uuidstr))) {
|
|
virReportError(VIR_ERR_NO_SECRET,
|
|
_("no secret with matching uuid '%s'"), uuidstr);
|
|
return NULL;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
|
|
/* Driver functions */
|
|
|
|
static int
|
|
secretConnectNumOfSecrets(virConnectPtr conn)
|
|
{
|
|
if (virConnectNumOfSecretsEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virSecretObjListNumOfSecrets(driver->secrets,
|
|
virConnectNumOfSecretsCheckACL,
|
|
conn);
|
|
}
|
|
|
|
|
|
static int
|
|
secretConnectListSecrets(virConnectPtr conn,
|
|
char **uuids,
|
|
int maxuuids)
|
|
{
|
|
memset(uuids, 0, maxuuids * sizeof(*uuids));
|
|
|
|
if (virConnectListSecretsEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virSecretObjListGetUUIDs(driver->secrets, uuids, maxuuids,
|
|
virConnectListSecretsCheckACL, conn);
|
|
}
|
|
|
|
|
|
static int
|
|
secretConnectListAllSecrets(virConnectPtr conn,
|
|
virSecretPtr **secrets,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(VIR_CONNECT_LIST_SECRETS_FILTERS_ALL, -1);
|
|
|
|
if (virConnectListAllSecretsEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virSecretObjListExport(conn, driver->secrets, secrets,
|
|
virConnectListAllSecretsCheckACL,
|
|
flags);
|
|
}
|
|
|
|
|
|
static virSecretPtr
|
|
secretLookupByUUID(virConnectPtr conn,
|
|
const unsigned char *uuid)
|
|
{
|
|
virSecretPtr ret = NULL;
|
|
virSecretObjPtr obj;
|
|
virSecretDefPtr def;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virUUIDFormat(uuid, uuidstr);
|
|
if (!(obj = virSecretObjListFindByUUID(driver->secrets, uuidstr))) {
|
|
virReportError(VIR_ERR_NO_SECRET,
|
|
_("no secret with matching uuid '%s'"), uuidstr);
|
|
goto cleanup;
|
|
}
|
|
|
|
def = virSecretObjGetDef(obj);
|
|
if (virSecretLookupByUUIDEnsureACL(conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virGetSecret(conn,
|
|
def->uuid,
|
|
def->usage_type,
|
|
def->usage_id);
|
|
|
|
cleanup:
|
|
virSecretObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static virSecretPtr
|
|
secretLookupByUsage(virConnectPtr conn,
|
|
int usageType,
|
|
const char *usageID)
|
|
{
|
|
virSecretPtr ret = NULL;
|
|
virSecretObjPtr obj;
|
|
virSecretDefPtr def;
|
|
|
|
if (!(obj = virSecretObjListFindByUsage(driver->secrets,
|
|
usageType, usageID))) {
|
|
virReportError(VIR_ERR_NO_SECRET,
|
|
_("no secret with matching usage '%s'"), usageID);
|
|
goto cleanup;
|
|
}
|
|
|
|
def = virSecretObjGetDef(obj);
|
|
if (virSecretLookupByUsageEnsureACL(conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virGetSecret(conn,
|
|
def->uuid,
|
|
def->usage_type,
|
|
def->usage_id);
|
|
|
|
cleanup:
|
|
virSecretObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static virSecretPtr
|
|
secretDefineXML(virConnectPtr conn,
|
|
const char *xml,
|
|
unsigned int flags)
|
|
{
|
|
virSecretPtr ret = NULL;
|
|
virSecretObjPtr obj = NULL;
|
|
virSecretDefPtr objDef;
|
|
virSecretDefPtr backup = NULL;
|
|
virSecretDefPtr def;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(def = virSecretDefParseString(xml)))
|
|
return NULL;
|
|
|
|
if (virSecretDefineXMLEnsureACL(conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(obj = virSecretObjListAdd(driver->secrets, def,
|
|
driver->configDir, &backup)))
|
|
goto cleanup;
|
|
objDef = g_steal_pointer(&def);
|
|
|
|
if (!objDef->isephemeral) {
|
|
if (backup && backup->isephemeral) {
|
|
if (virSecretObjSaveData(obj) < 0)
|
|
goto restore_backup;
|
|
}
|
|
|
|
if (virSecretObjSaveConfig(obj) < 0) {
|
|
if (backup && backup->isephemeral) {
|
|
/* Undo the virSecretObjSaveData() above; ignore errors */
|
|
virSecretObjDeleteData(obj);
|
|
}
|
|
goto restore_backup;
|
|
}
|
|
} else if (backup && !backup->isephemeral) {
|
|
if (virSecretObjDeleteConfig(obj) < 0)
|
|
goto restore_backup;
|
|
|
|
virSecretObjDeleteData(obj);
|
|
}
|
|
/* Saved successfully - drop old values */
|
|
virSecretDefFree(backup);
|
|
|
|
event = virSecretEventLifecycleNew(objDef->uuid,
|
|
objDef->usage_type,
|
|
objDef->usage_id,
|
|
VIR_SECRET_EVENT_DEFINED,
|
|
0);
|
|
|
|
ret = virGetSecret(conn,
|
|
objDef->uuid,
|
|
objDef->usage_type,
|
|
objDef->usage_id);
|
|
goto cleanup;
|
|
|
|
restore_backup:
|
|
/* If we have a backup, then secret was defined before, so just restore
|
|
* the backup; otherwise, this is a new secret, thus remove it. */
|
|
if (backup) {
|
|
virSecretObjSetDef(obj, backup);
|
|
def = g_steal_pointer(&objDef);
|
|
} else {
|
|
virSecretObjListRemove(driver->secrets, obj);
|
|
virObjectUnref(obj);
|
|
obj = NULL;
|
|
}
|
|
|
|
cleanup:
|
|
virSecretDefFree(def);
|
|
virSecretObjEndAPI(&obj);
|
|
virObjectEventStateQueue(driver->secretEventState, event);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
secretGetXMLDesc(virSecretPtr secret,
|
|
unsigned int flags)
|
|
{
|
|
char *ret = NULL;
|
|
virSecretObjPtr obj;
|
|
virSecretDefPtr def;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(obj = secretObjFromSecret(secret)))
|
|
goto cleanup;
|
|
|
|
def = virSecretObjGetDef(obj);
|
|
if (virSecretGetXMLDescEnsureACL(secret->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virSecretDefFormat(def);
|
|
|
|
cleanup:
|
|
virSecretObjEndAPI(&obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
secretSetValue(virSecretPtr secret,
|
|
const unsigned char *value,
|
|
size_t value_size,
|
|
unsigned int flags)
|
|
{
|
|
int ret = -1;
|
|
virSecretObjPtr obj;
|
|
virSecretDefPtr def;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(obj = secretObjFromSecret(secret)))
|
|
goto cleanup;
|
|
|
|
def = virSecretObjGetDef(obj);
|
|
if (virSecretSetValueEnsureACL(secret->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virSecretObjSetValue(obj, value, value_size) < 0)
|
|
goto cleanup;
|
|
|
|
event = virSecretEventValueChangedNew(def->uuid,
|
|
def->usage_type,
|
|
def->usage_id);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virSecretObjEndAPI(&obj);
|
|
virObjectEventStateQueue(driver->secretEventState, event);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static unsigned char *
|
|
secretGetValue(virSecretPtr secret,
|
|
size_t *value_size,
|
|
unsigned int flags,
|
|
unsigned int internalFlags)
|
|
{
|
|
unsigned char *ret = NULL;
|
|
virSecretObjPtr obj;
|
|
virSecretDefPtr def;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(obj = secretObjFromSecret(secret)))
|
|
goto cleanup;
|
|
|
|
def = virSecretObjGetDef(obj);
|
|
if (virSecretGetValueEnsureACL(secret->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if ((internalFlags & VIR_SECRET_GET_VALUE_INTERNAL_CALL) == 0 &&
|
|
def->isprivate) {
|
|
virReportError(VIR_ERR_INVALID_SECRET, "%s",
|
|
_("secret is private"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(ret = virSecretObjGetValue(obj)))
|
|
goto cleanup;
|
|
|
|
*value_size = virSecretObjGetValueSize(obj);
|
|
|
|
cleanup:
|
|
virSecretObjEndAPI(&obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
secretUndefine(virSecretPtr secret)
|
|
{
|
|
int ret = -1;
|
|
virSecretObjPtr obj;
|
|
virSecretDefPtr def;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
if (!(obj = secretObjFromSecret(secret)))
|
|
goto cleanup;
|
|
|
|
def = virSecretObjGetDef(obj);
|
|
if (virSecretUndefineEnsureACL(secret->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virSecretObjDeleteConfig(obj) < 0)
|
|
goto cleanup;
|
|
|
|
event = virSecretEventLifecycleNew(def->uuid,
|
|
def->usage_type,
|
|
def->usage_id,
|
|
VIR_SECRET_EVENT_UNDEFINED,
|
|
0);
|
|
|
|
virSecretObjDeleteData(obj);
|
|
|
|
virSecretObjListRemove(driver->secrets, obj);
|
|
virObjectUnref(obj);
|
|
obj = NULL;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virSecretObjEndAPI(&obj);
|
|
virObjectEventStateQueue(driver->secretEventState, event);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
secretStateCleanup(void)
|
|
{
|
|
if (!driver)
|
|
return -1;
|
|
|
|
secretDriverLock();
|
|
|
|
virObjectUnref(driver->secrets);
|
|
VIR_FREE(driver->configDir);
|
|
|
|
virObjectUnref(driver->secretEventState);
|
|
|
|
if (driver->lockFD != -1)
|
|
virPidFileRelease(driver->stateDir, "driver", driver->lockFD);
|
|
|
|
VIR_FREE(driver->stateDir);
|
|
secretDriverUnlock();
|
|
virMutexDestroy(&driver->lock);
|
|
VIR_FREE(driver);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
secretStateInitialize(bool privileged,
|
|
virStateInhibitCallback callback G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
if (VIR_ALLOC(driver) < 0)
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
|
|
|
driver->lockFD = -1;
|
|
if (virMutexInit(&driver->lock) < 0) {
|
|
VIR_FREE(driver);
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
|
}
|
|
secretDriverLock();
|
|
|
|
driver->secretEventState = virObjectEventStateNew();
|
|
driver->privileged = privileged;
|
|
|
|
if (privileged) {
|
|
driver->configDir = g_strdup_printf("%s/libvirt/secrets", SYSCONFDIR);
|
|
driver->stateDir = g_strdup_printf("%s/libvirt/secrets", RUNSTATEDIR);
|
|
} else {
|
|
g_autofree char *rundir = NULL;
|
|
g_autofree char *cfgdir = NULL;
|
|
|
|
if (!(cfgdir = virGetUserConfigDirectory()))
|
|
goto error;
|
|
driver->configDir = g_strdup_printf("%s/secrets/", cfgdir);
|
|
|
|
if (!(rundir = virGetUserRuntimeDirectory()))
|
|
goto error;
|
|
driver->stateDir = g_strdup_printf("%s/secrets/run", rundir);
|
|
}
|
|
|
|
if (virFileMakePathWithMode(driver->configDir, S_IRWXU) < 0) {
|
|
virReportSystemError(errno, _("cannot create config directory '%s'"),
|
|
driver->configDir);
|
|
goto error;
|
|
}
|
|
|
|
if (virFileMakePathWithMode(driver->stateDir, S_IRWXU) < 0) {
|
|
virReportSystemError(errno, _("cannot create state directory '%s'"),
|
|
driver->stateDir);
|
|
goto error;
|
|
}
|
|
|
|
if ((driver->lockFD =
|
|
virPidFileAcquire(driver->stateDir, "driver", false, getpid())) < 0)
|
|
goto error;
|
|
|
|
if (!(driver->secrets = virSecretObjListNew()))
|
|
goto error;
|
|
|
|
if (virSecretLoadAllConfigs(driver->secrets, driver->configDir) < 0)
|
|
goto error;
|
|
|
|
secretDriverUnlock();
|
|
return VIR_DRV_STATE_INIT_COMPLETE;
|
|
|
|
error:
|
|
secretDriverUnlock();
|
|
secretStateCleanup();
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
|
}
|
|
|
|
|
|
static int
|
|
secretStateReload(void)
|
|
{
|
|
if (!driver)
|
|
return -1;
|
|
|
|
secretDriverLock();
|
|
|
|
ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDir));
|
|
|
|
secretDriverUnlock();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virDrvOpenStatus
|
|
secretConnectOpen(virConnectPtr conn,
|
|
virConnectAuthPtr auth G_GNUC_UNUSED,
|
|
virConfPtr conf G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
|
|
|
|
if (driver == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("secret state driver is not active"));
|
|
return VIR_DRV_OPEN_ERROR;
|
|
}
|
|
|
|
if (!virConnectValidateURIPath(conn->uri->path,
|
|
"secret",
|
|
driver->privileged))
|
|
return VIR_DRV_OPEN_ERROR;
|
|
|
|
if (virConnectOpenEnsureACL(conn) < 0)
|
|
return VIR_DRV_OPEN_ERROR;
|
|
|
|
return VIR_DRV_OPEN_SUCCESS;
|
|
}
|
|
|
|
static int secretConnectClose(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int secretConnectIsSecure(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
/* Trivially secure, since always inside the daemon */
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int secretConnectIsEncrypted(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
/* Not encrypted, but remote driver takes care of that */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int secretConnectIsAlive(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
secretConnectSecretEventRegisterAny(virConnectPtr conn,
|
|
virSecretPtr secret,
|
|
int eventID,
|
|
virConnectSecretEventGenericCallback callback,
|
|
void *opaque,
|
|
virFreeCallback freecb)
|
|
{
|
|
int callbackID = -1;
|
|
|
|
if (virConnectSecretEventRegisterAnyEnsureACL(conn) < 0)
|
|
goto cleanup;
|
|
|
|
if (virSecretEventStateRegisterID(conn, driver->secretEventState,
|
|
secret, eventID, callback,
|
|
opaque, freecb, &callbackID) < 0)
|
|
callbackID = -1;
|
|
cleanup:
|
|
return callbackID;
|
|
}
|
|
|
|
|
|
static int
|
|
secretConnectSecretEventDeregisterAny(virConnectPtr conn,
|
|
int callbackID)
|
|
{
|
|
if (virConnectSecretEventDeregisterAnyEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
if (virObjectEventStateDeregisterID(conn,
|
|
driver->secretEventState,
|
|
callbackID, true) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virSecretDriver secretDriver = {
|
|
.name = "secret",
|
|
.connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */
|
|
.connectListSecrets = secretConnectListSecrets, /* 0.7.1 */
|
|
.connectListAllSecrets = secretConnectListAllSecrets, /* 0.10.2 */
|
|
.secretLookupByUUID = secretLookupByUUID, /* 0.7.1 */
|
|
.secretLookupByUsage = secretLookupByUsage, /* 0.7.1 */
|
|
.secretDefineXML = secretDefineXML, /* 0.7.1 */
|
|
.secretGetXMLDesc = secretGetXMLDesc, /* 0.7.1 */
|
|
.secretSetValue = secretSetValue, /* 0.7.1 */
|
|
.secretGetValue = secretGetValue, /* 0.7.1 */
|
|
.secretUndefine = secretUndefine, /* 0.7.1 */
|
|
.connectSecretEventRegisterAny = secretConnectSecretEventRegisterAny, /* 3.0.0 */
|
|
.connectSecretEventDeregisterAny = secretConnectSecretEventDeregisterAny, /* 3.0.0 */
|
|
};
|
|
|
|
|
|
static virHypervisorDriver secretHypervisorDriver = {
|
|
.name = "secret",
|
|
.connectOpen = secretConnectOpen, /* 4.1.0 */
|
|
.connectClose = secretConnectClose, /* 4.1.0 */
|
|
.connectIsEncrypted = secretConnectIsEncrypted, /* 4.1.0 */
|
|
.connectIsSecure = secretConnectIsSecure, /* 4.1.0 */
|
|
.connectIsAlive = secretConnectIsAlive, /* 4.1.0 */
|
|
};
|
|
|
|
|
|
static virConnectDriver secretConnectDriver = {
|
|
.localOnly = true,
|
|
.uriSchemes = (const char *[]){ "secret", NULL },
|
|
.hypervisorDriver = &secretHypervisorDriver,
|
|
.secretDriver = &secretDriver,
|
|
};
|
|
|
|
|
|
static virStateDriver stateDriver = {
|
|
.name = "secret",
|
|
.stateInitialize = secretStateInitialize,
|
|
.stateCleanup = secretStateCleanup,
|
|
.stateReload = secretStateReload,
|
|
};
|
|
|
|
|
|
int
|
|
secretRegister(void)
|
|
{
|
|
if (virRegisterConnectDriver(&secretConnectDriver, false) < 0)
|
|
return -1;
|
|
if (virSetSharedSecretDriver(&secretDriver) < 0)
|
|
return -1;
|
|
if (virRegisterStateDriver(&stateDriver) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|