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

[Nbd] [PATCHv5 1/7] Add GnuTLS infrastructure



Add configure.ac section to detect GnuTLS

Add buffer.[ch] and crypto-gnutls.[ch] from
  https://github.com/abligh/tlsproxy

Add Makefile.am changes to link these new files in

Signed-off-by: Alex Bligh <alex@...872...>
---
 Makefile.am           |   5 +
 buffer.c              | 225 ++++++++++++++++++
 buffer.h              |  45 ++++
 configure.ac          |  15 ++
 crypto-gnutls.c       | 624 ++++++++++++++++++++++++++++++++++++++++++++++++++
 crypto-gnutls.h       |  43 ++++
 tests/run/Makefile.am |   3 +
 7 files changed, 960 insertions(+)
 create mode 100644 buffer.c
 create mode 100644 buffer.h
 create mode 100644 crypto-gnutls.c
 create mode 100644 crypto-gnutls.h

diff --git a/Makefile.am b/Makefile.am
index 304db6d..9f7e4f7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -17,3 +17,8 @@ nbd_server_LDADD = @GLIB_LIBS@ libnbdsrv.la libcliserv.la
 nbd_trdump_LDADD = libcliserv.la
 make_integrityhuge_SOURCES = make-integrityhuge.c cliserv.h nbd.h nbd-debug.h
 EXTRA_DIST = maketr CodingStyle autogen.sh README.md
+TLSSRC = crypto-gnutls.c crypto-gnutls.h buffer.c buffer.h
+if GNUTLS
+nbd_client_SOURCES += $(TLSSRC)
+nbd_server_SOURCES += $(TLSSRC)
+endif
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000..e08efd8
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,225 @@
+/*
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Wrymouth Innovation Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <sys/types.h>
+
+#include "buffer.h"
+
+typedef struct buffer
+{
+  char *buf;
+  ssize_t size;
+  ssize_t hwm;
+  ssize_t ridx;
+  ssize_t widx;
+  int empty;
+} buffer_t;
+
+/* the buffer is organised internally as follows:
+ *
+ * * There are b->size bytes in the buffer.
+ *
+ * * Bytes are at offsets 0 to b->size-1
+ * 
+ * * b->ridx points to the first readable byte
+ *
+ * * b->widx points to the first empty space
+ *
+ * * b->ridx < b->widx indicates a non-wrapped buffer:
+ *
+ *     0       ridx     widx            size
+ *     |       |        |               |
+ *     V       V        V               V
+ *     ........XXXXXXXXX................
+ *
+ * * b->ridx > b->widx indicates a wrapped buffer:
+ *
+ *     0       widx     ridx            size
+ *     |       |        |               |
+ *     V       V        V               V
+ *     XXXXXXXX.........XXXXXXXXXXXXXXXX
+ *
+ * * b->ridx == b->widx indicates a FULL buffer:
+ *
+ * * b->ridx == b->widx indicates a wrapped buffer:
+ *
+ *     0       widx == ridx            size
+ *     |       |                       |
+ *     V       V                       V
+ *     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ *
+ * An empty buffer is indicated by empty=1
+ *
+ */
+
+buffer_t *
+bufNew (ssize_t size, ssize_t hwm)
+{
+  buffer_t *b = calloc (1, sizeof (buffer_t));
+  b->buf = calloc (1, size);
+  b->size = size;
+  b->hwm = hwm;
+  b->empty = 1;
+  return b;
+}
+
+
+void
+bufFree (buffer_t * b)
+{
+  free (b->buf);
+  free (b);
+}
+
+/* get a maximal span to read. Returns 0 if buffer
+ * is empty
+ */
+ssize_t
+bufGetReadSpan (buffer_t * b, void **addr)
+{
+  if (b->empty)
+    {
+      *addr = NULL;
+      return 0;
+    }
+  *addr = &(b->buf[b->ridx]);
+  ssize_t len = b->widx - b->ridx;
+  if (len <= 0)
+    len = b->size - b->ridx;
+  return len;
+}
+
+/* get a maximal span to write. Returns 0 id buffer is full
+ */
+ssize_t
+bufGetWriteSpan (buffer_t * b, void **addr)
+{
+  if (b->empty)
+    {
+      *addr = b->buf;
+      b->ridx = 0;
+      b->widx = 0;
+      return b->size;
+    }
+  if (b->ridx == b->widx)
+    {
+      *addr = NULL;
+      return 0;
+    }
+  *addr = &(b->buf[b->widx]);
+  ssize_t len = b->ridx - b->widx;
+  if (len <= 0)
+    len = b->size - b->widx;
+  return len;
+}
+
+/* mark size bytes as read */
+void
+bufDoneRead (buffer_t * b, ssize_t size)
+{
+  while (!b->empty && (size > 0))
+    {
+      /* empty can't occur here, so equal pointers means full */
+      ssize_t len = b->widx - b->ridx;
+      if (len <= 0)
+	len = b->size - b->ridx;
+
+      /* len is the number of bytes in one read span */
+      if (len > size)
+	len = size;
+
+      b->ridx += len;
+      if (b->ridx >= b->size)
+	b->ridx = 0;
+
+      if (b->ridx == b->widx)
+	{
+	  b->ridx = 0;
+	  b->widx = 0;
+	  b->empty = 1;
+	}
+
+      size -= len;
+    }
+}
+
+/* mark size bytes as written */
+void
+bufDoneWrite (buffer_t * b, ssize_t size)
+{
+  while ((b->empty || (b->ridx != b->widx)) && (size > 0))
+    {
+      /* full can't occur here, so equal pointers means empty */
+      ssize_t len = b->ridx - b->widx;
+      if (len <= 0)
+	len = b->size - b->widx;
+
+      /* len is the number of bytes in one write span */
+      if (len > size)
+	len = size;
+
+      b->widx += len;
+      if (b->widx >= b->size)
+	b->widx = 0;
+
+      /* it can't be empty as we've written at least one byte */
+      b->empty = 0;
+
+      size -= len;
+    }
+}
+
+int
+bufIsEmpty (buffer_t * b)
+{
+  return b->empty;
+}
+
+int
+bufIsFull (buffer_t * b)
+{
+  return !b->empty && (b->ridx == b->widx);
+}
+
+int
+bufIsOverHWM (buffer_t * b)
+{
+  return bufGetCount (b) > b->hwm;
+}
+
+ssize_t
+bufGetFree (buffer_t * b)
+{
+  return b->size - bufGetCount (b);
+}
+
+ssize_t
+bufGetCount (buffer_t * b)
+{
+  if (b->empty)
+    return 0;
+  return b->widx - b->ridx + ((b->ridx < b->widx) ? 0 : b->size);
+}
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000..c92b9a6
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,45 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Wrymouth Innovation Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __TLSPROXY_BUFFERS_H
+#define __TLSPROXY_BUFFERS_H
+
+#include <stdlib.h>
+#include <sys/types.h>
+
+typedef struct buffer buffer_t;
+
+buffer_t *bufNew (ssize_t size, ssize_t hwm);
+void bufFree (buffer_t * b);
+ssize_t bufGetReadSpan (buffer_t * b, void **addr);
+ssize_t bufGetWriteSpan (buffer_t * b, void **addr);
+void bufDoneRead (buffer_t * b, ssize_t size);
+void bufDoneWrite (buffer_t * b, ssize_t size);
+int bufIsEmpty (buffer_t * b);
+int bufIsFull (buffer_t * b);
+int bufIsOverHWM (buffer_t * b);
+ssize_t bufGetFree (buffer_t * b);
+ssize_t bufGetCount (buffer_t * b);
+
+#endif
diff --git a/configure.ac b/configure.ac
index 7806f65..204667f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,6 +114,21 @@ AC_CHECK_SIZEOF(unsigned long long int)
 AC_STRUCT_DIRENT_D_TYPE
 AC_CHECK_FUNCS([llseek alarm gethostbyname inet_ntoa memset socket strerror strstr mkstemp fdatasync])
 HAVE_FL_PH=no
+AC_ARG_ENABLE(
+  [gnutls],
+  [AS_HELP_STRING([--enable-gnutls],[Build support for GnuTLS])],
+  [
+    if test "x$enableval" = "xyes"]; then
+       AC_CHECK_LIB([gnutls], [gnutls_init], , AC_MSG_ERROR(no GnuTLS library found))
+       AC_DEFINE(WITH_GNUTLS, 1, [Define to 1 if you have and want support GnuTLS])
+       withgnutls=true
+    else
+       withgnutls=false
+    fi
+  ],
+)
+AM_CONDITIONAL([GNUTLS], [test "x$withgnutls" = "xtrue"])
+
 AC_CHECK_FUNC(fallocate,
   [
     AC_CHECK_HEADERS([linux/falloc.h])
diff --git a/crypto-gnutls.c b/crypto-gnutls.c
new file mode 100644
index 0000000..f6769f8
--- /dev/null
+++ b/crypto-gnutls.c
@@ -0,0 +1,624 @@
+/*
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Wrymouth Innovation Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+
+#include "crypto-gnutls.h"
+#include "buffer.h"
+
+#define MAX_CERTS 10
+
+#define FALSE 0
+#define TRUE 1
+
+#define PRIORITY "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2"
+
+typedef struct tlssession
+{
+  gnutls_certificate_credentials_t creds;
+  gnutls_session_t session;
+  char *hostname;
+  int (*quitfn) (void *opaque);
+  int (*erroutfn) (void *opaque, const char *format, va_list ap);
+  int debug;
+  void *opaque;
+} tlssession_t;
+
+#define BUF_SIZE 65536
+#define BUF_HWM ((BUF_SIZE*3)/4)
+
+static int
+falsequit (void *opaque)
+{
+  return FALSE;
+}
+
+static int
+quit (tlssession_t * s)
+{
+  return s->quitfn (s->opaque);
+}
+
+
+static int
+stderrout (void *opaque, const char *format, va_list ap)
+{
+  return vfprintf (stderr, format, ap);
+}
+
+static int
+errout (tlssession_t * s, const char *format, ...)
+{
+  va_list ap;
+  int ret;
+  va_start (ap, format);
+  ret = s->erroutfn (s->opaque, format, ap);
+  va_end (ap);
+  return ret;
+}
+
+static int
+debugout (tlssession_t * s, const char *format, ...)
+{
+  va_list ap;
+  int ret = 0;
+  va_start (ap, format);
+  if (s->debug)
+    ret = s->erroutfn (s->opaque, format, ap);
+  va_end (ap);
+  return ret;
+}
+
+static int
+socksetnonblock (int fd, int nb)
+{
+  int sf = fcntl (fd, F_GETFL, 0);
+  if (sf == -1)
+    return -1;
+  return fcntl (fd, F_SETFL, nb ? (sf | O_NONBLOCK) : (sf & ~O_NONBLOCK));
+}
+
+/* From (public domain) example file in GNUTLS
+ *
+ * This function will try to verify the peer's certificate, and
+ * also check if the hostname matches, and the activation, expiration dates.
+ */
+static int
+verify_certificate_callback (gnutls_session_t session)
+{
+  unsigned int status;
+  const gnutls_datum_t *cert_list;
+  unsigned int cert_list_size;
+  int ret;
+  gnutls_x509_crt_t cert;
+  tlssession_t *s;
+
+  /* read session pointer */
+  s = (tlssession_t *) gnutls_session_get_ptr (session);
+
+  /* This verification function uses the trusted CAs in the credentials
+   * structure. So you must have installed one or more CA certificates.
+   */
+  ret = gnutls_certificate_verify_peers2 (session, &status);
+  if (ret < 0)
+    {
+      debugout (s, "Could not verfify peer certificate due to an error\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  if (status & GNUTLS_CERT_INVALID)
+    debugout (s, "The certificate is not trusted.\n");
+
+  if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+    debugout (s, "The certificate hasn't got a known issuer.\n");
+
+  if (status & GNUTLS_CERT_REVOKED)
+    debugout (s, "The certificate has been revoked.\n");
+
+  if (status & GNUTLS_CERT_EXPIRED)
+    debugout (s, "The certificate has expired\n");
+
+  if (status & GNUTLS_CERT_NOT_ACTIVATED)
+    debugout (s, "The certificate is not yet activated\n");
+
+  if (status)
+    return GNUTLS_E_CERTIFICATE_ERROR;
+
+  if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
+    return GNUTLS_E_CERTIFICATE_ERROR;
+
+  if (gnutls_x509_crt_init (&cert) < 0)
+    {
+      debugout (s, "error in initialization\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+  if (cert_list == NULL)
+    {
+      debugout (s, "No certificate was found!\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  /* check only the first certificate - seems to be what curl does */
+  if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
+    {
+      debugout (s, "error parsing certificate\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  if (s->hostname && *s->hostname)
+    {
+      if (!gnutls_x509_crt_check_hostname (cert, s->hostname))
+	{
+	  debugout (s,
+		    "The certificate's owner does not match hostname '%s'\n",
+		    s->hostname);
+	  return GNUTLS_E_CERTIFICATE_ERROR;
+	}
+    }
+
+  gnutls_x509_crt_deinit (cert);
+
+  debugout (s, "Peer passed certificate verification\n");
+
+  /* notify gnutls to continue handshake normally */
+  return 0;
+}
+
+tlssession_t *
+tlssession_new (int isserver,
+		char *keyfile, char *certfile, char *cacertfile,
+		char *hostname, int insecure, int debug,
+		int (*quitfn) (void *opaque),
+		int (*erroutfn) (void *opaque, const char *format,
+				 va_list ap), void *opaque)
+{
+  int ret;
+  tlssession_t *s = calloc (1, sizeof (tlssession_t));
+
+  if (quitfn)
+    s->quitfn = quitfn;
+  else
+    s->quitfn = falsequit;
+
+  if (erroutfn)
+    s->erroutfn = erroutfn;
+  else
+    s->erroutfn = stderrout;
+
+  if (hostname)
+    s->hostname = strdup (hostname);
+
+  s->debug = debug;
+
+  if (gnutls_certificate_allocate_credentials (&s->creds) < 0)
+    {
+      errout (s, "Certificate allocation memory error\n");
+      goto error;
+    }
+
+  if (cacertfile != NULL)
+    {
+      ret =
+	gnutls_certificate_set_x509_trust_file (s->creds, cacertfile,
+						GNUTLS_X509_FMT_PEM);
+      if (ret < 0)
+	{
+	  errout (s, "Error setting the x509 trust file: %s\n",
+		  gnutls_strerror (ret));
+	  goto error;
+	}
+
+      if (!insecure)
+	{
+	  gnutls_certificate_set_verify_function (s->creds,
+						  verify_certificate_callback);
+	  gnutls_certificate_set_verify_flags (s->creds,
+					       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+	}
+    }
+
+  if (keyfile && !certfile)
+    certfile = keyfile;
+
+  if (certfile != NULL && keyfile != NULL)
+    {
+      ret =
+	gnutls_certificate_set_x509_key_file (s->creds, certfile, keyfile,
+					      GNUTLS_X509_FMT_PEM);
+
+      if (ret < 0)
+	{
+	  errout (s,
+		  "Error loading certificate or key file (%s, %s): %s\n",
+		  certfile, keyfile, gnutls_strerror (ret));
+	  goto error;
+	}
+    }
+
+  if (isserver)
+    {
+      ret = gnutls_init (&s->session, GNUTLS_SERVER);
+    }
+  else
+    {
+      ret = gnutls_init (&s->session, GNUTLS_CLIENT);
+    }
+  if (ret < 0)
+    {
+      errout (s, "Cannot initialize GNUTLS session: %s\n",
+	      gnutls_strerror (ret));
+      goto error;
+    }
+
+  gnutls_session_set_ptr (s->session, (void *) s);
+
+  ret = gnutls_set_default_priority (s->session);
+  if (ret < 0)
+    {
+      errout (s, "Cannot set default GNUTLS session priority: %s\n",
+	      gnutls_strerror (ret));
+      goto error;
+    }
+
+  const char *errpos = NULL;
+  ret = gnutls_priority_set_direct(s->session, PRIORITY, &errpos);
+  if (ret < 0)
+    {
+      errout (s, "Cannot set GNUTLS session priority: %s\n",
+	      gnutls_strerror (ret));
+      goto error;
+    }
+
+  gnutls_session_set_ptr (s->session, (void *) s);
+
+  ret = gnutls_credentials_set (s->session, GNUTLS_CRD_CERTIFICATE, s->creds);
+  if (ret < 0)
+    {
+      errout (s, "Cannot set session GNUTL credentials: %s\n",
+	      gnutls_strerror (ret));
+      goto error;
+    }
+
+  if (isserver)
+    {
+      /* requests but does not check a client certificate */
+      gnutls_certificate_server_set_request (s->session, GNUTLS_CERT_REQUEST);
+    }
+
+
+  return s;
+
+error:
+  if (s->session)
+    gnutls_deinit (s->session);
+  free (s);
+  return NULL;
+}
+
+void
+tlssession_close (tlssession_t * s)
+{
+  if (s->session)
+    gnutls_deinit (s->session);
+  free (s->hostname);
+  free (s);
+}
+
+int
+tlssession_init ()
+{
+  return gnutls_global_init ();
+}
+
+
+int
+tlssession_mainloop (int cryptfd, int plainfd, tlssession_t * s)
+{
+  fd_set readfds;
+  fd_set writefds;
+  int maxfd;
+  int tls_wr_interrupted = 0;
+  int plainEOF = FALSE;
+  int cryptEOF = FALSE;
+  int ret;
+
+  buffer_t *plainToCrypt = bufNew (BUF_SIZE, BUF_HWM);
+  buffer_t *cryptToPlain = bufNew (BUF_SIZE, BUF_HWM);
+
+  if (socksetnonblock (cryptfd, 0) < 0)
+    {
+      errout (s, "Could not turn on blocking: %m");
+      goto error;
+    }
+
+  /* set it up to work with our FD */
+  gnutls_transport_set_ptr (s->session,
+			    (gnutls_transport_ptr_t) (intptr_t) cryptfd);
+
+
+  /* Now do the handshake */
+  ret = gnutls_handshake (s->session);
+  if (ret < 0)
+    {
+      errout (s, "TLS handshake failed: %s\n", gnutls_strerror (ret));
+      goto error;
+    }
+
+  if (socksetnonblock (cryptfd, 1) < 0)
+    {
+      errout (s, "Could not turn on non-blocking on crypt FD: %m");
+      goto error;
+    }
+
+  if (socksetnonblock (plainfd, 1) < 0)
+    {
+      errout (s, "Could not turn on non-blocking on plain FD: %m");
+      goto error;
+    }
+
+  maxfd = (plainfd > cryptfd) ? plainfd + 1 : cryptfd + 1;
+
+  while ((!plainEOF || !cryptEOF) && !quit (s))
+    {
+      struct timeval timeout;
+      int result;
+      int selecterrno;
+      int wait = TRUE;
+
+      FD_ZERO (&readfds);
+      FD_ZERO (&writefds);
+
+      size_t buffered = gnutls_record_check_pending (s->session);
+      if (buffered)
+	wait = FALSE;		/* do not wait for select to return if we have buffered data */
+
+      if (plainEOF)
+	{
+	  /* plain text end has closed, but me may still have
+	   * data yet to write to the crypt end */
+	  if (bufIsEmpty (plainToCrypt) && !tls_wr_interrupted)
+	    {
+	      cryptEOF = TRUE;
+	      break;
+	    }
+	}
+      else
+	{
+	  if (!bufIsEmpty (cryptToPlain))
+	    FD_SET (plainfd, &writefds);
+	  if (!bufIsOverHWM (plainToCrypt))
+	    FD_SET (plainfd, &readfds);
+	}
+
+      if (cryptEOF)
+	{
+	  /* crypt end has closed, but me way still have data to
+	   * write from the crypt buffer */
+	  if (bufIsEmpty (cryptToPlain) && !buffered)
+	    {
+	      plainEOF = TRUE;
+	      break;
+	    }
+	}
+      else
+	{
+	  if (!bufIsEmpty (plainToCrypt) || tls_wr_interrupted)
+	    FD_SET (cryptfd, &writefds);
+	  if (!bufIsOverHWM (cryptToPlain))
+	    FD_SET (cryptfd, &readfds);
+	}
+
+      /* Repeat select whilst EINTR happens */
+      do
+	{
+	  timeout.tv_sec = wait ? 1 : 0;
+	  timeout.tv_usec = 0;
+	  result = select (maxfd, &readfds, &writefds, NULL, &timeout);
+
+	  selecterrno = errno;
+	}
+      while ((result == -1) && (selecterrno == EINTR) && !quit (s));
+      if (quit (s))
+	break;
+
+      if (FD_ISSET (plainfd, &readfds))
+	{
+	  /* we can read at least one byte */
+	  void *addr = NULL;
+	  /* get a span of characters to write to the
+	   * buffer. As the empty portion may wrap the end of the
+	   * circular buffer this might not be all we could read.
+	   */
+	  ssize_t len = bufGetWriteSpan (plainToCrypt, &addr);
+	  if (len > 0)
+	    {
+	      ssize_t ret;
+	      do
+		{
+		  ret = read (plainfd, addr, (size_t) len);
+		}
+	      while ((ret < 0) && (errno == EINTR) && !quit (s));
+	      if (quit (s))
+		break;
+	      if (ret < 0)
+		{
+		  errout (s, "Error on read from plain socket: %m\n");
+		  goto error;
+		}
+	      if (ret == 0)
+		{
+		  plainEOF = TRUE;
+		}
+	      else
+		{
+		  bufDoneWrite (plainToCrypt, ret);	/* mark ret bytes as written to the buffer */
+		}
+	    }
+	}
+
+      if (FD_ISSET (plainfd, &writefds))
+	{
+	  /* we can write at least one byte */
+	  void *addr = NULL;
+	  /* get a span of characters to read from the buffer
+	   * as the full portion may wrap the end of the circular buffer
+	   * this might not be all we have to write.
+	   */
+	  ssize_t len = bufGetReadSpan (cryptToPlain, &addr);
+	  if (len > 0)
+	    {
+	      ssize_t ret;
+	      do
+		{
+		  ret = write (plainfd, addr, (size_t) len);
+		}
+	      while ((ret < 0) && (errno == EINTR) && !quit (s));
+	      if (quit (s))
+		break;
+	      if (ret < 0)
+		{
+		  errout (s, "Error on write to plain socket: %m\n");
+		  goto error;
+		}
+	      bufDoneRead (cryptToPlain, ret);	/* mark ret bytes as read from the buffer */
+	    }
+	}
+
+      if (FD_ISSET (cryptfd, &readfds) || buffered)
+	{
+	  /* we can read at least one byte */
+	  void *addr = NULL;
+	  /* get a span of characters to write to the
+	   * buffer. As the empty portion may wrap the end of the
+	   * circular buffer this might not be all we could read.
+	   */
+	  ssize_t len = bufGetWriteSpan (cryptToPlain, &addr);
+	  if (len > 0)
+	    {
+	      ssize_t ret;
+	      do
+		{
+		  ret = gnutls_record_recv (s->session, addr, (size_t) len);
+		}
+	      while (ret == GNUTLS_E_INTERRUPTED && !quit (s));
+	      /* do not loop on GNUTLS_E_AGAIN - this means we'd block so we'd loop for
+	       * ever
+	       */
+	      if (quit (s))
+		break;
+	      if (ret < 0 && ret != GNUTLS_E_AGAIN)
+		{
+		  errout (s, "Error on read from crypt socket: %s\n",
+			  gnutls_strerror (ret));
+		  goto error;
+		}
+	      if (ret == 0)
+		{
+		  cryptEOF = TRUE;
+		}
+	      else
+		{
+		  bufDoneWrite (cryptToPlain, ret);	/* mark ret bytes as written to the buffer */
+		}
+	    }
+	}
+
+      if (FD_ISSET (cryptfd, &writefds))
+	{
+	  /* we can write at least one byte */
+	  void *addr = NULL;
+	  /* get a span of characters to read from the buffer
+	   * as the full portion may wrap the end of the circular buffer
+	   * this might not be all we have to write.
+	   */
+	  ssize_t len = bufGetReadSpan (plainToCrypt, &addr);
+	  if (len > 0)
+	    {
+	      ssize_t ret;
+	      do
+		{
+		  if (tls_wr_interrupted)
+		    {
+		      ret = gnutls_record_send (s->session, NULL, 0);
+		    }
+		  else
+		    {
+		      ret = gnutls_record_send (s->session, addr, len);
+		    }
+		}
+	      while (ret == GNUTLS_E_INTERRUPTED && !quit (s));
+	      if (quit (s))
+		break;
+	      if (ret == GNUTLS_E_AGAIN)
+		{
+		  /* we need to call this again with NULL parameters
+		   * as it blocked
+		   */
+		  tls_wr_interrupted = TRUE;
+		}
+	      else if (ret < 0)
+		{
+		  errout (s, "Error on write to crypto socket: %s\n",
+			  gnutls_strerror (ret));
+		  goto error;
+		}
+	      bufDoneRead (plainToCrypt, ret);	/* mark ret bytes as read from the buffer */
+	    }
+	}
+    }
+
+  ret = 0;
+  goto freereturn;
+
+error:
+  ret = -1;
+
+freereturn:
+  gnutls_bye (s->session, GNUTLS_SHUT_RDWR);
+  shutdown (plainfd, SHUT_RDWR);
+  bufFree (plainToCrypt);
+  bufFree (cryptToPlain);
+  return ret;
+}
diff --git a/crypto-gnutls.h b/crypto-gnutls.h
new file mode 100644
index 0000000..0ab6882
--- /dev/null
+++ b/crypto-gnutls.h
@@ -0,0 +1,43 @@
+/*
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Wrymouth Innovation Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#ifndef __TLSPROXY_CRYPTO_GNUTLS_H
+#define __TLSPROXY_CRYPTO_GNUTLS_H
+
+int tlssession_init ();
+
+typedef struct tlssession tlssession_t;
+tlssession_t *tlssession_new (int isserver,
+			      char *keyfile, char *certfile, char *cacertfile,
+			      char *hostname, int insecure, int debug,
+			      int (*quitfn) (void *opaque),
+			      int (*erroutfn) (void *opaque,
+					       const char *format,
+					       va_list ap), void *opaque);
+void tlssession_close (tlssession_t * s);
+int tlssession_mainloop (int cryptfd, int plainfd, tlssession_t * session);
+
+#endif
diff --git a/tests/run/Makefile.am b/tests/run/Makefile.am
index 5677bcf..29e4f7f 100644
--- a/tests/run/Makefile.am
+++ b/tests/run/Makefile.am
@@ -2,6 +2,9 @@ TESTS_ENVIRONMENT=$(srcdir)/simple_test
 TESTS = cfg1 cfgmulti cfgnew cfgsize write flush integrity dirconfig list rowrite tree rotree unix #integrityhuge
 check_PROGRAMS = nbd-tester-client
 nbd_tester_client_SOURCES = nbd-tester-client.c $(top_srcdir)/cliserv.h $(top_srcdir)/netdb-compat.h $(top_srcdir)/cliserv.c
+if GNUTLS
+nbd_tester_client_SOURCES += $(top_srcdir)/buffer.h $(top_srcdir)/buffer.c $(top_srcdir)/crypto-gnutls.h $(top_srcdir)/crypto-gnutls.c
+endif
 nbd_tester_client_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@
 nbd_tester_client_CPPFLAGS = -I$(top_srcdir)
 nbd_tester_client_LDADD = @GLIB_LIBS@
-- 
1.9.1




Reply to: