[libvirt] [PATCH v2 3/4] qemu: Create domain master key

Daniel P. Berrange berrange at redhat.com
Mon Apr 4 09:40:45 UTC 2016


On Tue, Mar 29, 2016 at 07:11:35PM -0400, John Ferlan wrote:
> 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.
> 
> Signed-off-by: John Ferlan <jferlan at redhat.com>
> ---
>  src/qemu/qemu_domain.c  | 252 ++++++++++++++++++++++++++++++++++++++++++++++++
>  src/qemu/qemu_domain.h  |  15 +++
>  src/qemu/qemu_process.c |  11 +++
>  3 files changed, 278 insertions(+)
> 
> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index 53cb2b6..99a91d2 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -44,6 +44,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 "storage/storage_driver.h"
> @@ -465,6 +471,252 @@ 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;
> +    }
> +
> +    masterKey[masterKeyLen] = '\0';
> +    ignore_value(VIR_REALLOC_N_QUIET(masterKey, masterKeyLen + 1));

We're storing the key as raw binary data, so NUL terminating
it doesn't make any sense - it may already contain embedded
NULs and so nothing should try strlen() on it. So just drop
that extra NUL byte,

> +    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;
> +}
> +


ACK with that fix above

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list