[Libguestfs] [libnbd PATCH] api: Add nbd_[aio_]opt_starttls

Eric Blake eblake at redhat.com
Mon Oct 10 16:20:21 UTC 2022


On Thu, Oct 06, 2022 at 03:26:20PM +0100, Richard W.M. Jones wrote:
> On Tue, Oct 04, 2022 at 07:51:18AM -0500, Eric Blake wrote:
> > Very similar to the recent addition of nbd_opt_structured_reply, with
> > the new nbd_opt_starttls() finally giving us fine-grained control over
> > NBD_OPT_STARTTLS, and allowing productive handshaking with a server in
> > SELECTIVETLS mode.
> > 
> > With this patch, it is now easy to reproduce the scenario of nbdkit's
> > CVE-2021-3716, where a plaintext meddler-in-the-middle attacker could
> > cause client denial of service when a --tls=on server failed to reset
> > state after NBD_OPT_STARTTLS.  This also exposed the fact that nbdkit
> > was not gracefully handling NBD_OPT_INFO from a plaintext client
> > connecting to a --tls=require server; the testsuite is skipped unless
> > using a fixed nbdkit (v1.33.2 or later).
> 
> I guess so.  It does seem complicated and a rather niche use case.
> Perhaps we should just document the new API rather than bothering to
> update the existing API call documentation, since almost certainly no
> one using those APIs would ever need to think about this API?

...
> > +++ b/generator/API.ml
> > @@ -599,7 +599,11 @@   "set_tls", {
> >  =item C<LIBNBD_TLS_DISABLE>
> > 
> >  Disable TLS.  (The default setting, unless using L<nbd_connect_uri(3)> with
> > -a URI that requires TLS)
> > +a URI that requires TLS).
> > +
> > +This setting is also useful during integration testing when using
> > +L<nbd_set_opt_mode(3)> and L<nbd_opt_starttls(3)> for manual
> > +control over when to request TLS negotiation.
> > 
> >  =item C<LIBNBD_TLS_ALLOW>

I think this one is important; but I added a bit more wording.  The
NBD standard talks about SELECTIVETLS vs FORCEDTLS servers.
SELECTIVETLS servers are uncommon (qemu refuses to be one, and while
nbdkit supports it with --tls=on instead of --tls=require, it
documents that it carries a risk of insecurity without also using
--filter=tls-fallback).  But the important part here is that when
talking to a SELECTIVETLS server, you want LIBNBD_TLS_DISABLE if you
plan to do anything prior to turning on TLS, because LIBNBD_TLS_ALLOW
turns on TLS before allowing any manual control over option
negotiating (that is, nbdkit's --tls=on coupled with libnbd TLS_ALLOW
intentionally defaults to automatic rather than manual TLS
negotiation).

> > @@ -657,7 +662,7 @@   "get_tls_negotiated", {
> >  After connecting you may call this to find out if the
> >  connection is using TLS.
> > 
> > -This is only really useful if you set the TLS request mode
> > +This is normally useful only if you set the TLS request mode
> >  to C<LIBNBD_TLS_ALLOW> (see L<nbd_set_tls(3)>), because in this
> >  mode we try to use TLS but fall back to unencrypted if it was
> >  not available.  This function will tell you if TLS was
> > @@ -665,8 +670,14 @@   "get_tls_negotiated", {
> > 
> >  In C<LIBNBD_TLS_REQUIRE> mode (the most secure) the connection
> >  would have failed if TLS could not be negotiated, and in
> > -C<LIBNBD_TLS_DISABLE> mode TLS is not tried.";
> > -    see_also = [Link "set_tls"; Link "get_tls"];
> > +C<LIBNBD_TLS_DISABLE> mode TLS is not tried automatically.
> > +
> > +However, when doing manual integration testing of server
> > +behavior, when you use L<nbd_set_opt_mode(3)> along with TLS
> > +request mode C<LIBNBD_TLS_DISABLE> before triggering the TLS
> > +handshake with L<nbd_opt_starttls(3)>, then this will report
> > +the result of that manual effort.";
> > +    see_also = [Link "set_tls"; Link "get_tls"; Link "opt_starttls"];
> >    };

Also useful, but also worth mentioning C<SELECTIVETLS>.

> > +++ b/generator/states-newstyle-opt-starttls.c
> > @@ -68,12 +73,14 @@  NEWSTYLE.OPT_STARTTLS.RECV_REPLY_PAYLOAD:
> >   NEWSTYLE.OPT_STARTTLS.CHECK_REPLY:
> >    uint32_t reply;
> >    struct socket *new_sock;
> > +  int err = ENOTSUP;
> > 
> >    reply = be32toh (h->sbuf.or.option_reply.reply);
> >    switch (reply) {
> >    case NBD_REP_ACK:
> >      nbd_internal_reset_size_and_flags (h);
> >      h->structured_replies = false;
> > +    h->meta_valid = false;

I enhanced the testsuite to actually test that this makes a
difference.

> >      new_sock = nbd_internal_crypto_create_session (h, h->sock);
> >      if (new_sock == NULL) {
> >        SET_NEXT_STATE (%.DEAD);
> > @@ -86,6 +93,9 @@  NEWSTYLE.OPT_STARTTLS.CHECK_REPLY:
> >        SET_NEXT_STATE (%TLS_HANDSHAKE_WRITE);
> >      return 0;
> > 
> > +  case NBD_REP_ERR_INVALID:
> > +    err = EINVAL;
> > +    /* fallthrough */
> >    default:
> >      if (handle_reply_error (h) == -1) {
> >        SET_NEXT_STATE (%.DEAD);
> > @@ -102,10 +112,16 @@  NEWSTYLE.OPT_STARTTLS.CHECK_REPLY:
> >        return 0;
> >      }
> > 
> > -    debug (h,
> > -           "server refused TLS (%s), continuing with unencrypted connection",
> > -           reply == NBD_REP_ERR_POLICY ? "policy" : "not supported");
> > -    SET_NEXT_STATE (%^OPT_STRUCTURED_REPLY.START);
> > +    debug (h, "server refused TLS (%s)",
> > +           reply == NBD_REP_ERR_POLICY ? "policy" :
> > +           reply == NBD_REP_ERR_INVALID ? "invalid request" : "not supported");
> > +    CALL_CALLBACK (h->opt_cb.completion, &err);

And this was missing a call to nbd_internal_free_option (h).

The commit is now in as 53617c92

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


More information about the Libguestfs mailing list