libcli/named_pipe_auth: add tstream_npa_connect_send/recv()
authorStefan Metzmacher <metze@samba.org>
Thu, 12 Mar 2009 17:10:19 +0000 (18:10 +0100)
committerStefan Metzmacher <metze@samba.org>
Fri, 1 May 2009 15:42:08 +0000 (17:42 +0200)
Add a tstream client implemenation for the named pipe auth
code. This supports byte and message mode.

metze

libcli/named_pipe_auth/config.mk [new file with mode: 0644]
libcli/named_pipe_auth/npa_tstream.c [new file with mode: 0644]
libcli/named_pipe_auth/npa_tstream.h [new file with mode: 0644]
source4/main.mk

diff --git a/libcli/named_pipe_auth/config.mk b/libcli/named_pipe_auth/config.mk
new file mode 100644 (file)
index 0000000..6d44ef4
--- /dev/null
@@ -0,0 +1,4 @@
+[SUBSYSTEM::NAMED_PIPE_AUTH_TSTREAM]
+PUBLIC_DEPENDENCIES = NDR_NAMED_PIPE_AUTH TEVENT TSOCKET
+
+NAMED_PIPE_AUTH_TSTREAM_OBJ_FILES = ../libcli/named_pipe_auth/npa_tstream.o
diff --git a/libcli/named_pipe_auth/npa_tstream.c b/libcli/named_pipe_auth/npa_tstream.c
new file mode 100644 (file)
index 0000000..c1a293e
--- /dev/null
@@ -0,0 +1,1059 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Stefan Metzmacher 2009
+
+   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 "../librpc/gen_ndr/ndr_named_pipe_auth.h"
+#include "../libcli/named_pipe_auth/npa_tstream.h"
+#include "libcli/raw/smb.h"
+
+static const struct tstream_context_ops tstream_npa_ops;
+
+struct tstream_npa {
+       struct tstream_context *unix_stream;
+
+       uint16_t file_type;
+
+       struct iovec pending;
+};
+
+struct tstream_npa_connect_state {
+       struct {
+               struct tevent_context *ev;
+               struct smb_iconv_convenience *smb_iconv_c;
+       } caller;
+
+       const char *unix_path;
+       struct tsocket_address *unix_laddr;
+       struct tsocket_address *unix_raddr;
+       struct tstream_context *unix_stream;
+
+       struct named_pipe_auth_req auth_req;
+       DATA_BLOB auth_req_blob;
+       struct iovec auth_req_iov;
+
+       struct named_pipe_auth_rep auth_rep;
+       DATA_BLOB auth_rep_blob;
+};
+
+static void tstream_npa_connect_unix_done(struct tevent_req *subreq);
+
+struct tevent_req *tstream_npa_connect_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct smb_iconv_convenience *smb_iconv_c,
+                                       const char *directory,
+                                       const char *npipe,
+                                       const struct tsocket_address *client,
+                                       const char *client_name,
+                                       const struct tsocket_address *server,
+                                       const char *server_name,
+                                       const struct netr_SamInfo3 *info3,
+                                       DATA_BLOB session_key)
+{
+       struct tevent_req *req;
+       struct tstream_npa_connect_state *state;
+       struct tevent_req *subreq;
+       int ret;
+       enum ndr_err_code ndr_err;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_npa_connect_state);
+       if (!req) {
+               return NULL;
+       }
+
+       state->caller.ev = ev;
+       state->caller.smb_iconv_c = smb_iconv_c;
+
+       state->unix_path = talloc_asprintf(state, "%s/%s",
+                                          directory,
+                                          npipe);
+       if (tevent_req_nomem(state->unix_path, req)) {
+               goto post;
+       }
+
+       ret = tsocket_address_unix_from_path(state,
+                                            "",
+                                            &state->unix_laddr);
+       if (ret == -1) {
+               tevent_req_error(req, errno);
+               goto post;
+       }
+
+       ret = tsocket_address_unix_from_path(state,
+                                            state->unix_path,
+                                            &state->unix_raddr);
+       if (ret == -1) {
+               tevent_req_error(req, errno);
+               goto post;
+       }
+
+       ZERO_STRUCT(state->auth_req);
+       if (client) {
+               struct named_pipe_auth_req_info2 *info2;
+
+               if (!server) {
+                       tevent_req_error(req, EINVAL);
+                       goto post;
+               }
+
+               state->auth_req.level = 2;
+               info2 = &state->auth_req.info.info2;
+
+               info2->client_name = client_name;
+               info2->client_addr = tsocket_address_inet_addr_string(client, state);
+               if (!info2->client_addr) {
+                       /* errno might be EINVAL */
+                       tevent_req_error(req, errno);
+                       goto post;
+               }
+               info2->client_port = tsocket_address_inet_port(client);
+               if (!info2->client_name) {
+                       info2->client_name = info2->client_addr;
+               }
+
+               info2->server_addr = tsocket_address_inet_addr_string(server, state);
+               if (!info2->server_addr) {
+                       /* errno might be EINVAL */
+                       tevent_req_error(req, errno);
+                       goto post;
+               }
+               info2->server_port = tsocket_address_inet_port(server);
+               if (!info2->server_name) {
+                       info2->server_name = info2->server_addr;
+               }
+
+               info2->sam_info3 = discard_const_p(struct netr_SamInfo3, info3);
+               info2->session_key_length = session_key.length;
+               info2->session_key = session_key.data;
+       } else if (info3) {
+               state->auth_req.level = 1;
+               state->auth_req.info.info1 = *info3;
+       } else {
+               state->auth_req.level = 0;
+       }
+
+       if (DEBUGLVL(10)) {
+               NDR_PRINT_DEBUG(named_pipe_auth_req, &state->auth_req);
+       }
+
+       ndr_err = ndr_push_struct_blob(&state->auth_req_blob,
+                       state, smb_iconv_c, &state->auth_req,
+                       (ndr_push_flags_fn_t)ndr_push_named_pipe_auth_req);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               tevent_req_error(req, EINVAL);
+               goto post;
+       }
+
+       state->auth_req_iov.iov_base = state->auth_req_blob.data;
+       state->auth_req_iov.iov_len = state->auth_req_blob.length;
+
+       subreq = tstream_unix_connect_send(state,
+                                          state->caller.ev,
+                                          state->unix_laddr,
+                                          state->unix_raddr);
+       if (tevent_req_nomem(subreq, req)) {
+               goto post;
+       }
+       tevent_req_set_callback(subreq, tstream_npa_connect_unix_done, req);
+
+       return req;
+
+post:
+       tevent_req_post(req, ev);
+       return req;
+}
+
+static void tstream_npa_connect_writev_done(struct tevent_req *subreq);
+
+static void tstream_npa_connect_unix_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct tstream_npa_connect_state *state =
+               tevent_req_data(req,
+               struct tstream_npa_connect_state);
+       int ret;
+       int sys_errno;
+
+       ret = tstream_unix_connect_recv(subreq, &sys_errno,
+                                       state, &state->unix_stream);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       subreq = tstream_writev_send(state,
+                                    state->caller.ev,
+                                    state->unix_stream,
+                                    &state->auth_req_iov, 1);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, tstream_npa_connect_writev_done, req);
+}
+
+static int tstream_npa_connect_next_vector(struct tstream_context *unix_stream,
+                                          void *private_data,
+                                          TALLOC_CTX *mem_ctx,
+                                          struct iovec **_vector,
+                                          size_t *_count);
+static void tstream_npa_connect_readv_done(struct tevent_req *subreq);
+
+static void tstream_npa_connect_writev_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct tstream_npa_connect_state *state =
+               tevent_req_data(req,
+               struct tstream_npa_connect_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->auth_rep_blob = data_blob_const(NULL, 0);
+
+       subreq = tstream_readv_pdu_send(state, state->caller.ev,
+                                       state->unix_stream,
+                                       tstream_npa_connect_next_vector,
+                                       state);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, tstream_npa_connect_readv_done, req);
+}
+
+static int tstream_npa_connect_next_vector(struct tstream_context *unix_stream,
+                                          void *private_data,
+                                          TALLOC_CTX *mem_ctx,
+                                          struct iovec **_vector,
+                                          size_t *_count)
+{
+       struct tstream_npa_connect_state *state = talloc_get_type_abort(private_data,
+                                       struct tstream_npa_connect_state);
+       struct iovec *vector;
+       size_t count;
+       off_t ofs = 0;
+
+       if (state->auth_rep_blob.length == 0) {
+               state->auth_rep_blob = data_blob_talloc(state, NULL, 4);
+               if (!state->auth_rep_blob.data) {
+                       return -1;
+               }
+       } else if (state->auth_rep_blob.length == 4) {
+               uint32_t msg_len;
+
+               ofs = 4;
+
+               msg_len = RIVAL(state->auth_rep_blob.data, 0);
+
+               if (msg_len > 0x00FFFFFF) {
+                       errno = EMSGSIZE;
+                       return -1;
+               }
+
+               if (msg_len == 0) {
+                       errno = EMSGSIZE;
+                       return -1;
+               }
+
+               msg_len += ofs;
+
+               state->auth_rep_blob.data = talloc_realloc(state,
+                                               state->auth_rep_blob.data,
+                                               uint8_t, msg_len);
+               if (!state->auth_rep_blob.data) {
+                       return -1;
+               }
+               state->auth_rep_blob.length = msg_len;
+       } else {
+               *_vector = NULL;
+               *_count = 0;
+               return 0;
+       }
+
+       /* we need to get a message header */
+       vector = talloc_array(mem_ctx, struct iovec, 1);
+       if (!vector) {
+               return -1;
+       }
+       vector[0].iov_base = state->auth_rep_blob.data + ofs;
+       vector[0].iov_len = state->auth_rep_blob.length - ofs;
+       count = 1;
+
+       *_vector = vector;
+       *_count = count;
+       return 0;
+}
+
+static void tstream_npa_connect_readv_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct tstream_npa_connect_state *state =
+               tevent_req_data(req,
+               struct tstream_npa_connect_state);
+       int ret;
+       int sys_errno;
+       enum ndr_err_code ndr_err;
+
+       ret = tstream_readv_pdu_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       DEBUG(10,("name_pipe_auth_rep(client)[%u]\n",
+                (uint32_t)state->auth_rep_blob.length));
+       dump_data(11, state->auth_rep_blob.data, state->auth_rep_blob.length);
+
+       ndr_err = ndr_pull_struct_blob(
+               &state->auth_rep_blob, state,
+               state->caller.smb_iconv_c, &state->auth_rep,
+               (ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_rep);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(0, ("ndr_pull_named_pipe_auth_rep failed: %s\n",
+                         ndr_map_error2string(ndr_err)));
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       if (DEBUGLVL(10)) {
+               NDR_PRINT_DEBUG(named_pipe_auth_rep, &state->auth_rep);
+       }
+
+       if (state->auth_rep.length < 16) {
+               DEBUG(0, ("req invalid length: %u < 16\n",
+                         state->auth_rep.length));
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       if (strcmp(NAMED_PIPE_AUTH_MAGIC, state->auth_rep.magic) != 0) {
+               DEBUG(0, ("req invalid magic: %s != %s\n",
+                         state->auth_rep.magic, NAMED_PIPE_AUTH_MAGIC));
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       if (!NT_STATUS_IS_OK(state->auth_rep.status)) {
+               DEBUG(0, ("req failed: %s\n",
+                         nt_errstr(state->auth_rep.status)));
+               tevent_req_error(req, EACCES);
+               return;
+       }
+
+       if (state->auth_rep.level != state->auth_req.level) {
+               DEBUG(0, ("req invalid level: %u != %u\n",
+                         state->auth_rep.level, state->auth_req.level));
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+int _tstream_npa_connect_recv(struct tevent_req *req,
+                             int *perrno,
+                             TALLOC_CTX *mem_ctx,
+                             struct tstream_context **_stream,
+                             uint16_t *_file_type,
+                             uint16_t *_device_state,
+                             uint64_t *_allocation_size,
+                             const char *location)
+{
+       struct tstream_npa_connect_state *state =
+               tevent_req_data(req,
+               struct tstream_npa_connect_state);
+       struct tstream_context *stream;
+       struct tstream_npa *npas;
+       uint16_t device_state = 0;
+       uint64_t allocation_size = 0;
+
+       if (tevent_req_is_unix_error(req, perrno)) {
+               tevent_req_received(req);
+               return -1;
+       }
+
+       stream = tstream_context_create(mem_ctx,
+                                       &tstream_npa_ops,
+                                       &npas,
+                                       struct tstream_npa,
+                                       location);
+       if (!stream) {
+               return -1;
+       }
+       ZERO_STRUCTP(npas);
+
+       npas->unix_stream = talloc_move(stream, &state->unix_stream);
+       switch (state->auth_rep.level) {
+       case 0:
+       case 1:
+               npas->file_type = FILE_TYPE_BYTE_MODE_PIPE;
+               device_state = 0x00ff;
+               allocation_size = 2048;
+               break;
+       case 2:
+               npas->file_type = state->auth_rep.info.info2.file_type;
+               device_state = state->auth_rep.info.info2.device_state;
+               allocation_size = state->auth_rep.info.info2.allocation_size;
+               break;
+       }
+
+       *_stream = stream;
+       *_file_type = npas->file_type;
+       *_device_state = device_state;
+       *_allocation_size = allocation_size;
+       tevent_req_received(req);
+       return 0;
+}
+
+static ssize_t tstream_npa_pending_bytes(struct tstream_context *stream)
+{
+       struct tstream_npa *npas = tstream_context_data(stream,
+                                  struct tstream_npa);
+       ssize_t ret;
+
+       if (!npas->unix_stream) {
+               errno = ENOTCONN;
+               return -1;
+       }
+
+       switch (npas->file_type) {
+       case FILE_TYPE_BYTE_MODE_PIPE:
+               ret = tstream_pending_bytes(npas->unix_stream);
+               break;
+
+       case FILE_TYPE_MESSAGE_MODE_PIPE:
+               ret = npas->pending.iov_len;
+               break;
+       }
+
+       return ret;
+}
+
+struct tstream_npa_readv_state {
+       struct tstream_context *stream;
+
+       struct iovec *vector;
+       size_t count;
+
+       /* the header for message mode */
+       uint8_t hdr[2];
+       bool wait_for_hdr;
+
+       int ret;
+};
+
+static void tstream_npa_readv_byte_mode_handler(struct tevent_req *subreq);
+static int tstream_npa_readv_next_vector(struct tstream_context *stream,
+                                        void *private_data,
+                                        TALLOC_CTX *mem_ctx,
+                                        struct iovec **_vector,
+                                        size_t *_count);
+static void tstream_npa_readv_msg_mode_handler(struct tevent_req *subreq);
+
+static struct tevent_req *tstream_npa_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_npa_readv_state *state;
+       struct tstream_npa *npas = tstream_context_data(stream, struct tstream_npa);
+       struct tevent_req *subreq;
+       off_t ofs;
+       size_t left;
+       uint8_t *pbase;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_npa_readv_state);
+       if (!req) {
+               return NULL;
+       }
+
+       state->stream   = stream;
+       state->ret      = 0;
+
+       if (!npas->unix_stream) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+
+       switch (npas->file_type) {
+       case FILE_TYPE_BYTE_MODE_PIPE:
+               state->vector = vector;
+               state->count = count;
+
+               subreq = tstream_readv_send(state,
+                                           ev,
+                                           npas->unix_stream,
+                                           state->vector,
+                                           state->count);
+               if (tevent_req_nomem(subreq,req)) {
+                       goto post;
+               }
+               tevent_req_set_callback(subreq,
+                                       tstream_npa_readv_byte_mode_handler,
+                                       req);
+
+               return req;
+
+       case FILE_TYPE_MESSAGE_MODE_PIPE:
+               /*
+                * we make a copy of the vector and prepend a header
+                * with the length
+                */
+               state->vector   = talloc_array(state, struct iovec, count);
+               if (tevent_req_nomem(state->vector, req)) {
+                       goto post;
+               }
+               memcpy(state->vector, vector, sizeof(struct iovec)*count);
+               state->count = count;
+
+               /*
+                * copy the pending buffer first
+                */
+               ofs = 0;
+               left = npas->pending.iov_len;
+               pbase = (uint8_t *)npas->pending.iov_base;
+
+               while (left > 0 && state->count > 0) {
+                       uint8_t *base;
+                       base = (uint8_t *)state->vector[0].iov_base;
+                       if (left < state->vector[0].iov_len) {
+                               memcpy(base, pbase + ofs, left);
+
+                               base += left;
+                               state->vector[0].iov_base = base;
+                               state->vector[0].iov_len -= left;
+
+                               ofs += left;
+                               left = 0;
+                               TALLOC_FREE(pbase);
+                               ZERO_STRUCT(npas->pending);
+                               break;
+                       }
+                       memcpy(base, pbase + ofs, state->vector[0].iov_len);
+
+                       ofs += state->vector[0].iov_len;
+                       left -= state->vector[0].iov_len;
+                       state->vector += 1;
+                       state->count -= 1;
+
+                       if (left == 0) {
+                               TALLOC_FREE(pbase);
+                               ZERO_STRUCT(npas->pending);
+                               break;
+                       }
+               }
+
+               if (left > 0) {
+                       memmove(pbase, pbase + ofs, left);
+                       npas->pending.iov_base = pbase;
+                       npas->pending.iov_len = left;
+                       /*
+                        * this cannot fail and even if it
+                        * fails we can handle it
+                        */
+                       pbase = talloc_realloc(npas, pbase, uint8_t, left);
+                       if (pbase) {
+                               npas->pending.iov_base = pbase;
+                       }
+                       pbase = NULL;
+               }
+
+               state->ret += ofs;
+
+               if (state->count == 0) {
+                       tevent_req_done(req);
+                       goto post;
+               }
+
+               ZERO_STRUCT(state->hdr);
+               state->wait_for_hdr = false;
+
+               subreq = tstream_readv_pdu_send(state,
+                                               ev,
+                                               npas->unix_stream,
+                                               tstream_npa_readv_next_vector,
+                                               state);
+               if (tevent_req_nomem(subreq, req)) {
+                       goto post;
+               }
+               tevent_req_set_callback(subreq,
+                                       tstream_npa_readv_msg_mode_handler,
+                                       req);
+
+               return req;
+       }
+
+       /* this can't happen */
+       tevent_req_error(req, EINVAL);
+       goto post;
+
+ post:
+       tevent_req_post(req, ev);
+       return req;
+}
+
+static void tstream_npa_readv_byte_mode_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       struct tstream_npa_readv_state *state = tevent_req_data(req,
+                                       struct tstream_npa_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_npa_readv_next_vector(struct tstream_context *unix_stream,
+                                        void *private_data,
+                                        TALLOC_CTX *mem_ctx,
+                                        struct iovec **_vector,
+                                        size_t *_count)
+{
+       struct tstream_npa_readv_state *state = talloc_get_type_abort(private_data,
+                                       struct tstream_npa_readv_state);
+       struct tstream_npa *npas = tstream_context_data(state->stream,
+                                  struct tstream_npa);
+       struct iovec *vector;
+       size_t count;
+       uint16_t msg_len;
+       size_t left;
+
+       if (state->count == 0) {
+               *_vector = NULL;
+               *_count = 0;
+               return 0;
+       }
+
+       if (!state->wait_for_hdr) {
+               /* we need to get a message header */
+               vector = talloc_array(mem_ctx, struct iovec, 1);
+               if (!vector) {
+                       return -1;
+               }
+               ZERO_STRUCT(state->hdr);
+               vector[0].iov_base = state->hdr;
+               vector[0].iov_len = sizeof(state->hdr);
+
+               count = 1;
+
+               state->wait_for_hdr = true;
+
+               *_vector = vector;
+               *_count = count;
+               return 0;
+       }
+
+       /* and now fill the callers buffers and maybe the pending buffer */
+       state->wait_for_hdr = false;
+
+       msg_len = SVAL(state->hdr, 0);
+
+       if (msg_len == 0) {
+               errno = EIO;
+               return -1;
+       }
+
+       state->wait_for_hdr = false;
+
+       /* +1 because we may need to fill the pending buffer */
+       vector = talloc_array(mem_ctx, struct iovec, state->count + 1);
+       if (!vector) {
+               return -1;
+       }
+
+       count = 0;
+       left = msg_len;
+       while (left > 0 && state->count > 0) {
+               if (left < state->vector[0].iov_len) {
+                       uint8_t *base;
+                       base = (uint8_t *)state->vector[0].iov_base;
+                       vector[count].iov_base = base;
+                       vector[count].iov_len = left;
+                       count++;
+                       base += left;
+                       state->vector[0].iov_base = base;
+                       state->vector[0].iov_len -= left;
+                       break;
+               }
+               vector[count] = state->vector[0];
+               count++;
+               left -= state->vector[0].iov_len;
+               state->vector += 1;
+               state->count -= 1;
+       }
+
+       if (left > 0) {
+               /*
+                * if the message if longer than the buffers the caller
+                * requested, we need to consume the rest of the message
+                * into the pending buffer, where the next readv can
+                * be served from.
+                */
+               npas->pending.iov_base = talloc_array(npas, uint8_t, left);
+               if (!npas->pending.iov_base) {
+                       return -1;
+               }
+               npas->pending.iov_len = left;
+
+               vector[count] = npas->pending;
+               count++;
+       }
+
+       state->ret += (msg_len - left);
+
+       *_vector = vector;
+       *_count = count;
+       return 0;
+}
+
+static void tstream_npa_readv_msg_mode_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       int ret;
+       int sys_errno;
+
+       ret = tstream_readv_pdu_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       /*
+        * we do not set state->ret here as ret includes the headr size.
+        * we set it in tstream_npa_readv_pdu_next_vector()
+        */
+
+       tevent_req_done(req);
+}
+
+static int tstream_npa_readv_recv(struct tevent_req *req,
+                                  int *perrno)
+{
+       struct tstream_npa_readv_state *state = tevent_req_data(req,
+                                       struct tstream_npa_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_npa_writev_state {
+       const struct iovec *vector;
+       size_t count;
+
+       /* the header for message mode */
+       uint8_t hdr[2];
+
+       int ret;
+};
+
+static void tstream_npa_writev_handler(struct tevent_req *subreq);
+
+static struct tevent_req *tstream_npa_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_npa_writev_state *state;
+       struct tstream_npa *npas = tstream_context_data(stream, struct tstream_npa);
+       struct tevent_req *subreq;
+       size_t msg_len;
+       size_t i;
+       struct iovec *new_vector;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_npa_writev_state);
+       if (!req) {
+               return NULL;
+       }
+
+       state->ret      = 0;
+
+       if (!npas->unix_stream) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+
+       switch (npas->file_type) {
+       case FILE_TYPE_BYTE_MODE_PIPE:
+               state->vector   = vector;
+               state->count    = count;
+               break;
+
+       case FILE_TYPE_MESSAGE_MODE_PIPE:
+               /*
+                * we make a copy of the vector and prepend a header
+                * with the length
+                */
+               new_vector      = talloc_array(state, struct iovec, count + 1);
+               if (tevent_req_nomem(new_vector, req)) {
+                       goto post;
+               }
+               new_vector[0].iov_base = state->hdr;
+               new_vector[0].iov_len = sizeof(state->hdr);
+               memcpy(new_vector + 1, vector, sizeof(struct iovec)*count);
+
+               state->vector   = new_vector;
+               state->count    = count + 1;
+
+               msg_len = 0;
+               for (i=0; i < count; i++) {
+                       msg_len += vector[i].iov_len;
+               }
+
+               if (msg_len > UINT16_MAX) {
+                       tevent_req_error(req, EMSGSIZE);
+                       goto post;
+               }
+
+               SSVAL(state->hdr, 0, msg_len);
+               break;
+       }
+
+       subreq = tstream_writev_send(state,
+                                    ev,
+                                    npas->unix_stream,
+                                    state->vector,
+                                    state->count);
+       if (tevent_req_nomem(subreq, req)) {
+               goto post;
+       }
+       tevent_req_set_callback(subreq, tstream_npa_writev_handler, req);
+
+       return req;
+
+ post:
+       tevent_req_post(req, ev);
+       return req;
+}
+
+static void tstream_npa_writev_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       struct tstream_npa_writev_state *state = tevent_req_data(req,
+                                       struct tstream_npa_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_npa_writev_recv(struct tevent_req *req,
+                                  int *perrno)
+{
+       struct tstream_npa_writev_state *state = tevent_req_data(req,
+                                       struct tstream_npa_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_npa_disconnect_state {
+       struct tstream_context *stream;
+};
+
+static void tstream_npa_disconnect_handler(struct tevent_req *subreq);
+
+static struct tevent_req *tstream_npa_disconnect_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct tstream_context *stream)
+{
+       struct tstream_npa *npas = tstream_context_data(stream, struct tstream_npa);
+       struct tevent_req *req;
+       struct tstream_npa_disconnect_state *state;
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tstream_npa_disconnect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->stream = stream;
+
+       if (!npas->unix_stream) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+
+       subreq = tstream_disconnect_send(state,
+                                        ev,
+                                        npas->unix_stream);
+       if (tevent_req_nomem(subreq, req)) {
+               goto post;
+       }
+       tevent_req_set_callback(subreq, tstream_npa_disconnect_handler, req);
+
+       return req;
+
+post:
+       tevent_req_post(req, ev);
+       return req;
+}
+
+static void tstream_npa_disconnect_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       struct tstream_npa_disconnect_state *state = tevent_req_data(req,
+                                       struct tstream_npa_disconnect_state);
+       struct tstream_context *stream = state->stream;
+       struct tstream_npa *npas = tstream_context_data(stream, struct tstream_npa);
+       int ret;
+       int sys_errno;
+
+       ret = tstream_disconnect_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       TALLOC_FREE(npas->unix_stream);
+
+       tevent_req_done(req);
+}
+
+static int tstream_npa_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_npa_ops = {
+       .name                   = "npa",
+
+       .pending_bytes          = tstream_npa_pending_bytes,
+
+       .readv_send             = tstream_npa_readv_send,
+       .readv_recv             = tstream_npa_readv_recv,
+
+       .writev_send            = tstream_npa_writev_send,
+       .writev_recv            = tstream_npa_writev_recv,
+
+       .disconnect_send        = tstream_npa_disconnect_send,
+       .disconnect_recv        = tstream_npa_disconnect_recv,
+};
+
+int _tstream_npa_existing_socket(TALLOC_CTX *mem_ctx,
+                                int fd,
+                                uint16_t file_type,
+                                struct tstream_context **_stream,
+                                const char *location)
+{
+       struct tstream_context *stream;
+       struct tstream_npa *npas;
+       int ret;
+
+       switch (file_type) {
+       case FILE_TYPE_BYTE_MODE_PIPE:
+               break;
+       case FILE_TYPE_MESSAGE_MODE_PIPE:
+               break;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+
+       stream = tstream_context_create(mem_ctx,
+                                       &tstream_npa_ops,
+                                       &npas,
+                                       struct tstream_npa,
+                                       location);
+       if (!stream) {
+               return -1;
+       }
+       ZERO_STRUCTP(npas);
+
+       npas->file_type = file_type;
+
+       ret = tstream_bsd_existing_socket(stream, fd,
+                                         &npas->unix_stream);
+       if (ret == -1) {
+               int saved_errno = errno;
+               talloc_free(stream);
+               errno = saved_errno;
+               return -1;
+       }
+
+       *_stream = stream;
+       return 0;
+}
+
diff --git a/libcli/named_pipe_auth/npa_tstream.h b/libcli/named_pipe_auth/npa_tstream.h
new file mode 100644 (file)
index 0000000..6ffd7b8
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Stefan Metzmacher 2009
+
+   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 NPA_TSTREAM_H
+#define NPA_TSTREAM_H
+
+struct tevent_req;
+struct tevent_context;
+struct netr_SamInfo3;
+
+struct tevent_req *tstream_npa_connect_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct smb_iconv_convenience *smb_iconv_c,
+                                       const char *directory,
+                                       const char *npipe,
+                                       const struct tsocket_address *client,
+                                       const char *client_name,
+                                       const struct tsocket_address *server,
+                                       const char *server_name,
+                                       const struct netr_SamInfo3 *info3,
+                                       DATA_BLOB session_key);
+int _tstream_npa_connect_recv(struct tevent_req *req,
+                             int *perrno,
+                             TALLOC_CTX *mem_ctx,
+                             struct tstream_context **stream,
+                             uint16_t *file_type,
+                             uint16_t *device_state,
+                             uint64_t *allocation_size,
+                             const char *location);
+#define tstream_npa_connect_recv(req, perrno, mem_ctx, stream, f, d, a) \
+       _tstream_npa_connect_recv(req, perrno, mem_ctx, stream, f, d, a, \
+                                 __location__)
+
+int _tstream_npa_existing_socket(TALLOC_CTX *mem_ctx,
+                                int fd,
+                                uint16_t file_type,
+                                struct tstream_context **_stream,
+                                const char *location);
+#define tstream_npa_existing_socket(mem_ctx, fd, ft, stream) \
+       _tstream_npa_existing_socket(mem_ctx, fd, ft, stream, \
+                                    __location__)
+
+#endif /* NPA_TSTREAM_H */
index d9e0254..2e74ba9 100644 (file)
@@ -57,3 +57,4 @@ mkinclude ../libcli/auth/config.mk
 mkinclude ../libcli/drsuapi/config.mk
 mkinclude ../libcli/samsync/config.mk
 mkinclude ../libgpo/config.mk
+mkinclude ../libcli/named_pipe_auth/config.mk