HACK-TODO: tls_tstream...
authorStefan Metzmacher <metze@samba.org>
Wed, 3 Feb 2010 13:36:10 +0000 (14:36 +0100)
committerStefan Metzmacher <metze@samba.org>
Thu, 4 Feb 2010 13:53:43 +0000 (14:53 +0100)
source4/lib/tls/config.mk
source4/lib/tls/tls.h
source4/lib/tls/tls_tstream.c [new file with mode: 0644]

index 0e1978cc1b0aafd3a1936e34576a2cf69c46c87f..fd8be55f3b3146d6e4157f2175fca2f121dc3e2a 100644 (file)
@@ -1,5 +1,6 @@
 [SUBSYSTEM::LIBTLS]
 PUBLIC_DEPENDENCIES = \
-               LIBTALLOC GNUTLS GCRYPT LIBSAMBA-HOSTCONFIG samba_socket
+               LIBTALLOC GNUTLS GCRYPT LIBSAMBA-HOSTCONFIG samba_socket \
+               TEVENT UTIL_TEVENT TSOCKET
 
-LIBTLS_OBJ_FILES = $(addprefix $(libtlssrcdir)/, tls.o tlscert.o)
+LIBTLS_OBJ_FILES = $(addprefix $(libtlssrcdir)/, tls.o tlscert.o tls_tstream.o)
index c5bd5c87cc13a0cd774a5e47da13daf1556489ac..f80282b9ccd51102483037a23af126b843e6580a 100644 (file)
@@ -65,4 +65,115 @@ bool tls_support(struct tls_params *parms);
 
 const struct socket_ops *socket_tls_ops(enum socket_type type);
 
+struct tstream_context;
+struct tstream_tls_params;
+
+/**
+ * @brief Initiate a TLS tunnel on top of a given tstream
+ *
+ * @param[in]  mem_ctx
+ * @param[in]  ev
+ *
+ * @param[in]  plain_stream            The plain tstream which is used as transport.
+ *                                     It's important that the caller keeps the "plain"
+ *                                     tstream_context arround during the whole life
+ *                                     time of the "tls" tstream_context!
+ *                                     Note: tstream_disconnect_send()/recv() doesn't
+ *                                     disconnect the "plain" tstream_context.
+ *
+ * @param[in]  tls_params              ...
+ *
+ * @return
+ *
+ * @see tstream_tls_connect_recv
+ */
+#ifdef DOXYGEN
+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);
+#else
+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,
+                                            const char *location);
+#define tstream_tls_connect_send(mem_ctx, ev, plain_stream, tls_params); \
+       _tstream_tls_connect_send(mem_ctx, ev, plain_stream, tls_params, __location__)
+#endif
+
+/**
+ * @brief Receives the async result of tevent_tls_connect_send
+ *
+ * @param[in]  req
+ *
+ * @param[out] perrno
+ *
+ * @param[in]  mem_ctx
+ *
+ * @param[out] tls_stream
+ *
+ * @return
+ *
+ * @see tstream_tls_connect_send
+ */
+int tstream_tls_connect_recv(struct tevent_req *req,
+                            int *perrno,
+                            TALLOC_CTX *mem_ctx,
+                            struct tstream_context **tls_stream);
+
+/**
+ * @brief Accept a TLS tunnel on top of a given tstream
+ *
+ * @param[in]  mem_ctx
+ * @param[in]  ev
+ *
+ * @param[in]  plain_stream            The plain tstream which is used as transport.
+ *                                     It's important that the caller keeps the "plain"
+ *                                     tstream_context arround during the whole life
+ *                                     time of the "tls" tstream_context!
+ *                                     Note: tstream_disconnect_send()/recv() doesn't
+ *                                     disconnect the "plain" tstream_context.
+ *
+ * @param[in]  tls_params              ...
+ *
+ * @return
+ *
+ * @see tstream_tls_accept_recv
+ */
+#ifdef DOXYGEN
+struct tevent_req *tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct tstream_context *plain_stream,
+                                          struct tstream_tls_params *tls_params);
+#else
+struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct tstream_context *plain_stream,
+                                           struct tstream_tls_params *tls_params,
+                                           const char *location);
+#define tstream_tls_accept_send(mem_ctx, ev, plain_stream, tls_params); \
+       _tstream_tls_accept_send(mem_ctx, ev, plain_stream, tls_params, __location__)
 #endif
+
+/**
+ * @brief Receives the async result of tevent_tls_accept_send
+ *
+ * @param[in]  req
+ *
+ * @param[out] perrno
+ *
+ * @param[in]  mem_ctx
+ *
+ * @param[out] tls_stream
+ *
+ * @return
+ *
+ * @see tstream_tls_accept_send
+ */
+int tstream_tls_accept_recv(struct tevent_req *req,
+                           int *perrno,
+                           TALLOC_CTX *mem_ctx,
+                           struct tstream_context **tls_stream);
+
+#endif /* _TLS_H_ */
diff --git a/source4/lib/tls/tls_tstream.c b/source4/lib/tls/tls_tstream.c
new file mode 100644 (file)
index 0000000..2e9d6e9
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Stefan Metzmacher 2010
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "../util/tevent_unix.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../lib/tsocket/tsocket_internal.h"
+#include "lib/tls/tls.h"
+#include "gnutls/gnutls.h"
+
+#define DH_BITS 1024
+
+static const struct tstream_context_ops tstream_tls_ops;
+
+struct tstream_tls {
+       struct tstream_context *plain_stream;
+       int plain_errno;
+
+       gnutls_session tls_session;
+       int tls_error;
+
+       gnutls_certificate_credentials xcred;
+
+       struct tevent_context *current_ev;
+
+       struct {
+               uint8_t buffer[1024];
+               struct iovec iov;
+       } push, pull;
+};
+
+static void tstream_tls_schedule_retry(struct tstream_context *tls,
+                                      struct tevent_context *ev)
+{
+       /* TODO */
+}
+static void tstream_tls_retry(struct tstream_context *tls)
+{
+       /* TODO */
+}
+
+static void tstream_tls_push_done(struct tevent_req *subreq);
+
+static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr,
+                                        const void *buf, size_t size)
+{
+       struct tstream_context *tls = talloc_get_type_abort(ptr,
+                                     struct tstream_context);
+       struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
+       struct tevent_req *subreq;
+
+       if (tlss->push.iov.iov_base) {
+               errno = EAGAIN;
+               return -1;
+       }
+
+       tlss->push.iov.iov_base = tlss->push.buffer;
+       tlss->push.iov.iov_len = MIN(size, sizeof(tlss->push.buffer));
+
+       memcpy(tlss->push.buffer, buf, tlss->push.iov.iov_len);
+
+       subreq = tstream_writev_send(tlss,
+                                    tlss->current_ev,
+                                    tlss->plain_stream,
+                                    &tlss->push.iov, 1);
+       if (subreq == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+       tevent_req_set_callback(subreq, tstream_tls_push_done, tls);
+
+       return tlss->push.iov.iov_len;
+}
+
+static void tstream_tls_push_done(struct tevent_req *subreq)
+{
+       struct tstream_context *tls = tevent_req_callback_data(subreq,
+                                     struct tstream_context);
+       struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
+       int ret;
+       int perrno;
+
+       ZERO_STRUCT(tlss->push.iov);
+
+       ret = tstream_writev_recv(subreq, &perrno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tlss->plain_errno = perrno;
+               tstream_tls_retry(tls);
+               return;
+       }
+
+       tstream_tls_retry(tls);
+}
+
+static void tstream_tls_pull_done(struct tevent_req *subreq);
+
+static ssize_t tstream_tls_pull_function(gnutls_transport_ptr ptr,
+                                        void *buf, size_t size)
+{
+       struct tstream_context *tls = talloc_get_type_abort(ptr,
+                                     struct tstream_context);
+       struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
+       struct tevent_req *subreq;
+
+       if (tlss->pull.iov.iov_base) {
+               size_t n;
+
+               n = MIN(tlss->pull.iov.iov_len, size);
+               memcpy(buf, tlss->pull.iov.iov_base, n);
+
+               tlss->pull.iov.iov_len -= n;
+               if (tlss->pull.iov.iov_len == 0) {
+                       tlss->pull.iov.iov_base = NULL;
+               }
+
+               return n;
+       }
+
+       if (size == 0) {
+               return 0;
+       }
+
+       tlss->pull.iov.iov_base = tlss->pull.buffer;
+       tlss->pull.iov.iov_len = MIN(size, sizeof(tlss->pull.buffer));
+
+       subreq = tstream_readv_send(tlss,
+                                   tlss->current_ev,
+                                   tlss->plain_stream,
+                                   &tlss->pull.iov, 1);
+       if (subreq == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+       tevent_req_set_callback(subreq, tstream_tls_pull_done, tls);
+
+       errno = EAGAIN;
+       return -1;
+}
+
+static void tstream_tls_pull_done(struct tevent_req *subreq)
+{
+       struct tstream_context *tls = tevent_req_callback_data(subreq,
+                                     struct tstream_context);
+       struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
+       int ret;
+       int perrno;
+
+       ret = tstream_readv_recv(subreq, &perrno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tlss->plain_errno = perrno;
+               tstream_tls_retry(tls);
+               return;
+       }
+
+       tstream_tls_retry(tls);
+}
+
+static int tstream_tls_destructor(struct tstream_tls *tlss)
+{
+       if (tlss->xcred) {
+               gnutls_certificate_free_credentials(tlss->xcred);
+               tlss->xcred = NULL;
+       }
+       if (tlss->tls_session) {
+               gnutls_deinit(tlss->tls_session);
+               tlss->tls_session = NULL;
+       }
+       return 0;
+}
+
+static ssize_t tstream_tls_pending_bytes(struct tstream_context *stream)
+{
+       struct tstream_tls *tlss = tstream_context_data(stream,
+                                  struct tstream_tls);
+       ssize_t ret;
+
+       if (!tlss->plain_stream) {
+               errno = ENOTCONN;
+               return -1;
+       }
+
+       if (!tlss->tls_session) {
+               ret = tstream_pending_bytes(tlss->plain_stream);
+               return ret;
+       }
+
+       ret = gnutls_record_check_pending(tlss->tls_session);
+       if (ret < 0) {
+               /* TODO: better mapping */
+               errno = EIO;
+               return -1;
+       }
+
+       return ret;
+}
+
+struct tstream_tls_readv_state {
+       int ret;
+};
+
+static void tstream_tls_readv_plain_handler(struct tevent_req *subreq);
+
+static struct tevent_req *tstream_tls_readv_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct tstream_context *stream,
+                                       struct iovec *vector,
+                                       size_t count)
+{
+       struct tevent_req *req;
+       struct tstream_tls_readv_state *state;
+       struct tstream_tls *tlss = tstream_context_data(stream, struct tstream_tls);
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_tls_readv_state);
+       if (!req) {
+               return NULL;
+       }
+
+       state->ret      = 0;
+
+       if (!tlss->plain_stream) {
+               tevent_req_error(req, ENOTCONN);
+               return tevent_req_post(req, ev);
+       }
+
+       if (!tlss->tls_session) {
+               subreq = tstream_readv_send(state,
+                                           ev,
+                                           tlss->plain_stream,
+                                           vector,
+                                           count);
+               if (tevent_req_nomem(subreq,req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq,
+                                       tstream_tls_readv_plain_handler,
+                                       req);
+
+               return req;
+       }
+
+       /* TODO */
+       tevent_req_error(req, ENOSYS);
+       return tevent_req_post(req, ev);
+}
+
+static void tstream_tls_readv_plain_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       struct tstream_tls_readv_state *state = tevent_req_data(req,
+                                       struct tstream_tls_readv_state);
+       int ret;
+       int sys_errno;
+
+       ret = tstream_readv_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       state->ret = ret;
+
+       tevent_req_done(req);
+}
+
+static int tstream_tls_readv_recv(struct tevent_req *req,
+                                 int *perrno)
+{
+       struct tstream_tls_readv_state *state = tevent_req_data(req,
+                                       struct tstream_tls_readv_state);
+       int ret;
+
+       ret = tsocket_simple_int_recv(req, perrno);
+       if (ret == 0) {
+               ret = state->ret;
+       }
+
+       tevent_req_received(req);
+       return ret;
+}
+
+struct tstream_tls_writev_state {
+       int ret;
+};
+
+static void tstream_tls_writev_plain_handler(struct tevent_req *subreq);
+
+static struct tevent_req *tstream_tls_writev_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct tstream_context *stream,
+                                       const struct iovec *vector,
+                                       size_t count)
+{
+       struct tevent_req *req;
+       struct tstream_tls_writev_state *state;
+       struct tstream_tls *tlss = tstream_context_data(stream, struct tstream_tls);
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_tls_writev_state);
+       if (!req) {
+               return NULL;
+       }
+
+       state->ret      = 0;
+
+       if (!tlss->plain_stream) {
+               tevent_req_error(req, ENOTCONN);
+               return tevent_req_post(req, ev);
+       }
+
+       if (!tlss->tls_session) {
+               subreq = tstream_writev_send(state,
+                                            ev,
+                                            tlss->plain_stream,
+                                            vector,
+                                            count);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, tstream_tls_writev_plain_handler, req);
+
+               return req;
+       }
+
+       /* TODO */
+       tevent_req_error(req, ENOSYS);
+       return tevent_req_post(req, ev);
+}
+
+static void tstream_tls_writev_plain_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       struct tstream_tls_writev_state *state = tevent_req_data(req,
+                                       struct tstream_tls_writev_state);
+       int ret;
+       int sys_errno;
+
+       ret = tstream_writev_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       state->ret = ret;
+
+       tevent_req_done(req);
+}
+
+static int tstream_tls_writev_recv(struct tevent_req *req,
+                                  int *perrno)
+{
+       struct tstream_tls_writev_state *state = tevent_req_data(req,
+                                       struct tstream_tls_writev_state);
+       int ret;
+
+       ret = tsocket_simple_int_recv(req, perrno);
+       if (ret == 0) {
+               ret = state->ret;
+       }
+
+       tevent_req_received(req);
+       return ret;
+}
+
+struct tstream_tls_disconnect_state {
+       uint8_t _dummy;
+};
+
+static struct tevent_req *tstream_tls_disconnect_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct tstream_context *stream)
+{
+       struct tstream_tls *tlss = tstream_context_data(stream, struct tstream_tls);
+       struct tevent_req *req;
+       struct tstream_tls_disconnect_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_tls_disconnect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (!tlss->plain_stream) {
+               tevent_req_error(req, ENOTCONN);
+               return tevent_req_post(req, ev);
+       }
+
+       if (!tlss->tls_session) {
+               /*
+                * The caller is responsible to do the real disconnect
+                * on the plain stream!
+                */
+               tlss->plain_stream = NULL;
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       /* TODO */
+       tevent_req_error(req, ENOSYS);
+       return tevent_req_post(req, ev);
+}
+
+static int tstream_tls_disconnect_recv(struct tevent_req *req,
+                                      int *perrno)
+{
+       int ret;
+
+       ret = tsocket_simple_int_recv(req, perrno);
+
+       tevent_req_received(req);
+       return ret;
+}
+
+static const struct tstream_context_ops tstream_tls_ops = {
+       .name                   = "tls",
+
+       .pending_bytes          = tstream_tls_pending_bytes,
+
+       .readv_send             = tstream_tls_readv_send,
+       .readv_recv             = tstream_tls_readv_recv,
+
+       .writev_send            = tstream_tls_writev_send,
+       .writev_recv            = tstream_tls_writev_recv,
+
+       .disconnect_send        = tstream_tls_disconnect_send,
+       .disconnect_recv        = tstream_tls_disconnect_recv,
+};
+
+struct tstream_tls_params {
+       const char *ca_path;
+       gnutls_certificate_credentials xcred;
+};
+
+struct tstream_tls_connect_state {
+       struct {
+               struct tevent_context *ev;
+       } caller;
+       struct tstream_context *tls_stream;
+};
+
+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,
+                                            const char *location)
+{
+       struct tevent_req *req;
+       struct tstream_tls_connect_state *state;
+       struct tstream_tls *tlss;
+       int ret;
+       static const int cert_type_priority[] = {
+               GNUTLS_CRT_X509,
+               GNUTLS_CRT_OPENPGP,
+               0
+       };
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_tls_connect_state);
+       if (!req) {
+               return NULL;
+       }
+
+       state->caller.ev = ev;
+
+       state->tls_stream = tstream_context_create(state,
+                                                  &tstream_tls_ops,
+                                                  &tlss,
+                                                  struct tstream_tls,
+                                                  location);
+       if (tevent_req_nomem(state->tls_stream, req)) {
+               return tevent_req_post(req, ev);
+       }
+       ZERO_STRUCTP(tlss);
+       talloc_set_destructor(tlss, tstream_tls_destructor);
+
+       tlss->plain_stream = plain_stream;
+
+       gnutls_global_init();
+
+       ret = gnutls_certificate_allocate_credentials(&tlss->xcred);
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               return tevent_req_post(req, ev);
+       }
+
+       gnutls_certificate_set_x509_trust_file(tlss->xcred,
+                                              tls_params->ca_path,
+                                              GNUTLS_X509_FMT_PEM);
+
+       ret = gnutls_init(&tlss->tls_session, GNUTLS_CLIENT);
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               return tevent_req_post(req, ev);
+       }
+
+       ret = gnutls_set_default_priority(tlss->tls_session);
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               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, tlss->xcred);
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               return tevent_req_post(req, ev);
+       }
+
+       gnutls_transport_set_ptr(tlss->tls_session, (gnutls_transport_ptr)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);
+
+       //tstream_tls_prepare_operation(tls, state->caller.ev);
+       ret = gnutls_handshake(tlss->tls_session);
+       if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+               tstream_tls_schedule_retry(state->tls_stream, state->caller.ev);
+               return req;
+       }
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               return tevent_req_post(req, ev);
+       }
+
+       return tevent_req_post(req, ev);
+}
+
+int tstream_tls_connect_recv(struct tevent_req *req,
+                            int *perrno,
+                            TALLOC_CTX *mem_ctx,
+                            struct tstream_context **tls_stream)
+{
+       struct tstream_tls_connect_state *state =
+               tevent_req_data(req,
+               struct tstream_tls_connect_state);
+
+       if (tevent_req_is_unix_error(req, perrno)) {
+               tevent_req_received(req);
+               return -1;
+       }
+
+       *tls_stream = talloc_move(mem_ctx, &state->tls_stream);
+       tevent_req_received(req);
+       return 0;
+}
+
+struct tstream_tls_accept_state {
+       struct {
+               struct tevent_context *ev;
+       } caller;
+       struct tstream_context *tls_stream;
+};
+
+struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct tstream_context *plain_stream,
+                                           struct tstream_tls_params *tls_params,
+                                           const char *location)
+{
+       struct tevent_req *req;
+       struct tstream_tls_accept_state *state;
+       struct tstream_tls *tlss;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_tls_accept_state);
+       if (!req) {
+               return NULL;
+       }
+
+       state->caller.ev = ev;
+
+       state->tls_stream = tstream_context_create(state,
+                                                  &tstream_tls_ops,
+                                                  &tlss,
+                                                  struct tstream_tls,
+                                                  location);
+       if (tevent_req_nomem(state->tls_stream, req)) {
+               return tevent_req_post(req, ev);
+       }
+       ZERO_STRUCTP(tlss);
+       talloc_set_destructor(tlss, tstream_tls_destructor);
+
+       tlss->plain_stream = plain_stream;
+
+       gnutls_global_init();
+
+       ret = gnutls_init(&tlss->tls_session, GNUTLS_SERVER);
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               return tevent_req_post(req, ev);
+       }
+
+       ret = gnutls_set_default_priority(tlss->tls_session);
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               return tevent_req_post(req, ev);
+       }
+
+       ret = gnutls_credentials_set(tlss->tls_session, GNUTLS_CRD_CERTIFICATE,
+                                    tls_params->xcred);
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               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_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);
+
+       //tstream_tls_prepare_operation(tls, state->caller.ev);
+       ret = gnutls_handshake(tlss->tls_session);
+       if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+               tstream_tls_schedule_retry(state->tls_stream, state->caller.ev);
+               return req;
+       }
+       if (tevent_req_error(req, ret)) {
+               DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+               return tevent_req_post(req, ev);
+       }
+
+       return tevent_req_post(req, ev);
+}
+
+int tstream_tls_accept_recv(struct tevent_req *req,
+                           int *perrno,
+                           TALLOC_CTX *mem_ctx,
+                           struct tstream_context **tls_stream)
+{
+       struct tstream_tls_accept_state *state =
+               tevent_req_data(req,
+               struct tstream_tls_accept_state);
+
+       if (tevent_req_is_unix_error(req, perrno)) {
+               tevent_req_received(req);
+               return -1;
+       }
+
+       *tls_stream = talloc_move(mem_ctx, &state->tls_stream);
+       tevent_req_received(req);
+       return 0;
+}
+