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

[Libguestfs] [libnbd PATCH 1/1] security: states: Fail oldstyle servers when tls==2



This test succeeds, which is wrong:

$ nbdsh -c 'h.set_tls(nbd.TLS_REQUIRE)' \
  -c 'h.connect_command(["nbdkit", "-o", "-s", "null"])' \
  -c 'print(h.get_size())'
0

Consider the case of a server that allows, but does not require, TLS
encryption.  A client that wants to only use the server if encrypted
(as evidenced by the request for LIBNBD_TLS_REQUIRE) can be subjected
to a protocol downgrade attack: a man-in-the-middle attacker can
translate the original server's unencrypted newstyle offerings into an
oldstyle server, such that the client is now unaware that it is
sending plain-text rather than the desired encrypted session.

Red Hat security will probably assign this a CVE, but we felt it
reasonable to publish the fix now, in part due to the rarity of
oldstyle servers these days.

Workaround: if the server offers extensions that are only possible in
newstyle connections, a client can check post-connection but before
sending any read or write requests that any of those extensions are
enabled, to ensure that a newstyle connection is in use
(unfortunately, this doesn't help for all servers).  Known witnesses:
- if nbd_can_df(h) returns true
- if the client requested nbd_add_meta_context(h, context) prior to
connection, then after connection nbd_can_meta_context(h, context)
returns true (the most common context is LIBNBD_CONTEXT_BASE_ALLOCATION)
---
 generator/states-oldstyle.c | 10 ++++++++++
 tests/oldstyle.c            | 17 +++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/generator/states-oldstyle.c b/generator/states-oldstyle.c
index 668931b..1aff185 100644
--- a/generator/states-oldstyle.c
+++ b/generator/states-oldstyle.c
@@ -46,6 +46,16 @@
   gflags = be16toh (h->sbuf.old_handshake.gflags);
   eflags = be16toh (h->sbuf.old_handshake.eflags);

+  /* Server is unable to upgrade to TLS.  If h->tls is not require (2)
+   * then we can continue unencrypted.
+   */
+  if (h->tls == 2) {
+    SET_NEXT_STATE (%.DEAD);
+    set_error (ENOTSUP, "handshake: server is oldstyle, "
+               "but handle TLS setting is require (2)");
+    return 0;
+  }
+
   h->gflags = gflags;
   debug (h, "gflags: 0x%" PRIx16, gflags);

diff --git a/tests/oldstyle.c b/tests/oldstyle.c
index 64862b7..c179c45 100644
--- a/tests/oldstyle.c
+++ b/tests/oldstyle.c
@@ -87,6 +87,23 @@ main (int argc, char *argv[])

   progname = argv[0];

+  /* Initial sanity check that we can't require TLS */
+  nbd = nbd_create ();
+  if (nbd == NULL) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_set_tls (nbd, LIBNBD_TLS_REQUIRE) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_connect_command (nbd, args) != -1) {
+    fprintf (stderr, "%s\n", "expected failure");
+    exit (EXIT_FAILURE);
+  }
+  nbd_close (nbd);
+
+  /* Now for a working connection */
   nbd = nbd_create ();
   if (nbd == NULL) {
     fprintf (stderr, "%s\n", nbd_get_error ());
-- 
2.21.0


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