s4:lib/tls: add support for gnutls_certificate_set_x509_{system_trust,trust_dir}()
[samba.git] / source4 / lib / tls / tls_tstream.c
index 09fe5714ebee359731bb1d0c269e23497f60c60f..6b2c4c674c9b88fe3626d3b8fdbf74e8cf7a383e 100644 (file)
 
 #include "includes.h"
 #include "system/network.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "lib/util/util_file.h"
 #include "../util/tevent_unix.h"
 #include "../lib/tsocket/tsocket.h"
 #include "../lib/tsocket/tsocket_internal.h"
+#include "../lib/util/util_net.h"
 #include "lib/tls/tls.h"
+#include "lib/param/param.h"
 
-#if ENABLE_GNUTLS
-#include "gnutls/gnutls.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include "lib/crypto/gnutls_helpers.h"
 
-#define DH_BITS 1024
+#define DH_BITS 2048
 
-#if defined(HAVE_GNUTLS_DATUM) && !defined(HAVE_GNUTLS_DATUM_T)
-typedef gnutls_datum gnutls_datum_t;
-#endif
+const char *tls_verify_peer_string(enum tls_verify_peer_state verify_peer)
+{
+       switch (verify_peer) {
+       case TLS_VERIFY_PEER_NO_CHECK:
+               return TLS_VERIFY_PEER_NO_CHECK_STRING;
+
+       case TLS_VERIFY_PEER_CA_ONLY:
+               return TLS_VERIFY_PEER_CA_ONLY_STRING;
+
+       case TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE:
+               return TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE_STRING;
 
-#endif /* ENABLE_GNUTLS */
+       case TLS_VERIFY_PEER_CA_AND_NAME:
+               return TLS_VERIFY_PEER_CA_AND_NAME_STRING;
+
+       case TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE:
+               return TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE_STRING;
+       }
+
+       return "unknown tls_verify_peer_state";
+}
 
 static const struct tstream_context_ops tstream_tls_ops;
 
@@ -41,24 +63,32 @@ struct tstream_tls {
        struct tstream_context *plain_stream;
        int error;
 
-#if ENABLE_GNUTLS
-       gnutls_session tls_session;
-#endif /* ENABLE_GNUTLS */
+       gnutls_session_t tls_session;
+
+       bool is_server;
+
+       enum tls_verify_peer_state verify_peer;
+       const char *peer_name;
+
+       DATA_BLOB channel_bindings;
 
        struct tevent_context *current_ev;
 
        struct tevent_immediate *retry_im;
 
+       struct {
+               struct tevent_req *mgmt_req;
+       } waiting_flush;
+
        struct {
                uint8_t *buf;
                off_t ofs;
                struct iovec iov;
                struct tevent_req *subreq;
-               struct tevent_immediate *im;
        } push;
 
        struct {
-               uint8_t buffer[1024];
+               uint8_t *buf;
                struct iovec iov;
                struct tevent_req *subreq;
        } pull;
@@ -101,6 +131,17 @@ static void tstream_tls_retry(struct tstream_context *stream, bool deferred)
                tstream_context_data(stream,
                struct tstream_tls);
 
+       if (tlss->push.subreq == NULL && tlss->pull.subreq == NULL) {
+               if (tlss->waiting_flush.mgmt_req != NULL) {
+                       struct tevent_req *req = tlss->waiting_flush.mgmt_req;
+
+                       tlss->waiting_flush.mgmt_req = NULL;
+
+                       tevent_req_done(req);
+                       return;
+               }
+       }
+
        if (tlss->disconnect.req) {
                tstream_tls_retry_disconnect(stream);
                return;
@@ -139,12 +180,9 @@ static void tstream_tls_retry_trigger(struct tevent_context *ctx,
        tstream_tls_retry(stream, true);
 }
 
-#if ENABLE_GNUTLS
-static void tstream_tls_push_trigger_write(struct tevent_context *ev,
-                                          struct tevent_immediate *im,
-                                          void *private_data);
+static void tstream_tls_push_done(struct tevent_req *subreq);
 
-static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr,
+static ssize_t tstream_tls_push_function(gnutls_transport_ptr_t ptr,
                                         const void *buf, size_t size)
 {
        struct tstream_context *stream =
@@ -153,6 +191,7 @@ static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr,
        struct tstream_tls *tlss =
                tstream_context_data(stream,
                struct tstream_tls);
+       struct tevent_req *subreq = NULL;
        uint8_t *nbuf;
        size_t len;
 
@@ -186,56 +225,7 @@ static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr,
        tlss->push.buf = nbuf;
 
        memcpy(tlss->push.buf + tlss->push.ofs, buf, len);
-
-       if (tlss->push.im == NULL) {
-               tlss->push.im = tevent_create_immediate(tlss);
-               if (tlss->push.im == NULL) {
-                       errno = ENOMEM;
-                       return -1;
-               }
-       }
-
-       if (tlss->push.ofs == 0) {
-               /*
-                * We'll do start the tstream_writev
-                * in the next event cycle.
-                *
-                * This way we can batch all push requests,
-                * if they fit into a UINT16_MAX buffer.
-                *
-                * This is important as gnutls_handshake()
-                * had a bug in some versions e.g. 2.4.1
-                * and others (See bug #7218) and it doesn't
-                * handle EAGAIN.
-                */
-               tevent_schedule_immediate(tlss->push.im,
-                                         tlss->current_ev,
-                                         tstream_tls_push_trigger_write,
-                                         stream);
-       }
-
        tlss->push.ofs += len;
-       return len;
-}
-
-static void tstream_tls_push_done(struct tevent_req *subreq);
-
-static void tstream_tls_push_trigger_write(struct tevent_context *ev,
-                                          struct tevent_immediate *im,
-                                          void *private_data)
-{
-       struct tstream_context *stream =
-               talloc_get_type_abort(private_data,
-               struct tstream_context);
-       struct tstream_tls *tlss =
-               tstream_context_data(stream,
-               struct tstream_tls);
-       struct tevent_req *subreq;
-
-       if (tlss->push.subreq) {
-               /* nothing todo */
-               return;
-       }
 
        tlss->push.iov.iov_base = (char *)tlss->push.buf;
        tlss->push.iov.iov_len = tlss->push.ofs;
@@ -245,13 +235,13 @@ static void tstream_tls_push_trigger_write(struct tevent_context *ev,
                                     tlss->plain_stream,
                                     &tlss->push.iov, 1);
        if (subreq == NULL) {
-               tlss->error = ENOMEM;
-               tstream_tls_retry(stream, false);
-               return;
+               errno = ENOMEM;
+               return -1;
        }
        tevent_req_set_callback(subreq, tstream_tls_push_done, stream);
 
        tlss->push.subreq = subreq;
+       return len;
 }
 
 static void tstream_tls_push_done(struct tevent_req *subreq)
@@ -283,7 +273,7 @@ static void tstream_tls_push_done(struct tevent_req *subreq)
 
 static void tstream_tls_pull_done(struct tevent_req *subreq);
 
-static ssize_t tstream_tls_pull_function(gnutls_transport_ptr ptr,
+static ssize_t tstream_tls_pull_function(gnutls_transport_ptr_t ptr,
                                         void *buf, size_t size)
 {
        struct tstream_context *stream =
@@ -293,6 +283,7 @@ static ssize_t tstream_tls_pull_function(gnutls_transport_ptr ptr,
                tstream_context_data(stream,
                struct tstream_tls);
        struct tevent_req *subreq;
+       size_t len;
 
        if (tlss->error != 0) {
                errno = tlss->error;
@@ -318,6 +309,7 @@ static ssize_t tstream_tls_pull_function(gnutls_transport_ptr ptr,
                tlss->pull.iov.iov_base = (char *)b;
                if (tlss->pull.iov.iov_len == 0) {
                        tlss->pull.iov.iov_base = NULL;
+                       TALLOC_FREE(tlss->pull.buf);
                }
 
                return n;
@@ -327,8 +319,15 @@ static ssize_t tstream_tls_pull_function(gnutls_transport_ptr ptr,
                return 0;
        }
 
-       tlss->pull.iov.iov_base = tlss->pull.buffer;
-       tlss->pull.iov.iov_len = MIN(size, sizeof(tlss->pull.buffer));
+       len = MIN(size, UINT16_MAX);
+
+       tlss->pull.buf = talloc_array(tlss, uint8_t, len);
+       if (tlss->pull.buf == NULL) {
+               return -1;
+       }
+
+       tlss->pull.iov.iov_base = (char *)tlss->pull.buf;
+       tlss->pull.iov.iov_len = len;
 
        subreq = tstream_readv_send(tlss,
                                    tlss->current_ev,
@@ -368,16 +367,14 @@ static void tstream_tls_pull_done(struct tevent_req *subreq)
 
        tstream_tls_retry(stream, false);
 }
-#endif /* ENABLE_GNUTLS */
 
 static int tstream_tls_destructor(struct tstream_tls *tlss)
 {
-#if ENABLE_GNUTLS
        if (tlss->tls_session) {
                gnutls_deinit(tlss->tls_session);
                tlss->tls_session = NULL;
        }
-#endif /* ENABLE_GNUTLS */
+
        return 0;
 }
 
@@ -393,13 +390,9 @@ static ssize_t tstream_tls_pending_bytes(struct tstream_context *stream)
                return -1;
        }
 
-#if ENABLE_GNUTLS
        ret = gnutls_record_check_pending(tlss->tls_session);
        ret += tlss->read.left;
-#else /* ENABLE_GNUTLS */
-       errno = ENOSYS;
-       ret = -1;
-#endif /* ENABLE_GNUTLS */
+
        return ret;
 }
 
@@ -427,6 +420,12 @@ static struct tevent_req *tstream_tls_readv_send(TALLOC_CTX *mem_ctx,
        struct tstream_tls_readv_state *state;
 
        tlss->read.req = NULL;
+
+       if (tlss->current_ev != ev) {
+               SMB_ASSERT(tlss->push.subreq == NULL);
+               SMB_ASSERT(tlss->pull.subreq == NULL);
+       }
+
        tlss->current_ev = ev;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -509,7 +508,6 @@ static void tstream_tls_retry_read(struct tstream_context *stream)
                tstream_context_data(stream,
                struct tstream_tls);
        struct tevent_req *req = tlss->read.req;
-#if ENABLE_GNUTLS
        int ret;
 
        if (tlss->error != 0) {
@@ -544,9 +542,6 @@ static void tstream_tls_retry_read(struct tstream_context *stream)
 
        tlss->read.left = ret;
        tstream_tls_readv_crypt_next(req);
-#else /* ENABLE_GNUTLS */
-       tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
 }
 
 static int tstream_tls_readv_recv(struct tevent_req *req,
@@ -595,6 +590,12 @@ static struct tevent_req *tstream_tls_writev_send(TALLOC_CTX *mem_ctx,
        struct tstream_tls_writev_state *state;
 
        tlss->write.req = NULL;
+
+       if (tlss->current_ev != ev) {
+               SMB_ASSERT(tlss->push.subreq == NULL);
+               SMB_ASSERT(tlss->pull.subreq == NULL);
+       }
+
        tlss->current_ev = ev;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -683,7 +684,6 @@ static void tstream_tls_retry_write(struct tstream_context *stream)
                tstream_context_data(stream,
                struct tstream_tls);
        struct tevent_req *req = tlss->write.req;
-#if ENABLE_GNUTLS
        int ret;
 
        if (tlss->error != 0) {
@@ -723,9 +723,6 @@ static void tstream_tls_retry_write(struct tstream_context *stream)
        }
 
        tstream_tls_writev_crypt_next(req);
-#else /* ENABLE_GNUTLS */
-       tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
 }
 
 static int tstream_tls_writev_recv(struct tevent_req *req,
@@ -765,6 +762,12 @@ static struct tevent_req *tstream_tls_disconnect_send(TALLOC_CTX *mem_ctx,
        struct tstream_tls_disconnect_state *state;
 
        tlss->disconnect.req = NULL;
+
+       if (tlss->current_ev != ev) {
+               SMB_ASSERT(tlss->push.subreq == NULL);
+               SMB_ASSERT(tlss->pull.subreq == NULL);
+       }
+
        tlss->current_ev = ev;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -793,7 +796,6 @@ static void tstream_tls_retry_disconnect(struct tstream_context *stream)
                tstream_context_data(stream,
                struct tstream_tls);
        struct tevent_req *req = tlss->disconnect.req;
-#if ENABLE_GNUTLS
        int ret;
 
        if (tlss->error != 0) {
@@ -822,10 +824,12 @@ static void tstream_tls_retry_disconnect(struct tstream_context *stream)
                return;
        }
 
+       if (tlss->push.subreq != NULL || tlss->pull.subreq != NULL) {
+               tlss->waiting_flush.mgmt_req = req;
+               return;
+       }
+
        tevent_req_done(req);
-#else /* ENABLE_GNUTLS */
-       tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
 }
 
 static int tstream_tls_disconnect_recv(struct tevent_req *req,
@@ -854,17 +858,21 @@ static const struct tstream_context_ops tstream_tls_ops = {
        .disconnect_recv        = tstream_tls_disconnect_recv,
 };
 
-struct tstream_tls_params {
-#if ENABLE_GNUTLS
-       gnutls_certificate_credentials x509_cred;
-       gnutls_dh_params dh_params;
-#endif /* ENABLE_GNUTLS */
+struct tstream_tls_params_internal {
+       gnutls_certificate_credentials_t x509_cred;
+       gnutls_dh_params_t dh_params;
+       const char *tls_priority;
        bool tls_enabled;
+       enum tls_verify_peer_state verify_peer;
+       const char *peer_name;
 };
 
-static int tstream_tls_params_destructor(struct tstream_tls_params *tlsp)
+struct tstream_tls_params {
+       struct tstream_tls_params_internal *internal;
+};
+
+static int tstream_tls_params_internal_destructor(struct tstream_tls_params_internal *tlsp)
 {
-#if ENABLE_GNUTLS
        if (tlsp->x509_cred) {
                gnutls_certificate_free_credentials(tlsp->x509_cred);
                tlsp->x509_cred = NULL;
@@ -873,73 +881,451 @@ static int tstream_tls_params_destructor(struct tstream_tls_params *tlsp)
                gnutls_dh_params_deinit(tlsp->dh_params);
                tlsp->dh_params = NULL;
        }
-#endif /* ENABLE_GNUTLS */
+
        return 0;
 }
 
-bool tstream_tls_params_enabled(struct tstream_tls_params *tlsp)
+bool tstream_tls_params_enabled(struct tstream_tls_params *tls_params)
 {
+       struct tstream_tls_params_internal *tlsp = tls_params->internal;
+
        return tlsp->tls_enabled;
 }
 
+static NTSTATUS tstream_tls_setup_channel_bindings(struct tstream_tls *tlss)
+{
+       gnutls_datum_t cb = { .size = 0 };
+       int ret;
+
+#ifdef HAVE_GNUTLS_CB_TLS_SERVER_END_POINT
+       ret = gnutls_session_channel_binding(tlss->tls_session,
+                                            GNUTLS_CB_TLS_SERVER_END_POINT,
+                                            &cb);
+#else /* not HAVE_GNUTLS_CB_TLS_SERVER_END_POINT */
+       ret = legacy_gnutls_server_end_point_cb(tlss->tls_session,
+                                               tlss->is_server,
+                                               &cb);
+#endif /* not HAVE_GNUTLS_CB_TLS_SERVER_END_POINT */
+       if (ret != GNUTLS_E_SUCCESS) {
+               return gnutls_error_to_ntstatus(ret,
+                               NT_STATUS_CRYPTO_SYSTEM_INVALID);
+       }
+
+       if (cb.size != 0) {
+               /*
+                * Looking at the OpenLDAP implementation
+                * for LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT
+                * revealed that we need to prefix it with
+                * 'tls-server-end-point:'
+                */
+               const char endpoint_prefix[] = "tls-server-end-point:";
+               size_t prefix_size = strlen(endpoint_prefix);
+               size_t size = prefix_size + cb.size;
+
+               tlss->channel_bindings = data_blob_talloc_named(tlss, NULL, size,
+                                                               "tls_channel_bindings");
+               if (tlss->channel_bindings.data == NULL) {
+                       gnutls_free(cb.data);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               memcpy(tlss->channel_bindings.data, endpoint_prefix, prefix_size);
+               memcpy(tlss->channel_bindings.data + prefix_size, cb.data, cb.size);
+               gnutls_free(cb.data);
+       }
+
+       return NT_STATUS_OK;
+}
+
+const DATA_BLOB *tstream_tls_channel_bindings(struct tstream_context *tls_tstream)
+{
+       struct tstream_tls *tlss =
+               talloc_get_type(_tstream_context_data(tls_tstream),
+               struct tstream_tls);
+
+       if (tlss == NULL) {
+               return NULL;
+       }
+
+       return &tlss->channel_bindings;
+}
+
 NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx,
+                                  bool system_cas,
+                                  const char * const *ca_dirs,
                                   const char *ca_file,
                                   const char *crl_file,
+                                  const char *tls_priority,
+                                  enum tls_verify_peer_state verify_peer,
+                                  const char *peer_name,
                                   struct tstream_tls_params **_tlsp)
 {
-#if ENABLE_GNUTLS
-       struct tstream_tls_params *tlsp;
+       struct tstream_tls_params *__tlsp = NULL;
+       struct tstream_tls_params_internal *tlsp = NULL;
+       bool got_ca = false;
+       size_t i;
        int ret;
 
-       ret = gnutls_global_init();
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               return NT_STATUS_NOT_SUPPORTED;
+       __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+       if (__tlsp == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
-       tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
-       NT_STATUS_HAVE_NO_MEMORY(tlsp);
+       tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+       if (tlsp == NULL) {
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+       __tlsp->internal = tlsp;
 
-       talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+       tlsp->verify_peer = verify_peer;
+       if (peer_name != NULL) {
+               tlsp->peer_name = talloc_strdup(tlsp, peer_name);
+               if (tlsp->peer_name == NULL) {
+                       TALLOC_FREE(__tlsp);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_CA_AND_NAME) {
+               DEBUG(0,("TLS failed to missing peer_name - "
+                        "with 'tls verify peer = %s'\n",
+                        tls_verify_peer_string(tlsp->verify_peer)));
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
 
        ret = gnutls_certificate_allocate_credentials(&tlsp->x509_cred);
        if (ret != GNUTLS_E_SUCCESS) {
                DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               talloc_free(tlsp);
+               TALLOC_FREE(__tlsp);
                return NT_STATUS_NO_MEMORY;
        }
 
-       if (ca_file && *ca_file) {
+       if (system_cas) {
+               ret = gnutls_certificate_set_x509_system_trust(tlsp->x509_cred);
+               if (ret < 0) {
+                       DBG_ERR("gnutls_certificate_set_x509_system_trust() - %s\n",
+                               gnutls_strerror(ret));
+                       TALLOC_FREE(__tlsp);
+                       return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               }
+               if (ret > 0) {
+                       got_ca = true;
+               }
+       }
+
+       for (i = 0; ca_dirs != NULL && ca_dirs[i] != NULL; i++) {
+               const char *ca_dir = ca_dirs[i];
+
+               if (!directory_exist(ca_dir)) {
+                       continue;
+               }
+
+               ret = gnutls_certificate_set_x509_trust_dir(tlsp->x509_cred,
+                                                           ca_dir,
+                                                           GNUTLS_X509_FMT_PEM);
+               if (ret < 0) {
+                       DBG_ERR("gnutls_certificate_set_x509_trust_dir(%s) - %s\n",
+                               ca_dir, gnutls_strerror(ret));
+                       TALLOC_FREE(__tlsp);
+                       return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               }
+               if (ret > 0) {
+                       got_ca = true;
+               }
+       }
+
+       if (ca_file && *ca_file && file_exist(ca_file)) {
                ret = gnutls_certificate_set_x509_trust_file(tlsp->x509_cred,
                                                             ca_file,
                                                             GNUTLS_X509_FMT_PEM);
                if (ret < 0) {
                        DEBUG(0,("TLS failed to initialise cafile %s - %s\n",
                                 ca_file, gnutls_strerror(ret)));
-                       talloc_free(tlsp);
+                       TALLOC_FREE(__tlsp);
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
                }
+               if (ret > 0) {
+                       got_ca = true;
+               }
        }
 
-       if (crl_file && *crl_file) {
+       if (!got_ca && tlsp->verify_peer >= TLS_VERIFY_PEER_CA_ONLY) {
+               D_ERR("TLS: 'tls verify peer = %s' requires "
+                     "'tls trust system cas', "
+                     "'tls ca directories' or "
+                     "'tls cafile'\n",
+                     tls_verify_peer_string(tlsp->verify_peer));
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       if (crl_file && *crl_file && file_exist(crl_file)) {
                ret = gnutls_certificate_set_x509_crl_file(tlsp->x509_cred,
                                                           crl_file, 
                                                           GNUTLS_X509_FMT_PEM);
                if (ret < 0) {
                        DEBUG(0,("TLS failed to initialise crlfile %s - %s\n",
                                 crl_file, gnutls_strerror(ret)));
-                       talloc_free(tlsp);
+                       TALLOC_FREE(__tlsp);
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
                }
+       } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE) {
+               DEBUG(0,("TLS failed to missing crlfile %s - "
+                        "with 'tls verify peer = %s'\n",
+                        crl_file,
+                        tls_verify_peer_string(tlsp->verify_peer)));
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       tlsp->tls_priority = talloc_strdup(tlsp, tls_priority);
+       if (tlsp->tls_priority == NULL) {
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_NO_MEMORY;
        }
 
        tlsp->tls_enabled = true;
 
-       *_tlsp = tlsp;
+       *_tlsp = __tlsp;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS tstream_tls_params_client_lpcfg(TALLOC_CTX *mem_ctx,
+                                        struct loadparm_context *lp_ctx,
+                                        const char *peer_name,
+                                        struct tstream_tls_params **tlsp)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       bool system_cas = false;
+       const char * const *ca_dirs = NULL;
+       const char *ptr = NULL;
+       char *ca_file = NULL;
+       char *crl_file = NULL;
+       const char *tls_priority = NULL;
+       enum tls_verify_peer_state verify_peer =
+               TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE;
+       NTSTATUS status;
+
+       system_cas = lpcfg_tls_trust_system_cas(lp_ctx);
+       ca_dirs = lpcfg_tls_ca_directories(lp_ctx);
+
+       ptr = lpcfg__tls_cafile(lp_ctx);
+       if (ptr != NULL) {
+               ca_file = lpcfg_tls_cafile(frame, lp_ctx);
+               if (ca_file == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       ptr = lpcfg__tls_crlfile(lp_ctx);
+       if (ptr != NULL) {
+               crl_file = lpcfg_tls_crlfile(frame, lp_ctx);
+               if (crl_file == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       tls_priority = lpcfg_tls_priority(lp_ctx);
+       verify_peer = lpcfg_tls_verify_peer(lp_ctx);
+
+       status = tstream_tls_params_client(mem_ctx,
+                                          system_cas,
+                                          ca_dirs,
+                                          ca_file,
+                                          crl_file,
+                                          tls_priority,
+                                          verify_peer,
+                                          peer_name,
+                                          tlsp);
+       TALLOC_FREE(frame);
+       return status;
+}
+
+static NTSTATUS tstream_tls_prepare_gnutls(struct tstream_tls_params *_tlsp,
+                                          struct tstream_tls *tlss)
+{
+       struct tstream_tls_params_internal *tlsp = NULL;
+       int ret;
+       unsigned int flags;
+       const char *hostname = NULL;
+
+       if (tlss->is_server) {
+               flags = GNUTLS_SERVER;
+       } else {
+               flags = GNUTLS_CLIENT;
+               /*
+                * tls_tstream can't properly handle 'New Session Ticket'
+                * messages sent 'after' the client sends the 'Finished'
+                * message.  GNUTLS_NO_TICKETS was introduced in GnuTLS 3.5.6.
+                * This flag is to indicate the session Flag session should not
+                * use resumption with session tickets.
+                */
+               flags |= GNUTLS_NO_TICKETS;
+       }
+
+       /*
+        * Note we need to make sure x509_cred and dh_params
+        * from tstream_tls_params_internal stay alive for
+        * the whole lifetime of this session!
+        *
+        * See 'man gnutls_credentials_set' and
+        * 'man gnutls_certificate_set_dh_params'.
+        *
+        * Note: here we use talloc_reference() in a way
+        *       that does not expose it to the caller.
+        */
+       tlsp = talloc_reference(tlss, _tlsp->internal);
+       if (tlsp == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       tlss->verify_peer = tlsp->verify_peer;
+       if (tlsp->peer_name != NULL) {
+               bool ip = is_ipaddress(tlsp->peer_name);
+
+               tlss->peer_name = talloc_strdup(tlss, tlsp->peer_name);
+               if (tlss->peer_name == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               if (!ip) {
+                       hostname = tlss->peer_name;
+               }
+
+               if (tlss->verify_peer < TLS_VERIFY_PEER_CA_AND_NAME) {
+                       hostname = NULL;
+               }
+       }
+
+       if (tlss->current_ev != NULL) {
+               tlss->retry_im = tevent_create_immediate(tlss);
+               if (tlss->retry_im == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       ret = gnutls_init(&tlss->tls_session, flags);
+       if (ret != GNUTLS_E_SUCCESS) {
+               return gnutls_error_to_ntstatus(ret,
+                       NT_STATUS_CRYPTO_SYSTEM_INVALID);
+       }
+
+       ret = gnutls_set_default_priority(tlss->tls_session);
+       if (ret != GNUTLS_E_SUCCESS) {
+               return gnutls_error_to_ntstatus(ret,
+                       NT_STATUS_CRYPTO_SYSTEM_INVALID);
+       }
+
+       if (strlen(tlsp->tls_priority) > 0) {
+               const char *error_pos = NULL;
+
+               ret = gnutls_priority_set_direct(tlss->tls_session,
+                                                tlsp->tls_priority,
+                                                &error_pos);
+               if (ret != GNUTLS_E_SUCCESS) {
+                       return gnutls_error_to_ntstatus(ret,
+                               NT_STATUS_CRYPTO_SYSTEM_INVALID);
+               }
+       }
+
+       ret = gnutls_credentials_set(tlss->tls_session,
+                                    GNUTLS_CRD_CERTIFICATE,
+                                    tlsp->x509_cred);
+       if (ret != GNUTLS_E_SUCCESS) {
+               return gnutls_error_to_ntstatus(ret,
+                               NT_STATUS_CRYPTO_SYSTEM_INVALID);
+       }
+
+       if (hostname != NULL) {
+               ret = gnutls_server_name_set(tlss->tls_session,
+                                            GNUTLS_NAME_DNS,
+                                            hostname,
+                                            strlen(hostname));
+               if (ret != GNUTLS_E_SUCCESS) {
+                       return gnutls_error_to_ntstatus(ret,
+                                       NT_STATUS_CRYPTO_SYSTEM_INVALID);
+               }
+       }
+
+       if (tlss->is_server) {
+               gnutls_certificate_server_set_request(tlss->tls_session,
+                                                     GNUTLS_CERT_REQUEST);
+               gnutls_dh_set_prime_bits(tlss->tls_session, DH_BITS);
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS tstream_tls_verify_peer(struct tstream_tls *tlss)
+{
+       unsigned int status = UINT32_MAX;
+       bool ip = true;
+       const char *hostname = NULL;
+       int ret;
+
+       if (tlss->verify_peer == TLS_VERIFY_PEER_NO_CHECK) {
+               return NT_STATUS_OK;
+       }
+
+       if (tlss->peer_name != NULL) {
+               ip = is_ipaddress(tlss->peer_name);
+       }
+
+       if (!ip) {
+               hostname = tlss->peer_name;
+       }
+
+       if (tlss->verify_peer == TLS_VERIFY_PEER_CA_ONLY) {
+               hostname = NULL;
+       }
+
+       if (tlss->verify_peer >= TLS_VERIFY_PEER_CA_AND_NAME) {
+               if (hostname == NULL) {
+                       DEBUG(1,("TLS %s - no hostname available for "
+                                "verify_peer[%s] and peer_name[%s]\n",
+                                __location__,
+                                tls_verify_peer_string(tlss->verify_peer),
+                                tlss->peer_name));
+                       return NT_STATUS_IMAGE_CERT_REVOKED;
+               }
+       }
+
+       ret = gnutls_certificate_verify_peers3(tlss->tls_session,
+                                              hostname,
+                                              &status);
+       if (ret != GNUTLS_E_SUCCESS) {
+               return gnutls_error_to_ntstatus(ret,
+                       NT_STATUS_CRYPTO_SYSTEM_INVALID);
+       }
+
+       if (status != 0) {
+               DEBUG(1,("TLS %s - check failed for "
+                        "verify_peer[%s] and peer_name[%s] "
+                        "status 0x%x (%s%s%s%s%s%s%s%s)\n",
+                        __location__,
+                        tls_verify_peer_string(tlss->verify_peer),
+                        tlss->peer_name,
+                        status,
+                        status & GNUTLS_CERT_INVALID ? "invalid " : "",
+                        status & GNUTLS_CERT_REVOKED ? "revoked " : "",
+                        status & GNUTLS_CERT_SIGNER_NOT_FOUND ?
+                               "signer_not_found " : "",
+                        status & GNUTLS_CERT_SIGNER_NOT_CA ?
+                               "signer_not_ca " : "",
+                        status & GNUTLS_CERT_INSECURE_ALGORITHM ?
+                               "insecure_algorithm " : "",
+                        status & GNUTLS_CERT_NOT_ACTIVATED ?
+                               "not_activated " : "",
+                        status & GNUTLS_CERT_EXPIRED ?
+                               "expired " : "",
+                        status & GNUTLS_CERT_UNEXPECTED_OWNER ?
+                               "unexpected_owner " : ""));
+               return NT_STATUS_IMAGE_CERT_REVOKED;
+       }
+
        return NT_STATUS_OK;
-#else /* ENABLE_GNUTLS */
-       return NT_STATUS_NOT_IMPLEMENTED;
-#endif /* ENABLE_GNUTLS */
 }
 
 struct tstream_tls_connect_state {
@@ -949,20 +1335,13 @@ struct tstream_tls_connect_state {
 struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
                                             struct tevent_context *ev,
                                             struct tstream_context *plain_stream,
-                                            struct tstream_tls_params *tls_params,
+                                            struct tstream_tls_params *_tls_params,
                                             const char *location)
 {
        struct tevent_req *req;
        struct tstream_tls_connect_state *state;
-#if ENABLE_GNUTLS
        struct tstream_tls *tlss;
-       int ret;
-       static const int cert_type_priority[] = {
-               GNUTLS_CRT_X509,
-               GNUTLS_CRT_OPENPGP,
-               0
-       };
-#endif /* ENABLE_GNUTLS */
+       NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct tstream_tls_connect_state);
@@ -970,7 +1349,6 @@ struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-#if ENABLE_GNUTLS
        state->tls_stream = tstream_context_create(state,
                                                   &tstream_tls_ops,
                                                   &tlss,
@@ -981,46 +1359,26 @@ struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
        }
        ZERO_STRUCTP(tlss);
        talloc_set_destructor(tlss, tstream_tls_destructor);
-
        tlss->plain_stream = plain_stream;
-
+       tlss->is_server = false;
        tlss->current_ev = ev;
-       tlss->retry_im = tevent_create_immediate(tlss);
-       if (tevent_req_nomem(tlss->retry_im, req)) {
-               return tevent_req_post(req, ev);
-       }
 
-       ret = gnutls_init(&tlss->tls_session, GNUTLS_CLIENT);
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               tevent_req_error(req, EINVAL);
-               return tevent_req_post(req, ev);
-       }
-
-       ret = gnutls_set_default_priority(tlss->tls_session);
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               tevent_req_error(req, EINVAL);
+       status = tstream_tls_prepare_gnutls(_tls_params, tlss);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+               tevent_req_oom(req);
                return tevent_req_post(req, ev);
        }
-
-       gnutls_certificate_type_set_priority(tlss->tls_session, cert_type_priority);
-
-       ret = gnutls_credentials_set(tlss->tls_session,
-                                    GNUTLS_CRD_CERTIFICATE,
-                                    tls_params->x509_cred);
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+       if (!NT_STATUS_IS_OK(status)) {
                tevent_req_error(req, EINVAL);
                return tevent_req_post(req, ev);
        }
 
-       gnutls_transport_set_ptr(tlss->tls_session, (gnutls_transport_ptr)state->tls_stream);
+       gnutls_transport_set_ptr(tlss->tls_session,
+                                (gnutls_transport_ptr_t)state->tls_stream);
        gnutls_transport_set_pull_function(tlss->tls_session,
                                           (gnutls_pull_func)tstream_tls_pull_function);
        gnutls_transport_set_push_function(tlss->tls_session,
                                           (gnutls_push_func)tstream_tls_push_function);
-       gnutls_transport_set_lowat(tlss->tls_session, 0);
 
        tlss->handshake.req = req;
        tstream_tls_retry_handshake(state->tls_stream);
@@ -1029,10 +1387,6 @@ struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
        }
 
        return req;
-#else /* ENABLE_GNUTLS */
-       tevent_req_error(req, ENOSYS);
-       return tevent_req_post(req, ev);
-#endif /* ENABLE_GNUTLS */
 }
 
 int tstream_tls_connect_recv(struct tevent_req *req,
@@ -1054,8 +1408,6 @@ int tstream_tls_connect_recv(struct tevent_req *req,
        return 0;
 }
 
-extern void tls_cert_generate(TALLOC_CTX *, const char *, const char *, const char *, const char *);
-
 /*
   initialise global tls state
 */
@@ -1067,42 +1419,72 @@ NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
                                   const char *ca_file,
                                   const char *crl_file,
                                   const char *dhp_file,
+                                  const char *tls_priority,
                                   struct tstream_tls_params **_tlsp)
 {
-       struct tstream_tls_params *tlsp;
-#if ENABLE_GNUTLS
+       struct tstream_tls_params *__tlsp = NULL;
+       struct tstream_tls_params_internal *tlsp = NULL;
        int ret;
+       struct stat st;
 
        if (!enabled || key_file == NULL || *key_file == 0) {
-               tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
-               NT_STATUS_HAVE_NO_MEMORY(tlsp);
-               talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+               __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+               if (__tlsp == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+               if (tlsp == NULL) {
+                       TALLOC_FREE(__tlsp);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+               __tlsp->internal = tlsp;
                tlsp->tls_enabled = false;
 
-               *_tlsp = tlsp;
+               *_tlsp = __tlsp;
                return NT_STATUS_OK;
        }
 
-       ret = gnutls_global_init();
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               return NT_STATUS_NOT_SUPPORTED;
+       __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+       if (__tlsp == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
-       tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
-       NT_STATUS_HAVE_NO_MEMORY(tlsp);
+       tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+       if (tlsp == NULL) {
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+       talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+       __tlsp->internal = tlsp;
 
        if (!file_exist(ca_file)) {
                tls_cert_generate(tlsp, dns_host_name,
                                  key_file, cert_file, ca_file);
        }
 
+       if (file_exist(key_file) &&
+           !file_check_permissions(key_file, geteuid(), 0600, &st))
+       {
+               DEBUG(0, ("Invalid permissions on TLS private key file '%s':\n"
+                         "owner uid %u should be %u, mode 0%o should be 0%o\n"
+                         "This is known as CVE-2013-4476.\n"
+                         "Removing all tls .pem files will cause an "
+                         "auto-regeneration with the correct permissions.\n",
+                         key_file,
+                         (unsigned int)st.st_uid, geteuid(),
+                         (unsigned int)(st.st_mode & 0777), 0600));
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
        ret = gnutls_certificate_allocate_credentials(&tlsp->x509_cred);
        if (ret != GNUTLS_E_SUCCESS) {
                DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               talloc_free(tlsp);
+               TALLOC_FREE(__tlsp);
                return NT_STATUS_NO_MEMORY;
        }
 
@@ -1113,7 +1495,7 @@ NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
                if (ret < 0) {
                        DEBUG(0,("TLS failed to initialise cafile %s - %s\n",
                                 ca_file, gnutls_strerror(ret)));
-                       talloc_free(tlsp);
+                       TALLOC_FREE(__tlsp);
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
                }
        }
@@ -1125,7 +1507,7 @@ NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
                if (ret < 0) {
                        DEBUG(0,("TLS failed to initialise crlfile %s - %s\n",
                                 crl_file, gnutls_strerror(ret)));
-                       talloc_free(tlsp);
+                       TALLOC_FREE(__tlsp);
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
                }
        }
@@ -1136,14 +1518,14 @@ NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
        if (ret != GNUTLS_E_SUCCESS) {
                DEBUG(0,("TLS failed to initialise certfile %s and keyfile %s - %s\n",
                         cert_file, key_file, gnutls_strerror(ret)));
-               talloc_free(tlsp);
+               TALLOC_FREE(__tlsp);
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
        ret = gnutls_dh_params_init(&tlsp->dh_params);
        if (ret != GNUTLS_E_SUCCESS) {
                DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               talloc_free(tlsp);
+               TALLOC_FREE(__tlsp);
                return NT_STATUS_NO_MEMORY;
        }
 
@@ -1156,7 +1538,7 @@ NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
                if (!dhparms.data) {
                        DEBUG(0,("TLS failed to read DH Parms from %s - %d:%s\n",
                                 dhp_file, errno, strerror(errno)));
-                       talloc_free(tlsp);
+                       TALLOC_FREE(__tlsp);
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
                }
                dhparms.size = size;
@@ -1167,7 +1549,7 @@ NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
                if (ret != GNUTLS_E_SUCCESS) {
                        DEBUG(0,("TLS failed to import pkcs3 %s - %s\n",
                                 dhp_file, gnutls_strerror(ret)));
-                       talloc_free(tlsp);
+                       TALLOC_FREE(__tlsp);
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
                }
        } else {
@@ -1175,23 +1557,22 @@ NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
                if (ret != GNUTLS_E_SUCCESS) {
                        DEBUG(0,("TLS failed to generate dh_params - %s\n",
                                 gnutls_strerror(ret)));
-                       talloc_free(tlsp);
+                       TALLOC_FREE(__tlsp);
                        return NT_STATUS_INTERNAL_ERROR;
                }
        }
 
        gnutls_certificate_set_dh_params(tlsp->x509_cred, tlsp->dh_params);
 
-       tlsp->tls_enabled = true;
+       tlsp->tls_priority = talloc_strdup(tlsp, tls_priority);
+       if (tlsp->tls_priority == NULL) {
+               TALLOC_FREE(__tlsp);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-#else /* ENABLE_GNUTLS */
-       tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
-       NT_STATUS_HAVE_NO_MEMORY(tlsp);
-       talloc_set_destructor(tlsp, tstream_tls_params_destructor);
-       tlsp->tls_enabled = false;
-#endif /* ENABLE_GNUTLS */
+       tlsp->tls_enabled = true;
 
-       *_tlsp = tlsp;
+       *_tlsp = __tlsp;
        return NT_STATUS_OK;
 }
 
@@ -1202,15 +1583,13 @@ struct tstream_tls_accept_state {
 struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
                                            struct tevent_context *ev,
                                            struct tstream_context *plain_stream,
-                                           struct tstream_tls_params *tlsp,
+                                           struct tstream_tls_params *_tlsp,
                                            const char *location)
 {
        struct tevent_req *req;
        struct tstream_tls_accept_state *state;
        struct tstream_tls *tlss;
-#if ENABLE_GNUTLS
-       int ret;
-#endif /* ENABLE_GNUTLS */
+       NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct tstream_tls_accept_state);
@@ -1228,48 +1607,26 @@ struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
        }
        ZERO_STRUCTP(tlss);
        talloc_set_destructor(tlss, tstream_tls_destructor);
-
-#if ENABLE_GNUTLS
        tlss->plain_stream = plain_stream;
-
+       tlss->is_server = true;
        tlss->current_ev = ev;
-       tlss->retry_im = tevent_create_immediate(tlss);
-       if (tevent_req_nomem(tlss->retry_im, req)) {
-               return tevent_req_post(req, ev);
-       }
-
-       ret = gnutls_init(&tlss->tls_session, GNUTLS_SERVER);
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               tevent_req_error(req, EINVAL);
-               return tevent_req_post(req, ev);
-       }
 
-       ret = gnutls_set_default_priority(tlss->tls_session);
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
-               tevent_req_error(req, EINVAL);
+       status = tstream_tls_prepare_gnutls(_tlsp, tlss);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+               tevent_req_oom(req);
                return tevent_req_post(req, ev);
        }
-
-       ret = gnutls_credentials_set(tlss->tls_session, GNUTLS_CRD_CERTIFICATE,
-                                    tlsp->x509_cred);
-       if (ret != GNUTLS_E_SUCCESS) {
-               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+       if (!NT_STATUS_IS_OK(status)) {
                tevent_req_error(req, EINVAL);
                return tevent_req_post(req, ev);
        }
 
-       gnutls_certificate_server_set_request(tlss->tls_session,
-                                             GNUTLS_CERT_REQUEST);
-       gnutls_dh_set_prime_bits(tlss->tls_session, DH_BITS);
-
-       gnutls_transport_set_ptr(tlss->tls_session, (gnutls_transport_ptr)state->tls_stream);
+       gnutls_transport_set_ptr(tlss->tls_session,
+                                (gnutls_transport_ptr_t)state->tls_stream);
        gnutls_transport_set_pull_function(tlss->tls_session,
                                           (gnutls_pull_func)tstream_tls_pull_function);
        gnutls_transport_set_push_function(tlss->tls_session,
                                           (gnutls_push_func)tstream_tls_push_function);
-       gnutls_transport_set_lowat(tlss->tls_session, 0);
 
        tlss->handshake.req = req;
        tstream_tls_retry_handshake(state->tls_stream);
@@ -1278,10 +1635,6 @@ struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
        }
 
        return req;
-#else /* ENABLE_GNUTLS */
-       tevent_req_error(req, ENOSYS);
-       return tevent_req_post(req, ev);
-#endif /* ENABLE_GNUTLS */
 }
 
 static void tstream_tls_retry_handshake(struct tstream_context *stream)
@@ -1290,7 +1643,7 @@ static void tstream_tls_retry_handshake(struct tstream_context *stream)
                tstream_context_data(stream,
                struct tstream_tls);
        struct tevent_req *req = tlss->handshake.req;
-#if ENABLE_GNUTLS
+       NTSTATUS status;
        int ret;
 
        if (tlss->error != 0) {
@@ -1319,10 +1672,31 @@ static void tstream_tls_retry_handshake(struct tstream_context *stream)
                return;
        }
 
+       status = tstream_tls_verify_peer(tlss);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IMAGE_CERT_REVOKED)) {
+               tlss->error = EINVAL;
+               tevent_req_error(req, tlss->error);
+               return;
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               tlss->error = EIO;
+               tevent_req_error(req, tlss->error);
+               return;
+       }
+
+       status = tstream_tls_setup_channel_bindings(tlss);
+       if (!NT_STATUS_IS_OK(status)) {
+               tlss->error = EIO;
+               tevent_req_error(req, tlss->error);
+               return;
+       }
+
+       if (tlss->push.subreq != NULL || tlss->pull.subreq != NULL) {
+               tlss->waiting_flush.mgmt_req = req;
+               return;
+       }
+
        tevent_req_done(req);
-#else /* ENABLE_GNUTLS */
-       tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
 }
 
 int tstream_tls_accept_recv(struct tevent_req *req,