[Libguestfs] [nbdkit PATCH 3/3] tlsdummy: New filter

Eric Blake eblake at redhat.com
Fri Aug 7 22:00:53 UTC 2020


Take advantage of the fact that we can now detect the type of client
during --tls=on in order to provide safe dummy content for plaintext
clients without having to rewrite plugins to do so.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 docs/nbdkit-plugin.pod                      |   4 +-
 docs/nbdkit-tls.pod                         |   5 +-
 filters/tlsdummy/nbdkit-tlsdummy-filter.pod |  72 ++++++
 configure.ac                                |   2 +
 filters/tlsdummy/Makefile.am                |  63 ++++++
 filters/tlsdummy/tlsdummy.c                 | 235 ++++++++++++++++++++
 6 files changed, 379 insertions(+), 2 deletions(-)
 create mode 100644 filters/tlsdummy/nbdkit-tlsdummy-filter.pod
 create mode 100644 filters/tlsdummy/Makefile.am
 create mode 100644 filters/tlsdummy/tlsdummy.c

diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 6237b749..43b0f6f9 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -1478,7 +1478,9 @@ On error, C<nbdkit_error> is called and the call returns C<NULL>.

 A server may use C<nbdkit_is_tls> to limit which export names work
 until after a client has completed TLS authentication.  See
-L<nbdkit-tls(1)>.
+L<nbdkit-tls(1)>.  It is also possible to use
+L<nbdkit-tlsdummy-filter(1)> to automatically ensure that the plugin
+is only used with authentication.

 =head2 C<nbdkit_is_tls>

diff --git a/docs/nbdkit-tls.pod b/docs/nbdkit-tls.pod
index 450e0850..fd78c21b 100644
--- a/docs/nbdkit-tls.pod
+++ b/docs/nbdkit-tls.pod
@@ -25,7 +25,9 @@ I<--tls=on> means that the client may choose to connect either with or
 without TLS.  In this mode, a plugin may wish to serve different
 content depending on whether the client has authenticated; the
 function C<nbdkit_is_tls()> can be used during the C<.open> callback
-for that purpose.
+for that purpose.  Or, you can opt to use L<nbdkit-tlsdummy-filter(1)>
+to provide safe dummy content to plaintext connections, saving the
+plugin content only for secure connections.

 Because I<--tls=on> is subject to downgrade attacks where a malicious
 proxy pretends not to support TLS in order to force either the client
@@ -275,6 +277,7 @@ More information can be found in L<gnutls_priority_init(3)>.
 =head1 SEE ALSO

 L<nbdkit(1)>,
+L<nbdkit-tlsdummy-filter(1)>,
 L<gnutls_priority_init(3)>,
 L<psktool(1)>,
 L<https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md>,
diff --git a/filters/tlsdummy/nbdkit-tlsdummy-filter.pod b/filters/tlsdummy/nbdkit-tlsdummy-filter.pod
new file mode 100644
index 00000000..f8eef69f
--- /dev/null
+++ b/filters/tlsdummy/nbdkit-tlsdummy-filter.pod
@@ -0,0 +1,72 @@
+=head1 NAME
+
+nbdkit-tlsdummy-filter - nbdkit TLS protection filter
+
+=head1 SYNOPSIS
+
+ nbdkit --tls=on --filter=tlsdummy plugin [plugin-args...] [tlsreadme=MESSAGE]
+
+=head1 DESCRIPTION
+
+C<nbdkit-tlsdummy-filter> is designed to be used when offering a
+connection that allows but does not require TLS from clients, in order
+to offer safe alternative content to plaintext clients, only exposing
+the underlying plugin to authenticated users.  This may provide a
+nicer failure mode for plaintext clients than the harsher C<nbdkit
+--tls=require>.
+
+When this filter detects a plaintext connection, it ignores the
+client's export name, and provides a single read-only export with 512
+bytes of data and content that defaults to the message "This NBD
+server requires TLS authentication before it will serve useful data."
+
+When using this filter, it is recommended to place this filter first
+in the command line, to reduce the chance that any work done by
+C<.open> in later filters or the plugin can be exploited by plaintext
+connections as a denial of service attack to starve further
+authenticated connections.  Note that this plugin will fail to load if
+the plugin requests the C<SERIALIZE_CONNECTIONS> thread model, as a
+plaintext client holding its connection open indefinitely would be
+such a starvation.
+
+=head1 PARAMETERS
+
+=over 4
+
+=item B<tlsreadme=>MESSAGE
+
+This optional parameter can be used to use C<MESSAGE> as the contents
+of the dummy export exposed to plaintext clients, using trailing NUL
+bytes to round the size up to 512 bytes.
+
+=back
+
+=head1 FILES
+
+=over 4
+
+=item F<$filterdir/nbdkit-tlsdummy-filter.so>
+
+The filter.
+
+Use C<nbdkit --dump-config> to find the location of C<$filterdir>.
+
+=back
+
+=head1 VERSION
+
+C<nbdkit-tlsdummy-filter> first appeared in nbdkit 1.22.
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-tls(1)>,
+L<nbdkit-filter(3)>.
+
+=head1 AUTHORS
+
+Eric Blake
+
+=head1 COPYRIGHT
+
+Copyright (C) 2020 Red Hat Inc.
diff --git a/configure.ac b/configure.ac
index 28f4a3cd..34730000 100644
--- a/configure.ac
+++ b/configure.ac
@@ -124,6 +124,7 @@ filters="\
         stats \
         swab \
         tar \
+	tlsdummy \
         truncate \
         xz \
         "
@@ -1165,6 +1166,7 @@ AC_CONFIG_FILES([Makefile
                  filters/stats/Makefile
                  filters/swab/Makefile
                  filters/tar/Makefile
+                 filters/tlsdummy/Makefile
                  filters/truncate/Makefile
                  filters/xz/Makefile
                  fuzzing/Makefile
diff --git a/filters/tlsdummy/Makefile.am b/filters/tlsdummy/Makefile.am
new file mode 100644
index 00000000..a1577511
--- /dev/null
+++ b/filters/tlsdummy/Makefile.am
@@ -0,0 +1,63 @@
+# nbdkit
+# Copyright (C) 2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+include $(top_srcdir)/common-rules.mk
+
+EXTRA_DIST = nbdkit-tlsdummy-filter.pod
+
+filter_LTLIBRARIES = nbdkit-tlsdummy-filter.la
+
+nbdkit_tlsdummy_filter_la_SOURCES = \
+	tlsdummy.c \
+	$(top_srcdir)/include/nbdkit-filter.h \
+	$(NULL)
+
+nbdkit_tlsdummy_filter_la_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/common/include \
+	$(NULL)
+nbdkit_tlsdummy_filter_la_CFLAGS = $(WARNINGS_CFLAGS)
+nbdkit_tlsdummy_filter_la_LDFLAGS = \
+	-module -avoid-version -shared $(SHARED_LDFLAGS) \
+	-Wl,--version-script=$(top_srcdir)/filters/filters.syms \
+	$(NULL)
+
+if HAVE_POD
+
+man_MANS = nbdkit-tlsdummy-filter.1
+CLEANFILES += $(man_MANS)
+
+nbdkit-tlsdummy-filter.1: nbdkit-tlsdummy-filter.pod
+	$(PODWRAPPER) --section=1 --man $@ \
+	    --html $(top_builddir)/html/$@.html \
+	    $<
+
+endif HAVE_POD
diff --git a/filters/tlsdummy/tlsdummy.c b/filters/tlsdummy/tlsdummy.c
new file mode 100644
index 00000000..6e1a7414
--- /dev/null
+++ b/filters/tlsdummy/tlsdummy.c
@@ -0,0 +1,235 @@
+/* nbdkit
+ * Copyright (C) 2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <assert.h>
+
+#include <nbdkit-filter.h>
+
+/* Needed to shut up newer gcc about use of strncpy on our message buffer */
+#if __GNUC__ >= 8
+#define NONSTRING __attribute__ ((nonstring))
+#else
+#define NONSTRING
+#endif
+
+static char message[512] NONSTRING = "This NBD server requires TLS "
+  "authentication before it will serve useful data.\n";
+
+/* Called for each key=value passed on the command line. */
+static int
+tlsdummy_config (nbdkit_next_config *next, void *nxdata,
+                 const char *key, const char *value)
+{
+  if (strcmp (key, "tlsreadme") == 0) {
+    strncpy (message, value, sizeof message); /* Yes, we really mean strncpy */
+    return 0;
+  }
+  return next (nxdata, key, value);
+}
+
+#define tlsdummy_config_help \
+  "tlsreadme=<MESSAGE>  Alternative contents for the plaintext dummy export.\n"
+
+int
+tlsdummy_get_ready (nbdkit_next_get_ready *next, void *nxdata,
+                    int thread_model)
+{
+  if (thread_model == NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) {
+    nbdkit_error ("the tlsdummy filter requires parallel connection support");
+    return -1;
+  }
+  return next (nxdata);
+}
+
+/* TODO: init_exports needs is_tls parameter */
+
+/* Helper for determining if this connection is insecure.  This works
+ * because we can treat all handles on a binary basis: secure or
+ * insecure, which lets .open get away without allocating a more
+ * complex handle.
+ */
+#define NOT_TLS (handle == &message)
+
+static void *
+tlsdummy_open (nbdkit_next_open *next, void *nxdata, int readonly,
+               const char *exportname, int is_tls)
+{
+  /* Bummer - we really do NOT want to call next() when insecure,
+   * because we don't know how long it will take.  But that requires a
+   * fix to nbdkit; right now, it asserts if we skip this :(
+   */
+  if (next (nxdata, readonly, exportname) == -1)
+    return NULL;
+  if (!is_tls)
+    return &message; /* See NOT_TLS for this choice of handle */
+  return NBDKIT_HANDLE_NOT_NEEDED;
+}
+
+/* When insecure, override ALL plugin .can_FOO to avoid an information leak */
+
+static int64_t
+tlsdummy_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+                   void *handle)
+{
+  if (NOT_TLS)
+    return sizeof message;
+  return next_ops->get_size (nxdata);
+}
+
+static int
+tlsdummy_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+                    void *handle)
+{
+  if (NOT_TLS)
+    return 0;
+  return next_ops->can_write (nxdata);
+}
+
+static int
+tlsdummy_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+                    void *handle)
+{
+  if (NOT_TLS)
+    return 0;
+  return next_ops->can_flush (nxdata);
+}
+
+static int
+tlsdummy_is_rotational (struct nbdkit_next_ops *next_ops, void *nxdata,
+                        void *handle)
+{
+  if (NOT_TLS)
+    return 0;
+  return next_ops->is_rotational (nxdata);
+}
+
+static int
+tlsdummy_can_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+                   void *handle)
+{
+  if (NOT_TLS)
+    return 0;
+  return next_ops->can_trim (nxdata);
+}
+
+static int
+tlsdummy_can_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+                   void *handle)
+{
+  if (NOT_TLS)
+    return NBDKIT_ZERO_NONE;
+  return next_ops->can_zero (nxdata);
+}
+
+static int
+tlsdummy_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+                        void *handle)
+{
+  if (NOT_TLS)
+    return 0;
+  return next_ops->can_fast_zero (nxdata);
+}
+
+static int
+tlsdummy_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+                      void *handle)
+{
+  if (NOT_TLS)
+    return 0;
+  return next_ops->can_extents (nxdata);
+}
+
+static int
+tlsdummy_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata,
+                  void *handle)
+{
+  if (NOT_TLS)
+    return NBDKIT_FUA_NONE;
+  return next_ops->can_fua (nxdata);
+}
+
+static int
+tlsdummy_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
+                         void *handle)
+{
+  if (NOT_TLS)
+    return 0;
+  return next_ops->can_multi_conn (nxdata);
+}
+
+static int
+tlsdummy_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+                    void *handle)
+{
+  if (NOT_TLS)
+    return NBDKIT_CACHE_NONE;
+  return next_ops->can_cache (nxdata);
+}
+
+static int
+tlsdummy_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+                void *handle, void *b, uint32_t count, uint64_t offs,
+                uint32_t flags, int *err)
+{
+  if (NOT_TLS) {
+    memcpy (b, message + offs, count);
+    return 0;
+  }
+  return next_ops->pread (nxdata, b, count, offs, flags, err);
+}
+
+static struct nbdkit_filter filter = {
+  .name              = "tlsdummy",
+  .longname          = "nbdkit tlsdummy filter",
+  .config            = tlsdummy_config,
+  .config_help       = tlsdummy_config_help,
+  .get_ready         = tlsdummy_get_ready,
+  /* XXX .init_exports needs is_tls parameter */
+  .open              = tlsdummy_open,
+  .get_size          = tlsdummy_get_size,
+  .can_write         = tlsdummy_can_write,
+  .can_flush         = tlsdummy_can_flush,
+  .is_rotational     = tlsdummy_is_rotational,
+  .can_trim          = tlsdummy_can_trim,
+  .can_zero          = tlsdummy_can_zero,
+  .can_fast_zero     = tlsdummy_can_fast_zero,
+  .can_extents       = tlsdummy_can_extents,
+  .can_fua           = tlsdummy_can_fua,
+  .can_multi_conn    = tlsdummy_can_multi_conn,
+  .can_cache         = tlsdummy_can_cache,
+  .pread             = tlsdummy_pread,
+};
+
+NBDKIT_REGISTER_FILTER(filter)
-- 
2.28.0




More information about the Libguestfs mailing list