/* * 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 * . */ #include #include #include #include #include "internal.h" #include "base64.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; VIR_STEAL_PTR(objDef, 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); VIR_STEAL_PTR(def, 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 ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) { if (VIR_ALLOC(driver) < 0) return -1; driver->lockFD = -1; if (virMutexInit(&driver->lock) < 0) { VIR_FREE(driver); return -1; } secretDriverLock(); driver->secretEventState = virObjectEventStateNew(); driver->privileged = privileged; if (privileged) { if (virAsprintf(&driver->configDir, "%s/libvirt/secrets", SYSCONFDIR) < 0) goto error; if (virAsprintf(&driver->stateDir, "%s/run/libvirt/secrets", LOCALSTATEDIR) < 0) goto error; } else { VIR_AUTOFREE(char *) rundir = NULL; VIR_AUTOFREE(char *) cfgdir = NULL; if (!(cfgdir = virGetUserConfigDirectory())) goto error; if (virAsprintf(&driver->configDir, "%s/secrets/", cfgdir) < 0) goto error; if (!(rundir = virGetUserRuntimeDirectory())) goto error; if (virAsprintf(&driver->stateDir, "%s/secrets/run", rundir) < 0) goto error; } 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 0; error: secretDriverUnlock(); secretStateCleanup(); return -1; } 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 ATTRIBUTE_UNUSED, virConfPtr conf ATTRIBUTE_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 (driver->privileged) { if (STRNEQ(conn->uri->path, "/system")) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected secret URI path '%s', try secret:///system"), conn->uri->path); return VIR_DRV_OPEN_ERROR; } } else { if (STRNEQ(conn->uri->path, "/session")) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected secret URI path '%s', try secret:///session"), conn->uri->path); return VIR_DRV_OPEN_ERROR; } } if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; return VIR_DRV_OPEN_SUCCESS; } static int secretConnectClose(virConnectPtr conn ATTRIBUTE_UNUSED) { return 0; } static int secretConnectIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED) { /* Trivially secure, since always inside the daemon */ return 1; } static int secretConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED) { /* Not encrypted, but remote driver takes care of that */ return 0; } static int secretConnectIsAlive(virConnectPtr conn ATTRIBUTE_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) { int ret = -1; if (virConnectSecretEventDeregisterAnyEnsureACL(conn) < 0) goto cleanup; if (virObjectEventStateDeregisterID(conn, driver->secretEventState, callbackID, true) < 0) goto cleanup; ret = 0; cleanup: return ret; } 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; }