Re: [libvirt PATCH v3 16/18] qemu: pass sensitive data to nbdkit via pipe

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Oct 20, 2022 at 16:59:07 -0500, Jonathon Jongsma wrote:
> Rather than passing passwords and cookies (which could contain
> passwords) to nbdkit via commandline arguments, use the alternate format
> that nbdkit supports where we can specify a file descriptor which nbdkit
> will read to get the password or cookies.
> 
> Signed-off-by: Jonathon Jongsma <jjongsma@xxxxxxxxxx>
> ---
>  build-aux/syntax-check.mk                     |  4 +-
>  src/qemu/qemu_nbdkit.c                        | 64 ++++++++++++-----
>  src/util/vircommand.c                         |  3 +-
>  src/util/virutil.h                            |  2 +-
>  .../disk-cdrom-network.args.disk0             |  7 ++
>  .../disk-cdrom-network.args.disk1             |  9 +++
>  .../disk-cdrom-network.args.disk1.pipe.1778   |  1 +
>  .../disk-cdrom-network.args.disk2             |  9 +++
>  .../disk-cdrom-network.args.disk2.pipe.1780   |  1 +
>  .../disk-network-http.args.disk0              |  7 ++
>  .../disk-network-http.args.disk1              |  6 ++
>  .../disk-network-http.args.disk2              |  7 ++
>  .../disk-network-http.args.disk2.pipe.1778    |  1 +
>  .../disk-network-http.args.disk3              |  8 +++
>  .../disk-network-http.args.disk3.pipe.1780    |  1 +
>  ...work-source-curl-nbdkit-backing.args.disk0 |  8 +++
>  ...e-curl-nbdkit-backing.args.disk0.pipe.1778 |  1 +
>  .../disk-network-source-curl.args.disk0       |  8 +++
>  ...k-network-source-curl.args.disk0.pipe.1778 |  1 +
>  .../disk-network-source-curl.args.disk1       |  8 +++
>  ...k-network-source-curl.args.disk1.pipe.1780 |  1 +
>  .../disk-network-source-curl.args.disk2       |  8 +++
>  ...k-network-source-curl.args.disk2.pipe.1782 |  1 +
>  .../disk-network-source-curl.args.disk3       |  7 ++
>  .../disk-network-source-curl.args.disk4       |  7 ++
>  tests/qemunbdkittest.c                        | 69 +++++++++++++++++--
>  26 files changed, 219 insertions(+), 30 deletions(-)
>  create mode 100644 tests/qemunbdkitdata/disk-cdrom-network.args.disk0
>  create mode 100644 tests/qemunbdkitdata/disk-cdrom-network.args.disk1
>  create mode 100644 tests/qemunbdkitdata/disk-cdrom-network.args.disk1.pipe.1778
>  create mode 100644 tests/qemunbdkitdata/disk-cdrom-network.args.disk2
>  create mode 100644 tests/qemunbdkitdata/disk-cdrom-network.args.disk2.pipe.1780
>  create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk0
>  create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk1
>  create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk2
>  create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk2.pipe.1778
>  create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk3
>  create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk3.pipe.1780
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0.pipe.1778
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk0
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk0.pipe.1778
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk1
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk1.pipe.1780
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk2
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk2.pipe.1782
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk3
>  create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk4
> 
> diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk
> index 68cd9dff5f..d44b1e5b17 100644
> --- a/build-aux/syntax-check.mk
> +++ b/build-aux/syntax-check.mk
> @@ -1355,10 +1355,10 @@ exclude_file_name_regexp--sc_prohibit_strdup = \
>    ^(docs/|examples/|tests/virnetserverclientmock.c|tests/commandhelper.c|tools/nss/libvirt_nss_(leases|macs)\.c$$)
>  
>  exclude_file_name_regexp--sc_prohibit_close = \
> -  (\.p[yl]$$|\.spec\.in$$|^docs/|^(src/util/vir(file|event)\.c|src/libvirt-stream\.c|tests/(vir.+mock\.c|commandhelper\.c|qemusecuritymock\.c)|tools/nss/libvirt_nss_(leases|macs)\.c)|tools/virt-qemu-qmp-proxy$$)
> +  (\.p[yl]$$|\.spec\.in$$|^docs/|^(src/util/vir(file|event)\.c|src/libvirt-stream\.c|tests/(vir.+mock\.c|commandhelper\.c|qemusecuritymock\.c|qemunbdkittest\.c)|tools/nss/libvirt_nss_(leases|macs)\.c)|tools/virt-qemu-qmp-proxy$$)

In other cases we simply close a non-existing FD, so this might not be
needed.


> @@ -729,6 +736,29 @@ qemuNbdkitStopStorageSource(virStorageSource *src)
>  }
>  
>  
> +static int
> +qemuNbdkitCommandPassDataByPipe(virCommand *cmd,
> +                                const char *argName,
> +                                unsigned char *buf,
> +                                size_t buflen)
> +{
> +    g_autofree char *fdfmt = NULL;
> +    int fd = virCommandSetSendBuffer(cmd, buf, buflen);

This function now takes a unsigned char ** for 'buf' and clears it.
You'll need to adapt the prototype.

> +
> +    if (fd < 0)
> +        return -1;
> +
> +    /* some nbdkit arguments accept a variation where nbdkit will read the data
> +     * from a file descriptor, e.g. password=-FD */
> +    fdfmt = g_strdup_printf("-%i", fd);
> +    virCommandAddArgPair(cmd, argName, fdfmt);
> +
> +    virCommandDoAsyncIO(cmd);
> +
> +    return 0;
> +}
> +
> +
>  static int
>  qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
>                                    virCommand *cmd)
> @@ -744,10 +774,10 @@ qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
>  
>      if (proc->source->auth) {
>          g_autoptr(virConnect) conn = virGetConnectSecret();
> -        g_autofree uint8_t *secret = NULL;
> +        uint8_t *secret = NULL;

... and then you can keep this here because it will be cleared
approrpately.

>          size_t secretlen = 0;
> -        g_autofree char *password = NULL;
>          int secrettype;
> +        virStorageAuthDef *authdef = proc->source->auth;
>  
>          virCommandAddArgPair(cmd, "user",
>                               proc->source->auth->username);
> @@ -760,7 +790,7 @@ qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
>          }
>  
>          if (virSecretGetSecretString(conn,
> -                                     &proc->source->auth->seclookupdef,
> +                                     &authdef->seclookupdef,
>                                       secrettype,
>                                       &secret,
>                                       &secretlen) < 0) {

This hunk belongs to the commit adding this bit.

> @@ -769,24 +799,20 @@ qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
>              return -1;
>          }
>  
> -        /* ensure that the secret is a NULL-terminated string */
> -        password = g_strndup((char*)secret, secretlen);
> -
> -        /* for now, just report an error rather than passing the password in
> -         * cleartext on the commandline */
> -        virReportError(VIR_ERR_INTERNAL_ERROR,
> -                       "%s",
> -                       "Password not yet supported for nbdkit sources");
> -        return -1;
> +        if (qemuNbdkitCommandPassDataByPipe(cmd, "password",
> +                                            secret, secretlen) < 0)
> +            return -1;
>      }
>  
> -    if (proc->source->ncookies > 0) {
> -        /* for now, just report an error rather than passing cookies in
> -         * cleartext on the commandline */
> -        virReportError(VIR_ERR_INTERNAL_ERROR,
> -                       "%s",
> -                       "Cookies not yet supported for nbdkit sources");
> -        return -1;
> +    /* Create a pipe to send the cookies to the nbdkit process. */
> +    if (proc->source->ncookies) {
> +        char *cookies =
> +            qemuBlockStorageSourceGetCookieString(proc->source);

no need for linebreak after = even when it exceeds line length slightly.

Also after changing to doulbe pointer you can add an autofree here too
to be consistent.

> +
> +        if (qemuNbdkitCommandPassDataByPipe(cmd, "cookie",
> +                                            (unsigned char*)cookies,
> +                                            strlen(cookies)) < 0)
> +            return -1;
>      }
>  
>      if (proc->source->sslverify == VIR_TRISTATE_BOOL_NO) {
> diff --git a/src/util/vircommand.c b/src/util/vircommand.c
> index 014bab9196..838eb6bd16 100644
> --- a/src/util/vircommand.c
> +++ b/src/util/vircommand.c
> @@ -1703,7 +1703,8 @@ virCommandSetSendBuffer(virCommand *cmd,
>          return -1;
>      }
>  
> -    if (fcntl(pipefd[1], F_SETFL, O_NONBLOCK) < 0) {
> +    if (!(dryRunBuffer || dryRunCallback) &&
> +        fcntl(pipefd[1], F_SETFL, O_NONBLOCK) < 0) {
>          cmd->has_error = errno;
>          VIR_FORCE_CLOSE(pipefd[0]);
>          VIR_FORCE_CLOSE(pipefd[1]);

This belongs to a separate commit with separate justification.

> diff --git a/src/util/virutil.h b/src/util/virutil.h
> index ab8511bf8d..094b399859 100644
> --- a/src/util/virutil.h
> +++ b/src/util/virutil.h
> @@ -186,7 +186,7 @@ char *virGetPassword(void);
>   *
>   * Returns: -1 on error, 0 on success
>   */
> -int virPipe(int fds[2]);
> +int virPipe(int fds[2]) G_NO_INLINE;
>  

Same here.

>  /*
>   * virPipeQuiet:
>  /* Some mock implementations for testing */

[...]

> +#define PIPE_FD_START 1777
> +static int mockpipefd = PIPE_FD_START;
> +int
> +virPipeQuiet(int fds[2])
> +{
> +    fds[0] = mockpipefd++;
> +    fds[1] = mockpipefd++;
> +
> +    if (fcntl(fds[0], F_GETFD) != -1 ||
> +        fcntl(fds[1], F_GETFD) != -1)
> +        abort();

You can even create a real pipe and after this check use dup2() to move
it to the required FD numbers so that the test output will be stable and
you'll have real pipes to work with.

In fact I plan to eventually fix all other cases where we use fake FDs
to use real FDs moved to stable numbers.

In your case it will actually work well because you already account for
multiple instances of the object which is not the case elsewhere.

> +
> +    return 0;
> +}
> +
> +static int (*real_close)(int fd);
> +static void
> +init_syms(void)
> +{
> +    VIR_MOCK_REAL_INIT(close);
> +}
> +
> +int
> +close(int fd)
> +{
> +    int ret;

In other cases we simply try to close() an invalid FD. I'd not bother
with mocking this.


> +
> +    init_syms();
> +
> +    if (fd >= PIPE_FD_START)
> +        ret = 0;
> +    else
> +        ret = real_close(fd);
> +
> +    return ret;
> +}




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux