[libvirt] [PATCH 12/12] qemu: Utilize qemu secret objects for SCSI/RBD auth/secret

John Ferlan jferlan at redhat.com
Tue Apr 12 14:42:45 UTC 2016


https://bugzilla.redhat.com/show_bug.cgi?id=1182074

If they're available and we need to pass secrets to qemu, then use the
qemu domain secret object in order to pass the secrets for iSCSI and
RBD volumes instead of passing plaintext or base64 encoded secrets on
the command line.

New APIs:

  qemuDomainGetIVKeyAlias:
    Generate/return the secret object alias for an initialization
    vector (IV) secret info type. This will be saved in the secret
    info block.

  qemuDomainSecretInfoGetAlias:
    Return a pointer to the alias to the specific secret info as
    long as the secret object is supported (future patches may add
    a new secret info type with a different pointer to return).

  qemuDomainSecretHaveEncrypt:
    Boolean function to determine whether the underly encryption
    API is available. This function will utilize a similar mechanism
    as the 'gnutls_rnd' did in configure.ac. This function creates
    the encrypted secret based upon the domain master key, an
    initialization vector (16 byte random value), and the stored secret.

  qemuDomainSecretIVSetup: (private)
    This API handles the details of the generation of the IV secret
    and saves the pieces that need to be passed to qemu in order for
    the secret to be decrypted. The requirement from qemu is the IV
    and encrypted secret are to be base64 encoded. They can be passed
    either directly or within a file. This implementation chooses
    to pass directly rather than a file (file passing is shown below).

  qemuBuildSecretInfoProps:
    Generate/return a JSON properties object for the IV secret to
    be used by both the command building and hotplug code in order
    to add the secret object.

Changes for qemuDomainSecret{Disk|Hostdev}Prepare:

  If both the encryption API exists and the secret object exist, then
  setup the IV secret (qemuDomainSecretIVSetup) as the default means
  for the disk/hostdev to provide the secret to qemu. Prior to command
  line generation and during hotplug, these prepare API's are called
  allowing for code after the API to perform the right steps.

Command Building:

  Adjust the qemuBuild{General|RBD}SecinfoURI API's in order to generate
  the specific command options for an IV secret, such as:

  For iSCSI:

    -object secret,id=sec0,keyid=$masterKey,filename=/path/to/example.pw

       or

    -object secret,id=sec0,keyid=$masterKey,data=$base64encodedencrypted,
            format=base64

    -drive driver=iscsi,url=iscsi://example.com/target-foo/lun1,
           user=dan,password-secret=sec0

  For RBD:

    -object secret,id=secret0,keyid=$masterKey,file=/path/to/poolkey.b64,
            format=base64

       or

    -object secret,id=secret0,keyid=$masterKey,data=$base64encodedencrypted,
            format=base64

    -drive driver=rbd,filename=rbd:pool/image:id=myname:
           auth_supported=cephx,password-secret=secret0

  where for both 'id=' value is the secret object alias, the 'keyid=
  $masterKey' is the master key shared with qemu, and the -drive
  syntax will reference that alias as the 'password-secret'. For
  the iSCSI object 'user=' replaces the URI generated 'user:secret@'
  prepended to the iSCSI 'host' name (example.com). For the RBD -drive
  syntax, the 'id=myname' is kept to define the username, while the
  'key=$base64 encoded secret' is removed.

  While according to the syntax described for qemu commits 'b189346eb'
  (iSCSI) and '60390a21' (RBD) or as seen in the email archive:

    https://lists.gnu.org/archive/html/qemu-devel/2016-01/msg04083.html

  it is possible to pass a plaintext password via a file, the qemu
  commit 'ac1d8878' describes the more feature rich 'keyid=' option
  based upon the shared masterKey.

Hotplug Adjustments:
  Once the qemuDomainSecret{Disk|Hostdev}Prepare completes successfully,
  a check (qemuDomainSecretInfoGetAlias) to determine whether the IV
  secret alias is available results in generating the JSON props (or not).
  (via qemuBuildSecretInfoProps). If the secret alias object exists, then
  prior to adding the device and drive, the secret object will be added
  to define the 'password-secret' parameter.

The goal is to make this the default and have no user interaction
required in order to allow using the IV mechanism. If the mechanism
is not available, then fall back to the current mechanism.

Signed-off-by: John Ferlan <jferlan at redhat.com>
---
 configure.ac            |   1 +
 src/qemu/qemu_alias.c   |  23 ++++++
 src/qemu/qemu_alias.h   |   2 +
 src/qemu/qemu_command.c | 126 ++++++++++++++++++++++++++++++
 src/qemu/qemu_command.h |   4 +
 src/qemu/qemu_domain.c  | 199 +++++++++++++++++++++++++++++++++++++++++++++---
 src/qemu/qemu_domain.h  |   3 +
 src/qemu/qemu_hotplug.c |  72 +++++++++++++++++-
 8 files changed, 418 insertions(+), 12 deletions(-)

diff --git a/configure.ac b/configure.ac
index 1eb19ee..83a1043 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1295,6 +1295,7 @@ if test "x$with_gnutls" != "xno"; then
   ]])
 
   AC_CHECK_FUNCS([gnutls_rnd])
+  AC_CHECK_FUNCS([gnutls_cipher_encrypt])
 
   CFLAGS="$old_CFLAGS"
   LIBS="$old_LIBS"
diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c
index ade2033..de9a74f 100644
--- a/src/qemu/qemu_alias.c
+++ b/src/qemu/qemu_alias.c
@@ -556,3 +556,26 @@ qemuDomainGetMasterKeyAlias(void)
 
     return alias;
 }
+
+
+/* qemuDomainGetIVKeyAlias:
+ *
+ * Generate and return an initialization vector alias
+ *
+ * Returns NULL or a string containing the IV key alias
+ */
+char *
+qemuDomainGetIVKeyAlias(const char *srcalias)
+{
+    char *alias;
+
+    if (!srcalias) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("secret iv alias requires valid source alias"));
+        return NULL;
+    }
+
+    ignore_value(virAsprintf(&alias, "%s-ivKey0", srcalias));
+
+    return alias;
+}
diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h
index 2d7bc9b..435747e 100644
--- a/src/qemu/qemu_alias.h
+++ b/src/qemu/qemu_alias.h
@@ -69,4 +69,6 @@ char *qemuAliasFromDisk(const virDomainDiskDef *disk);
 
 char *qemuDomainGetMasterKeyAlias(void);
 
+char *qemuDomainGetIVKeyAlias(const char *srcalias);
+
 #endif /* __QEMU_ALIAS_H__*/
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 3a69bd5..9b9e9a0 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -639,6 +639,10 @@ qemuBuildGeneralSecinfoURI(virURIPtr uri,
         break;
 
     case VIR_DOMAIN_SECRET_INFO_IV:
+        if (VIR_STRDUP(uri->user, secinfo->s.iv.username) < 0)
+            return -1;
+        break;
+
     case VIR_DOMAIN_SECRET_INFO_LAST:
         return -1;
     }
@@ -677,6 +681,10 @@ qemuBuildRBDSecinfoURI(virBufferPtr buf,
         break;
 
     case VIR_DOMAIN_SECRET_INFO_IV:
+        virBufferEscape(buf, '\\', ":", ":id=%s:auth_supported=cephx\\;none",
+                        secinfo->s.iv.username);
+        break;
+
     case VIR_DOMAIN_SECRET_INFO_LAST:
         return -1;
     }
@@ -809,6 +817,13 @@ qemuBuildNetworkDriveURI(virStorageSourcePtr src,
 
             ret = virURIFormat(uri);
 
+            if (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI &&
+                secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_IV) {
+                virBufferAsprintf(&buf, "%s,user=%s", ret,
+                                  secinfo->s.iv.username);
+                VIR_FREE(ret);
+                ret = virBufferContentAndReset(&buf);
+            }
             break;
 
         case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
@@ -1069,6 +1084,97 @@ qemuCheckFips(void)
 }
 
 
+/**
+ * qemuBuildSecretInfoProps:
+ * @secinfo: pointer to the secret info object
+ * @type: returns a pointer to a character string for object name
+ * @props: json properties to return
+ *
+ * Build the JSON properties for the secret info type.
+ *
+ * Returns 0 on success with the filled in JSON property; otherwise,
+ * returns -1 on failure error message set.
+ */
+int
+qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo,
+                         const char **type,
+                         virJSONValuePtr *propsret)
+{
+    int ret = -1;
+    char *keyid = NULL;
+    virJSONValuePtr props = NULL;
+
+    *type = "secret";
+
+    if (!(keyid = qemuDomainGetMasterKeyAlias()))
+        return -1;
+
+    if (!(props = virJSONValueNewObject()))
+        goto cleanup;
+
+    if (virJSONValueObjectAdd(props,
+                              "s:data", secinfo->s.iv.ciphertext,
+                              "s:keyid", keyid,
+                              "s:iv", secinfo->s.iv.iv,
+                              "s:format", "base64", NULL) < 0)
+        goto cleanup;
+
+    *propsret = props;
+    props = NULL;
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(keyid);
+    virJSONValueFree(props);
+
+    return ret;
+}
+
+
+/**
+ * qemuBuildSecretIVCommandLine:
+ * @cmd: the command to modify
+ * @secinfo: pointer to the secret info object
+ * @qemuCaps: pointer to the emulator capabilities
+ *
+ * If the secinfo is available and associated with an IV secret,
+ * then format the command line for the secret object. This object
+ * will be referenced by the device that needs/uses it, so it needs
+ * to be in place first.
+ *
+ * Returns 0 on success, -1 w/ error message on failure
+ */
+static int
+qemuBuildSecretIVCommandLine(virCommandPtr cmd,
+                             qemuDomainSecretInfoPtr secinfo,
+                             virQEMUCapsPtr qemuCaps)
+{
+    int ret = -1;
+    virJSONValuePtr props = NULL;
+    const char *objAlias;
+    const char *type;
+    char *tmp = NULL;
+
+    if (!(objAlias = qemuDomainSecretInfoGetAlias(secinfo, qemuCaps)))
+        return 0;
+
+    if (qemuBuildSecretInfoProps(secinfo, &type, &props) < 0)
+        return -1;
+
+    if (!(tmp = qemuBuildObjectCommandlineFromJSON(type, objAlias, props)))
+        goto cleanup;
+
+    virCommandAddArgList(cmd, "-object", tmp, NULL);
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(props);
+    VIR_FREE(tmp);
+
+    return ret;
+}
+
+
 char *
 qemuBuildDriveStr(virDomainDiskDefPtr disk,
                   bool bootable,
@@ -1343,6 +1449,14 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk,
         virBufferAddLit(&opt, ",cache=none");
     }
 
+    if (diskPriv->secinfo) {
+        qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo;
+
+        if (secinfo->type == VIR_DOMAIN_SECRET_INFO_IV)
+            virBufferAsprintf(&opt, ",password-secret='%s'",
+                              secinfo->s.iv.alias);
+    }
+
     if (disk->copy_on_read) {
         if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_COPY_ON_READ)) {
             virBufferAsprintf(&opt, ",copy-on-read=%s",
@@ -1888,6 +2002,8 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd,
         virDomainDiskDefPtr disk = def->disks[i];
         bool withDeviceArg = false;
         bool deviceFlagMasked = false;
+        qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+        qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo;
 
         /* Unless we have -device, then USB disks need special
            handling */
@@ -1930,6 +2046,9 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd,
             break;
         }
 
+        if (qemuBuildSecretIVCommandLine(cmd, secinfo, qemuCaps) < 0)
+            return -1;
+
         virCommandAddArg(cmd, "-drive");
 
         /* Unfortunately it is not possible to use
@@ -4947,8 +5066,15 @@ qemuBuildHostdevCommandLine(virCommandPtr cmd,
             subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
             if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE) &&
                 virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) {
+
+                qemuDomainHostdevPrivatePtr hostdevPriv =
+                    QEMU_DOMAIN_HOSTDEV_PRIVATE(hostdev);
+                qemuDomainSecretInfoPtr secinfo = hostdevPriv->secinfo;
                 char *drvstr;
 
+                if (qemuBuildSecretIVCommandLine(cmd, secinfo, qemuCaps) < 0)
+                    return -1;
+
                 virCommandAddArg(cmd, "-drive");
                 if (!(drvstr = qemuBuildSCSIHostdevDrvStr(hostdev, qemuCaps,
                                                           callbacks)))
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 056bb08..188cf5d 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -110,6 +110,10 @@ char *qemuBuildNicDevStr(virDomainDefPtr def,
 char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk,
                                virQEMUCapsPtr qemuCaps);
 
+int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo,
+                             const char **type,
+                             virJSONValuePtr *propsret);
+
 /* Both legacy & current support */
 char *qemuBuildDriveStr(virDomainDiskDefPtr disk,
                         bool bootable,
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 6510f57..1477d47 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -763,6 +763,32 @@ qemuDomainSecretInfoFree(qemuDomainSecretInfoPtr *secinfo)
 }
 
 
+/* qemuDomainSecretInfoGetAlias:
+ * @secinfo: pointer to the secret info object
+ * @qemuCaps: pointer to the emulator capabilities
+ *
+ * If the emulator supports it, secinfo is available and associated with
+ * an IV secret, then return the alias created during the disk or hostdev
+ * prepare step.
+ *
+ * Returns pointer to the object alias string or NULL if not found/supported
+ */
+const char *
+qemuDomainSecretInfoGetAlias(qemuDomainSecretInfoPtr secinfo,
+                             virQEMUCapsPtr qemuCaps)
+{
+    if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_SECRET)) {
+        VIR_INFO("secret object is not supported by this QEMU binary");
+        return NULL;
+    }
+
+    if (!secinfo || secinfo->type != VIR_DOMAIN_SECRET_INFO_IV)
+        return NULL;
+
+    return secinfo->s.iv.alias;
+}
+
+
 static virClassPtr qemuDomainDiskPrivateClass;
 static void qemuDomainDiskPrivateDispose(void *obj);
 
@@ -887,6 +913,143 @@ qemuDomainSecretPlainSetup(virConnectPtr conn,
 }
 
 
+/* qemuDomainSecretHaveEncypt:
+ *
+ * Returns true if we can support the encryption code, false otherwise
+ */
+static bool
+qemuDomainSecretHaveEncrypt(void)
+{
+#ifdef HAVE_GNUTLS_CIPHER_ENCRYPT
+    return true;
+#else
+    return false;
+#endif
+}
+
+
+/* qemuDomainSecretIVSetup:
+ * @conn: Pointer to connection
+ * @priv: pointer to domain private object
+ * @secinfo: Pointer to secret info
+ * @protocol: Protocol for secret
+ * @authdef: Pointer to auth data
+ *
+ * Taking a secinfo, fill in the initialization vector information
+ *
+ * Returns 0 on success, -1 on failure with error message
+ */
+static int
+qemuDomainSecretIVSetup(virConnectPtr conn,
+                        qemuDomainObjPrivatePtr priv,
+                        qemuDomainSecretInfoPtr secinfo,
+                        const char *srcalias,
+                        virStorageNetProtocol protocol,
+                        virStorageAuthDefPtr authdef)
+{
+    int ret = -1;
+    int rc;
+    uint8_t *raw_iv = NULL;
+    char *secret = NULL;
+    char *ciphertext = NULL;
+    size_t secretlen = 0, ciphertextlen = 0, paddinglen;
+    int secretType = VIR_SECRET_USAGE_TYPE_ISCSI;
+    const char *protocolstr = virStorageNetProtocolTypeToString(protocol);
+    gnutls_cipher_hd_t handle = NULL;
+    gnutls_datum_t enc_key;
+    gnutls_datum_t iv_key;
+
+    secinfo->type = VIR_DOMAIN_SECRET_INFO_IV;
+    if (VIR_STRDUP(secinfo->s.iv.username, authdef->username) < 0)
+        return -1;
+
+    if (protocol == VIR_STORAGE_NET_PROTOCOL_RBD)
+        secretType = VIR_SECRET_USAGE_TYPE_CEPH;
+
+    if (!(secinfo->s.iv.alias = qemuDomainGetIVKeyAlias(srcalias)))
+        return -1;
+
+    /* Create a random initialization vector */
+    if (!(raw_iv = qemuDomainGenerateRandomKey(QEMU_DOMAIN_IV_KEY_LEN)))
+        return -1;
+
+    /* Encode the IV and save that since qemu will need it */
+    base64_encode_alloc((const char *)raw_iv, QEMU_DOMAIN_IV_KEY_LEN,
+                        &secinfo->s.iv.iv);
+    if (!secinfo->s.iv.iv) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to encode initialization vector"));
+        goto cleanup;
+    }
+
+    /* Grab the unencoded secret */
+    if (!(secret = virSecretGetSecretString(conn, protocolstr, false,
+                                            authdef, secretType)))
+        goto cleanup;
+
+    /* Allocate a padded buffer */
+    secretlen = strlen(secret);
+    paddinglen = 16 - (secretlen % 16);
+    ciphertextlen = secretlen + paddinglen;
+    if (VIR_ALLOC_N(ciphertext, ciphertextlen) < 0)
+        goto cleanup;
+    memcpy(ciphertext, secret, secretlen);
+    memset(ciphertext + secretlen, paddinglen, paddinglen);
+
+    /* clear, free secret so that it doesn't hang around */
+    memset(secret, 0, strlen(secret));
+    VIR_FREE(secret);
+
+    /* Initialize the gnutls cipher */
+    enc_key.size = QEMU_DOMAIN_MASTER_KEY_LEN;
+    enc_key.data = priv->masterKey;
+    iv_key.size = QEMU_DOMAIN_IV_KEY_LEN;
+    iv_key.data = raw_iv;
+    if ((rc = gnutls_cipher_init(&handle, GNUTLS_CIPHER_AES_256_CBC,
+                                 &enc_key, &iv_key)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("failed to initialize cipher: '%s'"),
+                       gnutls_strerror(rc));
+        goto cleanup;
+    }
+
+    /* Encrypt the secret and free the memory for cipher operations */
+    rc = gnutls_cipher_encrypt(handle, ciphertext, ciphertextlen);
+    gnutls_cipher_deinit(handle);
+    if (rc < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("failed to encrypt the secret: '%s'"),
+                       gnutls_strerror(rc));
+        goto cleanup;
+    }
+
+    /* Now base64 encode the ciphertext and store to be passed to qemu */
+    base64_encode_alloc((const char *)ciphertext, ciphertextlen,
+                        &secinfo->s.iv.ciphertext);
+    if (!secinfo->s.iv.ciphertext) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to encode ciphertext"));
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+
+    /* Clear and free the raw_iv, plaintext secret, and ciphertext */
+    memset(raw_iv, 0, QEMU_DOMAIN_IV_KEY_LEN);
+    VIR_FREE(raw_iv);
+
+    memset(secret, 0, secretlen);
+    VIR_FREE(secret);
+
+    memset(ciphertext, 0, ciphertextlen);
+    VIR_FREE(ciphertext);
+
+    return ret;
+}
+
+
 /* qemuDomainSecretDiskDestroy:
  * @disk: Pointer to a disk definition
  *
@@ -915,7 +1078,7 @@ qemuDomainSecretDiskDestroy(virDomainDiskDefPtr disk)
  */
 int
 qemuDomainSecretDiskPrepare(virConnectPtr conn,
-                            qemuDomainObjPrivatePtr priv ATTRIBUTE_UNUSED,
+                            qemuDomainObjPrivatePtr priv,
                             virDomainDiskDefPtr disk)
 {
     virStorageSourcePtr src = disk->src;
@@ -932,9 +1095,18 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn,
         if (VIR_ALLOC(secinfo) < 0)
             return -1;
 
-        if (qemuDomainSecretPlainSetup(conn, secinfo, src->protocol,
-                                       src->auth) < 0)
-            goto error;
+        if (qemuDomainSecretHaveEncrypt() &&
+            virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET)) {
+            if (qemuDomainSecretIVSetup(conn, priv, secinfo,
+                                        disk->info.alias,
+                                        src->protocol,
+                                        src->auth) < 0)
+                goto error;
+        } else {
+            if (qemuDomainSecretPlainSetup(conn, secinfo, src->protocol,
+                                           src->auth) < 0)
+                goto error;
+        }
 
         diskPriv->secinfo = secinfo;
     }
@@ -976,7 +1148,7 @@ qemuDomainSecretHostdevDestroy(virDomainHostdevDefPtr hostdev)
  */
 int
 qemuDomainSecretHostdevPrepare(virConnectPtr conn,
-                               qemuDomainObjPrivatePtr priv ATTRIBUTE_UNUSED,
+                               qemuDomainObjPrivatePtr priv,
                                virDomainHostdevDefPtr hostdev)
 {
     virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys;
@@ -997,10 +1169,19 @@ qemuDomainSecretHostdevPrepare(virConnectPtr conn,
             if (VIR_ALLOC(secinfo) < 0)
                 return -1;
 
-            if (qemuDomainSecretPlainSetup(conn, secinfo,
-                                           VIR_STORAGE_NET_PROTOCOL_ISCSI,
-                                           iscsisrc->auth) < 0)
-                goto error;
+            if (qemuDomainSecretHaveEncrypt() &&
+                virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET)) {
+                if (qemuDomainSecretIVSetup(conn, priv, secinfo,
+                                            hostdev->info->alias,
+                                            VIR_STORAGE_NET_PROTOCOL_ISCSI,
+                                            iscsisrc->auth) < 0)
+                    goto error;
+            } else {
+                if (qemuDomainSecretPlainSetup(conn, secinfo,
+                                               VIR_STORAGE_NET_PROTOCOL_ISCSI,
+                                               iscsisrc->auth) < 0)
+                    goto error;
+            }
 
             hostdevPriv->secinfo = secinfo;
         }
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 2062435..3a5e028 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -624,6 +624,9 @@ int qemuDomainMasterKeyCreate(qemuDomainObjPrivatePtr priv);
 
 void qemuDomainMasterKeyRemove(qemuDomainObjPrivatePtr priv);
 
+const char *qemuDomainSecretInfoGetAlias(qemuDomainSecretInfoPtr secinfo,
+                                         virQEMUCapsPtr qemuCaps);
+
 void qemuDomainSecretDiskDestroy(virDomainDiskDefPtr disk)
     ATTRIBUTE_NONNULL(1);
 
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 04e3837..0a1b6f1 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -323,6 +323,11 @@ qemuDomainAttachVirtioDiskDevice(virConnectPtr conn,
     char *drivealias = NULL;
     bool releaseaddr = false;
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+    const char *objAlias;
+    const char *type;
+    qemuDomainDiskPrivatePtr diskPriv;
+    qemuDomainSecretInfoPtr secinfo;
+    virJSONValuePtr props = NULL;
     const char *src = virDomainDiskGetSource(disk);
 
     if (!disk->info.type) {
@@ -356,6 +361,13 @@ qemuDomainAttachVirtioDiskDevice(virConnectPtr conn,
     if (qemuDomainSecretDiskPrepare(conn, priv, disk) < 0)
         goto error;
 
+    diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+    secinfo = diskPriv->secinfo;
+    if ((objAlias = qemuDomainSecretInfoGetAlias(secinfo, priv->qemuCaps))) {
+        if (qemuBuildSecretInfoProps(secinfo, &type, &props) < 0)
+            goto error;
+    }
+
     if (!(drivestr = qemuBuildDriveStr(disk, false, priv->qemuCaps)))
         goto error;
 
@@ -368,9 +380,13 @@ qemuDomainAttachVirtioDiskDevice(virConnectPtr conn,
     if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0)
         goto error;
 
-    /* Attach the device - 2 step process */
+    /* Attach the device - possible 3 step process */
     qemuDomainObjEnterMonitor(driver, vm);
 
+    if (objAlias && qemuMonitorAddObject(priv->mon, type, objAlias, props) < 0)
+        goto failaddsecret;
+    props = NULL;   /* qemuMonitorAddObject consumes and frees props */
+
     if (qemuMonitorAddDrive(priv->mon, drivestr) < 0)
         goto failadddrive;
 
@@ -388,6 +404,7 @@ qemuDomainAttachVirtioDiskDevice(virConnectPtr conn,
     ret = 0;
 
  cleanup:
+    virJSONValueFree(props);
     qemuDomainSecretDiskDestroy(disk);
     VIR_FREE(devstr);
     VIR_FREE(drivestr);
@@ -409,8 +426,13 @@ qemuDomainAttachVirtioDiskDevice(virConnectPtr conn,
     }
 
  failadddrive:
+    if (objAlias)
+        ignore_value(qemuMonitorDelObject(priv->mon, objAlias));
+
+ failaddsecret:
     if (qemuDomainObjExitMonitor(driver, vm) < 0)
         releaseaddr = false;
+    props = NULL; /* qemuMonitorAddObject consumes props on failure too */
 
  failexitmonitor:
     virDomainAuditDisk(vm, NULL, disk->src, "attach", false);
@@ -565,6 +587,11 @@ qemuDomainAttachSCSIDisk(virConnectPtr conn,
     char *devstr = NULL;
     int ret = -1;
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+    const char *objAlias;
+    const char *type;
+    qemuDomainDiskPrivatePtr diskPriv;
+    qemuDomainSecretInfoPtr secinfo;
+    virJSONValuePtr props = NULL;
 
     if (qemuDomainPrepareDisk(driver, vm, disk, NULL, false) < 0)
         goto cleanup;
@@ -583,6 +610,13 @@ qemuDomainAttachSCSIDisk(virConnectPtr conn,
     if (qemuDomainSecretDiskPrepare(conn, priv, disk) < 0)
         goto error;
 
+    diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+    secinfo = diskPriv->secinfo;
+    if ((objAlias = qemuDomainSecretInfoGetAlias(secinfo, priv->qemuCaps))) {
+        if (qemuBuildSecretInfoProps(secinfo, &type, &props) < 0)
+            goto error;
+    }
+
     if (!(devstr = qemuBuildDriveDevStr(vm->def, disk, 0, priv->qemuCaps)))
         goto error;
 
@@ -592,9 +626,13 @@ qemuDomainAttachSCSIDisk(virConnectPtr conn,
     if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0)
         goto error;
 
-    /* Attach the device - 2 step process */
+    /* Attach the device - possible 3 step process */
     qemuDomainObjEnterMonitor(driver, vm);
 
+    if (objAlias && qemuMonitorAddObject(priv->mon, type, objAlias, props) < 0)
+        goto failaddsecret;
+    props = NULL;   /* qemuMonitorAddObject consumes and frees props */
+
     if (qemuMonitorAddDrive(priv->mon, drivestr) < 0)
         goto failadddrive;
 
@@ -610,6 +648,7 @@ qemuDomainAttachSCSIDisk(virConnectPtr conn,
     ret = 0;
 
  cleanup:
+    virJSONValueFree(props);
     qemuDomainSecretDiskDestroy(disk);
     VIR_FREE(devstr);
     VIR_FREE(drivestr);
@@ -621,7 +660,12 @@ qemuDomainAttachSCSIDisk(virConnectPtr conn,
     VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", drivestr, devstr);
 
  failadddrive:
+    if (objAlias)
+        ignore_value(qemuMonitorDelObject(priv->mon, objAlias));
+
+ failaddsecret:
     ignore_value(qemuDomainObjExitMonitor(driver, vm));
+    props = NULL; /* qemuMonitorAddObject consumes props on failure too */
 
  failexitmonitor:
     virDomainAuditDisk(vm, NULL, disk->src, "attach", false);
@@ -1938,6 +1982,11 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
     char *drvstr = NULL;
     bool teardowncgroup = false;
     bool teardownlabel = false;
+    const char *objAlias;
+    const char *type;
+    qemuDomainHostdevPrivatePtr hostdevPriv;
+    qemuDomainSecretInfoPtr secinfo;
+    virJSONValuePtr props = NULL;
 
     if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) ||
         !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) {
@@ -1983,6 +2032,13 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
     if (qemuDomainSecretHostdevPrepare(conn, priv, hostdev) < 0)
         goto cleanup;
 
+    hostdevPriv = QEMU_DOMAIN_HOSTDEV_PRIVATE(hostdev);
+    secinfo = hostdevPriv->secinfo;
+    if ((objAlias = qemuDomainSecretInfoGetAlias(secinfo, priv->qemuCaps))) {
+        if (qemuBuildSecretInfoProps(secinfo, &type, &props) < 0)
+            goto cleanup;
+    }
+
     if (!(drvstr = qemuBuildSCSIHostdevDrvStr(hostdev, priv->qemuCaps,
                                               &buildCommandLineCallbacks)))
         goto cleanup;
@@ -1993,9 +2049,13 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
     if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0)
         goto cleanup;
 
-    /* Attach the device - 2 step process */
+    /* Attach the device - possible 3 step process */
     qemuDomainObjEnterMonitor(driver, vm);
 
+    if (objAlias && qemuMonitorAddObject(priv->mon, type, objAlias, props) < 0)
+        goto failaddsecret;
+    props = NULL;   /* qemuMonitorAddObject consumes and frees props */
+
     if (qemuMonitorAddDrive(priv->mon, drvstr) < 0)
         goto failadddrive;
 
@@ -2012,6 +2072,7 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
     ret = 0;
 
  cleanup:
+    virJSONValueFree(props);
     qemuDomainSecretHostdevDestroy(hostdev);
     if (ret < 0) {
         qemuHostdevReAttachSCSIDevices(driver, vm->def->name, &hostdev, 1);
@@ -2038,7 +2099,12 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
     }
 
  failadddrive:
+    if (objAlias)
+        ignore_value(qemuMonitorDelObject(priv->mon, objAlias));
+
+ failaddsecret:
     ignore_value(qemuDomainObjExitMonitor(driver, vm));
+    props = NULL; /* qemuMonitorAddObject consumes props on failure too */
 
  failexitmonitor:
     virDomainAuditHostdev(vm, hostdev, "attach", false);
-- 
2.5.5




More information about the libvir-list mailing list