mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-27 19:11:16 +00:00
qemu: Create domain master key
Add a masterKey and masterKeyLen to _qemuDomainObjPrivate to store a random domain master key and its length in order to support the ability to encrypt/decrypt sensitive data shared between libvirt and qemu. The key will be base64 encoded and written to a file to be used by the command line building code to share with qemu. New API's from this patch: qemuDomainGetMasterKeyFilePath: Return a path to where the key is located qemuDomainWriteMasterKeyFile: (private) Open (create/trunc) the masterKey path and write the masterKey qemuDomainMasterKeyReadFile: Using the master key path, open/read the file, and store the masterKey and masterKeyLen. Expected use only from qemuProcessReconnect qemuDomainGenerateRandomKey: (private) Generate a random key using available algorithms The key is generated either from the gnutls_rnd function if it exists or a less cryptographically strong mechanism using virGenerateRandomBytes qemuDomainMasterKeyRemove: Remove traces of the master key, remove the *KeyFilePath qemuDomainMasterKeyCreate: Generate the domain master key and save the key in the location returned by qemuDomainGetMasterKeyFilePath. This API will first ensure the QEMU_CAPS_OBJECT_SECRET is set in the capabilities. If not, then there's no need to generate the secret or file. The creation of the key will be attempted from qemuProcessPrepareHost once the libDir directory structure exists. The removal of the key will handled from qemuProcessStop just prior to deleting the libDir tree. Since the key will not be written out to the domain object XML file, the qemuProcessReconnect will read the saved file and restore the masterKey and masterKeyLen.
This commit is contained in:
parent
6af73f53c6
commit
ad7520e83f
@ -45,6 +45,12 @@
|
||||
#include "virthreadjob.h"
|
||||
#include "viratomic.h"
|
||||
#include "virprocess.h"
|
||||
#include "virrandom.h"
|
||||
#include "base64.h"
|
||||
#include <gnutls/gnutls.h>
|
||||
#if HAVE_GNUTLS_CRYPTO_H
|
||||
# include <gnutls/crypto.h>
|
||||
#endif
|
||||
#include "logging/log_manager.h"
|
||||
#include "locking/domain_lock.h"
|
||||
|
||||
@ -467,6 +473,251 @@ qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
|
||||
}
|
||||
|
||||
|
||||
/* qemuDomainGetMasterKeyFilePath:
|
||||
* @libDir: Directory path to domain lib files
|
||||
*
|
||||
* Generate a path to the domain master key file for libDir.
|
||||
* It's up to the caller to handle checking if path exists.
|
||||
*
|
||||
* Returns path to memory containing the name of the file. It is up to the
|
||||
* caller to free; otherwise, NULL on failure.
|
||||
*/
|
||||
char *
|
||||
qemuDomainGetMasterKeyFilePath(const char *libDir)
|
||||
{
|
||||
if (!libDir) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("invalid path for master key file"));
|
||||
return NULL;
|
||||
}
|
||||
return virFileBuildPath(libDir, "master-key.aes", NULL);
|
||||
}
|
||||
|
||||
|
||||
/* qemuDomainWriteMasterKeyFile:
|
||||
* @priv: pointer to domain private object
|
||||
*
|
||||
* Get the desired path to the masterKey file and store it in the path.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure with error message indicating failure
|
||||
*/
|
||||
static int
|
||||
qemuDomainWriteMasterKeyFile(qemuDomainObjPrivatePtr priv)
|
||||
{
|
||||
char *path;
|
||||
int fd = -1;
|
||||
int ret = -1;
|
||||
|
||||
if (!(path = qemuDomainGetMasterKeyFilePath(priv->libDir)))
|
||||
return -1;
|
||||
|
||||
if ((fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0600)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to open domain master key file for write"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (safewrite(fd, priv->masterKey, priv->masterKeyLen) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to write master key file for domain"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FORCE_CLOSE(fd);
|
||||
VIR_FREE(path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* qemuDomainMasterKeyReadFile:
|
||||
* @priv: pointer to domain private object
|
||||
*
|
||||
* Expected to be called during qemuProcessReconnect once the domain
|
||||
* libDir has been generated through qemuStateInitialize calling
|
||||
* virDomainObjListLoadAllConfigs which will restore the libDir path
|
||||
* to the domain private object.
|
||||
*
|
||||
* This function will get the path to the master key file and if it
|
||||
* exists, it will read the contents of the file saving it in priv->masterKey.
|
||||
*
|
||||
* Once the file exists, the validity checks may cause failures; however,
|
||||
* if the file doesn't exist or the capability doesn't exist, we just
|
||||
* return (mostly) quietly.
|
||||
*
|
||||
* Returns 0 on success or lack of capability
|
||||
* -1 on failure with error message indicating failure
|
||||
*/
|
||||
int
|
||||
qemuDomainMasterKeyReadFile(qemuDomainObjPrivatePtr priv)
|
||||
{
|
||||
char *path;
|
||||
int fd = -1;
|
||||
uint8_t *masterKey = NULL;
|
||||
ssize_t masterKeyLen = 0;
|
||||
|
||||
/* If we don't have the capability, then do nothing. */
|
||||
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET))
|
||||
return 0;
|
||||
|
||||
if (!(path = qemuDomainGetMasterKeyFilePath(priv->libDir)))
|
||||
return -1;
|
||||
|
||||
if (!virFileExists(path)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("domain master key file doesn't exist in %s"),
|
||||
priv->libDir);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((fd = open(path, O_RDONLY)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to open domain master key file for read"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (VIR_ALLOC_N(masterKey, 1024) < 0)
|
||||
goto error;
|
||||
|
||||
if ((masterKeyLen = saferead(fd, masterKey, 1024)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("unable to read domain master key file"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (masterKeyLen != QEMU_DOMAIN_MASTER_KEY_LEN) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("invalid master key read, size=%zd"), masterKeyLen);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ignore_value(VIR_REALLOC_N_QUIET(masterKey, masterKeyLen));
|
||||
|
||||
priv->masterKey = masterKey;
|
||||
priv->masterKeyLen = masterKeyLen;
|
||||
|
||||
VIR_FORCE_CLOSE(fd);
|
||||
VIR_FREE(path);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (masterKeyLen > 0)
|
||||
memset(masterKey, 0, masterKeyLen);
|
||||
VIR_FREE(masterKey);
|
||||
|
||||
VIR_FORCE_CLOSE(fd);
|
||||
VIR_FREE(path);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* qemuDomainGenerateRandomKey
|
||||
* @nbytes: Size in bytes of random key to generate
|
||||
*
|
||||
* Generate a random key of nbytes length and return it.
|
||||
*
|
||||
* Since the gnutls_rnd could be missing, provide an alternate less
|
||||
* secure mechanism to at least have something.
|
||||
*
|
||||
* Returns pointer memory containing key on success, NULL on failure
|
||||
*/
|
||||
static uint8_t *
|
||||
qemuDomainGenerateRandomKey(size_t nbytes)
|
||||
{
|
||||
uint8_t *key;
|
||||
int ret;
|
||||
|
||||
if (VIR_ALLOC_N(key, nbytes) < 0)
|
||||
return NULL;
|
||||
|
||||
#if HAVE_GNUTLS_CRYPTO_H
|
||||
/* Generate a master key using gnutls if possible */
|
||||
if ((ret = gnutls_rnd(GNUTLS_RND_RANDOM, key, nbytes)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to generate master key, ret=%d"), ret);
|
||||
VIR_FREE(key);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
/* If we don't have gnutls, we will generate a less cryptographically
|
||||
* strong master key from /dev/urandom.
|
||||
*/
|
||||
if ((ret = virRandomBytes(key, nbytes)) < 0) {
|
||||
virReportSystemError(ret, "%s", _("failed to generate master key"));
|
||||
VIR_FREE(key);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
/* qemuDomainMasterKeyRemove:
|
||||
* @priv: Pointer to the domain private object
|
||||
*
|
||||
* Remove the traces of the master key, clear the heap, clear the file,
|
||||
* delete the file.
|
||||
*/
|
||||
void
|
||||
qemuDomainMasterKeyRemove(qemuDomainObjPrivatePtr priv)
|
||||
{
|
||||
char *path = NULL;
|
||||
|
||||
if (!priv->masterKey)
|
||||
return;
|
||||
|
||||
/* Clear the contents */
|
||||
memset(priv->masterKey, 0, priv->masterKeyLen);
|
||||
VIR_FREE(priv->masterKey);
|
||||
priv->masterKeyLen = 0;
|
||||
|
||||
/* Delete the master key file */
|
||||
path = qemuDomainGetMasterKeyFilePath(priv->libDir);
|
||||
unlink(path);
|
||||
|
||||
VIR_FREE(path);
|
||||
}
|
||||
|
||||
|
||||
/* qemuDomainMasterKeyCreate:
|
||||
* @priv: Pointer to the domain private object
|
||||
*
|
||||
* As long as the underlying qemu has the secret capability,
|
||||
* generate and store 'raw' in a file a random 32-byte key to
|
||||
* be used as a secret shared with qemu to share sensitive data.
|
||||
*
|
||||
* Returns: 0 on success, -1 w/ error message on failure
|
||||
*/
|
||||
int
|
||||
qemuDomainMasterKeyCreate(qemuDomainObjPrivatePtr priv)
|
||||
{
|
||||
/* If we don't have the capability, then do nothing. */
|
||||
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET))
|
||||
return 0;
|
||||
|
||||
if (!(priv->masterKey =
|
||||
qemuDomainGenerateRandomKey(QEMU_DOMAIN_MASTER_KEY_LEN)))
|
||||
goto error;
|
||||
|
||||
priv->masterKeyLen = QEMU_DOMAIN_MASTER_KEY_LEN;
|
||||
|
||||
if (qemuDomainWriteMasterKeyFile(priv) < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
qemuDomainMasterKeyRemove(priv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static virClassPtr qemuDomainDiskPrivateClass;
|
||||
|
||||
static int
|
||||
|
@ -148,6 +148,7 @@ struct qemuDomainJobObj {
|
||||
typedef void (*qemuDomainCleanupCallback)(virQEMUDriverPtr driver,
|
||||
virDomainObjPtr vm);
|
||||
|
||||
# define QEMU_DOMAIN_MASTER_KEY_LEN 32 /* 32 bytes for 256 bit random key */
|
||||
typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate;
|
||||
typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr;
|
||||
struct _qemuDomainObjPrivate {
|
||||
@ -215,6 +216,11 @@ struct _qemuDomainObjPrivate {
|
||||
char *machineName;
|
||||
char *libDir; /* base path for per-domain files */
|
||||
char *channelTargetDir; /* base path for per-domain channel targets */
|
||||
|
||||
/* random masterKey and length for encryption (not to be saved in our */
|
||||
/* private XML) - need to restore at process reconnect */
|
||||
uint8_t *masterKey;
|
||||
size_t masterKeyLen;
|
||||
};
|
||||
|
||||
# define QEMU_DOMAIN_DISK_PRIVATE(disk) \
|
||||
@ -558,4 +564,12 @@ void qemuDomainClearPrivatePaths(virDomainObjPtr vm);
|
||||
|
||||
virDomainDiskDefPtr qemuDomainDiskByName(virDomainDefPtr def, const char *name);
|
||||
|
||||
char *qemuDomainGetMasterKeyFilePath(const char *libDir);
|
||||
|
||||
int qemuDomainMasterKeyReadFile(qemuDomainObjPrivatePtr priv);
|
||||
|
||||
int qemuDomainMasterKeyCreate(qemuDomainObjPrivatePtr priv);
|
||||
|
||||
void qemuDomainMasterKeyRemove(qemuDomainObjPrivatePtr priv);
|
||||
|
||||
#endif /* __QEMU_DOMAIN_H__ */
|
||||
|
@ -3509,6 +3509,10 @@ qemuProcessReconnect(void *opaque)
|
||||
if (!(priv->pidfile = virPidFileBuildPath(cfg->stateDir, obj->def->name)))
|
||||
goto error;
|
||||
|
||||
/* Restore the masterKey */
|
||||
if (qemuDomainMasterKeyReadFile(priv) < 0)
|
||||
goto error;
|
||||
|
||||
virNWFilterReadLockFilterUpdates();
|
||||
|
||||
VIR_DEBUG("Reconnect monitor to %p '%s'", obj, obj->def->name);
|
||||
@ -5143,6 +5147,10 @@ qemuProcessPrepareHost(virQEMUDriverPtr driver,
|
||||
qemuProcessMakeDir(driver, vm, priv->channelTargetDir) < 0)
|
||||
goto cleanup;
|
||||
|
||||
VIR_DEBUG("Create domain masterKey");
|
||||
if (qemuDomainMasterKeyCreate(priv) < 0)
|
||||
goto cleanup;
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
virObjectUnref(cfg);
|
||||
@ -5829,6 +5837,9 @@ void qemuProcessStop(virQEMUDriverPtr driver,
|
||||
priv->monConfig = NULL;
|
||||
}
|
||||
|
||||
/* Remove the master key */
|
||||
qemuDomainMasterKeyRemove(priv);
|
||||
|
||||
virFileDeleteTree(priv->libDir);
|
||||
virFileDeleteTree(priv->channelTargetDir);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user