Blob Blame History Raw
From a432e773e0cdc24cb27ccdda4111744ea2c3b819 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 27 Jul 2022 17:08:14 +0100
Subject: [PATCH] lib/crypto: Use GNUTLS_NO_SIGNAL if available

libnbd has long used MSG_NOSIGNAL to avoid receiving SIGPIPE if we
accidentally write on a closed socket, which is a nice alternative to
using a SIGPIPE signal handler.  However with TLS connections, gnutls
did not use this flag and so programs using libnbd + TLS would receive
SIGPIPE in some situations, notably if the server closed the
connection abruptly while we were trying to write something.

GnuTLS 3.4.2 introduces GNUTLS_NO_SIGNAL which does the same thing.
Use this flag if available.

RHEL 7 has an older gnutls which lacks this flag.  To avoid qemu-nbd
interop tests failing (rarely, but more often with a forthcoming
change to TLS shutdown behaviour), register a SIGPIPE signal handler
in the test if the flag is missing.
---
 configure.ac      | 15 +++++++++++++++
 interop/interop.c | 10 ++++++++++
 lib/crypto.c      |  7 ++++++-
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 49ca8ab..6bd9e1b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -179,6 +179,21 @@ AS_IF([test "$GNUTLS_LIBS" != ""],[
         gnutls_session_set_verify_cert \
         gnutls_transport_is_ktls_enabled \
     ])
+    AC_MSG_CHECKING([if gnutls has GNUTLS_NO_SIGNAL])
+    AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([
+            #include <gnutls/gnutls.h>
+            gnutls_session_t session;
+         ], [
+            gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_NO_SIGNAL);
+         ])
+    ], [
+        AC_MSG_RESULT([yes])
+        AC_DEFINE([HAVE_GNUTLS_NO_SIGNAL], [1],
+                  [GNUTLS_NO_SIGNAL found at compile time])
+    ], [
+        AC_MSG_RESULT([no])
+    ])
     LIBS="$old_LIBS"
 ])
 
diff --git a/interop/interop.c b/interop/interop.c
index b41f3ca..036545b 100644
--- a/interop/interop.c
+++ b/interop/interop.c
@@ -84,6 +84,16 @@ main (int argc, char *argv[])
   REQUIRES
 #endif
 
+  /* Ignore SIGPIPE.  We only need this for GnuTLS < 3.4.2, since
+   * newer GnuTLS has the GNUTLS_NO_SIGNAL flag which adds
+   * MSG_NOSIGNAL to each write call.
+   */
+#if !HAVE_GNUTLS_NO_SIGNAL
+#if TLS
+  signal (SIGPIPE, SIG_IGN);
+#endif
+#endif
+
   /* Create a large sparse temporary file. */
 #ifdef NEEDS_TMPFILE
   int fd = mkstemp (TMPFILE);
diff --git a/lib/crypto.c b/lib/crypto.c
index 1272888..ca9520e 100644
--- a/lib/crypto.c
+++ b/lib/crypto.c
@@ -588,7 +588,12 @@ nbd_internal_crypto_create_session (struct nbd_handle *h,
   gnutls_psk_client_credentials_t pskcreds = NULL;
   gnutls_certificate_credentials_t xcreds = NULL;
 
-  err = gnutls_init (&session, GNUTLS_CLIENT|GNUTLS_NONBLOCK);
+  err = gnutls_init (&session,
+                     GNUTLS_CLIENT | GNUTLS_NONBLOCK
+#if HAVE_GNUTLS_NO_SIGNAL
+                     | GNUTLS_NO_SIGNAL
+#endif
+                     );
   if (err < 0) {
     set_error (errno, "gnutls_init: %s", gnutls_strerror (err));
     return NULL;
-- 
2.31.1