[libvirt] [PATCH] Support 'block_passwd' command for QEMU disk encryption

Daniel Veillard veillard at redhat.com
Thu Feb 11 15:28:12 UTC 2010


On Thu, Feb 11, 2010 at 02:31:37PM +0000, Daniel P. Berrange wrote:
> The old text mode monitor prompts for a password when disks are
> encrypted. This interactive approach doesn't work for JSON mode
> monitor. Thus there is a new 'block_passwd' command that can be
> used.
> 
> * src/qemu/qemu_driver.c: Split out code for looking up a disk
>   secret from findVolumeQcowPassphrase, into a new method
>   getVolumeQcowPassphrase. Enhance qemuInitPasswords() to also
>   set the disk encryption password via the monitor
> * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
>   src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
>   src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
>   support for the 'block_passwd' monitor command.
> ---
>  src/qemu/qemu_driver.c       |  117 +++++++++++++++++++++++++++++-------------
>  src/qemu/qemu_monitor.c      |   15 +++++
>  src/qemu/qemu_monitor.h      |    4 ++
>  src/qemu/qemu_monitor_json.c |   33 ++++++++++++
>  src/qemu/qemu_monitor_json.h |    4 ++
>  src/qemu/qemu_monitor_text.c |   47 +++++++++++++++++
>  src/qemu/qemu_monitor_text.h |    4 ++
>  7 files changed, 188 insertions(+), 36 deletions(-)
> 
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 0d77d57..03d0f5f 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -694,51 +694,46 @@ qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
>  }
>  
>  
> -static virStorageEncryptionPtr
> -findDomainDiskEncryption(virDomainObjPtr vm,
> -                         const char *path)
> +static virDomainDiskDefPtr
> +findDomainDiskByPath(virDomainObjPtr vm,
> +                     const char *path)
>  {
> -    bool seen_volume;
>      int i;
>  
> -    seen_volume = false;
>      for (i = 0; i < vm->def->ndisks; i++) {
>          virDomainDiskDefPtr disk;
>  
>          disk = vm->def->disks[i];
> -        if (disk->src != NULL && STREQ(disk->src, path)) {
> -            seen_volume = true;
> -            if (disk->encryption != NULL)
> -                return disk->encryption;
> -        }
> +        if (disk->src != NULL && STREQ(disk->src, path))
> +            return disk;
>      }
> -    if (seen_volume)
> -        qemuReportError(VIR_ERR_INVALID_DOMAIN,
> -                        _("missing <encryption> for volume %s"), path);
> -    else
> -        qemuReportError(VIR_ERR_INTERNAL_ERROR,
> -                        _("unexpected passphrase request for volume %s"),
> -                        path);
> +
> +    qemuReportError(VIR_ERR_INTERNAL_ERROR,
> +                    _("no disk found with path %s"),
> +                    path);
>      return NULL;
>  }
>  
> -
>  static int
> -findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
> -                         virConnectPtr conn,
> -                         virDomainObjPtr vm,
> -                         const char *path,
> -                         char **secretRet,
> -                         size_t *secretLen)
> +getVolumeQcowPassphrase(virConnectPtr conn,
> +                        virDomainDiskDefPtr disk,
> +                        char **secretRet,
> +                        size_t *secretLen)
>  {
> -    virStorageEncryptionPtr enc;
>      virSecretPtr secret;
>      char *passphrase;
>      unsigned char *data;
>      size_t size;
>      int ret = -1;
> +    virStorageEncryptionPtr enc;
>  
> -    virDomainObjLock(vm);
> +    if (!disk->encryption) {
> +        qemuReportError(VIR_ERR_INTERNAL_ERROR,
> +                        _("disk %s does not have any encryption information"),
> +                        disk->src);
> +        return -1;
> +    }
> +    enc = disk->encryption;
>  
>      if (!conn) {
>          qemuReportError(VIR_ERR_NO_SUPPORT,
> @@ -754,16 +749,12 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
>          goto cleanup;
>      }
>  
> -    enc = findDomainDiskEncryption(vm, path);
> -    if (enc == NULL)
> -        return -1;
> -
>      if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
>          enc->nsecrets != 1 ||
>          enc->secrets[0]->type !=
>          VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
>          qemuReportError(VIR_ERR_INVALID_DOMAIN,
> -                        _("invalid <encryption> for volume %s"), path);
> +                        _("invalid <encryption> for volume %s"), disk->src);
>          goto cleanup;
>      }
>  
> @@ -782,7 +773,7 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
>          VIR_FREE(data);
>          qemuReportError(VIR_ERR_INVALID_SECRET,
>                          _("format='qcow' passphrase for %s must not contain a "
> -                          "'\\0'"), path);
> +                          "'\\0'"), disk->src);
>          goto cleanup;
>      }
>  
> @@ -804,8 +795,30 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
>      ret = 0;
>  
>  cleanup:
> -    virDomainObjUnlock(vm);
> +    return ret;
> +}
>  
> +static int
> +findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
> +                         virConnectPtr conn,
> +                         virDomainObjPtr vm,
> +                         const char *path,
> +                         char **secretRet,
> +                         size_t *secretLen)
> +{
> +    virDomainDiskDefPtr disk;
> +    int ret = -1;
> +
> +    virDomainObjLock(vm);
> +    disk = findDomainDiskByPath(vm, path);
> +
> +    if (!disk)
> +        goto cleanup;
> +
> +    ret = getVolumeQcowPassphrase(conn, disk, secretRet, secretLen);
> +
> +cleanup:
> +    virDomainObjUnlock(vm);
>      return ret;
>  }
>  
> @@ -1681,8 +1694,10 @@ qemudInitCpuAffinity(virDomainObjPtr vm)
>  
>  
>  static int
> -qemuInitPasswords(struct qemud_driver *driver,
> -                  virDomainObjPtr vm) {
> +qemuInitPasswords(virConnectPtr conn,
> +                  struct qemud_driver *driver,
> +                  virDomainObjPtr vm,
> +                  unsigned long long qemuCmdFlags) {
>      int ret = 0;
>      qemuDomainObjPrivatePtr priv = vm->privateData;
>  
> @@ -1698,6 +1713,36 @@ qemuInitPasswords(struct qemud_driver *driver,
>          qemuDomainObjExitMonitorWithDriver(driver, vm);
>      }
>  
> +    if (ret < 0)
> +        goto cleanup;
> +
> +    if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
> +        int i;
> +
> +        for (i = 0 ; i < vm->def->ndisks ; i++) {
> +            char *secret;
> +            size_t secretLen;
> +
> +            if (!vm->def->disks[i]->encryption ||
> +                !vm->def->disks[i]->src)
> +                continue;
> +
> +            if (getVolumeQcowPassphrase(conn,
> +                                        vm->def->disks[i],
> +                                        &secret, &secretLen) < 0)
> +                goto cleanup;
> +
> +            qemuDomainObjEnterMonitorWithDriver(driver, vm);
> +            ret = qemuMonitorSetDrivePassphrase(priv->mon,
> +                                                vm->def->disks[i]->info.alias,
> +                                                secret);
> +            qemuDomainObjExitMonitorWithDriver(driver, vm);
> +            if (ret < 0)
> +                goto cleanup;
> +        }
> +    }
> +
> +cleanup:
>      return ret;
>  }
>  
> @@ -2721,7 +2766,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
>      if (qemudInitCpuAffinity(vm) < 0)
>          goto abort;
>  
> -    if (qemuInitPasswords(driver, vm) < 0)
> +    if (qemuInitPasswords(conn, driver, vm, qemuCmdFlags) < 0)
>          goto abort;
>  
>      /* If we have -device, then addresses are assigned explicitly.
> diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
> index 64c6cba..c1d369b 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -1331,3 +1331,18 @@ int qemuMonitorAddDrive(qemuMonitorPtr mon,
>          ret = qemuMonitorTextAddDrive(mon, drivestr);
>      return ret;
>  }
> +
> +
> +int qemuMonitorSetDrivePassphrase(qemuMonitorPtr mon,
> +                                  const char *alias,
> +                                  const char *passphrase)
> +{
> +    DEBUG("mon=%p, fd=%d alias=%s passphrase=%p(value hidden)", mon, mon->fd, alias, passphrase);
> +    int ret;
> +
> +    if (mon->json)
> +        ret = qemuMonitorJSONSetDrivePassphrase(mon, alias, passphrase);
> +    else
> +        ret = qemuMonitorTextSetDrivePassphrase(mon, alias, passphrase);
> +    return ret;
> +}
> diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
> index a330eff..786ad7a 100644
> --- a/src/qemu/qemu_monitor.h
> +++ b/src/qemu/qemu_monitor.h
> @@ -291,4 +291,8 @@ int qemuMonitorAddDevice(qemuMonitorPtr mon,
>  int qemuMonitorAddDrive(qemuMonitorPtr mon,
>                          const char *drivestr);
>  
> +int qemuMonitorSetDrivePassphrase(qemuMonitorPtr mon,
> +                                  const char *alias,
> +                                  const char *passphrase);
> +
>  #endif /* QEMU_MONITOR_H */
> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
> index 032afef..c9b8d60 100644
> --- a/src/qemu/qemu_monitor_json.c
> +++ b/src/qemu/qemu_monitor_json.c
> @@ -1841,3 +1841,36 @@ int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
>      virJSONValueFree(reply);
>      return ret;
>  }
> +
> +
> +int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
> +                                      const char *alias,
> +                                      const char *passphrase)
> +{
> +    int ret;
> +    virJSONValuePtr cmd;
> +    virJSONValuePtr reply = NULL;
> +    char *drive;
> +
> +    if (virAsprintf(&drive, "%s%s", QEMU_DRIVE_HOST_PREFIX, alias) < 0) {
> +        virReportOOMError();
> +        return -1;
> +    }
> +
> +    cmd = qemuMonitorJSONMakeCommand("block_passwd",
> +                                     "s:device", drive,
> +                                     "s:password", passphrase,
> +                                     NULL);
> +    VIR_FREE(drive);
> +    if (!cmd)
> +        return -1;
> +
> +    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
> +
> +    if (ret == 0)
> +        ret = qemuMonitorJSONCheckError(cmd, reply);
> +
> +    virJSONValueFree(cmd);
> +    virJSONValueFree(reply);
> +    return ret;
> +}
> diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
> index ac6458c..65a70e3 100644
> --- a/src/qemu/qemu_monitor_json.h
> +++ b/src/qemu/qemu_monitor_json.h
> @@ -162,4 +162,8 @@ int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
>  int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
>                              const char *drivestr);
>  
> +int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
> +                                      const char *alias,
> +                                      const char *passphrase);
> +
>  #endif /* QEMU_MONITOR_JSON_H */
> diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
> index a6a4598..e993699 100644
> --- a/src/qemu/qemu_monitor_text.c
> +++ b/src/qemu/qemu_monitor_text.c
> @@ -2127,3 +2127,50 @@ cleanup:
>      VIR_FREE(safe_str);
>      return ret;
>  }
> +
> +
> +int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon,
> +                                      const char *alias,
> +                                      const char *passphrase)
> +{
> +    char *cmd = NULL;
> +    char *reply = NULL;
> +    int ret = -1;
> +    char *safe_str;
> +
> +    safe_str = qemuMonitorEscapeArg(passphrase);
> +    if (!safe_str) {
> +        virReportOOMError();
> +        return -1;
> +    }
> +
> +    ret = virAsprintf(&cmd, "block_passwd %s%s \"%s\"", QEMU_DRIVE_HOST_PREFIX, alias, safe_str);
> +    if (ret == -1) {
> +        virReportOOMError();
> +        goto cleanup;
> +    }
> +
> +    if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
> +        qemuReportError(VIR_ERR_OPERATION_FAILED,
> +                        _("failed to close fd in qemu with '%s'"), cmd);
> +        goto cleanup;
> +    }
> +
> +    if (strstr(reply, "\nunknown command:")) {
> +        qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
> +                        _("setting disk password is not supported"));
> +        goto cleanup;
> +    } else if (strstr(reply, "The entered password is invalid")) {
> +        qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
> +                        _("the disk password is incorrect"));
> +        goto cleanup;
> +    }
> +
> +    ret = 0;
> +
> +cleanup:
> +    VIR_FREE(cmd);
> +    VIR_FREE(reply);
> +    VIR_FREE(safe_str);
> +    return ret;
> +}
> diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
> index 12d75f5..1937e99 100644
> --- a/src/qemu/qemu_monitor_text.h
> +++ b/src/qemu/qemu_monitor_text.h
> @@ -166,4 +166,8 @@ int qemuMonitorTextAddDevice(qemuMonitorPtr mon,
>  int qemuMonitorTextAddDrive(qemuMonitorPtr mon,
>                               const char *drivestr);
>  
> +int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon,
> +                                      const char *alias,
> +                                      const char *passphrase);
> +
>  #endif /* QEMU_MONITOR_TEXT_H */

  Looks fine, ACK,

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list