s4:gensec: add gensec_create_tstream()
authorStefan Metzmacher <metze@samba.org>
Wed, 22 Sep 2010 10:13:28 +0000 (12:13 +0200)
committerStefan Metzmacher <metze@samba.org>
Tue, 28 Sep 2010 01:48:11 +0000 (03:48 +0200)
Based on the initial patch from Andreas Schneider <asn@redhat.com>.

metze

source4/auth/gensec/gensec_tstream.c [new file with mode: 0644]
source4/auth/gensec/gensec_tstream.h [new file with mode: 0644]
source4/auth/gensec/wscript_build

diff --git a/source4/auth/gensec/gensec_tstream.c b/source4/auth/gensec/gensec_tstream.c
new file mode 100644 (file)
index 0000000..d2d4d5b
--- /dev/null
@@ -0,0 +1,723 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   tstream based generic authentication interface
+
+   Copyright (c) 2010 Stefan Metzmacher
+   Copyright (c) 2010 Andreas Schneider <asn@redhat.com>
+
+   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 "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "auth/gensec/gensec_tstream.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/tsocket/tsocket_internal.h"
+
+
+static const struct tstream_context_ops tstream_gensec_ops;
+
+struct tstream_gensec {
+       struct tstream_context *plain_stream;
+
+       struct gensec_security *gensec_security;
+
+       bool wrap;
+
+       int error;
+
+       struct {
+               size_t max_unwrapped_size;
+               size_t max_wrapped_size;
+       } write;
+
+       struct {
+               off_t ofs;
+               size_t left;
+               DATA_BLOB unwrapped;
+       } read;
+};
+
+_PUBLIC_ NTSTATUS _gensec_create_tstream(TALLOC_CTX *mem_ctx,
+                                        struct gensec_security *gensec_security,
+                                        struct tstream_context *plain_stream,
+                                        struct tstream_context **_gensec_stream,
+                                        const char *location)
+{
+       struct tstream_context *gensec_stream;
+       struct tstream_gensec *tgss;
+
+       gensec_stream = tstream_context_create(mem_ctx,
+                                              &tstream_gensec_ops,
+                                              &tgss,
+                                              struct tstream_gensec,
+                                              location);
+       if (gensec_stream == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       tgss->plain_stream = plain_stream;
+       tgss->gensec_security = gensec_security;
+       tgss->error = 0;
+
+       if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN) ||
+           gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+               tgss->wrap = true;
+       } else {
+               tgss->wrap = false;
+       }
+
+       tgss->write.max_unwrapped_size = gensec_max_input_size(gensec_security);
+       tgss->write.max_wrapped_size = gensec_max_wrapped_size(gensec_security);
+
+       ZERO_STRUCT(tgss->read);
+
+       *_gensec_stream = gensec_stream;
+       return NT_STATUS_OK;
+}
+
+static ssize_t tstream_gensec_pending_bytes(struct tstream_context *stream)
+{
+       struct tstream_gensec *tgss =
+               tstream_context_data(stream,
+               struct tstream_gensec);
+       ssize_t ret;
+
+       if (!tgss->plain_stream) {
+               errno = ENOTCONN;
+               return -1;
+       }
+
+       if (tgss->error != 0) {
+               errno = tgss->error;
+               return -1;
+       }
+
+       if (tgss->wrap) {
+               return tgss->read.left;
+       }
+
+       ret = tstream_pending_bytes(tgss->plain_stream);
+       if (ret == -1) {
+               tgss->error = errno;
+               return -1;
+       }
+
+       return ret;
+}
+
+struct tstream_gensec_readv_state {
+       struct tevent_context *ev;
+       struct tstream_context *stream;
+
+       struct iovec *vector;
+       int count;
+
+       struct {
+               bool asked_for_hdr;
+               uint8_t hdr[4];
+               bool asked_for_blob;
+               DATA_BLOB blob;
+       } wrapped;
+
+       int ret;
+};
+
+static void tstream_gensec_readv_plain_done(struct tevent_req *subreq);
+static void tstream_gensec_readv_wrapped_next(struct tevent_req *req);
+
+static struct tevent_req *tstream_gensec_readv_send(TALLOC_CTX *mem_ctx,
+                                                   struct tevent_context *ev,
+                                                   struct tstream_context *stream,
+                                                   struct iovec *vector,
+                                                   size_t count)
+{
+       struct tstream_gensec *tgss =
+               tstream_context_data(stream,
+               struct tstream_gensec);
+       struct tevent_req *req;
+       struct tstream_gensec_readv_state *state;
+       struct tevent_req *subreq;
+       ssize_t ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_gensec_readv_state);
+       if (!req) {
+               return NULL;
+       }
+
+       ret = tstream_gensec_pending_bytes(stream);
+       if (ret == -1) {
+               tevent_req_error(req, errno);
+               return tevent_req_post(req, ev);
+       }
+
+       state->ev = ev;
+       state->stream = stream;
+       state->ret = 0;
+
+       if (!tgss->wrap) {
+               subreq = tstream_readv_send(state,
+                                           ev,
+                                           tgss->plain_stream,
+                                           vector,
+                                           count);
+               if (tevent_req_nomem(subreq,req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq,
+                                       tstream_gensec_readv_plain_done,
+                                       req);
+
+               return req;
+       }
+
+       /*
+        * we make a copy of the vector so we can change the structure
+        */
+       state->vector = talloc_array(state, struct iovec, count);
+       if (tevent_req_nomem(state->vector, req)) {
+               return tevent_req_post(req, ev);
+       }
+       memcpy(state->vector, vector, sizeof(struct iovec) * count);
+       state->count = count;
+
+       tstream_gensec_readv_wrapped_next(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       return req;
+}
+
+static void tstream_gensec_readv_plain_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct tstream_gensec_readv_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_readv_state);
+       struct tstream_gensec *tgss =
+               tstream_context_data(state->stream,
+               struct tstream_gensec);
+       int ret;
+       int sys_errno;
+
+       ret = tstream_readv_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tgss->error = sys_errno;
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       state->ret = ret;
+
+       tevent_req_done(req);
+}
+
+static int tstream_gensec_readv_next_vector(struct tstream_context *unix_stream,
+                                           void *private_data,
+                                           TALLOC_CTX *mem_ctx,
+                                           struct iovec **_vector,
+                                           size_t *_count);
+static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq);
+
+static void tstream_gensec_readv_wrapped_next(struct tevent_req *req)
+{
+       struct tstream_gensec_readv_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_readv_state);
+       struct tstream_gensec *tgss =
+               tstream_context_data(state->stream,
+               struct tstream_gensec);
+       struct tevent_req *subreq;
+
+       /*
+        * copy the pending buffer first
+        */
+       while (tgss->read.left > 0 && state->count > 0) {
+               uint8_t *base = (uint8_t *)state->vector[0].iov_base;
+               size_t len = MIN(tgss->read.left, state->vector[0].iov_len);
+
+               memcpy(base, tgss->read.unwrapped.data + tgss->read.ofs, len);
+
+               base += len;
+               state->vector[0].iov_base = base;
+               state->vector[0].iov_len -= len;
+
+               tgss->read.ofs += len;
+               tgss->read.left -= len;
+
+               if (state->vector[0].iov_len == 0) {
+                       state->vector += 1;
+                       state->count -= 1;
+               }
+
+               state->ret += len;
+       }
+
+       if (state->count == 0) {
+               tevent_req_done(req);
+               return;
+       }
+
+       data_blob_free(&tgss->read.unwrapped);
+       ZERO_STRUCT(state->wrapped);
+
+       subreq = tstream_readv_pdu_send(state, state->ev,
+                                       tgss->plain_stream,
+                                       tstream_gensec_readv_next_vector,
+                                       state);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, tstream_gensec_readv_wrapped_done, req);
+}
+
+static int tstream_gensec_readv_next_vector(struct tstream_context *unix_stream,
+                                           void *private_data,
+                                           TALLOC_CTX *mem_ctx,
+                                           struct iovec **_vector,
+                                           size_t *_count)
+{
+       struct tstream_gensec_readv_state *state =
+               talloc_get_type_abort(private_data,
+               struct tstream_gensec_readv_state);
+       struct iovec *vector;
+       size_t count = 1;
+
+       /* we need to get a message header */
+       vector = talloc_array(mem_ctx, struct iovec, count);
+       if (!vector) {
+               return -1;
+       }
+
+       if (!state->wrapped.asked_for_hdr) {
+               state->wrapped.asked_for_hdr = true;
+               vector[0].iov_base = (char *)state->wrapped.hdr;
+               vector[0].iov_len = sizeof(state->wrapped.hdr);
+       } else if (!state->wrapped.asked_for_blob) {
+               state->wrapped.asked_for_blob = true;
+               uint32_t msg_len;
+
+               msg_len = RIVAL(state->wrapped.hdr, 0);
+
+               if (msg_len > 0x00FFFFFF) {
+                       errno = EMSGSIZE;
+                       return -1;
+               }
+
+               if (msg_len == 0) {
+                       errno = EMSGSIZE;
+                       return -1;
+               }
+
+               state->wrapped.blob = data_blob_talloc(state, NULL, msg_len);
+               if (state->wrapped.blob.data == NULL) {
+                       return -1;
+               }
+
+               vector[0].iov_base = (char *)state->wrapped.blob.data;
+               vector[0].iov_len = state->wrapped.blob.length;
+       } else {
+               *_vector = NULL;
+               *_count = 0;
+               return 0;
+       }
+
+       *_vector = vector;
+       *_count = count;
+       return 0;
+}
+
+static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct tstream_gensec_readv_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_readv_state);
+       struct tstream_gensec *tgss =
+               tstream_context_data(state->stream,
+               struct tstream_gensec);
+       int ret;
+       int sys_errno;
+       NTSTATUS status;
+
+       ret = tstream_readv_pdu_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tgss->error = sys_errno;
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       status = gensec_unwrap(tgss->gensec_security,
+                              state,
+                              &state->wrapped.blob,
+                              &tgss->read.unwrapped);
+       if (!NT_STATUS_IS_OK(status)) {
+               tgss->error = EIO;
+               tevent_req_error(req, tgss->error);
+               return;
+       }
+
+       data_blob_free(&state->wrapped.blob);
+
+       talloc_steal(tgss, tgss->read.unwrapped.data);
+       tgss->read.left = tgss->read.unwrapped.length;
+       tgss->read.ofs = 0;
+
+       tstream_gensec_readv_wrapped_next(req);
+}
+
+static int tstream_gensec_readv_recv(struct tevent_req *req, int *perrno)
+{
+       struct tstream_gensec_readv_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_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_gensec_writev_state {
+       struct tevent_context *ev;
+       struct tstream_context *stream;
+
+       struct iovec *vector;
+       int count;
+
+       struct {
+               off_t ofs;
+               size_t left;
+               DATA_BLOB blob;
+       } unwrapped;
+
+       struct {
+               uint8_t hdr[4];
+               DATA_BLOB blob;
+               struct iovec iov[2];
+       } wrapped;
+
+       int ret;
+};
+
+static void tstream_gensec_writev_plain_done(struct tevent_req *subreq);
+static void tstream_gensec_writev_wrapped_next(struct tevent_req *req);
+
+static struct tevent_req *tstream_gensec_writev_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct tstream_context *stream,
+                                       const struct iovec *vector,
+                                       size_t count)
+{
+       struct tstream_gensec *tgss =
+               tstream_context_data(stream,
+               struct tstream_gensec);
+       struct tevent_req *req;
+       struct tstream_gensec_writev_state *state;
+       struct tevent_req *subreq;
+       ssize_t ret;
+       int i;
+       int total;
+       int chunk;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_gensec_writev_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       ret = tstream_gensec_pending_bytes(stream);
+       if (ret == -1) {
+               tevent_req_error(req, errno);
+               return tevent_req_post(req, ev);
+       }
+
+       state->ev = ev;
+       state->stream = stream;
+       state->ret = 0;
+
+       if (!tgss->wrap) {
+               subreq = tstream_writev_send(state,
+                                            ev,
+                                            tgss->plain_stream,
+                                            vector,
+                                            count);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, tstream_gensec_writev_plain_done, req);
+
+               return req;
+       }
+
+       /*
+        * we make a copy of the vector so we can change the structure
+        */
+       state->vector = talloc_array(state, struct iovec, count);
+       if (tevent_req_nomem(state->vector, req)) {
+               return tevent_req_post(req, ev);
+       }
+       memcpy(state->vector, vector, sizeof(struct iovec) * count);
+       state->count = count;
+
+       total = 0;
+       for (i = 0; i < count; i++) {
+               /*
+                * the generic tstream code makes sure that
+                * this never wraps.
+                */
+               total += vector[i].iov_len;
+       }
+
+       /*
+        * We may need to send data in chunks.
+        */
+       chunk = MIN(total, tgss->write.max_unwrapped_size);
+
+       state->unwrapped.blob = data_blob_talloc(state, NULL, chunk);
+       if (tevent_req_nomem(state->unwrapped.blob.data, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tstream_gensec_writev_wrapped_next(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       return req;
+}
+
+static void tstream_gensec_writev_plain_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct tstream_gensec_writev_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_writev_state);
+       struct tstream_gensec *tgss =
+               tstream_context_data(state->stream,
+               struct tstream_gensec);
+       int ret;
+       int sys_errno;
+
+       ret = tstream_writev_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret < 0) {
+               tgss->error = sys_errno;
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       state->ret = ret;
+
+       tevent_req_done(req);
+}
+
+static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq);
+
+static void tstream_gensec_writev_wrapped_next(struct tevent_req *req)
+{
+       struct tstream_gensec_writev_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_writev_state);
+       struct tstream_gensec *tgss =
+               tstream_context_data(state->stream,
+               struct tstream_gensec);
+       struct tevent_req *subreq;
+       NTSTATUS status;
+
+       data_blob_free(&state->wrapped.blob);
+
+       state->unwrapped.left = state->unwrapped.blob.length;
+       state->unwrapped.ofs = 0;
+
+       /*
+        * first fill our buffer
+        */
+       while (state->unwrapped.left > 0 && state->count > 0) {
+               uint8_t *base = (uint8_t *)state->vector[0].iov_base;
+               size_t len = MIN(state->unwrapped.left, state->vector[0].iov_len);
+
+               memcpy(state->unwrapped.blob.data + state->unwrapped.ofs, base, len);
+
+               base += len;
+               state->vector[0].iov_base = base;
+               state->vector[0].iov_len -= len;
+
+               state->unwrapped.ofs += len;
+               state->unwrapped.left -= len;
+
+               if (state->vector[0].iov_len == 0) {
+                       state->vector += 1;
+                       state->count -= 1;
+               }
+
+               state->ret += len;
+       }
+
+       if (state->unwrapped.ofs == 0) {
+               tevent_req_done(req);
+               return;
+       }
+
+       state->unwrapped.blob.length = state->unwrapped.ofs;
+
+       status = gensec_wrap(tgss->gensec_security,
+                            state,
+                            &state->unwrapped.blob,
+                            &state->wrapped.blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               tgss->error = EIO;
+               tevent_req_error(req, tgss->error);
+               return;
+       }
+
+       RSIVAL(state->wrapped.hdr, 0, state->wrapped.blob.length);
+
+       state->wrapped.iov[0].iov_base = (void *)state->wrapped.hdr;
+       state->wrapped.iov[0].iov_len = sizeof(state->wrapped.hdr);
+       state->wrapped.iov[1].iov_base = (void *)state->wrapped.blob.data;
+       state->wrapped.iov[1].iov_len = state->wrapped.blob.length;
+
+       subreq = tstream_writev_send(state, state->ev,
+                                     tgss->plain_stream,
+                                     state->wrapped.iov, 2);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq,
+                               tstream_gensec_writev_wrapped_done,
+                               req);
+}
+
+static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct tstream_gensec_writev_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_writev_state);
+       struct tstream_gensec *tgss =
+               tstream_context_data(state->stream,
+               struct tstream_gensec);
+       int sys_errno;
+       int ret;
+
+       ret = tstream_writev_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tgss->error = sys_errno;
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       tstream_gensec_writev_wrapped_next(req);
+}
+
+static int tstream_gensec_writev_recv(struct tevent_req *req,
+                                  int *perrno)
+{
+       struct tstream_gensec_writev_state *state =
+               tevent_req_data(req,
+               struct tstream_gensec_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_gensec_disconnect_state {
+       uint8_t _dummy;
+};
+
+static struct tevent_req *tstream_gensec_disconnect_send(TALLOC_CTX *mem_ctx,
+                                                        struct tevent_context *ev,
+                                                        struct tstream_context *stream)
+{
+       struct tstream_gensec *tgss =
+               tstream_context_data(stream,
+               struct tstream_gensec);
+       struct tevent_req *req;
+       struct tstream_gensec_disconnect_state *state;
+       ssize_t ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_gensec_disconnect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       ret = tstream_gensec_pending_bytes(stream);
+       if (ret == -1) {
+               tevent_req_error(req, errno);
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * The caller is responsible to do the real disconnect
+        * on the plain stream!
+        */
+       tgss->plain_stream = NULL;
+
+       tevent_req_done(req);
+       return tevent_req_post(req, ev);
+}
+
+static int tstream_gensec_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_gensec_ops = {
+       .name                   = "gensec",
+
+       .pending_bytes          = tstream_gensec_pending_bytes,
+
+       .readv_send             = tstream_gensec_readv_send,
+       .readv_recv             = tstream_gensec_readv_recv,
+
+       .writev_send            = tstream_gensec_writev_send,
+       .writev_recv            = tstream_gensec_writev_recv,
+
+       .disconnect_send        = tstream_gensec_disconnect_send,
+       .disconnect_recv        = tstream_gensec_disconnect_recv,
+};
diff --git a/source4/auth/gensec/gensec_tstream.h b/source4/auth/gensec/gensec_tstream.h
new file mode 100644 (file)
index 0000000..18389d4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   tstream based generic authentication interface
+
+   Copyright (c) 2010 Stefan Metzmacher
+   Copyright (c) 2010 Andreas Schneider <asn@redhat.com>
+
+   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/>.
+*/
+
+#ifndef _GENSEC_TSTREAM_H_
+#define _GENSEC_TSTREAM_H_
+
+struct gensec_context;
+struct tstream_context;
+
+NTSTATUS _gensec_create_tstream(TALLOC_CTX *mem_ctx,
+                               struct gensec_security *gensec_security,
+                               struct tstream_context *plain_tstream,
+                               struct tstream_context **gensec_tstream,
+                               const char *location);
+#define gensec_create_tstream(mem_ctx, gensec_security, \
+                             plain_tstream, gensec_tstream) \
+       _gensec_create_tstream(mem_ctx, gensec_security, \
+                              plain_tstream, gensec_tstream, \
+                              __location__)
+
+#endif /* _GENSEC_TSTREAM_H_ */
index 0defdaf594b516d3ebfebea83a3bdfda70aa6e9b..38c0e158b9204a79afc478834bf18c02c9525fd6 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 
 bld.SAMBA_LIBRARY('gensec',
-       source='gensec.c socket.c',
+       source='gensec.c socket.c gensec_tstream.c',
        pc_files='gensec.pc',
        autoproto='gensec_proto.h',
        public_deps='CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL samba_socket LIBPACKET LIBTSOCKET UTIL_TEVENT',