summaryrefslogtreecommitdiffstats
path: root/source4/librpc/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'source4/librpc/rpc')
-rw-r--r--source4/librpc/rpc/dcerpc.c2630
-rw-r--r--source4/librpc/rpc/dcerpc.h264
-rw-r--r--source4/librpc/rpc/dcerpc.py18
-rw-r--r--source4/librpc/rpc/dcerpc_auth.c556
-rw-r--r--source4/librpc/rpc/dcerpc_connect.c1251
-rw-r--r--source4/librpc/rpc/dcerpc_roh.c914
-rw-r--r--source4/librpc/rpc/dcerpc_roh.h111
-rw-r--r--source4/librpc/rpc/dcerpc_roh_channel_in.c303
-rw-r--r--source4/librpc/rpc/dcerpc_roh_channel_out.c582
-rw-r--r--source4/librpc/rpc/dcerpc_schannel.c620
-rw-r--r--source4/librpc/rpc/dcerpc_secondary.c431
-rw-r--r--source4/librpc/rpc/dcerpc_smb.c310
-rw-r--r--source4/librpc/rpc/dcerpc_sock.c499
-rw-r--r--source4/librpc/rpc/dcerpc_util.c811
-rw-r--r--source4/librpc/rpc/pyrpc.c653
-rw-r--r--source4/librpc/rpc/pyrpc.h60
-rw-r--r--source4/librpc/rpc/pyrpc_util.c506
-rw-r--r--source4/librpc/rpc/pyrpc_util.h70
18 files changed, 10589 insertions, 0 deletions
diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c
new file mode 100644
index 0000000..baf6df6
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc.c
@@ -0,0 +1,2630 @@
+/*
+ Unix SMB/CIFS implementation.
+ raw dcerpc operations
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Jelmer Vernooij 2004-2005
+
+ 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/filesys.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/rpc/dcerpc_util.h"
+#include "librpc/rpc/dcerpc_pkt_auth.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "librpc/rpc/rpc_common.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
+
+
+enum rpc_request_state {
+ RPC_REQUEST_QUEUED,
+ RPC_REQUEST_PENDING,
+ RPC_REQUEST_DONE
+};
+
+/*
+ handle for an async dcerpc request
+*/
+struct rpc_request {
+ struct rpc_request *next, *prev;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ uint32_t call_id;
+ enum rpc_request_state state;
+ DATA_BLOB payload;
+ uint32_t flags;
+ uint32_t fault_code;
+
+ /* this is used to distinguish bind and alter_context requests
+ from normal requests */
+ void (*recv_handler)(struct rpc_request *conn,
+ DATA_BLOB *blob, struct ncacn_packet *pkt);
+
+ const struct GUID *object;
+ uint16_t opnum;
+ DATA_BLOB request_data;
+ bool ignore_timeout;
+ bool wait_for_sync;
+ bool verify_bitmask1;
+ bool verify_pcontext;
+
+ struct {
+ void (*callback)(struct rpc_request *);
+ void *private_data;
+ } async;
+};
+
+_PUBLIC_ NTSTATUS dcerpc_init(void)
+{
+ return gensec_init();
+}
+
+static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status);
+static void dcerpc_schedule_io_trigger(struct dcecli_connection *c);
+
+static struct rpc_request *dcerpc_request_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct GUID *object,
+ uint16_t opnum,
+ DATA_BLOB *stub_data);
+static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *stub_data);
+static NTSTATUS dcerpc_ndr_validate_in(struct dcecli_connection *c,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB blob,
+ size_t struct_size,
+ ndr_push_flags_fn_t ndr_push,
+ ndr_pull_flags_fn_t ndr_pull);
+static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
+ struct ndr_pull *pull_in,
+ void *struct_ptr,
+ size_t struct_size,
+ ndr_push_flags_fn_t ndr_push,
+ ndr_pull_flags_fn_t ndr_pull,
+ ndr_print_function_t ndr_print);
+static NTSTATUS dcerpc_shutdown_pipe(struct dcecli_connection *p, NTSTATUS status);
+static NTSTATUS dcerpc_send_request(struct dcecli_connection *p, DATA_BLOB *data,
+ bool trigger_read);
+static NTSTATUS dcerpc_send_read(struct dcecli_connection *p);
+
+/* destroy a dcerpc connection */
+static int dcerpc_connection_destructor(struct dcecli_connection *conn)
+{
+ if (conn->dead) {
+ conn->free_skipped = true;
+ return -1;
+ }
+ dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+
+/* initialise a dcerpc connection.
+ the event context is optional
+*/
+static struct dcecli_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct dcecli_connection *c;
+
+ c = talloc_zero(mem_ctx, struct dcecli_connection);
+ if (!c) {
+ return NULL;
+ }
+
+ c->event_ctx = ev;
+
+ if (c->event_ctx == NULL) {
+ talloc_free(c);
+ return NULL;
+ }
+
+ c->call_id = 1;
+ c->security_state.auth_type = DCERPC_AUTH_TYPE_NONE;
+ c->security_state.auth_level = DCERPC_AUTH_LEVEL_NONE;
+ c->security_state.auth_context_id = 0;
+ c->security_state.session_key = dcecli_generic_session_key;
+ c->security_state.generic_state = NULL;
+ c->flags = 0;
+ /*
+ * Windows uses 5840 for ncacn_ip_tcp,
+ * so we also use it (for every transport)
+ * by default. But we give the transport
+ * the chance to overwrite it.
+ */
+ c->srv_max_xmit_frag = 5840;
+ c->srv_max_recv_frag = 5840;
+ c->max_total_response_size = DCERPC_NCACN_RESPONSE_DEFAULT_MAX_SIZE;
+ c->pending = NULL;
+
+ c->io_trigger = tevent_create_immediate(c);
+ if (c->io_trigger == NULL) {
+ talloc_free(c);
+ return NULL;
+ }
+
+ talloc_set_destructor(c, dcerpc_connection_destructor);
+
+ return c;
+}
+
+struct dcerpc_bh_state {
+ struct dcerpc_pipe *p;
+};
+
+static bool dcerpc_bh_is_connected(struct dcerpc_binding_handle *h)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+
+ if (!hs->p) {
+ return false;
+ }
+
+ if (!hs->p->conn) {
+ return false;
+ }
+
+ if (hs->p->conn->dead) {
+ return false;
+ }
+
+ return true;
+}
+
+static uint32_t dcerpc_bh_set_timeout(struct dcerpc_binding_handle *h,
+ uint32_t timeout)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+ uint32_t old;
+
+ if (!hs->p) {
+ return DCERPC_REQUEST_TIMEOUT;
+ }
+
+ old = hs->p->request_timeout;
+ hs->p->request_timeout = timeout;
+
+ return old;
+}
+
+static void dcerpc_bh_auth_info(struct dcerpc_binding_handle *h,
+ enum dcerpc_AuthType *auth_type,
+ enum dcerpc_AuthLevel *auth_level)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+
+ if (hs->p == NULL) {
+ return;
+ }
+
+ if (hs->p->conn == NULL) {
+ return;
+ }
+
+ *auth_type = hs->p->conn->security_state.auth_type;
+ *auth_level = hs->p->conn->security_state.auth_level;
+}
+
+struct dcerpc_bh_raw_call_state {
+ struct tevent_context *ev;
+ struct dcerpc_binding_handle *h;
+ DATA_BLOB in_data;
+ DATA_BLOB out_data;
+ uint32_t out_flags;
+};
+
+static void dcerpc_bh_raw_call_done(struct rpc_request *subreq);
+
+static struct tevent_req *dcerpc_bh_raw_call_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h,
+ const struct GUID *object,
+ uint32_t opnum,
+ uint32_t in_flags,
+ const uint8_t *in_data,
+ size_t in_length)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+ struct tevent_req *req;
+ struct dcerpc_bh_raw_call_state *state;
+ bool ok;
+ struct rpc_request *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dcerpc_bh_raw_call_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->h = h;
+ state->in_data.data = discard_const_p(uint8_t, in_data);
+ state->in_data.length = in_length;
+
+ ok = dcerpc_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_request_send(state,
+ hs->p,
+ object,
+ opnum,
+ &state->in_data);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ subreq->async.callback = dcerpc_bh_raw_call_done;
+ subreq->async.private_data = req;
+
+ return req;
+}
+
+static void dcerpc_bh_raw_call_done(struct rpc_request *subreq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct dcerpc_bh_raw_call_state);
+ NTSTATUS status;
+ uint32_t fault_code;
+
+ state->out_flags = 0;
+ if (subreq->flags & DCERPC_PULL_BIGENDIAN) {
+ state->out_flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ fault_code = subreq->fault_code;
+
+ status = dcerpc_request_recv(subreq, state, &state->out_data);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ status = dcerpc_fault_to_nt_status(fault_code);
+ }
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS dcerpc_bh_raw_call_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **out_data,
+ size_t *out_length,
+ uint32_t *out_flags)
+{
+ struct dcerpc_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct dcerpc_bh_raw_call_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *out_data = talloc_move(mem_ctx, &state->out_data.data);
+ *out_length = state->out_data.length;
+ *out_flags = state->out_flags;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct dcerpc_bh_disconnect_state {
+ uint8_t _dummy;
+};
+
+static struct tevent_req *dcerpc_bh_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+ struct tevent_req *req;
+ struct dcerpc_bh_disconnect_state *state;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dcerpc_bh_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ok = dcerpc_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ /* TODO: do a real disconnect ... */
+ hs->p = NULL;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS dcerpc_bh_disconnect_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static bool dcerpc_bh_push_bigendian(struct dcerpc_binding_handle *h)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+
+ if (hs->p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool dcerpc_bh_ref_alloc(struct dcerpc_binding_handle *h)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+
+ if (hs->p->conn->flags & DCERPC_NDR_REF_ALLOC) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool dcerpc_bh_use_ndr64(struct dcerpc_binding_handle *h)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+
+ if (hs->p->conn->flags & DCERPC_NDR64) {
+ return true;
+ }
+
+ return false;
+}
+
+static void dcerpc_bh_do_ndr_print(struct dcerpc_binding_handle *h,
+ int ndr_flags,
+ const void *_struct_ptr,
+ const struct ndr_interface_call *call)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+ void *struct_ptr = discard_const(_struct_ptr);
+ bool print_in = false;
+ bool print_out = false;
+
+ if (hs->p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
+ print_in = true;
+ }
+
+ if (hs->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print_out = true;
+ }
+
+ if (DEBUGLEVEL >= 11) {
+ print_in = true;
+ print_out = true;
+ }
+
+ if (ndr_flags & NDR_IN) {
+ if (print_in) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+ }
+ if (ndr_flags & NDR_OUT) {
+ if (print_out) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+ }
+}
+
+static void dcerpc_bh_ndr_push_failed(struct dcerpc_binding_handle *h,
+ NTSTATUS error,
+ const void *struct_ptr,
+ const struct ndr_interface_call *call)
+{
+ DEBUG(2,("Unable to ndr_push structure for %s - %s\n",
+ call->name, nt_errstr(error)));
+}
+
+static void dcerpc_bh_ndr_pull_failed(struct dcerpc_binding_handle *h,
+ NTSTATUS error,
+ const DATA_BLOB *blob,
+ const struct ndr_interface_call *call)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+ const uint32_t num_examples = 20;
+ uint32_t i;
+
+ DEBUG(2,("Unable to ndr_pull structure for %s - %s\n",
+ call->name, nt_errstr(error)));
+
+ if (hs->p->conn->packet_log_dir == NULL) return;
+
+ for (i=0;i<num_examples;i++) {
+ char *name=NULL;
+ int ret;
+
+ ret = asprintf(&name, "%s/rpclog/%s-out.%d",
+ hs->p->conn->packet_log_dir,
+ call->name, i);
+ if (ret == -1) {
+ return;
+ }
+ if (!file_exist(name)) {
+ if (file_save(name, blob->data, blob->length)) {
+ DEBUG(10,("Logged rpc packet to %s\n", name));
+ }
+ free(name);
+ break;
+ }
+ free(name);
+ }
+}
+
+static NTSTATUS dcerpc_bh_ndr_validate_in(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ const struct ndr_interface_call *call)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+
+ if (hs->p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
+ NTSTATUS status;
+
+ status = dcerpc_ndr_validate_in(hs->p->conn,
+ mem_ctx,
+ *blob,
+ call->struct_size,
+ call->ndr_push,
+ call->ndr_pull);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Validation [in] failed for %s - %s\n",
+ call->name, nt_errstr(status)));
+ return status;
+ }
+ }
+
+ DEBUG(10,("rpc request data:\n"));
+ dump_data(10, blob->data, blob->length);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcerpc_bh_ndr_validate_out(struct dcerpc_binding_handle *h,
+ struct ndr_pull *pull_in,
+ const void *_struct_ptr,
+ const struct ndr_interface_call *call)
+{
+ struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct dcerpc_bh_state);
+ void *struct_ptr = discard_const(_struct_ptr);
+
+ DEBUG(10,("rpc reply data:\n"));
+ dump_data(10, pull_in->data, pull_in->data_size);
+
+ if (pull_in->offset != pull_in->data_size) {
+ DEBUG(0,("Warning! ignoring %u unread bytes at ofs:%u (0x%08X) for %s!\n",
+ pull_in->data_size - pull_in->offset,
+ pull_in->offset, pull_in->offset,
+ call->name));
+ /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
+ but it turns out that early versions of NT
+ (specifically NT3.1) add junk onto the end of rpc
+ packets, so if we want to interoperate at all with
+ those versions then we need to ignore this error */
+ }
+
+ if (hs->p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
+ NTSTATUS status;
+
+ status = dcerpc_ndr_validate_out(hs->p->conn,
+ pull_in,
+ struct_ptr,
+ call->struct_size,
+ call->ndr_push,
+ call->ndr_pull,
+ call->ndr_print);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Validation [out] failed for %s - %s\n",
+ call->name, nt_errstr(status)));
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static const struct dcerpc_binding_handle_ops dcerpc_bh_ops = {
+ .name = "dcerpc",
+ .is_connected = dcerpc_bh_is_connected,
+ .set_timeout = dcerpc_bh_set_timeout,
+ .auth_info = dcerpc_bh_auth_info,
+ .raw_call_send = dcerpc_bh_raw_call_send,
+ .raw_call_recv = dcerpc_bh_raw_call_recv,
+ .disconnect_send = dcerpc_bh_disconnect_send,
+ .disconnect_recv = dcerpc_bh_disconnect_recv,
+
+ .push_bigendian = dcerpc_bh_push_bigendian,
+ .ref_alloc = dcerpc_bh_ref_alloc,
+ .use_ndr64 = dcerpc_bh_use_ndr64,
+ .do_ndr_print = dcerpc_bh_do_ndr_print,
+ .ndr_push_failed = dcerpc_bh_ndr_push_failed,
+ .ndr_pull_failed = dcerpc_bh_ndr_pull_failed,
+ .ndr_validate_in = dcerpc_bh_ndr_validate_in,
+ .ndr_validate_out = dcerpc_bh_ndr_validate_out,
+};
+
+/* initialise a dcerpc pipe. */
+struct dcerpc_binding_handle *dcerpc_pipe_binding_handle(struct dcerpc_pipe *p,
+ const struct GUID *object,
+ const struct ndr_interface_table *table)
+{
+ struct dcerpc_binding_handle *h;
+ struct dcerpc_bh_state *hs;
+
+ h = dcerpc_binding_handle_create(p,
+ &dcerpc_bh_ops,
+ object,
+ table,
+ &hs,
+ struct dcerpc_bh_state,
+ __location__);
+ if (h == NULL) {
+ return NULL;
+ }
+ hs->p = p;
+
+ dcerpc_binding_handle_set_sync_ev(h, p->conn->event_ctx);
+
+ return h;
+}
+
+/* initialise a dcerpc pipe. */
+_PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev)
+{
+ struct dcerpc_pipe *p;
+
+ p = talloc_zero(mem_ctx, struct dcerpc_pipe);
+ if (!p) {
+ return NULL;
+ }
+
+ p->conn = dcerpc_connection_init(p, ev);
+ if (p->conn == NULL) {
+ talloc_free(p);
+ return NULL;
+ }
+
+ p->request_timeout = DCERPC_REQUEST_TIMEOUT;
+
+ if (DEBUGLVL(100)) {
+ p->conn->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ }
+
+ return p;
+}
+
+
+/*
+ choose the next call id to use
+*/
+static uint32_t next_call_id(struct dcecli_connection *c)
+{
+ c->call_id++;
+ if (c->call_id == 0) {
+ c->call_id++;
+ }
+ return c->call_id;
+}
+
+/**
+ setup for a ndr pull, also setting up any flags from the binding string
+*/
+static struct ndr_pull *ndr_pull_init_flags(struct dcecli_connection *c,
+ DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
+{
+ struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
+
+ if (ndr == NULL) return ndr;
+
+ if (c->flags & DCERPC_DEBUG_PAD_CHECK) {
+ ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
+ }
+
+ if (c->flags & DCERPC_NDR_REF_ALLOC) {
+ ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
+ }
+
+ if (c->flags & DCERPC_NDR64) {
+ ndr->flags |= LIBNDR_FLAG_NDR64;
+ }
+
+ return ndr;
+}
+
+/*
+ parse the authentication information on a dcerpc response packet
+*/
+static NTSTATUS ncacn_pull_pkt_auth(struct dcecli_connection *c,
+ TALLOC_CTX *mem_ctx,
+ enum dcerpc_pkt_type ptype,
+ uint8_t required_flags,
+ uint8_t optional_flags,
+ uint8_t payload_offset,
+ DATA_BLOB *payload_and_verifier,
+ DATA_BLOB *raw_packet,
+ const struct ncacn_packet *pkt)
+{
+ const struct dcerpc_auth tmp_auth = {
+ .auth_type = c->security_state.auth_type,
+ .auth_level = c->security_state.auth_level,
+ .auth_context_id = c->security_state.auth_context_id,
+ };
+ NTSTATUS status;
+
+ status = dcerpc_ncacn_pull_pkt_auth(&tmp_auth,
+ c->security_state.generic_state,
+ true, /* check_pkt_auth_fields */
+ mem_ctx,
+ ptype,
+ required_flags,
+ optional_flags,
+ payload_offset,
+ payload_and_verifier,
+ raw_packet,
+ pkt);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a dcerpc request packet into a blob, possibly signing it.
+*/
+static NTSTATUS ncacn_push_request_sign(struct dcecli_connection *c,
+ DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ size_t sig_size,
+ struct ncacn_packet *pkt)
+{
+ const struct dcerpc_auth tmp_auth = {
+ .auth_type = c->security_state.auth_type,
+ .auth_level = c->security_state.auth_level,
+ .auth_context_id = c->security_state.auth_context_id,
+ };
+ NTSTATUS status;
+ uint8_t payload_offset = DCERPC_REQUEST_LENGTH;
+
+ if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
+ payload_offset += 16;
+ }
+
+ status = dcerpc_ncacn_push_pkt_auth(&tmp_auth,
+ c->security_state.generic_state,
+ mem_ctx, blob,
+ sig_size,
+ payload_offset,
+ &pkt->u.request.stub_and_verifier,
+ pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ fill in the fixed values in a dcerpc header
+*/
+static void init_ncacn_hdr(struct dcecli_connection *c, struct ncacn_packet *pkt)
+{
+ pkt->rpc_vers = 5;
+ pkt->rpc_vers_minor = 0;
+ if (c->flags & DCERPC_PUSH_BIGENDIAN) {
+ pkt->drep[0] = 0;
+ } else {
+ pkt->drep[0] = DCERPC_DREP_LE;
+ }
+ pkt->drep[1] = 0;
+ pkt->drep[2] = 0;
+ pkt->drep[3] = 0;
+}
+
+/*
+ map a bind nak reason to a NTSTATUS
+*/
+static NTSTATUS dcerpc_map_nak_reason(enum dcerpc_bind_nak_reason reason)
+{
+ switch (reason) {
+ case DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED:
+ return NT_STATUS_REVISION_MISMATCH;
+ case DCERPC_BIND_NAK_REASON_INVALID_AUTH_TYPE:
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ break;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS dcerpc_map_ack_reason(const struct dcerpc_ack_ctx *ack)
+{
+ if (ack == NULL) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ switch (ack->result) {
+ case DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK:
+ /*
+ * We have not asked for this...
+ */
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ default:
+ break;
+ }
+
+ switch (ack->reason.value) {
+ case DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED:
+ return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+ case DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED:
+ return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+ default:
+ break;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ remove requests from the pending or queued queues
+ */
+static int dcerpc_req_dequeue(struct rpc_request *req)
+{
+ switch (req->state) {
+ case RPC_REQUEST_QUEUED:
+ DLIST_REMOVE(req->p->conn->request_queue, req);
+ break;
+ case RPC_REQUEST_PENDING:
+ DLIST_REMOVE(req->p->conn->pending, req);
+ break;
+ case RPC_REQUEST_DONE:
+ break;
+ }
+ return 0;
+}
+
+
+/*
+ mark the dcerpc connection dead. All outstanding requests get an error
+*/
+static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status)
+{
+ if (conn->dead) return;
+
+ conn->dead = true;
+
+ TALLOC_FREE(conn->io_trigger);
+ conn->io_trigger_pending = false;
+
+ dcerpc_shutdown_pipe(conn, status);
+
+ /* all pending requests get the error */
+ while (conn->pending) {
+ struct rpc_request *req = conn->pending;
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->status = status;
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+ }
+
+ /* all requests, which are not shipped */
+ while (conn->request_queue) {
+ struct rpc_request *req = conn->request_queue;
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->status = status;
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+ }
+
+ talloc_set_destructor(conn, NULL);
+ if (conn->free_skipped) {
+ talloc_free(conn);
+ }
+}
+
+/*
+ forward declarations of the recv_data handlers for the types of
+ packets we need to handle
+*/
+static void dcerpc_request_recv_data(struct dcecli_connection *c,
+ DATA_BLOB *raw_packet, struct ncacn_packet *pkt);
+
+/*
+ receive a dcerpc reply from the transport. Here we work out what
+ type of reply it is (normal request, bind or alter context) and
+ dispatch to the appropriate handler
+*/
+static void dcerpc_recv_data(struct dcecli_connection *conn, DATA_BLOB *blob, NTSTATUS status)
+{
+ struct ncacn_packet pkt;
+
+ if (conn->dead) {
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(status) && blob->length == 0) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ /* the transport may be telling us of a severe error, such as
+ a dropped socket */
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(blob);
+ dcerpc_connection_dead(conn, status);
+ return;
+ }
+
+ /* parse the basic packet to work out what type of response this is */
+ status = dcerpc_pull_ncacn_packet(blob->data, blob, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(blob);
+ dcerpc_connection_dead(conn, status);
+ return;
+ }
+
+ dcerpc_request_recv_data(conn, blob, &pkt);
+}
+
+/*
+ handle timeouts of individual dcerpc requests
+*/
+static void dcerpc_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct rpc_request *req = talloc_get_type(private_data, struct rpc_request);
+
+ if (req->ignore_timeout) {
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+ return;
+ }
+
+ dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
+}
+
+struct dcerpc_bind_state {
+ struct tevent_context *ev;
+ struct dcerpc_pipe *p;
+};
+
+static void dcerpc_bind_fail_handler(struct rpc_request *subreq);
+static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt);
+
+struct tevent_req *dcerpc_bind_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_pipe *p,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
+{
+ struct tevent_req *req;
+ struct dcerpc_bind_state *state;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct rpc_request *subreq;
+ uint32_t flags;
+ struct ndr_syntax_id bind_time_features;
+
+ bind_time_features = dcerpc_construct_bind_time_features(
+ DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING |
+ DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dcerpc_bind_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->p = p;
+
+ p->syntax = *syntax;
+ p->transfer_syntax = *transfer_syntax;
+
+ flags = dcerpc_binding_get_flags(p->binding);
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ pkt.ptype = DCERPC_PKT_BIND;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = p->conn->call_id;
+ pkt.auth_length = 0;
+
+ if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
+ if (p->conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ }
+
+ pkt.u.bind.max_xmit_frag = p->conn->srv_max_xmit_frag;
+ pkt.u.bind.max_recv_frag = p->conn->srv_max_recv_frag;
+ pkt.u.bind.assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding);
+ pkt.u.bind.num_contexts = 2;
+ pkt.u.bind.ctx_list = talloc_zero_array(state, struct dcerpc_ctx_list,
+ pkt.u.bind.num_contexts);
+ if (tevent_req_nomem(pkt.u.bind.ctx_list, req)) {
+ return tevent_req_post(req, ev);
+ }
+ pkt.u.bind.ctx_list[0].context_id = p->context_id;
+ pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
+ pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
+ pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
+ pkt.u.bind.ctx_list[1].context_id = p->context_id + 1;
+ pkt.u.bind.ctx_list[1].num_transfer_syntaxes = 1;
+ pkt.u.bind.ctx_list[1].abstract_syntax = p->syntax;
+ pkt.u.bind.ctx_list[1].transfer_syntaxes = &bind_time_features;
+ pkt.u.bind.auth_info = data_blob(NULL, 0);
+
+ /* construct the NDR form of the packet */
+ status = dcerpc_ncacn_push_auth(&blob,
+ state,
+ &pkt,
+ p->conn->security_state.tmp_auth_info.out);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we allocate a dcerpc_request so we can be in the same
+ * request queue as normal requests
+ */
+ subreq = talloc_zero(state, struct rpc_request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq->state = RPC_REQUEST_PENDING;
+ subreq->call_id = pkt.call_id;
+ subreq->async.private_data = req;
+ subreq->async.callback = dcerpc_bind_fail_handler;
+ subreq->p = p;
+ subreq->recv_handler = dcerpc_bind_recv_handler;
+ DLIST_ADD_END(p->conn->pending, subreq);
+ talloc_set_destructor(subreq, dcerpc_req_dequeue);
+
+ status = dcerpc_send_request(p->conn, &blob, true);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_add_timer(ev, subreq,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_timeout_handler, subreq);
+
+ return req;
+}
+
+static void dcerpc_bind_fail_handler(struct rpc_request *subreq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_bind_state *state =
+ tevent_req_data(req,
+ struct dcerpc_bind_state);
+ NTSTATUS status = subreq->status;
+
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ tevent_req_nterror(req, status);
+}
+
+static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_bind_state *state =
+ tevent_req_data(req,
+ struct dcerpc_bind_state);
+ struct dcecli_connection *conn = state->p->conn;
+ struct dcecli_security *sec = &conn->security_state;
+ struct dcerpc_binding *b = NULL;
+ NTSTATUS status;
+ uint32_t flags;
+
+ /*
+ * Note that pkt is allocated under raw_packet->data,
+ * while raw_packet->data is a child of subreq.
+ */
+ talloc_steal(state, raw_packet->data);
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
+ status = dcerpc_map_nak_reason(pkt->u.bind_nak.reject_reason);
+
+ DEBUG(2,("dcerpc: bind_nak reason %d - %s\n",
+ pkt->u.bind_nak.reject_reason, nt_errstr(status)));
+
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ status = dcerpc_verify_ncacn_packet_header(pkt,
+ DCERPC_PKT_BIND_ACK,
+ pkt->u.bind_ack.auth_info.length,
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ DCERPC_PFC_FLAG_CONC_MPX |
+ DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+ if (!NT_STATUS_IS_OK(status)) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+
+ if (pkt->u.bind_ack.num_results < 1) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+
+ if (pkt->u.bind_ack.ctx_list[0].result != 0) {
+ status = dcerpc_map_ack_reason(&pkt->u.bind_ack.ctx_list[0]);
+ DEBUG(2,("dcerpc: bind_ack failed - reason %d - %s\n",
+ pkt->u.bind_ack.ctx_list[0].reason.value,
+ nt_errstr(status)));
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (pkt->u.bind_ack.num_results >= 2) {
+ if (pkt->u.bind_ack.ctx_list[1].result == DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK) {
+ conn->bind_time_features = pkt->u.bind_ack.ctx_list[1].reason.negotiate;
+ } else {
+ status = dcerpc_map_ack_reason(&pkt->u.bind_ack.ctx_list[1]);
+ DEBUG(10,("dcerpc: bind_time_feature failed - reason %d - %s\n",
+ pkt->u.bind_ack.ctx_list[1].reason.value,
+ nt_errstr(status)));
+ status = NT_STATUS_OK;
+ }
+ }
+
+ /*
+ * DCE-RPC 1.1 (c706) specifies
+ * CONST_MUST_RCV_FRAG_SIZE as 1432
+ */
+ if (pkt->u.bind_ack.max_xmit_frag < 1432) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+ if (pkt->u.bind_ack.max_recv_frag < 1432) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+ conn->srv_max_xmit_frag = MIN(conn->srv_max_xmit_frag,
+ pkt->u.bind_ack.max_xmit_frag);
+ conn->srv_max_recv_frag = MIN(conn->srv_max_recv_frag,
+ pkt->u.bind_ack.max_recv_frag);
+
+ flags = dcerpc_binding_get_flags(state->p->binding);
+
+ if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ if (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX) {
+ conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
+ } else {
+ conn->flags &= ~DCERPC_CONCURRENT_MULTIPLEX;
+ }
+ }
+
+ if (!(conn->flags & DCERPC_CONCURRENT_MULTIPLEX)) {
+ struct dcerpc_binding *pb =
+ discard_const_p(struct dcerpc_binding, state->p->binding);
+ /*
+ * clear DCERPC_CONCURRENT_MULTIPLEX
+ */
+ status = dcerpc_binding_set_flags(pb, 0,
+ DCERPC_CONCURRENT_MULTIPLEX);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+ if ((conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) &&
+ (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
+ conn->flags |= DCERPC_HEADER_SIGNING;
+ }
+
+ /* the bind_ack might contain a reply set of credentials */
+ if (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) {
+ status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem,
+ &pkt->u.bind_ack.auth_info,
+ sec->tmp_auth_info.in,
+ NULL, true);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+
+ /*
+ * We're the owner of the binding, so we're allowed to modify it.
+ */
+ b = discard_const_p(struct dcerpc_binding, state->p->binding);
+ status = dcerpc_binding_set_assoc_group_id(b,
+ pkt->u.bind_ack.assoc_group_id);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS dcerpc_bind_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ perform a continued bind (and auth3)
+*/
+NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ struct ncacn_packet pkt;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ uint32_t flags;
+
+ flags = dcerpc_binding_get_flags(p->binding);
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ pkt.ptype = DCERPC_PKT_AUTH3;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = next_call_id(p->conn);
+ pkt.auth_length = 0;
+ pkt.u.auth3.auth_info = data_blob(NULL, 0);
+
+ if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
+ /* construct the NDR form of the packet */
+ status = dcerpc_ncacn_push_auth(&blob,
+ mem_ctx,
+ &pkt,
+ p->conn->security_state.tmp_auth_info.out);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* send it on its way */
+ status = dcerpc_send_request(p->conn, &blob, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ process a fragment received from the transport layer during a
+ request
+
+ This function frees the data
+*/
+static void dcerpc_request_recv_data(struct dcecli_connection *c,
+ DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
+{
+ struct rpc_request *req;
+ unsigned int length;
+ NTSTATUS status = NT_STATUS_OK;
+
+ /*
+ if this is an authenticated connection then parse and check
+ the auth info. We have to do this before finding the
+ matching packet, as the request structure might have been
+ removed due to a timeout, but if it has been we still need
+ to run the auth routines so that we don't get the sign/seal
+ info out of step with the server
+ */
+ switch (pkt->ptype) {
+ case DCERPC_PKT_RESPONSE:
+ status = ncacn_pull_pkt_auth(c, raw_packet->data,
+ DCERPC_PKT_RESPONSE,
+ 0, /* required_flags */
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ DCERPC_REQUEST_LENGTH,
+ &pkt->u.response.stub_and_verifier,
+ raw_packet, pkt);
+ break;
+ default:
+ break;
+ }
+
+ /* find the matching request */
+ for (req=c->pending;req;req=req->next) {
+ if (pkt->call_id == req->call_id) break;
+ }
+
+#if 0
+ /* useful for testing certain vendors RPC servers */
+ if (req == NULL && c->pending && pkt->call_id == 0) {
+ DEBUG(0,("HACK FOR INCORRECT CALL ID\n"));
+ req = c->pending;
+ }
+#endif
+
+ if (req == NULL) {
+ DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id));
+ data_blob_free(raw_packet);
+ return;
+ }
+
+ talloc_steal(req, raw_packet->data);
+
+ if (req->recv_handler != NULL) {
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+
+ /*
+ * We have to look at shipping further requests before calling
+ * the async function, that one might close the pipe
+ */
+ dcerpc_schedule_io_trigger(c);
+
+ req->recv_handler(req, raw_packet, pkt);
+ return;
+ }
+
+ if (pkt->ptype == DCERPC_PKT_FAULT) {
+ status = dcerpc_fault_to_nt_status(pkt->u.fault.status);
+ DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+ dcerpc_connection_dead(c, status);
+ return;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ dcerpc_connection_dead(c, status);
+ return;
+ }
+ req->fault_code = pkt->u.fault.status;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ goto req_done;
+ }
+
+ if (pkt->ptype != DCERPC_PKT_RESPONSE) {
+ DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
+ (int)pkt->ptype));
+ dcerpc_connection_dead(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ /* now check the status from the auth routines, and if it failed then fail
+ this request accordingly */
+ if (!NT_STATUS_IS_OK(status)) {
+ dcerpc_connection_dead(c, status);
+ return;
+ }
+
+ length = pkt->u.response.stub_and_verifier.length;
+
+ if (req->payload.length + length > c->max_total_response_size) {
+ DEBUG(2,("Unexpected total payload 0x%X > 0x%X dcerpc response\n",
+ (unsigned)req->payload.length + length,
+ (unsigned)c->max_total_response_size));
+ dcerpc_connection_dead(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ if (length > 0) {
+ req->payload.data = talloc_realloc(req,
+ req->payload.data,
+ uint8_t,
+ req->payload.length + length);
+ if (!req->payload.data) {
+ req->status = NT_STATUS_NO_MEMORY;
+ goto req_done;
+ }
+ memcpy(req->payload.data+req->payload.length,
+ pkt->u.response.stub_and_verifier.data, length);
+ req->payload.length += length;
+ }
+
+ if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+ data_blob_free(raw_packet);
+ dcerpc_send_read(c);
+ return;
+ }
+
+ if (req->verify_bitmask1) {
+ req->p->conn->security_state.verified_bitmask1 = true;
+ }
+ if (req->verify_pcontext) {
+ req->p->verified_pcontext = true;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ req->flags |= DCERPC_PULL_BIGENDIAN;
+ } else {
+ req->flags &= ~DCERPC_PULL_BIGENDIAN;
+ }
+
+req_done:
+ data_blob_free(raw_packet);
+
+ /* we've got the full payload */
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+
+ /*
+ * We have to look at shipping further requests before calling
+ * the async function, that one might close the pipe
+ */
+ dcerpc_schedule_io_trigger(c);
+
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+}
+
+static NTSTATUS dcerpc_request_prepare_vt(struct rpc_request *req);
+
+/*
+ perform the send side of a async dcerpc request
+*/
+static struct rpc_request *dcerpc_request_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct GUID *object,
+ uint16_t opnum,
+ DATA_BLOB *stub_data)
+{
+ struct rpc_request *req;
+ NTSTATUS status;
+
+ req = talloc_zero(mem_ctx, struct rpc_request);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ req->p = p;
+ req->call_id = next_call_id(p->conn);
+ req->state = RPC_REQUEST_QUEUED;
+
+ if (object != NULL) {
+ req->object = (struct GUID *)talloc_memdup(req, (const void *)object, sizeof(*object));
+ if (req->object == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ req->opnum = opnum;
+ req->request_data.length = stub_data->length;
+ req->request_data.data = stub_data->data;
+
+ status = dcerpc_request_prepare_vt(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ DLIST_ADD_END(p->conn->request_queue, req);
+ talloc_set_destructor(req, dcerpc_req_dequeue);
+
+ dcerpc_schedule_io_trigger(p->conn);
+
+ if (p->request_timeout) {
+ tevent_add_timer(p->conn->event_ctx, req,
+ timeval_current_ofs(p->request_timeout, 0),
+ dcerpc_timeout_handler, req);
+ }
+
+ return req;
+}
+
+static NTSTATUS dcerpc_request_prepare_vt(struct rpc_request *req)
+{
+ struct dcecli_security *sec = &req->p->conn->security_state;
+ struct dcerpc_sec_verification_trailer *t;
+ struct dcerpc_sec_vt *c = NULL;
+ struct ndr_push *ndr = NULL;
+ enum ndr_err_code ndr_err;
+
+ if (sec->auth_level < DCERPC_AUTH_LEVEL_PACKET) {
+ return NT_STATUS_OK;
+ }
+
+ t = talloc_zero(req, struct dcerpc_sec_verification_trailer);
+ if (t == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!sec->verified_bitmask1) {
+ t->commands = talloc_realloc(t, t->commands,
+ struct dcerpc_sec_vt,
+ t->count.count + 1);
+ if (t->commands == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ c = &t->commands[t->count.count++];
+ ZERO_STRUCTP(c);
+
+ c->command = DCERPC_SEC_VT_COMMAND_BITMASK1;
+ if (req->p->conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) {
+ c->u.bitmask1 = DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING;
+ }
+ req->verify_bitmask1 = true;
+ }
+
+ if (!req->p->verified_pcontext) {
+ t->commands = talloc_realloc(t, t->commands,
+ struct dcerpc_sec_vt,
+ t->count.count + 1);
+ if (t->commands == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ c = &t->commands[t->count.count++];
+ ZERO_STRUCTP(c);
+
+ c->command = DCERPC_SEC_VT_COMMAND_PCONTEXT;
+ c->u.pcontext.abstract_syntax = req->p->syntax;
+ c->u.pcontext.transfer_syntax = req->p->transfer_syntax;
+
+ req->verify_pcontext = true;
+ }
+
+ if (!(req->p->conn->flags & DCERPC_HEADER_SIGNING)) {
+ t->commands = talloc_realloc(t, t->commands,
+ struct dcerpc_sec_vt,
+ t->count.count + 1);
+ if (t->commands == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ c = &t->commands[t->count.count++];
+ ZERO_STRUCTP(c);
+
+ c->command = DCERPC_SEC_VT_COMMAND_HEADER2;
+ c->u.header2.ptype = DCERPC_PKT_REQUEST;
+ if (req->p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
+ c->u.header2.drep[0] = 0;
+ } else {
+ c->u.header2.drep[0] = DCERPC_DREP_LE;
+ }
+ c->u.header2.drep[1] = 0;
+ c->u.header2.drep[2] = 0;
+ c->u.header2.drep[3] = 0;
+ c->u.header2.call_id = req->call_id;
+ c->u.header2.context_id = req->p->context_id;
+ c->u.header2.opnum = req->opnum;
+ }
+
+ if (t->count.count == 0) {
+ TALLOC_FREE(t);
+ return NT_STATUS_OK;
+ }
+
+ c = &t->commands[t->count.count - 1];
+ c->command |= DCERPC_SEC_VT_COMMAND_END;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(dcerpc_sec_verification_trailer, t);
+ }
+
+ ndr = ndr_push_init_ctx(req);
+ if (ndr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * for now we just copy and append
+ */
+
+ ndr_err = ndr_push_bytes(ndr, req->request_data.data,
+ req->request_data.length);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ ndr_err = ndr_push_dcerpc_sec_verification_trailer(ndr,
+ NDR_SCALARS | NDR_BUFFERS,
+ t);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ req->request_data = ndr_push_blob(ndr);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Send a request using the transport
+*/
+
+static void dcerpc_ship_next_request(struct dcecli_connection *c)
+{
+ struct rpc_request *req;
+ struct dcerpc_pipe *p;
+ DATA_BLOB *stub_data;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ uint32_t remaining, chunk_size;
+ bool first_packet = true;
+ size_t sig_size = 0;
+ bool need_async = false;
+ bool can_async = true;
+
+ req = c->request_queue;
+ if (req == NULL) {
+ return;
+ }
+
+ p = req->p;
+ stub_data = &req->request_data;
+
+ if (c->pending) {
+ need_async = true;
+ }
+
+ if (c->security_state.auth_level >= DCERPC_AUTH_LEVEL_PACKET) {
+ can_async = gensec_have_feature(c->security_state.generic_state,
+ GENSEC_FEATURE_ASYNC_REPLIES);
+ }
+
+ if (need_async && !can_async) {
+ req->wait_for_sync = true;
+ return;
+ }
+
+ DLIST_REMOVE(c->request_queue, req);
+ DLIST_ADD(c->pending, req);
+ req->state = RPC_REQUEST_PENDING;
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ remaining = stub_data->length;
+
+ /* we can write a full max_recv_frag size, minus the dcerpc
+ request header size */
+ chunk_size = p->conn->srv_max_recv_frag;
+ chunk_size -= DCERPC_REQUEST_LENGTH;
+ if (c->security_state.auth_level >= DCERPC_AUTH_LEVEL_PACKET) {
+ size_t max_payload = chunk_size;
+
+ max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
+ max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);
+
+ sig_size = gensec_sig_size(c->security_state.generic_state,
+ max_payload);
+ if (sig_size) {
+ chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
+ chunk_size -= sig_size;
+ }
+ }
+ chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);
+
+ pkt.ptype = DCERPC_PKT_REQUEST;
+ pkt.call_id = req->call_id;
+ pkt.auth_length = 0;
+ pkt.pfc_flags = 0;
+ pkt.u.request.context_id = p->context_id;
+ pkt.u.request.opnum = req->opnum;
+
+ if (req->object) {
+ pkt.u.request.object.object = *req->object;
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
+ chunk_size -= ndr_size_GUID(req->object,0);
+ }
+
+ /* we send a series of pdus without waiting for a reply */
+ while (remaining > 0 || first_packet) {
+ uint32_t chunk = MIN(chunk_size, remaining);
+ bool last_frag = false;
+ bool do_trans = false;
+
+ first_packet = false;
+ pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST);
+
+ if (remaining == stub_data->length) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
+ }
+ if (chunk == remaining) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
+ last_frag = true;
+ }
+
+ pkt.u.request.alloc_hint = remaining;
+ pkt.u.request.stub_and_verifier.data = stub_data->data +
+ (stub_data->length - remaining);
+ pkt.u.request.stub_and_verifier.length = chunk;
+
+ req->status = ncacn_push_request_sign(p->conn, &blob, req, sig_size, &pkt);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(p->conn->pending, req);
+ return;
+ }
+
+ if (last_frag && !need_async) {
+ do_trans = true;
+ }
+
+ req->status = dcerpc_send_request(p->conn, &blob, do_trans);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(p->conn->pending, req);
+ return;
+ }
+
+ if (last_frag && !do_trans) {
+ req->status = dcerpc_send_read(p->conn);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(p->conn->pending, req);
+ return;
+ }
+ }
+
+ remaining -= chunk;
+ }
+}
+
+static void dcerpc_io_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct dcecli_connection *c =
+ talloc_get_type_abort(private_data,
+ struct dcecli_connection);
+
+ c->io_trigger_pending = false;
+
+ dcerpc_schedule_io_trigger(c);
+
+ dcerpc_ship_next_request(c);
+}
+
+static void dcerpc_schedule_io_trigger(struct dcecli_connection *c)
+{
+ if (c->dead) {
+ return;
+ }
+
+ if (c->request_queue == NULL) {
+ return;
+ }
+
+ if (c->request_queue->wait_for_sync && c->pending) {
+ return;
+ }
+
+ if (c->io_trigger_pending) {
+ return;
+ }
+
+ c->io_trigger_pending = true;
+
+ tevent_schedule_immediate(c->io_trigger,
+ c->event_ctx,
+ dcerpc_io_trigger,
+ c);
+}
+
+/*
+ perform the receive side of a async dcerpc request
+*/
+static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *stub_data)
+{
+ NTSTATUS status;
+
+ while (req->state != RPC_REQUEST_DONE) {
+ struct tevent_context *ctx = req->p->conn->event_ctx;
+ if (tevent_loop_once(ctx) != 0) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ }
+ *stub_data = req->payload;
+ status = req->status;
+ if (stub_data->data) {
+ stub_data->data = talloc_steal(mem_ctx, stub_data->data);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ req->p->last_fault_code = req->fault_code;
+ }
+ talloc_unlink(talloc_parent(req), req);
+ return status;
+}
+
+/*
+ this is a paranoid NDR validator. For every packet we push onto the wire
+ we pull it back again, then push it again. Then we compare the raw NDR data
+ for that to the NDR we initially generated. If they don't match then we know
+ we must have a bug in either the pull or push side of our code
+*/
+static NTSTATUS dcerpc_ndr_validate_in(struct dcecli_connection *c,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB blob,
+ size_t struct_size,
+ ndr_push_flags_fn_t ndr_push,
+ ndr_pull_flags_fn_t ndr_pull)
+{
+ void *st;
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ DATA_BLOB blob2;
+ enum ndr_err_code ndr_err;
+
+ st = talloc_size(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pull = ndr_pull_init_flags(c, &blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ if (c->flags & DCERPC_PUSH_BIGENDIAN) {
+ pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ if (c->flags & DCERPC_NDR64) {
+ pull->flags |= LIBNDR_FLAG_NDR64;
+ }
+
+ ndr_err = ndr_pull(pull, NDR_IN, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed input validation pull - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (c->flags & DCERPC_PUSH_BIGENDIAN) {
+ push->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ if (c->flags & DCERPC_NDR64) {
+ push->flags |= LIBNDR_FLAG_NDR64;
+ }
+
+ ndr_err = ndr_push(push, NDR_IN, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed input validation push - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ blob2 = ndr_push_blob(push);
+
+ if (data_blob_cmp(&blob, &blob2) != 0) {
+ DEBUG(3,("original:\n"));
+ dump_data(3, blob.data, blob.length);
+ DEBUG(3,("secondary:\n"));
+ dump_data(3, blob2.data, blob2.length);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed input validation blobs doesn't match");
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ this is a paranoid NDR input validator. For every packet we pull
+ from the wire we push it back again then pull and push it
+ again. Then we compare the raw NDR data for that to the NDR we
+ initially generated. If they don't match then we know we must have a
+ bug in either the pull or push side of our code
+*/
+static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
+ struct ndr_pull *pull_in,
+ void *struct_ptr,
+ size_t struct_size,
+ ndr_push_flags_fn_t ndr_push,
+ ndr_pull_flags_fn_t ndr_pull,
+ ndr_print_function_t ndr_print)
+{
+ void *st;
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ DATA_BLOB blob, blob2;
+ TALLOC_CTX *mem_ctx = pull_in;
+ char *s1, *s2;
+ enum ndr_err_code ndr_err;
+
+ st = talloc_size(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(st, struct_ptr, struct_size);
+
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push(push, NDR_OUT, struct_ptr);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation push - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ blob = ndr_push_blob(push);
+
+ pull = ndr_pull_init_flags(c, &blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
+ ndr_err = ndr_pull(pull, NDR_OUT, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed output validation pull - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push(push, NDR_OUT, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation push2 - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ blob2 = ndr_push_blob(push);
+
+ if (data_blob_cmp(&blob, &blob2) != 0) {
+ DEBUG(3,("original:\n"));
+ dump_data(3, blob.data, blob.length);
+ DEBUG(3,("secondary:\n"));
+ dump_data(3, blob2.data, blob2.length);
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation blobs doesn't match");
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ /* this checks the printed forms of the two structures, which effectively
+ tests all of the value() attributes */
+ s1 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
+ NDR_OUT, struct_ptr);
+ s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
+ NDR_OUT, st);
+ if (strcmp(s1, s2) != 0) {
+#if 1
+ DEBUG(3,("VALIDATE ERROR:\nWIRE:\n%s\n GEN:\n%s\n", s1, s2));
+#else
+ /* this is sometimes useful */
+ printf("VALIDATE ERROR\n");
+ file_save("wire.dat", s1, strlen(s1));
+ file_save("gen.dat", s2, strlen(s2));
+ system("diff -u wire.dat gen.dat");
+#endif
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation strings doesn't match");
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a useful function for retrieving the server name we connected to
+*/
+_PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
+{
+ return p->conn ? p->conn->server_name : NULL;
+}
+
+
+/*
+ get the dcerpc auth_level for a open connection
+*/
+uint32_t dcerpc_auth_level(struct dcecli_connection *c)
+{
+ uint8_t auth_level;
+
+ if (c->flags & DCERPC_SEAL) {
+ auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+ } else if (c->flags & DCERPC_SIGN) {
+ auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+ } else if (c->flags & DCERPC_PACKET) {
+ auth_level = DCERPC_AUTH_LEVEL_PACKET;
+ } else if (c->flags & DCERPC_CONNECT) {
+ auth_level = DCERPC_AUTH_LEVEL_CONNECT;
+ } else {
+ auth_level = DCERPC_AUTH_LEVEL_NONE;
+ }
+ return auth_level;
+}
+
+struct dcerpc_alter_context_state {
+ struct tevent_context *ev;
+ struct dcerpc_pipe *p;
+};
+
+static void dcerpc_alter_context_fail_handler(struct rpc_request *subreq);
+static void dcerpc_alter_context_recv_handler(struct rpc_request *req,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt);
+
+struct tevent_req *dcerpc_alter_context_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_pipe *p,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
+{
+ struct tevent_req *req;
+ struct dcerpc_alter_context_state *state;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct rpc_request *subreq;
+ uint32_t flags;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dcerpc_alter_context_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->p = p;
+
+ p->syntax = *syntax;
+ p->transfer_syntax = *transfer_syntax;
+
+ flags = dcerpc_binding_get_flags(p->binding);
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ pkt.ptype = DCERPC_PKT_ALTER;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = p->conn->call_id;
+ pkt.auth_length = 0;
+
+ if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
+ pkt.u.alter.max_xmit_frag = p->conn->srv_max_xmit_frag;
+ pkt.u.alter.max_recv_frag = p->conn->srv_max_recv_frag;
+ pkt.u.alter.assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding);
+ pkt.u.alter.num_contexts = 1;
+ pkt.u.alter.ctx_list = talloc_zero_array(state, struct dcerpc_ctx_list,
+ pkt.u.alter.num_contexts);
+ if (tevent_req_nomem(pkt.u.alter.ctx_list, req)) {
+ return tevent_req_post(req, ev);
+ }
+ pkt.u.alter.ctx_list[0].context_id = p->context_id;
+ pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
+ pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
+ pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
+ pkt.u.alter.auth_info = data_blob(NULL, 0);
+
+ /* construct the NDR form of the packet */
+ status = dcerpc_ncacn_push_auth(&blob,
+ state,
+ &pkt,
+ p->conn->security_state.tmp_auth_info.out);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we allocate a dcerpc_request so we can be in the same
+ * request queue as normal requests
+ */
+ subreq = talloc_zero(state, struct rpc_request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq->state = RPC_REQUEST_PENDING;
+ subreq->call_id = pkt.call_id;
+ subreq->async.private_data = req;
+ subreq->async.callback = dcerpc_alter_context_fail_handler;
+ subreq->p = p;
+ subreq->recv_handler = dcerpc_alter_context_recv_handler;
+ DLIST_ADD_END(p->conn->pending, subreq);
+ talloc_set_destructor(subreq, dcerpc_req_dequeue);
+
+ status = dcerpc_send_request(p->conn, &blob, true);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_add_timer(ev, subreq,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_timeout_handler, subreq);
+
+ return req;
+}
+
+static void dcerpc_alter_context_fail_handler(struct rpc_request *subreq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_alter_context_state *state =
+ tevent_req_data(req,
+ struct dcerpc_alter_context_state);
+ NTSTATUS status = subreq->status;
+
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ tevent_req_nterror(req, status);
+}
+
+static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(subreq->async.private_data,
+ struct tevent_req);
+ struct dcerpc_alter_context_state *state =
+ tevent_req_data(req,
+ struct dcerpc_alter_context_state);
+ struct dcecli_connection *conn = state->p->conn;
+ struct dcecli_security *sec = &conn->security_state;
+ NTSTATUS status;
+
+ /*
+ * Note that pkt is allocated under raw_packet->data,
+ * while raw_packet->data is a child of subreq.
+ */
+ talloc_steal(state, raw_packet->data);
+ TALLOC_FREE(subreq);
+
+ /*
+ * We trigger the callback in the next event run
+ * because the code in this file might trigger
+ * multiple request callbacks from within a single
+ * while loop.
+ *
+ * In order to avoid segfaults from within
+ * dcerpc_connection_dead() we call
+ * tevent_req_defer_callback().
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ if (pkt->ptype == DCERPC_PKT_FAULT) {
+ DEBUG(5,("dcerpc: alter_resp - rpc fault: %s\n",
+ dcerpc_errstr(state, pkt->u.fault.status)));
+ if (pkt->u.fault.status == DCERPC_FAULT_ACCESS_DENIED) {
+ state->p->last_fault_code = pkt->u.fault.status;
+ tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
+ } else if (pkt->u.fault.status == DCERPC_FAULT_SEC_PKG_ERROR) {
+ state->p->last_fault_code = pkt->u.fault.status;
+ tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
+ } else {
+ state->p->last_fault_code = pkt->u.fault.status;
+ status = dcerpc_fault_to_nt_status(pkt->u.fault.status);
+ tevent_req_nterror(req, status);
+ }
+ return;
+ }
+
+ status = dcerpc_verify_ncacn_packet_header(pkt,
+ DCERPC_PKT_ALTER_RESP,
+ pkt->u.alter_resp.auth_info.length,
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ DCERPC_PFC_FLAG_CONC_MPX |
+ DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+ if (!NT_STATUS_IS_OK(status)) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+
+ if (pkt->u.alter_resp.num_results != 1) {
+ state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+ tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+
+ if (pkt->u.alter_resp.ctx_list[0].result != 0) {
+ status = dcerpc_map_ack_reason(&pkt->u.alter_resp.ctx_list[0]);
+ DEBUG(2,("dcerpc: alter_resp failed - reason %d - %s\n",
+ pkt->u.alter_resp.ctx_list[0].reason.value,
+ nt_errstr(status)));
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ /* the alter_resp might contain a reply set of credentials */
+ if (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) {
+ status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem,
+ &pkt->u.alter_resp.auth_info,
+ sec->tmp_auth_info.in,
+ NULL, true);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS dcerpc_alter_context_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ send a dcerpc alter_context request
+*/
+_PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
+{
+ struct tevent_req *subreq;
+ struct tevent_context *ev = p->conn->event_ctx;
+ bool ok;
+
+ /* TODO: create a new event context here */
+
+ subreq = dcerpc_alter_context_send(mem_ctx, ev,
+ p, syntax, transfer_syntax);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ NTSTATUS status;
+ status = map_nt_error_from_unix_common(errno);
+ return status;
+ }
+
+ return dcerpc_alter_context_recv(subreq);
+}
+
+static void dcerpc_transport_dead(struct dcecli_connection *c, NTSTATUS status)
+{
+ if (c->transport.stream == NULL) {
+ return;
+ }
+
+ tevent_queue_stop(c->transport.write_queue);
+ TALLOC_FREE(c->transport.read_subreq);
+ TALLOC_FREE(c->transport.stream);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
+ status = NT_STATUS_END_OF_FILE;
+ }
+
+ dcerpc_recv_data(c, NULL, status);
+}
+
+
+/*
+ shutdown SMB pipe connection
+*/
+struct dcerpc_shutdown_pipe_state {
+ struct dcecli_connection *c;
+ NTSTATUS status;
+};
+
+static void dcerpc_shutdown_pipe_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_shutdown_pipe(struct dcecli_connection *c, NTSTATUS status)
+{
+ struct dcerpc_shutdown_pipe_state *state;
+ struct tevent_req *subreq;
+
+ if (c->transport.stream == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ state = talloc_zero(c, struct dcerpc_shutdown_pipe_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->c = c;
+ state->status = status;
+
+ subreq = tstream_disconnect_send(state, c->event_ctx, c->transport.stream);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, dcerpc_shutdown_pipe_done, state);
+
+ return status;
+}
+
+static void dcerpc_shutdown_pipe_done(struct tevent_req *subreq)
+{
+ struct dcerpc_shutdown_pipe_state *state =
+ tevent_req_callback_data(subreq, struct dcerpc_shutdown_pipe_state);
+ struct dcecli_connection *c = state->c;
+ NTSTATUS status = state->status;
+ int error;
+
+ /*
+ * here we ignore the return values...
+ */
+ tstream_disconnect_recv(subreq, &error);
+ TALLOC_FREE(subreq);
+
+ TALLOC_FREE(state);
+
+ dcerpc_transport_dead(c, status);
+}
+
+
+
+struct dcerpc_send_read_state {
+ struct dcecli_connection *p;
+};
+
+static int dcerpc_send_read_state_destructor(struct dcerpc_send_read_state *state)
+{
+ struct dcecli_connection *p = state->p;
+
+ p->transport.read_subreq = NULL;
+
+ return 0;
+}
+
+static void dcerpc_send_read_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_send_read(struct dcecli_connection *p)
+{
+ struct dcerpc_send_read_state *state;
+
+ if (p->transport.read_subreq != NULL) {
+ p->transport.pending_reads++;
+ return NT_STATUS_OK;
+ }
+
+ state = talloc_zero(p, struct dcerpc_send_read_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->p = p;
+
+ talloc_set_destructor(state, dcerpc_send_read_state_destructor);
+
+ p->transport.read_subreq = dcerpc_read_ncacn_packet_send(state,
+ p->event_ctx,
+ p->transport.stream);
+ if (p->transport.read_subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(p->transport.read_subreq, dcerpc_send_read_done, state);
+
+ return NT_STATUS_OK;
+}
+
+static void dcerpc_send_read_done(struct tevent_req *subreq)
+{
+ struct dcerpc_send_read_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcerpc_send_read_state);
+ struct dcecli_connection *p = state->p;
+ NTSTATUS status;
+ struct ncacn_packet *pkt;
+ DATA_BLOB blob;
+
+ status = dcerpc_read_ncacn_packet_recv(subreq, state,
+ &pkt, &blob);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+
+ /*
+ * here we steal into thet connection context,
+ * but p->transport.recv_data() will steal or free it again
+ */
+ talloc_steal(p, blob.data);
+ TALLOC_FREE(state);
+
+ if (p->transport.pending_reads > 0) {
+ p->transport.pending_reads--;
+
+ status = dcerpc_send_read(p);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+ }
+
+ dcerpc_recv_data(p, &blob, NT_STATUS_OK);
+}
+
+struct dcerpc_send_request_state {
+ struct dcecli_connection *p;
+ DATA_BLOB blob;
+ struct iovec iov;
+};
+
+static int dcerpc_send_request_state_destructor(struct dcerpc_send_request_state *state)
+{
+ struct dcecli_connection *p = state->p;
+
+ p->transport.read_subreq = NULL;
+
+ return 0;
+}
+
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq);
+static void dcerpc_send_request_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_send_request(struct dcecli_connection *p, DATA_BLOB *data,
+ bool trigger_read)
+{
+ struct dcerpc_send_request_state *state;
+ struct tevent_req *subreq;
+ bool use_trans = trigger_read;
+
+ if (p->transport.stream == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ state = talloc_zero(p, struct dcerpc_send_request_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->p = p;
+
+ state->blob = data_blob_talloc(state, data->data, data->length);
+ if (state->blob.data == NULL) {
+ TALLOC_FREE(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->iov.iov_base = (void *)state->blob.data;
+ state->iov.iov_len = state->blob.length;
+
+ if (p->transport.read_subreq != NULL) {
+ use_trans = false;
+ }
+
+ if (!tstream_is_smbXcli_np(p->transport.stream)) {
+ use_trans = false;
+ }
+
+ if (use_trans) {
+ /*
+ * we need to block reads until our write is
+ * the next in the write queue.
+ */
+ p->transport.read_subreq = tevent_queue_wait_send(state, p->event_ctx,
+ p->transport.write_queue);
+ if (p->transport.read_subreq == NULL) {
+ TALLOC_FREE(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(p->transport.read_subreq,
+ dcerpc_send_request_wait_done,
+ state);
+
+ talloc_set_destructor(state, dcerpc_send_request_state_destructor);
+
+ trigger_read = false;
+ }
+
+ subreq = tstream_writev_queue_send(state, p->event_ctx,
+ p->transport.stream,
+ p->transport.write_queue,
+ &state->iov, 1);
+ if (subreq == NULL) {
+ TALLOC_FREE(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, dcerpc_send_request_done, state);
+
+ if (trigger_read) {
+ dcerpc_send_read(p);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq)
+{
+ struct dcerpc_send_request_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcerpc_send_request_state);
+ struct dcecli_connection *p = state->p;
+ NTSTATUS status;
+ bool ok;
+
+ p->transport.read_subreq = NULL;
+ talloc_set_destructor(state, NULL);
+
+ ok = tevent_queue_wait_recv(subreq);
+ if (!ok) {
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ if (tevent_queue_length(p->transport.write_queue) <= 2) {
+ status = tstream_smbXcli_np_use_trans(p->transport.stream);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+ }
+
+ /* we free subreq after tstream_cli_np_use_trans */
+ TALLOC_FREE(subreq);
+
+ dcerpc_send_read(p);
+}
+
+static void dcerpc_send_request_done(struct tevent_req *subreq)
+{
+ struct dcerpc_send_request_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcerpc_send_request_state);
+ int ret;
+ int error;
+
+ ret = tstream_writev_queue_recv(subreq, &error);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ struct dcecli_connection *p = state->p;
+ NTSTATUS status = map_nt_error_from_unix_common(error);
+
+ TALLOC_FREE(state);
+ dcerpc_transport_dead(p, status);
+ return;
+ }
+
+ TALLOC_FREE(state);
+}
diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h
new file mode 100644
index 0000000..57124f1
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc.h
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DCERPC client side interface structures
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003-2005
+
+ 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/>.
+*/
+
+/* This is a public header file that is installed as part of Samba.
+ * If you remove any functions or change their signature, update
+ * the so version number. */
+
+#ifndef __S4_DCERPC_H__
+#define __S4_DCERPC_H__
+
+#include "../lib/util/data_blob.h"
+#include "librpc/gen_ndr/dcerpc.h"
+#include "../librpc/ndr/libndr.h"
+#include "../librpc/rpc/rpc_common.h"
+
+struct tevent_context;
+struct tevent_req;
+struct dcerpc_binding_handle;
+struct tstream_context;
+struct ndr_interface_table;
+struct resolve_context;
+
+/*
+ this defines a generic security context for signed/sealed dcerpc pipes.
+*/
+struct dcecli_connection;
+struct gensec_settings;
+struct cli_credentials;
+struct dcecli_security {
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+ uint32_t auth_context_id;
+ struct {
+ struct dcerpc_auth *out;
+ struct dcerpc_auth *in;
+ TALLOC_CTX *mem;
+ } tmp_auth_info;
+ struct gensec_security *generic_state;
+
+ /* get the session key */
+ NTSTATUS (*session_key)(struct dcecli_connection *, DATA_BLOB *);
+
+ bool verified_bitmask1;
+
+};
+
+/*
+ this holds the information that is not specific to a particular rpc context_id
+*/
+struct rpc_request;
+struct dcecli_connection {
+ uint32_t call_id;
+ uint32_t srv_max_xmit_frag;
+ uint32_t srv_max_recv_frag;
+ uint32_t flags;
+ struct dcecli_security security_state;
+ struct tevent_context *event_ctx;
+
+ struct tevent_immediate *io_trigger;
+ bool io_trigger_pending;
+
+ /** Directory in which to save ndrdump-parseable files */
+ const char *packet_log_dir;
+
+ bool dead;
+ bool free_skipped;
+
+ struct dcerpc_transport {
+ enum dcerpc_transport_t transport;
+ void *private_data;
+ bool encrypted;
+
+ struct tstream_context *stream;
+ /** to serialize write events */
+ struct tevent_queue *write_queue;
+ /** the current active read request if any */
+ struct tevent_req *read_subreq;
+ /** number of read requests other than the current active */
+ uint32_t pending_reads;
+ } transport;
+
+ const char *server_name;
+
+ /* Requests that have been sent, waiting for a reply */
+ struct rpc_request *pending;
+
+ /* Sync requests waiting to be shipped */
+ struct rpc_request *request_queue;
+
+ /* the next context_id to be assigned */
+ uint32_t next_context_id;
+
+ /* The maximum total payload of reassembled response pdus */
+ size_t max_total_response_size;
+
+ /* the negotiated bind time features */
+ uint16_t bind_time_features;
+};
+
+/*
+ this encapsulates a full dcerpc client side pipe
+*/
+struct dcerpc_pipe {
+ struct dcerpc_binding_handle *binding_handle;
+
+ uint32_t context_id;
+
+ struct GUID object;
+ struct ndr_syntax_id syntax;
+ struct ndr_syntax_id transfer_syntax;
+
+ struct dcecli_connection *conn;
+ const struct dcerpc_binding *binding;
+
+ /** the last fault code from a DCERPC fault */
+ uint32_t last_fault_code;
+
+ /** timeout for individual rpc requests, in seconds */
+ uint32_t request_timeout;
+
+ /*
+ * Set for the timeout in dcerpc_pipe_connect_b_send(), to
+ * allow the timeout not to destory the stack during a nested
+ * event loop caused by gensec_update()
+ */
+ bool inhibit_timeout_processing;
+ bool timed_out;
+
+ bool verified_pcontext;
+};
+
+/* default timeout for all rpc requests, in seconds */
+#define DCERPC_REQUEST_TIMEOUT 60
+
+struct epm_tower;
+struct epm_floor;
+
+struct smbcli_tree;
+struct smb2_tree;
+struct smbXcli_conn;
+struct smbXcli_session;
+struct smbXcli_tcon;
+struct roh_connection;
+struct tstream_tls_params;
+struct socket_address;
+
+NTSTATUS dcerpc_pipe_connect(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+const char *dcerpc_server_name(struct dcerpc_pipe *p);
+struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev);
+NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
+ struct smbcli_tree *tree,
+ const char *pipe_name);
+NTSTATUS dcerpc_pipe_open_smb2(struct dcerpc_pipe *p,
+ struct smb2_tree *tree,
+ const char *pipe_name);
+NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table);
+NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
+ DATA_BLOB *session_key);
+bool dcerpc_transport_encrypted(struct dcerpc_pipe *p);
+struct composite_context;
+NTSTATUS dcerpc_secondary_connection_recv(struct composite_context *c,
+ struct dcerpc_pipe **p2);
+
+struct composite_context* dcerpc_pipe_connect_b_send(TALLOC_CTX *parent_ctx,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+
+NTSTATUS dcerpc_pipe_connect_b_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p);
+
+NTSTATUS dcerpc_pipe_connect_b(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+
+NTSTATUS dcerpc_pipe_auth(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx);
+NTSTATUS dcerpc_init(void);
+struct composite_context *dcerpc_secondary_smb_send(struct dcecli_connection *c1,
+ struct dcecli_connection *c2,
+ const char *pipe_name);
+NTSTATUS dcerpc_secondary_smb_recv(struct composite_context *c);
+NTSTATUS dcerpc_secondary_context(struct dcerpc_pipe *p,
+ struct dcerpc_pipe **pp2,
+ const struct ndr_interface_table *table);
+NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax);
+
+NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct gensec_settings *gensec_settings,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service);
+struct composite_context* dcerpc_pipe_connect_send(TALLOC_CTX *parent_ctx,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev, struct loadparm_context *lp_ctx);
+NTSTATUS dcerpc_pipe_connect_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **pp);
+
+NTSTATUS dcerpc_epm_map_binding(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table, struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+struct composite_context* dcerpc_secondary_auth_connection_send(struct dcerpc_pipe *p,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx);
+NTSTATUS dcerpc_secondary_auth_connection_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p);
+NTSTATUS dcerpc_secondary_auth_connection(struct dcerpc_pipe *p,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p2);
+
+struct composite_context* dcerpc_secondary_connection_send(struct dcerpc_pipe *p,
+ const struct dcerpc_binding *b);
+
+#endif /* __S4_DCERPC_H__ */
diff --git a/source4/librpc/rpc/dcerpc.py b/source4/librpc/rpc/dcerpc.py
new file mode 100644
index 0000000..64dd6e3
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc.py
@@ -0,0 +1,18 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+from samba.dcerpc.base import *
diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c
new file mode 100644
index 0000000..52dffec
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_auth.c
@@ -0,0 +1,556 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic Authentication Interface
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Stefan Metzmacher 2004
+
+ 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 <tevent.h>
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "param/param.h"
+
+/*
+ return the rpc syntax and transfer syntax given the pipe uuid and version
+*/
+static NTSTATUS dcerpc_init_syntaxes(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct ndr_syntax_id *syntax,
+ struct ndr_syntax_id *transfer_syntax)
+{
+ struct GUID *object = NULL;
+
+ p->object = dcerpc_binding_get_object(p->binding);
+ if (!GUID_all_zero(&p->object)) {
+ object = &p->object;
+ }
+
+ p->binding_handle = dcerpc_pipe_binding_handle(p, object, table);
+ if (p->binding_handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ syntax->uuid = table->syntax_id.uuid;
+ syntax->if_version = table->syntax_id.if_version;
+
+ if (p->conn->flags & DCERPC_NDR64) {
+ *transfer_syntax = ndr_transfer_syntax_ndr64;
+ } else {
+ *transfer_syntax = ndr_transfer_syntax_ndr;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ Send request to do a non-authenticated dcerpc bind
+*/
+static void dcerpc_bind_auth_none_done(struct tevent_req *subreq);
+
+struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table)
+{
+ struct ndr_syntax_id syntax;
+ struct ndr_syntax_id transfer_syntax;
+
+ struct composite_context *c;
+ struct tevent_req *subreq;
+
+ c = composite_create(mem_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ c->status = dcerpc_init_syntaxes(p, table,
+ &syntax, &transfer_syntax);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(2,("Invalid uuid string in "
+ "dcerpc_bind_auth_none_send\n"));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ subreq = dcerpc_bind_send(mem_ctx, p->conn->event_ctx, p,
+ &syntax, &transfer_syntax);
+ if (composite_nomem(subreq, c)) return c;
+ tevent_req_set_callback(subreq, dcerpc_bind_auth_none_done, c);
+
+ return c;
+}
+
+static void dcerpc_bind_auth_none_done(struct tevent_req *subreq)
+{
+ struct composite_context *ctx =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+
+ ctx->status = dcerpc_bind_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(ctx)) return;
+
+ composite_done(ctx);
+}
+
+/*
+ Receive result of a non-authenticated dcerpc bind
+*/
+NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
+{
+ NTSTATUS result = composite_wait(ctx);
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+
+/*
+ Perform sync non-authenticated dcerpc bind
+*/
+_PUBLIC_ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table)
+{
+ struct composite_context *ctx;
+
+ ctx = dcerpc_bind_auth_none_send(p, p, table);
+ return dcerpc_bind_auth_none_recv(ctx);
+}
+
+
+struct bind_auth_state {
+ struct dcerpc_pipe *pipe;
+ struct ndr_syntax_id syntax;
+ struct ndr_syntax_id transfer_syntax;
+ struct dcerpc_auth out_auth_info;
+ struct dcerpc_auth in_auth_info;
+ bool more_processing; /* Is there anything more to do after the
+ * first bind itself received? */
+};
+
+static void bind_auth_next_gensec_done(struct tevent_req *subreq);
+static void bind_auth_recv_alter(struct tevent_req *subreq);
+
+static void bind_auth_next_step(struct composite_context *c)
+{
+ struct bind_auth_state *state;
+ struct dcecli_security *sec;
+ struct tevent_req *subreq;
+
+ state = talloc_get_type(c->private_data, struct bind_auth_state);
+ sec = &state->pipe->conn->security_state;
+
+ if (state->in_auth_info.auth_type != sec->auth_type) {
+ composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ if (state->in_auth_info.auth_level != sec->auth_level) {
+ composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ if (state->in_auth_info.auth_context_id != sec->auth_context_id) {
+ composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ state->out_auth_info = (struct dcerpc_auth) {
+ .auth_type = sec->auth_type,
+ .auth_level = sec->auth_level,
+ .auth_context_id = sec->auth_context_id,
+ };
+
+ /* The status value here, from GENSEC is vital to the security
+ * of the system. Even if the other end accepts, if GENSEC
+ * claims 'MORE_PROCESSING_REQUIRED' then you must keep
+ * feeding it blobs, or else the remote host/attacker might
+ * avoid mutal authentication requirements.
+ *
+ * Likewise, you must not feed GENSEC too much (after the OK),
+ * it doesn't like that either
+ */
+
+ state->pipe->inhibit_timeout_processing = true;
+ state->pipe->timed_out = false;
+
+ subreq = gensec_update_send(state,
+ state->pipe->conn->event_ctx,
+ sec->generic_state,
+ state->in_auth_info.credentials);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, bind_auth_next_gensec_done, c);
+}
+
+static void bind_auth_next_gensec_done(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct bind_auth_state *state =
+ talloc_get_type_abort(c->private_data,
+ struct bind_auth_state);
+ struct dcerpc_pipe *p = state->pipe;
+ struct dcecli_security *sec = &p->conn->security_state;
+ bool more_processing = false;
+
+ state->pipe->inhibit_timeout_processing = false;
+
+ c->status = gensec_update_recv(subreq, state,
+ &state->out_auth_info.credentials);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ more_processing = true;
+ c->status = NT_STATUS_OK;
+ }
+
+ if (!composite_is_ok(c)) return;
+
+ if (!more_processing) {
+ if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
+ gensec_want_feature(sec->generic_state,
+ GENSEC_FEATURE_SIGN_PKT_HEADER);
+ }
+ }
+
+ if (state->out_auth_info.credentials.length == 0) {
+ composite_done(c);
+ return;
+ }
+
+ state->in_auth_info = (struct dcerpc_auth) {
+ .auth_type = DCERPC_AUTH_TYPE_NONE,
+ };
+ sec->tmp_auth_info.in = &state->in_auth_info;
+ sec->tmp_auth_info.mem = state;
+ sec->tmp_auth_info.out = &state->out_auth_info;
+
+ if (!more_processing) {
+ /* NO reply expected, so just send it */
+ c->status = dcerpc_auth3(state->pipe, state);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+ return;
+ }
+
+ /* We are demanding a reply, so use a request that will get us one */
+
+ subreq = dcerpc_alter_context_send(state, state->pipe->conn->event_ctx,
+ state->pipe,
+ &state->pipe->syntax,
+ &state->pipe->transfer_syntax);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, bind_auth_recv_alter, c);
+}
+
+
+static void bind_auth_recv_alter(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct bind_auth_state *state = talloc_get_type(c->private_data,
+ struct bind_auth_state);
+ struct dcecli_security *sec = &state->pipe->conn->security_state;
+
+ ZERO_STRUCT(sec->tmp_auth_info);
+
+ c->status = dcerpc_alter_context_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ bind_auth_next_step(c);
+}
+
+
+static void bind_auth_recv_bindreply(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct bind_auth_state *state = talloc_get_type(c->private_data,
+ struct bind_auth_state);
+ struct dcecli_security *sec = &state->pipe->conn->security_state;
+
+ ZERO_STRUCT(sec->tmp_auth_info);
+
+ c->status = dcerpc_bind_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!state->more_processing) {
+ /* The first gensec_update has not requested a second run, so
+ * we're done here. */
+ composite_done(c);
+ return;
+ }
+
+ bind_auth_next_step(c);
+}
+
+
+static void dcerpc_bind_auth_gensec_done(struct tevent_req *subreq);
+
+/**
+ Bind to a DCE/RPC pipe, send async request
+ @param mem_ctx TALLOC_CTX for the allocation of the composite_context
+ @param p The dcerpc_pipe to bind (must already be connected)
+ @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
+ @param credentials The credentials of the account to connect with
+ @param auth_type Select the authentication scheme to use
+ @param auth_level Chooses between unprotected (connect), signed or sealed
+ @param service The service (used by Kerberos to select the service principal to contact)
+ @retval A composite context describing the partial state of the bind
+*/
+
+struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct gensec_settings *gensec_settings,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service)
+{
+ struct composite_context *c;
+ struct bind_auth_state *state;
+ struct dcecli_security *sec;
+ struct tevent_req *subreq;
+ const char *target_principal = NULL;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct bind_auth_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->pipe = p;
+
+ c->status = dcerpc_init_syntaxes(p, table,
+ &state->syntax,
+ &state->transfer_syntax);
+ if (!composite_is_ok(c)) return c;
+
+ sec = &p->conn->security_state;
+
+ c->status = gensec_client_start(p, &sec->generic_state,
+ gensec_settings);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to start GENSEC client mode: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ c->status = gensec_set_credentials(sec->generic_state, credentials);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to set GENSEC client credentials: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ c->status = gensec_set_target_hostname(sec->generic_state,
+ dcerpc_server_name(p));
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ if (service != NULL) {
+ c->status = gensec_set_target_service(sec->generic_state,
+ service);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to set GENSEC target service: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+ }
+
+ if (p->binding != NULL) {
+ target_principal = dcerpc_binding_get_string_option(p->binding,
+ "target_principal");
+ }
+ if (target_principal != NULL) {
+ c->status = gensec_set_target_principal(sec->generic_state,
+ target_principal);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to set GENSEC target principal to %s: %s\n",
+ target_principal, nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+ }
+
+ c->status = gensec_start_mech_by_authtype(sec->generic_state,
+ auth_type, auth_level);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_authtype(sec->generic_state, auth_type),
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ sec->auth_type = auth_type;
+ sec->auth_level = auth_level,
+ /*
+ * We use auth_context_id = 1 as some older
+ * Samba versions (<= 4.2.3) use that value hardcoded
+ * in a response.
+ */
+ sec->auth_context_id = 1;
+
+ state->out_auth_info = (struct dcerpc_auth) {
+ .auth_type = sec->auth_type,
+ .auth_level = sec->auth_level,
+ .auth_context_id = sec->auth_context_id,
+ };
+
+ /* The status value here, from GENSEC is vital to the security
+ * of the system. Even if the other end accepts, if GENSEC
+ * claims 'MORE_PROCESSING_REQUIRED' then you must keep
+ * feeding it blobs, or else the remote host/attacker might
+ * avoid mutal authentication requirements.
+ *
+ * Likewise, you must not feed GENSEC too much (after the OK),
+ * it doesn't like that either
+ */
+
+ state->pipe->inhibit_timeout_processing = true;
+ state->pipe->timed_out = false;
+
+ subreq = gensec_update_send(state,
+ p->conn->event_ctx,
+ sec->generic_state,
+ data_blob_null);
+ if (composite_nomem(subreq, c)) return c;
+ tevent_req_set_callback(subreq, dcerpc_bind_auth_gensec_done, c);
+
+ return c;
+}
+
+static void dcerpc_bind_auth_gensec_done(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct bind_auth_state *state =
+ talloc_get_type_abort(c->private_data,
+ struct bind_auth_state);
+ struct dcerpc_pipe *p = state->pipe;
+ struct dcecli_security *sec = &p->conn->security_state;
+
+ state->pipe->inhibit_timeout_processing = false;
+
+ c->status = gensec_update_recv(subreq, state,
+ &state->out_auth_info.credentials);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(c->status) &&
+ !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ state->more_processing = NT_STATUS_EQUAL(c->status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED);
+
+ if (state->out_auth_info.credentials.length == 0) {
+ composite_done(c);
+ return;
+ }
+
+ if (gensec_have_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER)) {
+ if (sec->auth_level >= DCERPC_AUTH_LEVEL_PACKET) {
+ state->pipe->conn->flags |= DCERPC_PROPOSE_HEADER_SIGNING;
+ }
+ }
+
+ state->in_auth_info = (struct dcerpc_auth) {
+ .auth_type = DCERPC_AUTH_TYPE_NONE,
+ };
+ sec->tmp_auth_info.in = &state->in_auth_info;
+ sec->tmp_auth_info.mem = state;
+ sec->tmp_auth_info.out = &state->out_auth_info;
+
+ /* The first request always is a dcerpc_bind. The subsequent ones
+ * depend on gensec results */
+ subreq = dcerpc_bind_send(state, p->conn->event_ctx, p,
+ &state->syntax, &state->transfer_syntax);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, bind_auth_recv_bindreply, c);
+
+ return;
+}
+
+
+/**
+ Bind to a DCE/RPC pipe, receive result
+ @param creq A composite context describing state of async call
+ @retval NTSTATUS code
+*/
+
+NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
+{
+ NTSTATUS result = composite_wait(creq);
+ struct bind_auth_state *state = talloc_get_type(creq->private_data,
+ struct bind_auth_state);
+
+ if (NT_STATUS_IS_OK(result)) {
+ /*
+ after a successful authenticated bind the session
+ key reverts to the generic session key
+ */
+ state->pipe->conn->security_state.session_key = dcecli_generic_session_key;
+ }
+
+ talloc_free(creq);
+ return result;
+}
+
+
+/**
+ Perform a GENSEC authenticated bind to a DCE/RPC pipe, sync
+ @param p The dcerpc_pipe to bind (must already be connected)
+ @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
+ @param credentials The credentials of the account to connect with
+ @param auth_type Select the authentication scheme to use
+ @param auth_level Chooses between unprotected (connect), signed or sealed
+ @param service The service (used by Kerberos to select the service principal to contact)
+ @retval NTSTATUS status code
+*/
+
+_PUBLIC_ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct gensec_settings *gensec_settings,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service)
+{
+ struct composite_context *creq;
+ creq = dcerpc_bind_auth_send(p, p, table, credentials, gensec_settings,
+ auth_type, auth_level, service);
+ return dcerpc_bind_auth_recv(creq);
+}
diff --git a/source4/librpc/rpc/dcerpc_connect.c b/source4/librpc/rpc/dcerpc_connect.c
new file mode 100644
index 0000000..9a7b9ae
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_connect.c
@@ -0,0 +1,1251 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc connect functions
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2007
+ Copyright (C) Rafal Szczesniak 2005
+
+ 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 "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/events/events.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/http/http.h"
+#include "lib/util/util_net.h"
+
+#undef strcasecmp
+
+struct dcerpc_pipe_connect {
+ struct dcecli_connection *conn;
+ struct dcerpc_binding *binding;
+ const struct ndr_interface_table *interface;
+ struct cli_credentials *creds;
+ struct resolve_context *resolve_ctx;
+ struct {
+ const char *dir;
+ } ncalrpc;
+ struct {
+ struct smbXcli_conn *conn;
+ struct smbXcli_session *session;
+ struct smbXcli_tcon *tcon;
+ const char *pipe_name;
+ } smb;
+};
+
+struct pipe_np_smb_state {
+ struct smb_composite_connect conn;
+ struct dcerpc_pipe_connect io;
+};
+
+
+/*
+ Stage 3 of ncacn_np_smb: Named pipe opened (or not)
+*/
+static void continue_pipe_open_smb(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of named pipe open request on smb */
+ c->status = dcerpc_pipe_open_smb_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+static void continue_smb_open(struct composite_context *c);
+static void continue_smb2_connect(struct tevent_req *subreq);
+static void continue_smbXcli_connect(struct tevent_req *subreq);
+
+/*
+ Stage 2 of ncacn_np_smb: Open a named pipe after successful smb connection
+*/
+static void continue_smb_connect(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_np_smb_state *s = talloc_get_type(c->private_data,
+ struct pipe_np_smb_state);
+ struct smbcli_tree *t;
+
+ /* receive result of smb connect request */
+ c->status = smb_composite_connect_recv(ctx, s->io.conn);
+ if (!composite_is_ok(c)) return;
+
+ t = s->conn.out.tree;
+
+ /* prepare named pipe open parameters */
+ s->io.smb.conn = t->session->transport->conn;
+ s->io.smb.session = t->session->smbXcli;
+ s->io.smb.tcon = t->smbXcli;
+ smb1cli_tcon_set_id(s->io.smb.tcon, t->tid);
+ s->io.smb.pipe_name = dcerpc_binding_get_string_option(s->io.binding,
+ "endpoint");
+
+ continue_smb_open(c);
+}
+
+static void continue_smb_open(struct composite_context *c)
+{
+ struct pipe_np_smb_state *s = talloc_get_type(c->private_data,
+ struct pipe_np_smb_state);
+ struct composite_context *open_ctx;
+
+ /* send named pipe open request */
+ open_ctx = dcerpc_pipe_open_smb_send(s->io.conn,
+ s->io.smb.conn,
+ s->io.smb.session,
+ s->io.smb.tcon,
+ DCERPC_REQUEST_TIMEOUT * 1000,
+ s->io.smb.pipe_name);
+ if (composite_nomem(open_ctx, c)) return;
+
+ composite_continue(c, open_ctx, continue_pipe_open_smb, c);
+}
+
+
+/*
+ Initiate async open of a rpc connection to a rpc pipe on SMB using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context *dcerpc_pipe_connect_ncacn_np_smb_send(TALLOC_CTX *mem_ctx, struct dcerpc_pipe_connect *io, struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_np_smb_state *s;
+ struct tevent_req *subreq = NULL;
+ struct smb_composite_connect *conn;
+ uint32_t flags;
+ const char *target_hostname = NULL;
+ const char *dest_address = NULL;
+ const char *calling_name = NULL;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_np_smb_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->io = *io;
+ conn = &s->conn;
+
+ if (smbXcli_conn_is_connected(s->io.smb.conn)) {
+ continue_smb_open(c);
+ return c;
+ }
+
+ if (s->io.creds == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ /* prepare smb connection parameters: we're connecting to IPC$ share on
+ remote rpc server */
+ target_hostname = dcerpc_binding_get_string_option(s->io.binding, "target_hostname");
+ conn->in.dest_host = dcerpc_binding_get_string_option(s->io.binding, "host");
+ conn->in.dest_ports = lpcfg_smb_ports(lp_ctx);
+ conn->in.called_name = target_hostname;
+ if (conn->in.called_name == NULL) {
+ conn->in.called_name = "*SMBSERVER";
+ }
+ conn->in.socket_options = lpcfg_socket_options(lp_ctx);
+ conn->in.service = "IPC$";
+ conn->in.service_type = NULL;
+ conn->in.workgroup = lpcfg_workgroup(lp_ctx);
+ conn->in.gensec_settings = lpcfg_gensec_settings(conn, lp_ctx);
+
+ lpcfg_smbcli_options(lp_ctx, &conn->in.options);
+ lpcfg_smbcli_session_options(lp_ctx, &conn->in.session_options);
+
+ /*
+ * provide proper credentials - user supplied, but allow a
+ * fallback to anonymous if this is an schannel connection
+ * (might be NT4 not allowing machine logins at session
+ * setup) or if asked to do so by the caller (perhaps a SAMR password change?)
+ */
+ s->conn.in.credentials = s->io.creds;
+ flags = dcerpc_binding_get_flags(s->io.binding);
+ if (flags & (DCERPC_SCHANNEL|DCERPC_ANON_FALLBACK)) {
+ conn->in.fallback_to_anonymous = true;
+ } else {
+ conn->in.fallback_to_anonymous = false;
+ }
+
+ conn->in.options.min_protocol = lpcfg_client_ipc_min_protocol(lp_ctx);
+ conn->in.options.max_protocol = lpcfg_client_ipc_max_protocol(lp_ctx);
+ if ((flags & DCERPC_SMB1) && (flags & DCERPC_SMB2)) {
+ /* auto */
+ } else if (flags & DCERPC_SMB2) {
+ if (conn->in.options.min_protocol < PROTOCOL_SMB2_02) {
+ conn->in.options.min_protocol = PROTOCOL_SMB2_02;
+ }
+ if (conn->in.options.max_protocol < PROTOCOL_SMB2_02) {
+ conn->in.options.max_protocol = PROTOCOL_LATEST;
+ }
+ } else if (flags & DCERPC_SMB1) {
+ conn->in.options.min_protocol = PROTOCOL_NT1;
+ conn->in.options.max_protocol = PROTOCOL_NT1;
+ } else {
+ /* auto */
+ }
+
+ conn->in.options.signing = lpcfg_client_ipc_signing(lp_ctx);
+
+ if (s->conn.in.credentials != NULL) {
+ calling_name = cli_credentials_get_workstation(s->conn.in.credentials);
+ }
+ if (calling_name == NULL) {
+ calling_name = "SMBCLIENT";
+ }
+
+ if (target_hostname == NULL) {
+ target_hostname = conn->in.dest_host;
+ }
+
+ if (conn->in.dest_host != NULL && is_ipaddress(conn->in.dest_host)) {
+ dest_address = conn->in.dest_host;
+ }
+
+ subreq = smb_connect_nego_send(s,
+ c->event_ctx,
+ s->io.resolve_ctx,
+ &conn->in.options,
+ conn->in.socket_options,
+ conn->in.dest_host,
+ dest_address,
+ conn->in.dest_ports,
+ target_hostname,
+ conn->in.called_name,
+ calling_name);
+ if (composite_nomem(subreq, c)) return c;
+ tevent_req_set_callback(subreq,
+ continue_smbXcli_connect,
+ c);
+
+ return c;
+}
+
+static void continue_smbXcli_connect(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct pipe_np_smb_state *s =
+ talloc_get_type_abort(c->private_data,
+ struct pipe_np_smb_state);
+ struct smb_composite_connect *conn = &s->conn;
+ struct composite_context *creq = NULL;
+ enum protocol_types protocol;
+
+ c->status = smb_connect_nego_recv(subreq, s,
+ &conn->in.existing_conn);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ protocol = smbXcli_conn_protocol(conn->in.existing_conn);
+ if (protocol >= PROTOCOL_SMB2_02) {
+ /*
+ * continue with smb2 session setup/tree connect
+ * on the established connection.
+ */
+ subreq = smb2_connect_send(s, c->event_ctx,
+ conn->in.dest_host,
+ conn->in.dest_ports,
+ conn->in.service,
+ s->io.resolve_ctx,
+ conn->in.credentials,
+ conn->in.fallback_to_anonymous,
+ &conn->in.existing_conn,
+ 0, /* previous_session_id */
+ &conn->in.options,
+ conn->in.socket_options,
+ conn->in.gensec_settings);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, continue_smb2_connect, c);
+ return;
+ }
+
+ /*
+ * continue with smb1 session setup/tree connect
+ * on the established connection.
+ */
+ creq = smb_composite_connect_send(conn, s->io.conn,
+ s->io.resolve_ctx,
+ c->event_ctx);
+ if (composite_nomem(creq, c)) return;
+
+ composite_continue(c, creq, continue_smb_connect, c);
+ return;
+}
+
+
+/*
+ Receive result of a rpc connection to a rpc pipe on SMB
+*/
+static NTSTATUS dcerpc_pipe_connect_ncacn_np_smb_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+/*
+ Stage 2 of ncacn_np_smb2: Open a named pipe after successful smb2 connection
+*/
+static void continue_smb2_connect(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct pipe_np_smb_state *s = talloc_get_type(c->private_data,
+ struct pipe_np_smb_state);
+ struct smb2_tree *t;
+
+ /* receive result of smb2 connect request */
+ c->status = smb2_connect_recv(subreq, s->io.conn, &t);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ s->io.smb.conn = t->session->transport->conn;
+ s->io.smb.session = t->session->smbXcli;
+ s->io.smb.tcon = t->smbXcli;
+ s->io.smb.pipe_name = dcerpc_binding_get_string_option(s->io.binding,
+ "endpoint");
+
+ continue_smb_open(c);
+}
+
+
+struct pipe_ip_tcp_state {
+ struct dcerpc_pipe_connect io;
+ const char *localaddr;
+ const char *host;
+ const char *target_hostname;
+ uint32_t port;
+};
+
+
+/*
+ Stage 2 of ncacn_ip_tcp: rpc pipe opened (or not)
+*/
+static void continue_pipe_open_ncacn_ip_tcp(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_ip_tcp_state *s = talloc_get_type(c->private_data,
+ struct pipe_ip_tcp_state);
+ char *localaddr = NULL;
+ char *remoteaddr = NULL;
+
+ /* receive result of named pipe open request on tcp/ip */
+ c->status = dcerpc_pipe_open_tcp_recv(ctx, s, &localaddr, &remoteaddr);
+ if (!composite_is_ok(c)) return;
+
+ c->status = dcerpc_binding_set_string_option(s->io.binding,
+ "localaddress",
+ localaddr);
+ if (!composite_is_ok(c)) return;
+
+ c->status = dcerpc_binding_set_string_option(s->io.binding,
+ "host",
+ remoteaddr);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate async open of a rpc connection to a rpc pipe on TCP/IP using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context* dcerpc_pipe_connect_ncacn_ip_tcp_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe_connect *io)
+{
+ struct composite_context *c;
+ struct pipe_ip_tcp_state *s;
+ struct composite_context *pipe_req;
+ const char *endpoint;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_ip_tcp_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store input parameters in state structure */
+ s->io = *io;
+ s->localaddr = dcerpc_binding_get_string_option(io->binding,
+ "localaddress");
+ s->host = dcerpc_binding_get_string_option(io->binding, "host");
+ s->target_hostname = dcerpc_binding_get_string_option(io->binding,
+ "target_hostname");
+ endpoint = dcerpc_binding_get_string_option(io->binding, "endpoint");
+ /* port number is a binding endpoint here */
+ if (endpoint != NULL) {
+ s->port = atoi(endpoint);
+ }
+
+ if (s->port == 0) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ /* send pipe open request on tcp/ip */
+ pipe_req = dcerpc_pipe_open_tcp_send(s->io.conn, s->localaddr, s->host, s->target_hostname,
+ s->port, io->resolve_ctx);
+ composite_continue(c, pipe_req, continue_pipe_open_ncacn_ip_tcp, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a rpc pipe on TCP/IP
+*/
+static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_http_state {
+ struct dcerpc_pipe_connect io;
+ const char *localaddr;
+ const char *target_hostname;
+ const char *rpc_server;
+ uint32_t rpc_server_port;
+ char *rpc_proxy;
+ uint32_t rpc_proxy_port;
+ char *http_proxy;
+ uint32_t http_proxy_port;
+ bool use_tls;
+ bool use_proxy;
+ enum http_auth_method http_auth;
+ struct loadparm_context *lp_ctx;
+};
+
+/*
+ Stage 2 of ncacn_http: rpc pipe opened (or not)
+ */
+static void continue_pipe_open_ncacn_http(struct tevent_req *subreq)
+{
+ struct composite_context *c = NULL;
+ struct pipe_http_state *s = NULL;
+ struct tstream_context *stream = NULL;
+ struct tevent_queue *queue = NULL;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct pipe_http_state);
+
+ /* receive result of RoH connect request */
+ c->status = dcerpc_pipe_open_roh_recv(subreq, s->io.conn,
+ &stream, &queue);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ s->io.conn->transport.transport = NCACN_HTTP;
+ s->io.conn->transport.stream = stream;
+ s->io.conn->transport.write_queue = queue;
+ s->io.conn->transport.pending_reads = 0;
+ s->io.conn->server_name = strupper_talloc(s->io.conn,
+ s->target_hostname);
+
+ composite_done(c);
+}
+
+/*
+ Initiate async open of a rpc connection to a rpc pipe using HTTP transport,
+ and using the binding structure to determine the endpoint and options
+*/
+static struct composite_context* dcerpc_pipe_connect_ncacn_http_send(
+ TALLOC_CTX *mem_ctx, struct dcerpc_pipe_connect *io,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_http_state *s;
+ struct tevent_req *subreq;
+ const char *endpoint;
+ const char *use_proxy;
+ char *proxy;
+ char *port;
+ const char *opt;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_http_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store input parameters in state structure */
+ s->lp_ctx = lp_ctx;
+ s->io = *io;
+ s->localaddr = dcerpc_binding_get_string_option(io->binding,
+ "localaddress");
+ /* RPC server and port (the endpoint) */
+ s->rpc_server = dcerpc_binding_get_string_option(io->binding, "host");
+ s->target_hostname = dcerpc_binding_get_string_option(io->binding,
+ "target_hostname");
+ endpoint = dcerpc_binding_get_string_option(io->binding, "endpoint");
+ if (endpoint == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+ s->rpc_server_port = atoi(endpoint);
+ if (s->rpc_server_port == 0) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ /* Use TLS */
+ opt = dcerpc_binding_get_string_option(io->binding, "HttpUseTls");
+ if (opt) {
+ if (strcasecmp(opt, "true") == 0) {
+ s->use_tls = true;
+ } else if (strcasecmp(opt, "false") == 0) {
+ s->use_tls = false;
+ } else {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+ } else {
+ s->use_tls = true;
+ }
+
+ /* RPC Proxy */
+ proxy = port = talloc_strdup(s, dcerpc_binding_get_string_option(
+ io->binding, "RpcProxy"));
+ s->rpc_proxy = strsep(&port, ":");
+ if (proxy && port) {
+ s->rpc_proxy_port = atoi(port);
+ } else {
+ s->rpc_proxy_port = s->use_tls ? 443 : 80;
+ }
+ if (s->rpc_proxy == NULL) {
+ s->rpc_proxy = talloc_strdup(s, s->rpc_server);
+ if (composite_nomem(s->rpc_proxy, c)) return c;
+ }
+
+ /* HTTP Proxy */
+ proxy = port = talloc_strdup(s, dcerpc_binding_get_string_option(
+ io->binding, "HttpProxy"));
+ s->http_proxy = strsep(&port, ":");
+ if (proxy && port) {
+ s->http_proxy_port = atoi(port);
+ } else {
+ s->http_proxy_port = s->use_tls ? 443 : 80;
+ }
+
+ /* Use local proxy */
+ use_proxy = dcerpc_binding_get_string_option(io->binding,
+ "HttpConnectOption");
+ if (use_proxy && strcasecmp(use_proxy, "UseHttpProxy")) {
+ s->use_proxy = true;
+ }
+
+ /* If use local proxy set, the http proxy should be provided */
+ if (s->use_proxy && !s->http_proxy) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ /* Check which HTTP authentication method to use */
+ opt = dcerpc_binding_get_string_option(io->binding, "HttpAuthOption");
+ if (opt) {
+ if (strcasecmp(opt, "basic") == 0) {
+ s->http_auth = HTTP_AUTH_BASIC;
+ } else if (strcasecmp(opt, "ntlm") == 0) {
+ s->http_auth = HTTP_AUTH_NTLM;
+ } else if (strcasecmp(opt, "negotiate") == 0) {
+ s->http_auth = HTTP_AUTH_NEGOTIATE;
+ } else {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+ } else {
+ s->http_auth = HTTP_AUTH_NTLM;
+ }
+
+ subreq = dcerpc_pipe_open_roh_send(s->io.conn, s->localaddr,
+ s->rpc_server, s->rpc_server_port,
+ s->rpc_proxy, s->rpc_proxy_port,
+ s->http_proxy, s->http_proxy_port,
+ s->use_tls, s->use_proxy,
+ s->io.creds, io->resolve_ctx,
+ s->lp_ctx, s->http_auth);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_pipe_open_ncacn_http, c);
+ return c;
+}
+
+static NTSTATUS dcerpc_pipe_connect_ncacn_http_recv(struct composite_context *c)
+{
+ return composite_wait_free(c);
+}
+
+
+struct pipe_unix_state {
+ struct dcerpc_pipe_connect io;
+ const char *path;
+};
+
+
+/*
+ Stage 2 of ncacn_unix: rpc pipe opened (or not)
+*/
+static void continue_pipe_open_ncacn_unix_stream(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of pipe open request on unix socket */
+ c->status = dcerpc_pipe_open_unix_stream_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate async open of a rpc connection to a rpc pipe on unix socket using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context* dcerpc_pipe_connect_ncacn_unix_stream_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe_connect *io)
+{
+ struct composite_context *c;
+ struct pipe_unix_state *s;
+ struct composite_context *pipe_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_unix_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* prepare pipe open parameters and store them in state structure
+ also, verify whether biding endpoint is not null */
+ s->io = *io;
+
+ s->path = dcerpc_binding_get_string_option(io->binding, "endpoint");
+ if (s->path == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ /* send pipe open request on unix socket */
+ pipe_req = dcerpc_pipe_open_unix_stream_send(s->io.conn, s->path);
+ composite_continue(c, pipe_req, continue_pipe_open_ncacn_unix_stream, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a pipe on unix socket
+*/
+static NTSTATUS dcerpc_pipe_connect_ncacn_unix_stream_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_ncalrpc_state {
+ struct dcerpc_pipe_connect io;
+};
+
+static NTSTATUS dcerpc_pipe_connect_ncalrpc_recv(struct composite_context *c);
+
+/*
+ Stage 2 of ncalrpc: rpc pipe opened (or not)
+*/
+static void continue_pipe_open_ncalrpc(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of pipe open request on ncalrpc */
+ c->status = dcerpc_pipe_connect_ncalrpc_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate async open of a rpc connection request on NCALRPC using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context* dcerpc_pipe_connect_ncalrpc_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe_connect *io)
+{
+ struct composite_context *c;
+ struct pipe_ncalrpc_state *s;
+ struct composite_context *pipe_req;
+ const char *endpoint;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_ncalrpc_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store input parameters in state structure */
+ s->io = *io;
+
+ endpoint = dcerpc_binding_get_string_option(io->binding, "endpoint");
+ if (endpoint == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ /* send pipe open request */
+ pipe_req = dcerpc_pipe_open_pipe_send(s->io.conn,
+ s->io.ncalrpc.dir,
+ endpoint);
+ composite_continue(c, pipe_req, continue_pipe_open_ncalrpc, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a rpc pipe on NCALRPC
+*/
+static NTSTATUS dcerpc_pipe_connect_ncalrpc_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_connect_state {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct cli_credentials *credentials;
+ struct loadparm_context *lp_ctx;
+};
+
+
+static void continue_map_binding(struct composite_context *ctx);
+static void continue_connect(struct composite_context *c, struct pipe_connect_state *s);
+static void continue_pipe_connect_ncacn_np_smb(struct composite_context *ctx);
+static void continue_pipe_connect_ncacn_ip_tcp(struct composite_context *ctx);
+static void continue_pipe_connect_ncacn_http(struct composite_context *ctx);
+static void continue_pipe_connect_ncacn_unix(struct composite_context *ctx);
+static void continue_pipe_connect_ncalrpc(struct composite_context *ctx);
+static void continue_pipe_connect(struct composite_context *c, struct pipe_connect_state *s);
+static void continue_pipe_auth(struct composite_context *ctx);
+
+
+/*
+ Stage 2 of pipe_connect_b: Receive result of endpoint mapping
+*/
+static void continue_map_binding(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+ const char *endpoint;
+
+ c->status = dcerpc_epm_map_binding_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ endpoint = dcerpc_binding_get_string_option(s->binding, "endpoint");
+ DEBUG(4,("Mapped to DCERPC endpoint %s\n", endpoint));
+
+ continue_connect(c, s);
+}
+
+
+/*
+ Stage 2 of pipe_connect_b: Continue connection after endpoint is known
+*/
+static void continue_connect(struct composite_context *c, struct pipe_connect_state *s)
+{
+ struct dcerpc_pipe_connect pc;
+
+ /* potential exits to another stage by sending an async request */
+ struct composite_context *ncacn_np_smb_req;
+ struct composite_context *ncacn_ip_tcp_req;
+ struct composite_context *ncacn_http_req;
+ struct composite_context *ncacn_unix_req;
+ struct composite_context *ncalrpc_req;
+ enum dcerpc_transport_t transport;
+
+ /* dcerpc pipe connect input parameters */
+ ZERO_STRUCT(pc);
+ pc.conn = s->pipe->conn;
+ pc.binding = s->binding;
+ pc.interface = s->table;
+ pc.creds = s->credentials;
+ pc.resolve_ctx = lpcfg_resolve_context(s->lp_ctx);
+
+ transport = dcerpc_binding_get_transport(s->binding);
+
+ /* connect dcerpc pipe depending on required transport */
+ switch (transport) {
+ case NCACN_NP:
+ /*
+ * SMB1/2/3...
+ */
+ ncacn_np_smb_req = dcerpc_pipe_connect_ncacn_np_smb_send(c, &pc, s->lp_ctx);
+ composite_continue(c, ncacn_np_smb_req, continue_pipe_connect_ncacn_np_smb, c);
+ return;
+
+ case NCACN_IP_TCP:
+ ncacn_ip_tcp_req = dcerpc_pipe_connect_ncacn_ip_tcp_send(c, &pc);
+ composite_continue(c, ncacn_ip_tcp_req, continue_pipe_connect_ncacn_ip_tcp, c);
+ return;
+
+ case NCACN_HTTP:
+ ncacn_http_req = dcerpc_pipe_connect_ncacn_http_send(c, &pc, s->lp_ctx);
+ composite_continue(c, ncacn_http_req, continue_pipe_connect_ncacn_http, c);
+ return;
+
+ case NCACN_UNIX_STREAM:
+ ncacn_unix_req = dcerpc_pipe_connect_ncacn_unix_stream_send(c, &pc);
+ composite_continue(c, ncacn_unix_req, continue_pipe_connect_ncacn_unix, c);
+ return;
+
+ case NCALRPC:
+ pc.ncalrpc.dir = lpcfg_ncalrpc_dir(s->lp_ctx);
+ c->status = dcerpc_binding_set_string_option(s->binding, "ncalrpc_dir",
+ pc.ncalrpc.dir);
+ if (!composite_is_ok(c)) return;
+ ncalrpc_req = dcerpc_pipe_connect_ncalrpc_send(c, &pc);
+ composite_continue(c, ncalrpc_req, continue_pipe_connect_ncalrpc, c);
+ return;
+
+ default:
+ /* looks like a transport we don't support now */
+ composite_error(c, NT_STATUS_NOT_SUPPORTED);
+ }
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on
+ named pipe on smb
+*/
+static void continue_pipe_connect_ncacn_np_smb(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_np_smb_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on tcp/ip
+*/
+static void continue_pipe_connect_ncacn_ip_tcp(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_ip_tcp_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on http
+*/
+static void continue_pipe_connect_ncacn_http(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_http_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on unix socket
+*/
+static void continue_pipe_connect_ncacn_unix(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_unix_stream_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on local rpc
+*/
+static void continue_pipe_connect_ncalrpc(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncalrpc_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 4 of pipe_connect_b: Start an authentication on connected dcerpc pipe
+ depending on credentials and binding flags passed.
+*/
+static void continue_pipe_connect(struct composite_context *c, struct pipe_connect_state *s)
+{
+ struct composite_context *auth_bind_req;
+
+ s->pipe->binding = dcerpc_binding_dup(s->pipe, s->binding);
+ if (composite_nomem(s->pipe->binding, c)) {
+ return;
+ }
+
+ auth_bind_req = dcerpc_pipe_auth_send(s->pipe, s->binding, s->table,
+ s->credentials, s->lp_ctx);
+ composite_continue(c, auth_bind_req, continue_pipe_auth, c);
+}
+
+
+/*
+ Stage 5 of pipe_connect_b: Receive result of pipe authentication request
+ and say if all went ok
+*/
+static void continue_pipe_auth(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data, struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_auth_recv(ctx, s, &s->pipe);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ handle timeouts of a dcerpc connect
+*/
+static void dcerpc_connect_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct composite_context *c = talloc_get_type_abort(private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type_abort(c->private_data, struct pipe_connect_state);
+ if (!s->pipe->inhibit_timeout_processing) {
+ composite_error(c, NT_STATUS_IO_TIMEOUT);
+ } else {
+ s->pipe->timed_out = true;
+ }
+}
+
+/*
+ start a request to open a rpc connection to a rpc pipe, using
+ specified binding structure to determine the endpoint and options
+*/
+_PUBLIC_ struct composite_context* dcerpc_pipe_connect_b_send(TALLOC_CTX *parent_ctx,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_connect_state *s;
+ enum dcerpc_transport_t transport;
+ const char *endpoint = NULL;
+ struct cli_credentials *epm_creds = NULL;
+
+ /* composite context allocation and setup */
+ c = composite_create(parent_ctx, ev);
+ if (c == NULL) {
+ return NULL;
+ }
+
+ s = talloc_zero(c, struct pipe_connect_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* initialise dcerpc pipe structure */
+ s->pipe = dcerpc_pipe_init(c, ev);
+ if (composite_nomem(s->pipe, c)) return c;
+
+ if (DEBUGLEVEL >= 10)
+ s->pipe->conn->packet_log_dir = lpcfg_lock_directory(lp_ctx);
+
+ /* store parameters in state structure */
+ s->binding = dcerpc_binding_dup(s, binding);
+ if (composite_nomem(s->binding, c)) return c;
+ s->table = table;
+ s->credentials = credentials;
+ s->lp_ctx = lp_ctx;
+
+ s->pipe->timed_out = false;
+ s->pipe->inhibit_timeout_processing = false;
+
+ tevent_add_timer(c->event_ctx, c,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_connect_timeout_handler, c);
+
+ transport = dcerpc_binding_get_transport(s->binding);
+
+ switch (transport) {
+ case NCACN_NP:
+ case NCACN_IP_TCP:
+ case NCALRPC:
+ endpoint = dcerpc_binding_get_string_option(s->binding, "endpoint");
+
+ /* anonymous credentials for rpc connection used to get endpoint mapping */
+ epm_creds = cli_credentials_init_anon(s);
+ if (composite_nomem(epm_creds, c)) return c;
+
+ break;
+ case NCACN_HTTP:
+ endpoint = dcerpc_binding_get_string_option(s->binding, "endpoint");
+ epm_creds = credentials;
+ break;
+ default:
+ DBG_INFO("Unknown transport; continuing with anon, no endpoint.\n");
+ epm_creds = cli_credentials_init_anon(s);
+ if (composite_nomem(epm_creds, c)){
+ return c;
+ }
+ break;
+ }
+
+ if (endpoint == NULL) {
+ struct composite_context *binding_req;
+
+ binding_req = dcerpc_epm_map_binding_send(c, s->binding, s->table,
+ epm_creds,
+ s->pipe->conn->event_ctx,
+ s->lp_ctx);
+ composite_continue(c, binding_req, continue_map_binding, c);
+ return c;
+ }
+
+ continue_connect(c, s);
+ return c;
+}
+
+
+/*
+ receive result of a request to open a rpc connection to a rpc pipe
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_connect_b_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p)
+{
+ NTSTATUS status;
+ struct pipe_connect_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct pipe_connect_state);
+ talloc_steal(mem_ctx, s->pipe);
+ *p = s->pipe;
+ }
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ open a rpc connection to a rpc pipe, using the specified
+ binding structure to determine the endpoint and options - sync version
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_connect_b(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+
+ c = dcerpc_pipe_connect_b_send(parent_ctx, binding, table,
+ credentials, ev, lp_ctx);
+ return dcerpc_pipe_connect_b_recv(c, parent_ctx, pp);
+}
+
+
+struct pipe_conn_state {
+ struct dcerpc_pipe *pipe;
+};
+
+
+static void continue_pipe_connect_b(struct composite_context *ctx);
+
+
+/*
+ Initiate rpc connection to a rpc pipe, using the specified string
+ binding to determine the endpoint and options.
+ The string is to be parsed to a binding structure first.
+*/
+_PUBLIC_ struct composite_context* dcerpc_pipe_connect_send(TALLOC_CTX *parent_ctx,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev, struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_conn_state *s;
+ struct dcerpc_binding *b;
+ struct composite_context *pipe_conn_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(parent_ctx, ev);
+ if (c == NULL) {
+ return NULL;
+ }
+
+ s = talloc_zero(c, struct pipe_conn_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* parse binding string to the structure */
+ c->status = dcerpc_parse_binding(c, binding, &b);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(0, ("Failed to parse dcerpc binding '%s'\n", binding));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ DEBUG(3, ("Using binding %s\n", dcerpc_binding_string(c, b)));
+
+ /*
+ start connecting to a rpc pipe after binding structure
+ is established
+ */
+ pipe_conn_req = dcerpc_pipe_connect_b_send(c, b, table,
+ credentials, ev, lp_ctx);
+ composite_continue(c, pipe_conn_req, continue_pipe_connect_b, c);
+ return c;
+}
+
+
+/*
+ Stage 2 of pipe_connect: Receive result of actual pipe connect request
+ and say if we're done ok
+*/
+static void continue_pipe_connect_b(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_conn_state *s = talloc_get_type(c->private_data,
+ struct pipe_conn_state);
+
+ c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->pipe);
+ talloc_steal(s, s->pipe);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Receive result of pipe connect (using binding string) request
+ and return connected pipe structure.
+*/
+NTSTATUS dcerpc_pipe_connect_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **pp)
+{
+ NTSTATUS status;
+ struct pipe_conn_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct pipe_conn_state);
+ *pp = talloc_steal(mem_ctx, s->pipe);
+ }
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Open a rpc connection to a rpc pipe, using the specified string
+ binding to determine the endpoint and options - sync version
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_connect(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ c = dcerpc_pipe_connect_send(parent_ctx, binding,
+ table, credentials, ev, lp_ctx);
+ return dcerpc_pipe_connect_recv(c, parent_ctx, pp);
+}
+
diff --git a/source4/librpc/rpc/dcerpc_roh.c b/source4/librpc/rpc/dcerpc_roh.c
new file mode 100644
index 0000000..3aa7551
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_roh.c
@@ -0,0 +1,914 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ [MS-RPCH] - RPC over HTTP client
+
+ Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+
+ 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 "lib/events/events.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/tls/tls.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "tsocket/tsocket.h"
+#include "tsocket/tsocket_internal.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_roh.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "lib/param/param.h"
+#include "libcli/http/http.h"
+#include "lib/util/util_net.h"
+
+static ssize_t tstream_roh_pending_bytes(struct tstream_context *stream);
+static struct tevent_req * tstream_roh_readv_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ struct iovec *vector,
+ size_t count);
+static int tstream_roh_readv_recv(struct tevent_req *req, int *perrno);
+static struct tevent_req * tstream_roh_writev_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ const struct iovec *vector,
+ size_t count);
+static int tstream_roh_writev_recv(struct tevent_req *req, int *perrno);
+static struct tevent_req * tstream_roh_disconnect_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream);
+static int tstream_roh_disconnect_recv(struct tevent_req *req, int *perrno);
+
+static const struct tstream_context_ops tstream_roh_ops = {
+ .name = "roh",
+ .pending_bytes = tstream_roh_pending_bytes,
+ .readv_send = tstream_roh_readv_send,
+ .readv_recv = tstream_roh_readv_recv,
+ .writev_send = tstream_roh_writev_send,
+ .writev_recv = tstream_roh_writev_recv,
+ .disconnect_send = tstream_roh_disconnect_send,
+ .disconnect_recv = tstream_roh_disconnect_recv,
+};
+
+struct tstream_roh_context {
+ struct roh_connection *roh_conn;
+};
+
+struct roh_open_connection_state {
+ struct tevent_req *req;
+ struct tevent_context *event_ctx;
+ struct cli_credentials *credentials;
+ struct resolve_context *resolve_ctx;
+ const char **rpcproxy_addresses;
+ unsigned int rpcproxy_address_index;
+
+ struct dcecli_connection *conn;
+ bool tls;
+
+ const char *rpc_proxy;
+ unsigned int rpc_proxy_port;
+ const char *rpc_server;
+ unsigned int rpc_server_port;
+ const char *target_hostname;
+
+ struct roh_connection *roh;
+ struct tstream_tls_params *tls_params;
+ struct loadparm_context *lp_ctx;
+ uint8_t http_auth;
+};
+
+NTSTATUS dcerpc_pipe_open_roh_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream,
+ struct tevent_queue **queue)
+{
+ struct roh_open_connection_state *state;
+ struct tstream_roh_context *roh_stream_ctx;
+ NTSTATUS status;
+
+ state = tevent_req_data(req, struct roh_open_connection_state);
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *stream = tstream_context_create(mem_ctx, &tstream_roh_ops,
+ &roh_stream_ctx,
+ struct tstream_roh_context,
+ __location__);
+ if (!stream) {
+ tevent_req_received(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(roh_stream_ctx);
+
+ roh_stream_ctx->roh_conn = talloc_move(mem_ctx, &state->roh);
+ *queue = http_conn_send_queue(
+ roh_stream_ctx->roh_conn->default_channel_in->http_conn);
+
+ tevent_req_received(req);
+
+ return NT_STATUS_OK;
+}
+
+struct roh_connect_channel_state {
+ struct roh_channel *channel;
+};
+
+static void roh_connect_channel_done(struct tevent_req *subreq);
+static struct tevent_req *roh_connect_channel_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *rpcproxy_ip_address,
+ unsigned int rpcproxy_port,
+ struct cli_credentials *credentials,
+ bool tls,
+ struct tstream_tls_params *tls_params)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct roh_connect_channel_state *state = NULL;
+
+ DBG_DEBUG("Connecting ROH channel socket, RPC proxy is "
+ "%s:%d (TLS: %s)\n", rpcproxy_ip_address, rpcproxy_port,
+ (tls ? "true" : "false"));
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct roh_connect_channel_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (!is_ipaddress(rpcproxy_ip_address)) {
+ DBG_ERR("Invalid host (%s), needs to be an IP address\n",
+ rpcproxy_ip_address);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /* Initialize channel structure */
+ state->channel = talloc_zero(state, struct roh_channel);
+ if (tevent_req_nomem(state->channel, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->channel->channel_cookie = GUID_random();
+
+ subreq = http_connect_send(state,
+ ev,
+ rpcproxy_ip_address,
+ rpcproxy_port,
+ credentials,
+ tls ? tls_params : NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_connect_channel_done, req);
+
+ return req;
+}
+
+static void roh_connect_channel_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = NULL;
+ struct roh_connect_channel_state *state = NULL;
+ NTSTATUS status;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_connect_channel_state);
+
+ ret = http_connect_recv(subreq,
+ state->channel,
+ &state->channel->http_conn);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ status = map_nt_error_from_unix_common(ret);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ DBG_DEBUG("HTTP connected\n");
+ tevent_req_done(req);
+}
+
+static NTSTATUS roh_connect_channel_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct roh_channel **channel)
+{
+ struct roh_connect_channel_state *state = tevent_req_data(
+ req, struct roh_connect_channel_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *channel = talloc_move(mem_ctx, &state->channel);
+ tevent_req_received(req);
+
+ return NT_STATUS_OK;
+}
+
+static void roh_continue_resolve_name(struct composite_context *ctx);
+
+/**
+ * Send rpc pipe open request to given host:port using http transport
+ */
+struct tevent_req *dcerpc_pipe_open_roh_send(struct dcecli_connection *conn,
+ const char *localaddr,
+ const char *rpc_server,
+ uint32_t rpc_server_port,
+ const char *rpc_proxy,
+ uint32_t rpc_proxy_port,
+ const char *http_proxy,
+ uint32_t http_proxy_port,
+ bool use_tls,
+ bool use_proxy,
+ struct cli_credentials *credentials,
+ struct resolve_context *resolve_ctx,
+ struct loadparm_context *lp_ctx,
+ uint8_t http_auth)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct composite_context *ctx;
+ struct roh_open_connection_state *state;
+ struct nbt_name name;
+
+ req = tevent_req_create(conn, &state, struct roh_open_connection_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Set state fields */
+ state->req = req;
+ state->event_ctx = conn->event_ctx;
+ state->lp_ctx = lp_ctx,
+ state->credentials = credentials;
+ state->conn = conn;
+ state->tls = use_tls;
+
+ /* Initialize connection structure (3.2.1.3) */
+ /* TODO Initialize virtual connection cookie table */
+ state->rpc_server = talloc_strdup(state, rpc_server);
+ state->rpc_server_port = rpc_server_port;
+ state->rpc_proxy = talloc_strdup(state, rpc_proxy);
+ state->rpc_proxy_port = rpc_proxy_port;
+ state->http_auth = http_auth;
+
+ state->roh = talloc_zero(state, struct roh_connection);
+ state->roh->protocol_version = ROH_V2;
+ state->roh->connection_state = ROH_STATE_OPEN_START;
+ state->roh->connection_cookie = GUID_random();
+ state->roh->association_group_id_cookie = GUID_random();
+
+ /* Additional initialization steps (3.2.2.3) */
+ state->roh->proxy_use = use_proxy;
+ state->roh->current_keep_alive_time = 0;
+ state->roh->current_keep_alive_interval = 0;
+
+ /* Initialize TLS */
+ if (use_tls) {
+ char *ca_file = lpcfg_tls_cafile(state, lp_ctx);
+ char *crl_file = lpcfg_tls_crlfile(state, lp_ctx);
+ const char *tls_priority = lpcfg_tls_priority(lp_ctx);
+ enum tls_verify_peer_state verify_peer =
+ lpcfg_tls_verify_peer(lp_ctx);
+
+ status = tstream_tls_params_client(state->roh,
+ ca_file, crl_file,
+ tls_priority,
+ verify_peer,
+ state->rpc_proxy,
+ &state->tls_params);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: Failed tstream_tls_params_client - %s\n",
+ __func__, nt_errstr(status)));
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, conn->event_ctx);
+ }
+ }
+
+ /* Resolve RPC proxy server name */
+ make_nbt_name_server(&name, state->rpc_proxy);
+ ctx = resolve_name_send(resolve_ctx, state, &name, state->event_ctx);
+ if (tevent_req_nomem(ctx, req)) {
+ return tevent_req_post(req, state->event_ctx);
+ }
+ ctx->async.fn = roh_continue_resolve_name;
+ ctx->async.private_data = state;
+
+ return req;
+}
+
+static void roh_connect_channel_in_done(struct tevent_req *subreq);
+static void roh_continue_resolve_name(struct composite_context *ctx)
+{
+ NTSTATUS status;
+ struct roh_open_connection_state *state;
+ struct tevent_req *subreq;
+
+ state = talloc_get_type_abort(ctx->async.private_data,
+ struct roh_open_connection_state);
+ status = resolve_name_multiple_recv(ctx, state,
+ &state->rpcproxy_addresses);
+ if (tevent_req_nterror(state->req, status)) {
+ DEBUG(2, ("%s: No server found: %s\n", __func__,
+ nt_errstr(status)));
+ return;
+ }
+
+ state->rpcproxy_address_index = 0;
+ if (state->rpcproxy_addresses[state->rpcproxy_address_index] == NULL) {
+ DEBUG(2, ("%s: No server found\n", __func__));
+ tevent_req_nterror(state->req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * TODO Determine proxy use
+ * If state->roh->proxy_use == true, the client has requested to
+ * always use local proxy. Otherwise, run the proxy use discovery
+ */
+ state->roh->connection_state = ROH_STATE_OPEN_START;
+ subreq = roh_connect_channel_send(state,
+ state->event_ctx,
+ state->rpcproxy_addresses[state->rpcproxy_address_index],
+ state->rpc_proxy_port,
+ state->credentials,
+ state->tls,
+ state->tls_params);
+ if (tevent_req_nomem(subreq, state->req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_connect_channel_in_done, state->req);
+}
+
+static void roh_connect_channel_out_done(struct tevent_req *);
+static void roh_connect_channel_in_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_connect_channel_recv(subreq, state->roh,
+ &state->roh->default_channel_in);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = roh_connect_channel_send(state,
+ state->event_ctx,
+ state->rpcproxy_addresses[state->rpcproxy_address_index],
+ state->rpc_proxy_port,
+ state->credentials,
+ state->tls,
+ state->tls_params);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_connect_channel_out_done, req);
+}
+
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *);
+static void roh_connect_channel_out_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_connect_channel_recv(subreq, state->roh,
+ &state->roh->default_channel_out);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = roh_send_RPC_DATA_IN_send(state, state->lp_ctx,
+ state->event_ctx,
+ state->credentials,
+ state->roh,
+ state->rpc_server,
+ state->rpc_server_port,
+ state->rpc_proxy,
+ state->http_auth);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_send_RPC_DATA_IN_done, req);
+}
+
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *);
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_send_RPC_DATA_IN_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = roh_send_RPC_DATA_OUT_send(state,
+ state->lp_ctx,
+ state->event_ctx,
+ state->credentials,
+ state->roh,
+ state->rpc_server,
+ state->rpc_server_port,
+ state->rpc_proxy,
+ state->http_auth);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_send_RPC_DATA_OUT_done, req);
+}
+
+static void roh_send_CONN_A1_done(struct tevent_req *);
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_send_RPC_DATA_OUT_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = roh_send_CONN_A1_send(state, state->event_ctx, state->roh);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_send_CONN_A1_done, req);
+}
+
+static void roh_send_CONN_B1_done(struct tevent_req *);
+static void roh_send_CONN_A1_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_send_CONN_A1_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = roh_send_CONN_B1_send(state, state->event_ctx, state->roh);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_send_CONN_B1_done, req);
+}
+
+static void roh_recv_out_channel_response_done(struct tevent_req *);
+static void roh_send_CONN_B1_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_send_CONN_B1_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->roh->connection_state = ROH_STATE_OUT_CHANNEL_WAIT;
+ subreq = roh_recv_out_channel_response_send(state, state->event_ctx,
+ state->roh);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_recv_out_channel_response_done, req);
+}
+
+static void roh_recv_CONN_A3_done(struct tevent_req *);
+static void roh_recv_out_channel_response_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ char *response;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_recv_out_channel_response_recv(subreq, state, &response);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->roh->connection_state = ROH_STATE_WAIT_A3W;
+ subreq = roh_recv_CONN_A3_send(state, state->event_ctx, state->roh);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_recv_CONN_A3_done, req);
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *);
+static void roh_recv_CONN_A3_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_recv_CONN_A3_recv(subreq, &state->roh->default_channel_out->connection_timeout);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->roh->connection_state = ROH_STATE_WAIT_C2;
+ subreq = roh_recv_CONN_C2_send(state, state->event_ctx, state->roh);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, roh_recv_CONN_C2_done, req);
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_open_connection_state *state;
+ unsigned int version;
+ unsigned int recv;
+ unsigned int timeout;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_open_connection_state);
+
+ status = roh_recv_CONN_C2_recv(subreq, &version, &recv, &timeout);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->roh->connection_state = ROH_STATE_OPENED;
+
+ tevent_req_done(req);
+}
+
+static ssize_t tstream_roh_pending_bytes(struct tstream_context *stream)
+{
+ struct tstream_roh_context *ctx = NULL;
+ struct tstream_context *tstream = NULL;
+
+ ctx = tstream_context_data(stream, struct tstream_roh_context);
+ if (!ctx->roh_conn) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ tstream = http_conn_tstream(
+ ctx->roh_conn->default_channel_out->http_conn);
+ if (tstream == NULL) {
+ errno = ENOTCONN;
+ return -1;
+ }
+ return tstream_pending_bytes(tstream);
+}
+
+struct tstream_roh_readv_state {
+ struct roh_connection *roh_conn;
+ int ret;
+};
+
+static void tstream_roh_readv_handler(struct tevent_req *subreq);
+static struct tevent_req * tstream_roh_readv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ struct iovec *vector,
+ size_t count)
+{
+ struct tstream_roh_context *ctx = NULL;
+ struct tstream_roh_readv_state *state;
+ struct tevent_req *req, *subreq;
+ struct tstream_context *channel_stream = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct tstream_roh_readv_state);
+ if (!req) {
+ return NULL;
+ }
+
+ ctx = tstream_context_data(stream, struct tstream_roh_context);
+ if (!ctx->roh_conn) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+ if (!ctx->roh_conn->default_channel_out) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+ channel_stream = http_conn_tstream(
+ ctx->roh_conn->default_channel_out->http_conn);
+ if (channel_stream == NULL) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+
+ state->roh_conn = ctx->roh_conn;
+
+ subreq = tstream_readv_send(state, ev,
+ channel_stream,
+ vector, count);
+ if (tevent_req_nomem(subreq, req)) {
+ goto post;
+ }
+ tevent_req_set_callback(subreq, tstream_roh_readv_handler, req);
+
+ return req;
+post:
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void tstream_roh_readv_handler(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct tstream_roh_readv_state *state;
+ int ret;
+ int sys_errno;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct tstream_roh_readv_state);
+ 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_roh_readv_recv(struct tevent_req *req, int *perrno)
+{
+ struct tstream_roh_readv_state *state;
+ int ret;
+
+ state = tevent_req_data(req, struct tstream_roh_readv_state);
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret == 0) {
+ ret = state->ret;
+ }
+
+ tevent_req_received(req);
+ return ret;
+}
+
+struct tstream_roh_writev_state {
+ struct roh_connection *roh_conn;
+ int nwritten;
+};
+
+static void tstream_roh_writev_handler(struct tevent_req *subreq);
+static struct tevent_req * tstream_roh_writev_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ const struct iovec *vector,
+ size_t count)
+{
+ struct tstream_roh_context *ctx = NULL;
+ struct tstream_roh_writev_state *state = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct tstream_context *channel_stream = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_roh_writev_state);
+ if (!req) {
+ return NULL;
+ }
+
+ ctx = tstream_context_data(stream, struct tstream_roh_context);
+ if (!ctx->roh_conn) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+ if (!ctx->roh_conn->default_channel_in) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+ channel_stream = http_conn_tstream(
+ ctx->roh_conn->default_channel_in->http_conn);
+ if (channel_stream == NULL) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+
+ state->roh_conn = ctx->roh_conn;
+
+ subreq = tstream_writev_send(state, ev,
+ channel_stream,
+ vector, count);
+ if (tevent_req_nomem(subreq, req)) {
+ goto post;
+ }
+ tevent_req_set_callback(subreq, tstream_roh_writev_handler, req);
+
+ return req;
+post:
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void tstream_roh_writev_handler(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct tstream_roh_writev_state *state;
+ int nwritten;
+ int sys_errno;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct tstream_roh_writev_state);
+ nwritten = tstream_writev_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (nwritten == -1) {
+ tevent_req_error(req, sys_errno);
+ return;
+ }
+ state->nwritten = nwritten;
+ state->roh_conn->default_channel_in->sent_bytes += nwritten;
+
+ tevent_req_done(req);
+}
+
+static int tstream_roh_writev_recv(struct tevent_req *req, int *perrno)
+{
+ struct tstream_roh_writev_state *state;
+ int ret;
+
+ state = tevent_req_data(req, struct tstream_roh_writev_state);
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret == 0) {
+ ret = state->nwritten;
+ }
+
+ return ret;
+}
+
+struct tstream_roh_disconnect_state {
+ struct tstream_context *stream;
+ struct tevent_context *ev;
+};
+
+static void tstream_roh_disconnect_channel_in_handler(struct tevent_req *subreq);
+static struct tevent_req * tstream_roh_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream)
+{
+ struct tstream_roh_context *ctx = NULL;
+ struct tevent_req *req, *subreq;
+ struct tstream_roh_disconnect_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct tstream_roh_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->stream = stream;
+ state->ev = ev;
+
+ ctx = tstream_context_data(stream, struct tstream_roh_context);
+ if (!ctx->roh_conn) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+ if (!ctx->roh_conn->default_channel_in) {
+ tevent_req_error(req, ENOTCONN);
+ goto post;
+ }
+
+ subreq = http_disconnect_send(
+ state,
+ ev,
+ ctx->roh_conn->default_channel_in->http_conn);
+ if (tevent_req_nomem(subreq, req)) {
+ goto post;
+ }
+ tevent_req_set_callback(subreq, tstream_roh_disconnect_channel_in_handler, req);
+
+ return req;
+post:
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void tstream_roh_disconnect_channel_out_handler(struct tevent_req *subreq);
+
+static void tstream_roh_disconnect_channel_in_handler(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct tstream_roh_disconnect_state *state;
+ struct tstream_context *stream;
+ struct tstream_roh_context *roh_stream;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct tstream_roh_disconnect_state);
+ stream = state->stream;
+ roh_stream = tstream_context_data(stream, struct tstream_roh_context);
+
+ ret = http_disconnect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ TALLOC_FREE(roh_stream->roh_conn->default_channel_in);
+
+ subreq = http_disconnect_send(
+ state,
+ state->ev,
+ roh_stream->roh_conn->default_channel_out->http_conn);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, tstream_roh_disconnect_channel_out_handler, req);
+
+ return;
+}
+
+static void tstream_roh_disconnect_channel_out_handler(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct tstream_roh_disconnect_state *state;
+ struct tstream_context *stream;
+ struct tstream_roh_context *roh_stream;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct tstream_roh_disconnect_state);
+ stream = state->stream;
+ roh_stream = tstream_context_data(stream, struct tstream_roh_context);
+
+ ret = http_disconnect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ TALLOC_FREE(roh_stream->roh_conn->default_channel_out);
+
+ tevent_req_done(req);
+}
+
+static int tstream_roh_disconnect_recv(struct tevent_req *req, int *perrno)
+{
+ int ret;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+ tevent_req_received(req);
+
+ return ret;
+}
diff --git a/source4/librpc/rpc/dcerpc_roh.h b/source4/librpc/rpc/dcerpc_roh.h
new file mode 100644
index 0000000..f7a9c75
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_roh.h
@@ -0,0 +1,111 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ [MS-RPCH] - RPC over HTTP
+
+ Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+
+ 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 DCERPC_ROH_H_
+#define DCERPC_ROH_H_
+
+#include "librpc/gen_ndr/misc.h"
+
+struct tevent_queue;
+struct tstream_context;
+struct tstream_tls_params;
+
+struct roh_channel {
+ /*
+ * The ConnectionTimeout command specifies the desired frequency for
+ * sending keep-alive PDUs (2.2.3.5.3)
+ */
+ unsigned int connection_timeout;
+
+ unsigned int sent_bytes;
+
+ struct GUID channel_cookie;
+
+ struct http_conn *http_conn;
+};
+
+enum roh_protocol_version {
+ ROH_V1,
+ ROH_V2,
+};
+
+enum roh_connection_state {
+ ROH_STATE_OPEN_START,
+ ROH_STATE_OUT_CHANNEL_WAIT,
+ ROH_STATE_WAIT_A3W,
+ ROH_STATE_WAIT_C2,
+ ROH_STATE_OPENED,
+};
+
+/*
+ * protocol_version: A client node should be capable of using v1 and v2,
+ * try to use v2 in first place. If it fails, fallback
+ * to v1
+ * connection_state: Tracks the protocol current state
+ * connection_cookie: Identifies the virtual connection among a client, one
+ * or more inbound proxies, one or more outbound proxies,
+ * and a server
+ * association_group_id_cookie: Used by higher layer protocols to link
+ * multiple virtual connections (2.2.3.1)
+ * default_channel_in:
+ * default_channel_out:
+ * non_default_channel_in:
+ * non_default_channel_out:
+ */
+struct roh_connection {
+ enum roh_protocol_version protocol_version;
+ enum roh_connection_state connection_state;
+
+ struct GUID connection_cookie;
+ struct GUID association_group_id_cookie;
+
+ struct roh_channel *default_channel_in;
+ struct roh_channel *non_default_channel_in;
+
+ struct roh_channel *default_channel_out;
+ struct roh_channel *non_default_channel_out;
+
+ /* Client role specific fields (3.2.2.1) */
+ bool proxy_use;
+ uint32_t current_keep_alive_time;
+ uint32_t current_keep_alive_interval;
+
+ /* TODO Add timers 3.2.2.2 */
+};
+
+/* Command type constants */
+#define ROH_CMD_TYPE_RECV_WINDOWS_SIZE 0x00000000 /* Section 2.2.3.5.1 */
+#define ROH_CMD_TYPE_FLOW_CONTROL_ACK 0x00000001 /* Section 2.2.3.5.2 */
+#define ROH_CMD_TYPE_CONNECTION_TIMEOUT 0x00000002 /* Section 2.2.3.5.3 */
+#define ROH_CMD_TYPE_COOKIE 0x00000003 /* Section 2.2.3.5.4 */
+#define ROH_CMD_TYPE_CHANNEL_LIFETIME 0x00000004 /* Section 2.2.3.5.5 */
+#define ROH_CMD_TYPE_CLIENT_KEEPALIVE 0x00000005 /* Section 2.2.3.5.6 */
+#define ROH_CMD_TYPE_VERSION 0x00000006 /* Section 2.2.3.5.7 */
+#define ROH_CMD_TYPE_EMPTY 0x00000007 /* Section 2.2.3.5.8 */
+#define ROH_CMD_TYPE_PADDING 0x00000008 /* Section 2.2.3.5.9 */
+#define ROH_CMD_TYPE_NEGATIVE_ANCE 0x00000009 /* Section 2.2.3.5.10 */
+#define ROH_CMD_TYPE_ANCE 0x0000000A /* Section 2.2.3.5.11 */
+#define ROH_CMD_TYPE_CLIENT_ADDRESS 0x0000000B /* Section 2.2.3.5.12 */
+#define ROH_CMD_TYPE_ASSOCIATION_GRP_ID 0x0000000C /* Section 2.2.3.5.13 */
+#define ROH_CMD_TYPE_DESTINATION 0x0000000D /* Section 2.2.3.5.14 */
+#define ROH_CMD_TYPE_PING 0x0000000E /* Section 2.2.3.5.15 */
+
+#endif /* DCERPC_ROH_H_ */
diff --git a/source4/librpc/rpc/dcerpc_roh_channel_in.c b/source4/librpc/rpc/dcerpc_roh_channel_in.c
new file mode 100644
index 0000000..5010452
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_roh_channel_in.c
@@ -0,0 +1,303 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ [MS-RPCH] - RPC over HTTP client
+
+ Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+ Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2013
+
+ 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 <tevent.h>
+#include <talloc.h>
+#include "lib/tsocket/tsocket.h"
+#include "lib/tls/tls.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/util_net.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+#include <gen_ndr/dcerpc.h>
+#include <gen_ndr/ndr_dcerpc.h>
+
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_roh.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "libcli/http/http.h"
+
+struct roh_request_state {
+ struct http_request *request;
+ struct http_request *response;
+};
+
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_RPC_DATA_IN_send(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev,
+ struct cli_credentials *credentials,
+ struct roh_connection *roh,
+ const char *rpc_server,
+ uint32_t rpc_server_port,
+ const char *rpc_proxy,
+ uint8_t http_auth)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct roh_request_state *state;
+ const char *path;
+ char *query;
+ char *uri;
+
+ DEBUG(8, ("%s: Sending RPC_IN_DATA request\n", __func__));
+
+ req = tevent_req_create(mem_ctx, &state, struct roh_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->request = talloc_zero(state, struct http_request);
+ if (tevent_req_nomem(state->request, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /* Build URI, as specified in section 2.2.2 */
+ query = talloc_asprintf(state, "%s:%d", rpc_server, rpc_server_port);
+ if (tevent_req_nomem(query, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO This path changes to "/rpcwithcert/rpcproxy.dll" when using
+ * certificates
+ */
+ path = "/rpc/rpcproxy.dll";
+ uri = talloc_asprintf(state, "%s?%s", path, query);
+ if (tevent_req_nomem(uri, req)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+ TALLOC_FREE(query);
+
+ /*
+ * Create the HTTP channel IN request as specified in the
+ * section 2.1.2.1.1
+ */
+ state->request->type = HTTP_REQ_RPC_IN_DATA;
+ state->request->uri = uri;
+ state->request->body.length = 0;
+ state->request->body.data = NULL;
+ state->request->major = '1';
+ state->request->minor = '0';
+
+ http_add_header(state, &state->request->headers,
+ "Accept", "application/rpc");
+ http_add_header(state, &state->request->headers,
+ "User-Agent", "MSRPC");
+ http_add_header(state, &state->request->headers,
+ "Host", rpc_proxy);
+ http_add_header(state, &state->request->headers,
+ "Connection", "keep-alive");
+ http_add_header(state, &state->request->headers,
+ "Content-Length", "1073741824");
+ http_add_header(state, &state->request->headers,
+ "Cache-Control", "no-cache");
+ http_add_header(state, &state->request->headers,
+ "Pragma", "no-cache");
+
+ subreq = http_send_auth_request_send(state,
+ ev,
+ roh->default_channel_in->http_conn,
+ state->request,
+ credentials,
+ lp_ctx,
+ http_auth);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_send_RPC_DATA_IN_done, req);
+
+ return req;
+}
+
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ /* Receive the sent bytes to check if request has been properly sent */
+ status = http_send_auth_request_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ DEBUG(8, ("%s: RPC_IN_DATA sent\n", __func__));
+
+ tevent_req_done(req);
+}
+
+NTSTATUS roh_send_RPC_DATA_IN_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct roh_send_pdu_state {
+ DATA_BLOB buffer;
+ struct iovec iov;
+ int bytes_written;
+ int sys_errno;
+};
+
+static void roh_send_CONN_B1_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_CONN_B1_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct roh_connection *roh)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct roh_send_pdu_state *state;
+ struct dcerpc_rts rts;
+ struct ncacn_packet pkt;
+ struct ndr_push *ndr;
+ struct tstream_context *stream = NULL;
+ struct tevent_queue *send_queue = NULL;
+
+ DEBUG(8, ("%s: Sending CONN/B1 request\n", __func__));
+
+ req = tevent_req_create(mem_ctx, &state, struct roh_send_pdu_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ rts.Flags = RTS_FLAG_NONE;
+ rts.NumberOfCommands = 6;
+ rts.Commands = talloc_array(state, struct dcerpc_rts_cmd, 6);
+
+ /* CONN/B1: Version RTS command */
+ rts.Commands[0].CommandType = 0x00000006;
+ rts.Commands[0].Command.Version.Version = 0x00000001;
+
+ /* CONN/B1: VirtualConnectionCookie RTS command */
+ rts.Commands[1].CommandType = 0x00000003;
+ rts.Commands[1].Command.Cookie.Cookie.Cookie = roh->connection_cookie;
+
+ /* CONN/B1: InChannelCookie RTS command */
+ rts.Commands[2].CommandType = 0x00000003;
+ rts.Commands[2].Command.Cookie.Cookie.Cookie =
+ roh->default_channel_in->channel_cookie;
+
+ /* CONN/B1: ChannelLifetime */
+ rts.Commands[3].CommandType = 0x00000004;
+ rts.Commands[3].Command.ReceiveWindowSize.ReceiveWindowSize =
+ 0x40000000;
+
+ /* CONN/B1: ClientKeepAlive */
+ rts.Commands[4].CommandType = 0x00000005;
+ rts.Commands[4].Command.ClientKeepalive.ClientKeepalive = 0x000493e0;
+
+ /* CONN/B1: AssociationGroupId */
+ rts.Commands[5].CommandType = 0x0000000C;
+ rts.Commands[5].Command.AssociationGroupId.AssociationGroupId.Cookie =
+ roh->association_group_id_cookie;
+
+ pkt.rpc_vers = 5;
+ pkt.rpc_vers_minor = 0;
+ pkt.ptype = DCERPC_PKT_RTS;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_LAST | DCERPC_PFC_FLAG_FIRST;
+ pkt.drep[0] = DCERPC_DREP_LE;
+ pkt.drep[1] = 0;
+ pkt.drep[2] = 0;
+ pkt.drep[3] = 0;
+ pkt.frag_length = 104;
+ pkt.auth_length = 0;
+ pkt.call_id = 0;
+ pkt.u.rts = rts;
+
+ ndr = ndr_push_init_ctx(state);
+ if (ndr == NULL) {
+ return NULL;
+ }
+ ndr->offset = 0;
+ ndr_push_ncacn_packet(ndr, NDR_SCALARS, &pkt);
+
+ state->buffer = ndr_push_blob(ndr);
+ state->iov.iov_base = (char *) state->buffer.data;
+ state->iov.iov_len = state->buffer.length;
+
+ stream = http_conn_tstream(roh->default_channel_in->http_conn);
+ send_queue = http_conn_send_queue(roh->default_channel_in->http_conn);
+
+ subreq = tstream_writev_queue_send(mem_ctx,
+ ev,
+ stream,
+ send_queue,
+ &state->iov,
+ 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_send_CONN_B1_done, req);
+
+ return req;
+}
+
+static void roh_send_CONN_B1_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_send_pdu_state *state;
+ int sys_errno;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_send_pdu_state);
+
+ state->bytes_written = tstream_writev_queue_recv(subreq, &sys_errno);
+ state->sys_errno = sys_errno;
+ TALLOC_FREE(subreq);
+ if (state->bytes_written <= 0 && state->sys_errno != 0) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ tevent_req_nterror(req, status);
+ return;
+ }
+ DEBUG(8, ("%s: CONN/B1 sent (%d bytes written)\n",
+ __func__, state->bytes_written));
+
+ tevent_req_done(req);
+}
+
+NTSTATUS roh_send_CONN_B1_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source4/librpc/rpc/dcerpc_roh_channel_out.c b/source4/librpc/rpc/dcerpc_roh_channel_out.c
new file mode 100644
index 0000000..2abafb0
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_roh_channel_out.c
@@ -0,0 +1,582 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ [MS-RPCH] - RPC over HTTP client
+
+ Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+ Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2013
+
+ 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 <tevent.h>
+#include <talloc.h>
+#include "lib/tsocket/tsocket.h"
+#include "lib/tls/tls.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/util_net.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+#include <gen_ndr/dcerpc.h>
+#include <gen_ndr/ndr_dcerpc.h>
+
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_roh.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/rpc/dcerpc_util.h"
+#include "libcli/http/http.h"
+
+struct roh_request_state {
+ struct http_request *request;
+ struct http_request *response;
+};
+
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_RPC_DATA_OUT_send(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev,
+ struct cli_credentials *credentials,
+ struct roh_connection *roh,
+ const char *rpc_server,
+ uint32_t rpc_server_port,
+ const char *rpc_proxy,
+ uint8_t http_auth)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct roh_request_state *state;
+ const char *path;
+ char *query;
+ char *uri;
+
+ DEBUG(8, ("%s: Sending RPC_OUT_DATA request\n", __func__));
+
+ req = tevent_req_create(mem_ctx, &state, struct roh_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->request = talloc_zero(state, struct http_request);
+ if (tevent_req_nomem(state->request, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /* Build URI, as specified in section 2.2.2 */
+ query = talloc_asprintf(state, "%s:%d", rpc_server, rpc_server_port);
+ if (tevent_req_nomem(query, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO This path changes to "/rpcwithcert/rpcproxy.dll" when using
+ * certificates
+ */
+ path = "/rpc/rpcproxy.dll";
+ uri = talloc_asprintf(state, "%s?%s", path, query);
+ if (tevent_req_nomem(uri, req)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+ TALLOC_FREE(query);
+
+ /*
+ * Create the HTTP channel OUT request as specified in the
+ * section 2.1.2.1.2
+ */
+ state->request->type = HTTP_REQ_RPC_OUT_DATA;
+ state->request->uri = uri;
+ state->request->body.length = 0;
+ state->request->body.data = NULL;
+ state->request->major = '1';
+ state->request->minor = '0';
+
+ http_add_header(state, &state->request->headers,
+ "Accept", "application/rpc");
+ http_add_header(state, &state->request->headers,
+ "User-Agent", "MSRPC");
+ http_add_header(state, &state->request->headers,
+ "Host", rpc_proxy);
+ http_add_header(state, &state->request->headers,
+ "Connection", "keep-alive");
+ http_add_header(state, &state->request->headers,
+ "Content-Length", "76");
+ http_add_header(state, &state->request->headers,
+ "Cache-Control", "no-cache");
+ http_add_header(state, &state->request->headers,
+ "Pragma", "no-cache");
+
+ subreq = http_send_auth_request_send(state,
+ ev,
+ roh->default_channel_out->http_conn,
+ state->request,
+ credentials,
+ lp_ctx,
+ http_auth);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_send_RPC_DATA_OUT_done, req);
+
+ return req;
+}
+
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ /* Receive the sent bytes to check if request has been properly sent */
+ status = http_send_auth_request_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ DEBUG(8, ("%s: RPC_OUT_DATA sent", __func__));
+
+ tevent_req_done(req);
+}
+
+NTSTATUS roh_send_RPC_DATA_OUT_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct roh_send_pdu_state {
+ DATA_BLOB buffer;
+ struct iovec iov;
+ int bytes_written;
+ int sys_errno;
+};
+
+static void roh_send_CONN_A1_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_CONN_A1_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct roh_connection *roh)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct roh_send_pdu_state *state;
+ struct dcerpc_rts rts;
+ struct ncacn_packet pkt;
+ struct ndr_push *ndr;
+ struct tstream_context *stream = NULL;
+ struct tevent_queue *send_queue = NULL;
+
+ DEBUG(8, ("%s: Sending CONN/A1 request\n", __func__));
+
+ req = tevent_req_create(mem_ctx, &state, struct roh_send_pdu_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ rts.Flags = RTS_FLAG_NONE;
+ rts.NumberOfCommands = 4;
+ rts.Commands = talloc_array(state, struct dcerpc_rts_cmd, 4);
+
+ /* CONN/A1: Version RTS command */
+ rts.Commands[0].CommandType = 0x00000006;
+ rts.Commands[0].Command.Version.Version = 0x00000001;
+
+ /* CONN/A1: VirtualConnectionCookie RTS command */
+ rts.Commands[1].CommandType = 0x00000003;
+ rts.Commands[1].Command.Cookie.Cookie.Cookie = roh->connection_cookie;
+
+ /* CONN/A1: OutChannelCookie RTS command */
+ rts.Commands[2].CommandType = 0x00000003;
+ rts.Commands[2].Command.Cookie.Cookie.Cookie =
+ roh->default_channel_out->channel_cookie;
+
+ /* CONN/A1: ReceiveWindowSize */
+ rts.Commands[3].CommandType = 0x00000000;
+ rts.Commands[3].Command.ReceiveWindowSize.ReceiveWindowSize = 0x40000;
+
+ pkt.rpc_vers = 5;
+ pkt.rpc_vers_minor = 0;
+ pkt.ptype = DCERPC_PKT_RTS;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_LAST | DCERPC_PFC_FLAG_FIRST;
+ pkt.drep[0] = DCERPC_DREP_LE;
+ pkt.drep[1] = 0;
+ pkt.drep[2] = 0;
+ pkt.drep[3] = 0;
+ pkt.frag_length = 76;
+ pkt.auth_length = 0;
+ pkt.call_id = 0;
+ pkt.u.rts = rts;
+
+ ndr = ndr_push_init_ctx(state);
+ if (ndr == NULL) {
+ return NULL;
+ }
+ ndr->offset = 0;
+ ndr_push_ncacn_packet(ndr, NDR_SCALARS, &pkt);
+
+ state->buffer = ndr_push_blob(ndr);
+ state->iov.iov_base = (char *) state->buffer.data;
+ state->iov.iov_len = state->buffer.length;
+
+ stream = http_conn_tstream(roh->default_channel_out->http_conn);
+ send_queue = http_conn_send_queue(roh->default_channel_out->http_conn);
+
+ subreq = tstream_writev_queue_send(mem_ctx,
+ ev,
+ stream,
+ send_queue,
+ &state->iov,
+ 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_send_CONN_A1_done, req);
+
+ return req;
+}
+
+static void roh_send_CONN_A1_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_send_pdu_state *state;
+ int sys_errno;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_send_pdu_state);
+
+ state->bytes_written = tstream_writev_queue_recv(subreq, &sys_errno);
+ state->sys_errno = sys_errno;
+ TALLOC_FREE(subreq);
+ if (state->bytes_written <= 0 && sys_errno != 0) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ tevent_req_nterror(req, status);
+ return;
+ }
+ DEBUG(8, ("%s: CONN/A1 sent (%d bytes written)\n",
+ __func__, state->bytes_written));
+
+ tevent_req_done(req);
+}
+
+NTSTATUS roh_send_CONN_A1_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct roh_recv_response_state
+{
+ struct http_request *response;
+};
+
+static void roh_recv_out_channel_response_done(struct tevent_req *);
+struct tevent_req *roh_recv_out_channel_response_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct roh_connection *roh)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct roh_recv_response_state *state;
+
+ DEBUG(8, ("%s: Waiting for RPC_OUT_DATA response\n", __func__));
+
+ req = tevent_req_create(mem_ctx, &state, struct roh_recv_response_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = http_read_response_send(state, ev,
+ roh->default_channel_out->http_conn,
+ 0); /* we'll get the content later */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_recv_out_channel_response_done, req);
+
+ return req;
+}
+
+static void roh_recv_out_channel_response_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_recv_response_state *state;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_recv_response_state);
+ status = http_read_response_recv(subreq, state, &state->response);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ DEBUG(8, ("%s: RCP_OUT_DATA response received\n", __func__));
+
+ /* TODO Map response code to nt error */
+ switch (state->response->response_code) {
+ case 200:
+ break;
+ case 401:
+ DEBUG(0, ("%s: Server response: Access denied\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ case 503:
+ /* TODO Decode error info as specified in section 2.1.2.1.3 */
+ DEBUG(0, ("%s: Server response: RPC error\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_GENERIC_NOT_MAPPED);
+ return;
+ default:
+ DEBUG(0, ("%s: Server response: Unknown error\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_GENERIC_NOT_MAPPED);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS roh_recv_out_channel_response_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **response_msg)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct roh_recv_pdu_state {
+ struct roh_connection *roh;
+ uint32_t connection_timeout;
+ uint32_t version;
+ uint32_t recv_window_size;
+};
+
+static void roh_recv_CONN_A3_done(struct tevent_req *subreq);
+struct tevent_req *roh_recv_CONN_A3_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct roh_connection *roh)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct roh_recv_pdu_state *state;
+ struct tstream_context *stream = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct roh_recv_pdu_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(8, ("%s: Waiting for CONN/A3\n", __func__));
+
+ stream = http_conn_tstream(roh->default_channel_out->http_conn);
+
+ subreq = dcerpc_read_ncacn_packet_send(state, ev, stream);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_recv_CONN_A3_done, req);
+
+ return req;
+}
+
+static void roh_recv_CONN_A3_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_recv_pdu_state *state;
+ struct ncacn_packet *pkt;
+ DATA_BLOB buffer;
+ struct dcerpc_rts rts;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_recv_pdu_state);
+ status = dcerpc_read_ncacn_packet_recv(subreq, state, &pkt, &buffer);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(0, ("%s: Error receiving PDU\n", __func__));
+ return;
+ }
+
+ /*
+ * Check if it is a CONN/A3 (2.2.4.4) packet and get the connection
+ * timeout
+ */
+ rts = pkt->u.rts;
+ if (rts.NumberOfCommands != 1) {
+ DEBUG(0, ("%s: Invalid number of commands received\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (rts.Commands[0].CommandType != ROH_CMD_TYPE_CONNECTION_TIMEOUT) {
+ DEBUG(0, ("%s: Invalid command type received\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* Extract connection timeout */
+ state->connection_timeout = rts.Commands[0].Command.ConnectionTimeout.ConnectionTimeout;
+
+ DEBUG(8, ("%s: CONN/A3 received, connection timeout is %u\n",
+ __func__, state->connection_timeout));
+ tevent_req_done(req);
+}
+
+NTSTATUS roh_recv_CONN_A3_recv(struct tevent_req *req,
+ unsigned int *connection_timeout)
+{
+ NTSTATUS status;
+ struct roh_recv_pdu_state *state;
+
+ state = tevent_req_data(req, struct roh_recv_pdu_state);
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *connection_timeout = state->connection_timeout;
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *subreq);
+struct tevent_req *roh_recv_CONN_C2_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct roh_connection *roh)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct roh_recv_pdu_state *state;
+ struct tstream_context *stream = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct roh_recv_pdu_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(8, ("%s: Waiting for CONN/C2\n", __func__));
+ stream = http_conn_tstream(roh->default_channel_out->http_conn);
+
+ subreq = dcerpc_read_ncacn_packet_send(state, ev, stream);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, roh_recv_CONN_C2_done, req);
+
+ return req;
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct tevent_req *req;
+ struct roh_recv_pdu_state *state;
+ struct ncacn_packet *pkt;
+ DATA_BLOB buffer;
+ struct dcerpc_rts rts;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct roh_recv_pdu_state);
+
+ status = dcerpc_read_ncacn_packet_recv(subreq, state, &pkt, &buffer);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(0, ("%s: Error receiving PDU\n", __func__));
+ return;
+ }
+
+ /*
+ * Check if it is a CONN/C2 packet (2.2.4.9), and get the version, the
+ * receive windows size and the connection timeout for the IN channel
+ */
+ rts = pkt->u.rts;
+ if (rts.NumberOfCommands != 3) {
+ DEBUG(0, ("%s: Invalid number of commands received\n",
+ __func__));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (rts.Commands[0].CommandType != ROH_CMD_TYPE_VERSION) {
+ DEBUG(0, ("%s: Invalid command type received\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (rts.Commands[1].CommandType != ROH_CMD_TYPE_RECV_WINDOWS_SIZE) {
+ DEBUG(0, ("%s: Invalid command type received\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (rts.Commands[2].CommandType != ROH_CMD_TYPE_CONNECTION_TIMEOUT) {
+ DEBUG(0, ("%s: Invalid command type received\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* Extract data */
+ state->version = rts.Commands[0].Command.Version.Version;
+ state->recv_window_size = rts.Commands[1].Command.ReceiveWindowSize.ReceiveWindowSize;
+ state->connection_timeout = rts.Commands[2].Command.ConnectionTimeout.ConnectionTimeout;
+
+ DEBUG(8, ("%s: CONN/C2 received, version is %u, receive windows size is %u, connection timeout is %u\n",
+ __func__, state->version, state->recv_window_size,
+ state->connection_timeout));
+ tevent_req_done(req);
+}
+
+NTSTATUS roh_recv_CONN_C2_recv(struct tevent_req *req,
+ unsigned int *version,
+ unsigned int *recv_window_size,
+ unsigned int *connection_timeout)
+{
+ NTSTATUS status;
+ struct roh_recv_pdu_state *state;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ state = tevent_req_data(req, struct roh_recv_pdu_state);
+ *version = state->version;
+ *recv_window_size = state->recv_window_size;
+ *connection_timeout = state->connection_timeout;
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
new file mode 100644
index 0000000..68bc3b3
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_schannel.c
@@ -0,0 +1,620 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc schannel operations
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Rafal Szczesniak 2006
+
+ 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 <tevent.h>
+#include "auth/auth.h"
+#include "libcli/composite/composite.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "param/param.h"
+#include "lib/param/loadparm.h"
+
+struct schannel_key_state {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_pipe *pipe2;
+ struct dcerpc_binding *binding;
+ bool dcerpc_schannel_auto;
+ struct cli_credentials *credentials;
+ struct netlogon_creds_CredentialState *creds;
+ uint32_t local_negotiate_flags;
+ uint32_t remote_negotiate_flags;
+ struct netr_Credential credentials1;
+ struct netr_Credential credentials2;
+ struct netr_Credential credentials3;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate2 a;
+ const struct samr_Password *mach_pwd;
+};
+
+
+static void continue_secondary_connection(struct composite_context *ctx);
+static void continue_bind_auth_none(struct composite_context *ctx);
+static void continue_srv_challenge(struct tevent_req *subreq);
+static void continue_srv_auth2(struct tevent_req *subreq);
+static void continue_get_capabilities(struct tevent_req *subreq);
+
+
+/*
+ Stage 2 of schannel_key: Receive endpoint mapping and request secondary
+ rpc connection
+*/
+static void continue_epm_map_binding(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct composite_context *sec_conn_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive endpoint mapping */
+ c->status = dcerpc_epm_map_binding_recv(ctx);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(0,("Failed to map DCERPC/TCP NCACN_NP pipe for '%s' - %s\n",
+ NDR_NETLOGON_UUID, nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* send a request for secondary rpc connection */
+ sec_conn_req = dcerpc_secondary_connection_send(s->pipe,
+ s->binding);
+ if (composite_nomem(sec_conn_req, c)) return;
+
+ composite_continue(c, sec_conn_req, continue_secondary_connection, c);
+}
+
+
+/*
+ Stage 3 of schannel_key: Receive secondary rpc connection and perform
+ non-authenticated bind request
+*/
+static void continue_secondary_connection(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct composite_context *auth_none_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive secondary rpc connection */
+ c->status = dcerpc_secondary_connection_recv(ctx, &s->pipe2);
+ if (!composite_is_ok(c)) return;
+
+ talloc_steal(s, s->pipe2);
+
+ /* initiate a non-authenticated bind */
+ auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe2, &ndr_table_netlogon);
+ if (composite_nomem(auth_none_req, c)) return;
+
+ composite_continue(c, auth_none_req, continue_bind_auth_none, c);
+}
+
+
+/*
+ Stage 4 of schannel_key: Receive non-authenticated bind and get
+ a netlogon challenge
+*/
+static void continue_bind_auth_none(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive result of non-authenticated bind request */
+ c->status = dcerpc_bind_auth_none_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare a challenge request */
+ s->r.in.server_name = talloc_asprintf(c, "\\\\%s", dcerpc_server_name(s->pipe));
+ if (composite_nomem(s->r.in.server_name, c)) return;
+ s->r.in.computer_name = cli_credentials_get_workstation(s->credentials);
+ s->r.in.credentials = &s->credentials1;
+ s->r.out.return_credentials = &s->credentials2;
+
+ generate_random_buffer(s->credentials1.data, sizeof(s->credentials1.data));
+
+ /*
+ request a netlogon challenge - a rpc request over opened secondary pipe
+ */
+ subreq = dcerpc_netr_ServerReqChallenge_r_send(s, c->event_ctx,
+ s->pipe2->binding_handle,
+ &s->r);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_srv_challenge, c);
+}
+
+
+/*
+ Stage 5 of schannel_key: Receive a challenge and perform authentication
+ on the netlogon pipe
+*/
+static void continue_srv_challenge(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive rpc request result - netlogon challenge */
+ c->status = dcerpc_netr_ServerReqChallenge_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare credentials for auth2 request */
+ s->mach_pwd = cli_credentials_get_nt_hash(s->credentials, c);
+
+ /* auth2 request arguments */
+ s->a.in.server_name = s->r.in.server_name;
+ s->a.in.account_name = cli_credentials_get_username(s->credentials);
+ s->a.in.secure_channel_type =
+ cli_credentials_get_secure_channel_type(s->credentials);
+ s->a.in.computer_name = cli_credentials_get_workstation(s->credentials);
+ s->a.in.negotiate_flags = &s->local_negotiate_flags;
+ s->a.in.credentials = &s->credentials3;
+ s->a.out.negotiate_flags = &s->remote_negotiate_flags;
+ s->a.out.return_credentials = &s->credentials3;
+
+ s->creds = netlogon_creds_client_init(s,
+ s->a.in.account_name,
+ s->a.in.computer_name,
+ s->a.in.secure_channel_type,
+ &s->credentials1, &s->credentials2,
+ s->mach_pwd, &s->credentials3,
+ s->local_negotiate_flags);
+ if (composite_nomem(s->creds, c)) {
+ return;
+ }
+ /*
+ authenticate on the netlogon pipe - a rpc request over secondary pipe
+ */
+ subreq = dcerpc_netr_ServerAuthenticate2_r_send(s, c->event_ctx,
+ s->pipe2->binding_handle,
+ &s->a);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_srv_auth2, c);
+}
+
+
+/*
+ Stage 6 of schannel_key: Receive authentication request result and verify
+ received credentials
+*/
+static void continue_srv_auth2(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive rpc request result - auth2 credentials */
+ c->status = dcerpc_netr_ServerAuthenticate2_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_EQUAL(s->a.out.result, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_IS_OK(s->a.out.result)) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+
+ /*
+ * Strong keys could be unsupported (NT4) or disables. So retry with the
+ * flags returned by the server. - asn
+ */
+ if (NT_STATUS_EQUAL(s->a.out.result, NT_STATUS_ACCESS_DENIED)) {
+ uint32_t lf = s->local_negotiate_flags;
+ const char *ln = NULL;
+ uint32_t rf = s->remote_negotiate_flags;
+ const char *rn = NULL;
+
+ if (!s->dcerpc_schannel_auto) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+ s->dcerpc_schannel_auto = false;
+
+ if (lf & NETLOGON_NEG_SUPPORTS_AES) {
+ ln = "aes";
+ if (rf & NETLOGON_NEG_SUPPORTS_AES) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+ } else if (lf & NETLOGON_NEG_STRONG_KEYS) {
+ ln = "strong";
+ if (rf & NETLOGON_NEG_STRONG_KEYS) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+ } else {
+ ln = "des";
+ }
+
+ if (rf & NETLOGON_NEG_SUPPORTS_AES) {
+ rn = "aes";
+ } else if (rf & NETLOGON_NEG_STRONG_KEYS) {
+ rn = "strong";
+ } else {
+ rn = "des";
+ }
+
+ DEBUG(3, ("Server doesn't support %s keys, downgrade to %s"
+ "and retry! local[0x%08X] remote[0x%08X]\n",
+ ln, rn, lf, rf));
+
+ s->local_negotiate_flags = s->remote_negotiate_flags;
+
+ generate_random_buffer(s->credentials1.data,
+ sizeof(s->credentials1.data));
+
+ subreq = dcerpc_netr_ServerReqChallenge_r_send(s,
+ c->event_ctx,
+ s->pipe2->binding_handle,
+ &s->r);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_srv_challenge, c);
+ return;
+ }
+
+ s->creds->negotiate_flags = s->remote_negotiate_flags;
+
+ /* verify credentials */
+ if (!netlogon_creds_client_check(s->creds, s->a.out.return_credentials)) {
+ composite_error(c, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ composite_done(c);
+}
+
+/*
+ Initiate establishing a schannel key using netlogon challenge
+ on a secondary pipe
+*/
+static struct composite_context *dcerpc_schannel_key_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct composite_context *epm_map_req;
+ enum netr_SchannelType schannel_type = cli_credentials_get_secure_channel_type(credentials);
+ struct cli_credentials *epm_creds = NULL;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct schannel_key_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in the state structure */
+ s->pipe = p;
+ s->credentials = credentials;
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+
+ /* allocate credentials */
+ if (s->pipe->conn->flags & DCERPC_SCHANNEL_128) {
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ }
+ if (s->pipe->conn->flags & DCERPC_SCHANNEL_AES) {
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ s->local_negotiate_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ }
+ if (s->pipe->conn->flags & DCERPC_SCHANNEL_AUTO) {
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ s->local_negotiate_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ s->dcerpc_schannel_auto = true;
+ }
+
+ /* type of authentication depends on schannel type */
+ if (schannel_type == SEC_CHAN_RODC) {
+ s->local_negotiate_flags |= NETLOGON_NEG_RODC_PASSTHROUGH;
+ }
+
+ if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ s->local_negotiate_flags &= ~NETLOGON_NEG_ARCFOUR;
+ }
+
+ epm_creds = cli_credentials_init_anon(s);
+ if (composite_nomem(epm_creds, c)) return c;
+
+ /* allocate binding structure */
+ s->binding = dcerpc_binding_dup(s, s->pipe->binding);
+ if (composite_nomem(s->binding, c)) return c;
+
+ /* request the netlogon endpoint mapping */
+ epm_map_req = dcerpc_epm_map_binding_send(c, s->binding,
+ &ndr_table_netlogon,
+ epm_creds,
+ s->pipe->conn->event_ctx,
+ lp_ctx);
+ if (composite_nomem(epm_map_req, c)) return c;
+
+ composite_continue(c, epm_map_req, continue_epm_map_binding, c);
+ return c;
+}
+
+
+/*
+ Receive result of schannel key request
+ */
+static NTSTATUS dcerpc_schannel_key_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **creds)
+{
+ NTSTATUS status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct schannel_key_state *s =
+ talloc_get_type_abort(c->private_data,
+ struct schannel_key_state);
+ *creds = talloc_move(mem_ctx, &s->creds);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct auth_schannel_state {
+ struct dcerpc_pipe *pipe;
+ struct cli_credentials *credentials;
+ const struct ndr_interface_table *table;
+ struct loadparm_context *lp_ctx;
+ uint8_t auth_level;
+ struct netlogon_creds_CredentialState *creds_state;
+ struct netlogon_creds_CredentialState save_creds_state;
+ struct netr_Authenticator auth;
+ struct netr_Authenticator return_auth;
+ union netr_Capabilities capabilities;
+ struct netr_LogonGetCapabilities c;
+};
+
+
+static void continue_bind_auth(struct composite_context *ctx);
+
+
+/*
+ Stage 2 of auth_schannel: Receive schannel key and intitiate an
+ authenticated bind using received credentials
+ */
+static void continue_schannel_key(struct composite_context *ctx)
+{
+ struct composite_context *auth_req;
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct auth_schannel_state *s = talloc_get_type(c->private_data,
+ struct auth_schannel_state);
+ NTSTATUS status;
+
+ /* receive schannel key */
+ status = c->status = dcerpc_schannel_key_recv(ctx, s, &s->creds_state);
+ if (!composite_is_ok(c)) {
+ DEBUG(1, ("Failed to setup credentials: %s\n", nt_errstr(status)));
+ return;
+ }
+
+ /* send bind auth request with received creds */
+ cli_credentials_set_netlogon_creds(s->credentials, s->creds_state);
+
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, s->credentials,
+ lpcfg_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_SCHANNEL, s->auth_level,
+ NULL);
+ if (composite_nomem(auth_req, c)) return;
+
+ composite_continue(c, auth_req, continue_bind_auth, c);
+}
+
+
+/*
+ Stage 3 of auth_schannel: Receivce result of authenticated bind
+ and say if we're done ok.
+*/
+static void continue_bind_auth(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct auth_schannel_state *s = talloc_get_type(c->private_data,
+ struct auth_schannel_state);
+ struct tevent_req *subreq;
+
+ c->status = dcerpc_bind_auth_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ /* if we have a AES encrypted connection, verify the capabilities */
+ if (ndr_syntax_id_equal(&s->table->syntax_id,
+ &ndr_table_netlogon.syntax_id)) {
+ NTSTATUS status;
+ ZERO_STRUCT(s->return_auth);
+
+ s->save_creds_state = *s->creds_state;
+ status = netlogon_creds_client_authenticator(&s->save_creds_state,
+ &s->auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(c, status);
+ return;
+ }
+
+ s->c.in.server_name = talloc_asprintf(c,
+ "\\\\%s",
+ dcerpc_server_name(s->pipe));
+ if (composite_nomem(s->c.in.server_name, c)) return;
+ s->c.in.computer_name = cli_credentials_get_workstation(s->credentials);
+ s->c.in.credential = &s->auth;
+ s->c.in.return_authenticator = &s->return_auth;
+ s->c.in.query_level = 1;
+
+ s->c.out.capabilities = &s->capabilities;
+ s->c.out.return_authenticator = &s->return_auth;
+
+ DEBUG(5, ("We established a AES connection, verifying logon "
+ "capabilities\n"));
+
+ subreq = dcerpc_netr_LogonGetCapabilities_r_send(s,
+ c->event_ctx,
+ s->pipe->binding_handle,
+ &s->c);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_get_capabilities, c);
+ return;
+ }
+
+ composite_done(c);
+}
+
+/*
+ Stage 4 of auth_schannel: Get the Logon Capablities and verify them.
+*/
+static void continue_get_capabilities(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct auth_schannel_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct auth_schannel_state);
+
+ /* receive rpc request result */
+ c->status = dcerpc_netr_LogonGetCapabilities_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ if (s->creds_state->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ } else {
+ /* This is probably NT */
+ composite_done(c);
+ return;
+ }
+ } else if (!composite_is_ok(c)) {
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(s->c.out.result, NT_STATUS_NOT_IMPLEMENTED)) {
+ if (s->creds_state->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ /* This means AES isn't supported. */
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* This is probably an old Samba version */
+ composite_done(c);
+ return;
+ }
+
+ /* verify credentials */
+ if (!netlogon_creds_client_check(&s->save_creds_state,
+ &s->c.out.return_authenticator->cred)) {
+ composite_error(c, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ *s->creds_state = s->save_creds_state;
+ cli_credentials_set_netlogon_creds(s->credentials, s->creds_state);
+
+ if (!NT_STATUS_IS_OK(s->c.out.result)) {
+ composite_error(c, s->c.out.result);
+ return;
+ }
+
+ /* compare capabilities */
+ if (s->creds_state->negotiate_flags != s->capabilities.server_capabilities) {
+ DEBUG(2, ("The client capabilities don't match the server "
+ "capabilities: local[0x%08X] remote[0x%08X]\n",
+ s->creds_state->negotiate_flags,
+ s->capabilities.server_capabilities));
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* TODO: Add downgrade dectection. */
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate schannel authentication request
+*/
+struct composite_context *dcerpc_bind_auth_schannel_send(TALLOC_CTX *tmp_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ uint8_t auth_level)
+{
+ struct composite_context *c;
+ struct auth_schannel_state *s;
+ struct composite_context *schan_key_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(tmp_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct auth_schannel_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in the state structure */
+ s->pipe = p;
+ s->credentials = credentials;
+ s->table = table;
+ s->auth_level = auth_level;
+ s->lp_ctx = lp_ctx;
+
+ /* start getting schannel key first */
+ schan_key_req = dcerpc_schannel_key_send(c, p, credentials, lp_ctx);
+ if (composite_nomem(schan_key_req, c)) return c;
+
+ composite_continue(c, schan_key_req, continue_schannel_key, c);
+ return c;
+}
+
+
+/*
+ Receive result of schannel authentication request
+*/
+NTSTATUS dcerpc_bind_auth_schannel_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/librpc/rpc/dcerpc_secondary.c b/source4/librpc/rpc/dcerpc_secondary.c
new file mode 100644
index 0000000..55068dc
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_secondary.c
@@ -0,0 +1,431 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc connect functions
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2007
+ Copyright (C) Rafal Szczesniak 2005
+
+ 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 "libcli/composite/composite.h"
+#include "lib/events/events.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+#include "lib/util/util_net.h"
+
+struct sec_conn_state {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_pipe *pipe2;
+ struct dcerpc_binding *binding;
+};
+
+
+static void continue_open_smb(struct composite_context *ctx);
+static void continue_open_tcp(struct composite_context *ctx);
+static void continue_open_ncalrpc(struct composite_context *ctx);
+static void continue_open_ncacn_unix(struct composite_context *ctx);
+static void continue_pipe_open(struct composite_context *c);
+
+
+/*
+ Send request to create a secondary dcerpc connection from a primary
+ connection
+*/
+_PUBLIC_ struct composite_context* dcerpc_secondary_connection_send(struct dcerpc_pipe *p,
+ const struct dcerpc_binding *b)
+{
+ struct composite_context *c;
+ struct sec_conn_state *s;
+ struct composite_context *pipe_smb_req;
+ struct composite_context *pipe_tcp_req;
+ const char *localaddress = NULL;
+ struct composite_context *pipe_ncalrpc_req;
+ const char *ncalrpc_dir = NULL;
+ struct composite_context *pipe_unix_req;
+ const char *host;
+ const char *target_hostname;
+ const char *endpoint;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct sec_conn_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->pipe = p;
+ s->binding = dcerpc_binding_dup(s, b);
+ if (composite_nomem(s->binding, c)) return c;
+
+ /* initialise second dcerpc pipe based on primary pipe's event context */
+ s->pipe2 = dcerpc_pipe_init(c, s->pipe->conn->event_ctx);
+ if (composite_nomem(s->pipe2, c)) return c;
+
+ if (DEBUGLEVEL >= 10)
+ s->pipe2->conn->packet_log_dir = s->pipe->conn->packet_log_dir;
+
+ host = dcerpc_binding_get_string_option(s->binding, "host");
+ if (host == NULL) {
+ /*
+ * We may fallback to the host of the given connection
+ */
+ host = dcerpc_binding_get_string_option(s->pipe->binding,
+ "host");
+ }
+ target_hostname = dcerpc_binding_get_string_option(s->binding, "target_hostname");
+ if (target_hostname == NULL) {
+ /*
+ * We may fallback to the target_hostname of the given connection
+ */
+ target_hostname = dcerpc_binding_get_string_option(s->pipe->binding,
+ "target_hostname");
+ }
+ endpoint = dcerpc_binding_get_string_option(s->binding, "endpoint");
+ if (endpoint == NULL) {
+ /*
+ * We may fallback to the endpoint of the given connection
+ */
+ endpoint = dcerpc_binding_get_string_option(s->pipe->binding, "endpoint");
+ }
+ if (endpoint == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ /* open second dcerpc pipe using the same transport as for primary pipe */
+ switch (s->pipe->conn->transport.transport) {
+ case NCACN_NP:
+ pipe_smb_req = dcerpc_secondary_smb_send(s->pipe->conn,
+ s->pipe2->conn,
+ endpoint);
+ composite_continue(c, pipe_smb_req, continue_open_smb, c);
+ return c;
+
+ case NCACN_IP_TCP:
+ if (host == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ if (!is_ipaddress(host)) {
+ /*
+ * We may fallback to the host of the given connection
+ */
+ host = dcerpc_binding_get_string_option(s->pipe->binding,
+ "host");
+ if (host == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+ if (!is_ipaddress(host)) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+ }
+
+ localaddress = dcerpc_binding_get_string_option(s->binding,
+ "localaddress");
+ if (localaddress == NULL) {
+ /*
+ * We may fallback to the localaddress of the given connection
+ */
+ localaddress = dcerpc_binding_get_string_option(s->pipe->binding,
+ "localaddress");
+ }
+
+ pipe_tcp_req = dcerpc_pipe_open_tcp_send(s->pipe2->conn,
+ localaddress,
+ host,
+ target_hostname,
+ atoi(endpoint),
+ resolve_context_init(s));
+ composite_continue(c, pipe_tcp_req, continue_open_tcp, c);
+ return c;
+
+ case NCALRPC:
+ ncalrpc_dir = dcerpc_binding_get_string_option(s->binding,
+ "ncalrpc_dir");
+ if (ncalrpc_dir == NULL) {
+ ncalrpc_dir = dcerpc_binding_get_string_option(s->pipe->binding,
+ "ncalrpc_dir");
+ }
+ if (ncalrpc_dir == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ pipe_ncalrpc_req = dcerpc_pipe_open_pipe_send(s->pipe2->conn,
+ ncalrpc_dir,
+ endpoint);
+ composite_continue(c, pipe_ncalrpc_req, continue_open_ncalrpc, c);
+ return c;
+
+ case NCACN_UNIX_STREAM:
+ pipe_unix_req = dcerpc_pipe_open_unix_stream_send(s->pipe2->conn,
+ endpoint);
+ composite_continue(c, pipe_unix_req, continue_open_ncacn_unix, c);
+ return c;
+
+ default:
+ /* looks like a transport we don't support */
+ composite_error(c, NT_STATUS_NOT_SUPPORTED);
+ }
+
+ return c;
+}
+
+
+/*
+ Stage 2 of secondary_connection: Receive result of pipe open request on smb
+*/
+static void continue_open_smb(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_secondary_smb_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_open(c);
+}
+
+
+/*
+ Stage 2 of secondary_connection: Receive result of pipe open request on tcp/ip
+*/
+static void continue_open_tcp(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct sec_conn_state *s = talloc_get_type_abort(c->private_data,
+ struct sec_conn_state);
+ char *localaddr = NULL;
+ char *remoteaddr = NULL;
+
+ c->status = dcerpc_pipe_open_tcp_recv(ctx, s, &localaddr, &remoteaddr);
+ if (!composite_is_ok(c)) return;
+
+ c->status = dcerpc_binding_set_string_option(s->binding,
+ "localaddress",
+ localaddr);
+ if (!composite_is_ok(c)) return;
+
+ c->status = dcerpc_binding_set_string_option(s->binding,
+ "host",
+ remoteaddr);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_open(c);
+}
+
+/*
+ Stage 2 of secondary_connection: Receive result of pipe open request on ncalrpc
+*/
+static void continue_open_ncalrpc(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_pipe_open_pipe_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_open(c);
+}
+
+/*
+ Stage 2 of secondary_connection: Receive result of pipe open request on ncacn_unix
+*/
+static void continue_open_ncacn_unix(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_pipe_open_unix_stream_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_open(c);
+}
+
+
+/*
+ Stage 3 of secondary_connection: Get binding data and flags from primary pipe
+ and say if we're done ok.
+*/
+static void continue_pipe_open(struct composite_context *c)
+{
+ struct sec_conn_state *s;
+
+ s = talloc_get_type(c->private_data, struct sec_conn_state);
+
+ s->pipe2->conn->flags = s->pipe->conn->flags;
+ s->pipe2->binding = dcerpc_binding_dup(s->pipe2, s->binding);
+ if (composite_nomem(s->pipe2->binding, c)) {
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ Receive result of secondary rpc connection request and return
+ second dcerpc pipe.
+*/
+_PUBLIC_ NTSTATUS dcerpc_secondary_connection_recv(struct composite_context *c,
+ struct dcerpc_pipe **p2)
+{
+ NTSTATUS status = composite_wait(c);
+ struct sec_conn_state *s;
+
+ s = talloc_get_type(c->private_data, struct sec_conn_state);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *p2 = talloc_steal(s->pipe, s->pipe2);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+/*
+ Create a secondary DCERPC connection, then bind (and possibly
+ authenticate) using the supplied credentials.
+
+ This creates a second connection, to the same host (and on ncacn_np on the same connection) as the first
+*/
+struct sec_auth_conn_state {
+ struct dcerpc_pipe *pipe2;
+ const struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct cli_credentials *credentials;
+ struct composite_context *ctx;
+ struct loadparm_context *lp_ctx;
+};
+
+static void dcerpc_secondary_auth_connection_bind(struct composite_context *ctx);
+static void dcerpc_secondary_auth_connection_continue(struct composite_context *ctx);
+
+_PUBLIC_ struct composite_context* dcerpc_secondary_auth_connection_send(struct dcerpc_pipe *p,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+
+ struct composite_context *c, *secondary_conn_ctx;
+ struct sec_auth_conn_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct sec_auth_conn_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+ s->ctx = c;
+
+ s->binding = binding;
+ s->table = table;
+ s->credentials = credentials;
+ s->lp_ctx = lp_ctx;
+
+ secondary_conn_ctx = dcerpc_secondary_connection_send(p, binding);
+
+ if (composite_nomem(secondary_conn_ctx, s->ctx)) {
+ talloc_free(c);
+ return NULL;
+ }
+
+ composite_continue(s->ctx, secondary_conn_ctx, dcerpc_secondary_auth_connection_bind,
+ s);
+ return c;
+}
+
+/*
+ Stage 2 of secondary_auth_connection:
+ Having made the secondary connection, we will need to do an (authenticated) bind
+*/
+static void dcerpc_secondary_auth_connection_bind(struct composite_context *ctx)
+{
+ struct composite_context *secondary_auth_ctx;
+ struct sec_auth_conn_state *s = talloc_get_type(ctx->async.private_data,
+ struct sec_auth_conn_state);
+
+ s->ctx->status = dcerpc_secondary_connection_recv(ctx, &s->pipe2);
+ if (!composite_is_ok(s->ctx)) return;
+
+ secondary_auth_ctx = dcerpc_pipe_auth_send(s->pipe2, s->binding, s->table, s->credentials,
+ s->lp_ctx);
+ composite_continue(s->ctx, secondary_auth_ctx, dcerpc_secondary_auth_connection_continue, s);
+
+}
+
+/*
+ Stage 3 of secondary_auth_connection: Receive result of authenticated bind request
+*/
+static void dcerpc_secondary_auth_connection_continue(struct composite_context *ctx)
+{
+ struct sec_auth_conn_state *s = talloc_get_type(ctx->async.private_data,
+ struct sec_auth_conn_state);
+
+ s->ctx->status = dcerpc_pipe_auth_recv(ctx, s, &s->pipe2);
+ if (!composite_is_ok(s->ctx)) return;
+
+ composite_done(s->ctx);
+}
+
+/*
+ Receive an authenticated pipe, created as a secondary connection
+*/
+_PUBLIC_ NTSTATUS dcerpc_secondary_auth_connection_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p)
+{
+ NTSTATUS status = composite_wait(c);
+ struct sec_auth_conn_state *s;
+
+ s = talloc_get_type(c->private_data, struct sec_auth_conn_state);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *p = talloc_steal(mem_ctx, s->pipe2);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+_PUBLIC_ NTSTATUS dcerpc_secondary_auth_connection(struct dcerpc_pipe *p,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p2)
+{
+ struct composite_context *c;
+
+ c = dcerpc_secondary_auth_connection_send(p, binding, table,
+ credentials, lp_ctx);
+ return dcerpc_secondary_auth_connection_recv(c, mem_ctx, p2);
+}
diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c
new file mode 100644
index 0000000..259de71
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_smb.c
@@ -0,0 +1,310 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc over SMB transport
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003
+
+ 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/filesys.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/smb/smb_constants.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "libcli/composite/composite.h"
+
+#undef strncasecmp
+
+/* transport private information used by SMB pipe transport */
+struct smb_private {
+ DATA_BLOB session_key;
+
+ /*
+ * these are needed to open a secondary connection
+ */
+ struct smbXcli_conn *conn;
+ struct smbXcli_session *session;
+ struct smbXcli_tcon *tcon;
+ uint32_t timeout_msec;
+};
+
+/*
+ fetch the user session key
+*/
+static NTSTATUS smb_session_key(struct dcecli_connection *c, DATA_BLOB *session_key)
+{
+ struct smb_private *smb = talloc_get_type_abort(
+ c->transport.private_data, struct smb_private);
+
+ if (smb == NULL) return NT_STATUS_CONNECTION_DISCONNECTED;
+
+ if (smb->session_key.length == 0) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ *session_key = smb->session_key;
+ return NT_STATUS_OK;
+}
+
+struct dcerpc_pipe_open_smb_state {
+ struct dcecli_connection *c;
+ struct composite_context *ctx;
+
+ const char *fname;
+
+ struct smb_private *smb;
+};
+
+static void dcerpc_pipe_open_smb_done(struct tevent_req *subreq);
+
+struct composite_context *dcerpc_pipe_open_smb_send(struct dcecli_connection *c,
+ struct smbXcli_conn *conn,
+ struct smbXcli_session *session,
+ struct smbXcli_tcon *tcon,
+ uint32_t timeout_msec,
+ const char *pipe_name)
+{
+ struct composite_context *ctx;
+ struct dcerpc_pipe_open_smb_state *state;
+ uint16_t pid = 0;
+ struct tevent_req *subreq;
+
+ ctx = composite_create(c, c->event_ctx);
+ if (ctx == NULL) return NULL;
+
+ state = talloc(ctx, struct dcerpc_pipe_open_smb_state);
+ if (composite_nomem(state, ctx)) return ctx;
+ ctx->private_data = state;
+
+ state->c = c;
+ state->ctx = ctx;
+
+ if ((strncasecmp(pipe_name, "/pipe/", 6) == 0) ||
+ (strncasecmp(pipe_name, "\\pipe\\", 6) == 0)) {
+ pipe_name += 6;
+ }
+ if ((strncasecmp(pipe_name, "/", 1) == 0) ||
+ (strncasecmp(pipe_name, "\\", 1) == 0)) {
+ pipe_name += 1;
+ }
+ state->fname = talloc_strdup(state, pipe_name);
+ if (composite_nomem(state->fname, ctx)) return ctx;
+
+ state->smb = talloc_zero(state, struct smb_private);
+ if (composite_nomem(state->smb, ctx)) return ctx;
+
+ state->smb->conn = conn;
+ state->smb->session = session;
+ state->smb->tcon = tcon;
+ state->smb->timeout_msec = timeout_msec;
+
+ state->c->server_name = strupper_talloc(state->c,
+ smbXcli_conn_remote_name(conn));
+ if (composite_nomem(state->c->server_name, ctx)) return ctx;
+
+ ctx->status = smbXcli_session_application_key(session,
+ state->smb,
+ &state->smb->session_key);
+ if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_NO_USER_SESSION_KEY)) {
+ state->smb->session_key = data_blob_null;
+ ctx->status = NT_STATUS_OK;
+ }
+ if (!composite_is_ok(ctx)) return ctx;
+
+ subreq = tstream_smbXcli_np_open_send(state, c->event_ctx,
+ conn, session, tcon, pid,
+ timeout_msec, state->fname);
+ if (composite_nomem(subreq, ctx)) return ctx;
+ tevent_req_set_callback(subreq, dcerpc_pipe_open_smb_done, state);
+
+ return ctx;
+}
+
+static void dcerpc_pipe_open_smb_done(struct tevent_req *subreq)
+{
+ struct dcerpc_pipe_open_smb_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcerpc_pipe_open_smb_state);
+ struct composite_context *ctx = state->ctx;
+ struct dcecli_connection *c = state->c;
+ uint16_t enc_cipher;
+
+ ctx->status = tstream_smbXcli_np_open_recv(subreq,
+ state->smb,
+ &state->c->transport.stream);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(ctx)) return;
+
+ state->c->transport.write_queue =
+ tevent_queue_create(state->c, "dcerpc_smb write queue");
+ if (composite_nomem(state->c->transport.write_queue, ctx)) return;
+
+ /*
+ fill in the transport methods
+ */
+ c->transport.transport = NCACN_NP;
+ c->transport.private_data = NULL;
+
+ /*
+ * Windows uses 4280 for ncacn_np,
+ * so we also use it, this is what our
+ * tstream_smbXcli_np code relies on.
+ */
+ c->srv_max_xmit_frag = 4280;
+ c->srv_max_recv_frag = 4280;
+
+ /* Over-ride the default session key with the SMB session key */
+ c->security_state.session_key = smb_session_key;
+
+ enc_cipher = smb2cli_session_get_encryption_cipher(state->smb->session);
+ switch (enc_cipher) {
+ case SMB2_ENCRYPTION_AES128_CCM:
+ case SMB2_ENCRYPTION_AES128_GCM:
+ c->transport.encrypted = true;
+ break;
+ default:
+ c->transport.encrypted = false;
+ }
+
+ c->transport.private_data = talloc_move(c, &state->smb);
+
+ composite_done(ctx);
+}
+
+NTSTATUS dcerpc_pipe_open_smb_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+_PUBLIC_ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
+ struct smbcli_tree *t,
+ const char *pipe_name)
+{
+ struct smbXcli_conn *conn;
+ struct smbXcli_session *session;
+ struct smbXcli_tcon *tcon;
+ struct composite_context *ctx;
+
+ conn = t->session->transport->conn;
+ session = t->session->smbXcli;
+ tcon = t->smbXcli;
+ smb1cli_tcon_set_id(tcon, t->tid);
+
+ /* if we don't have a binding on this pipe yet, then create one */
+ if (p->binding == NULL) {
+ struct dcerpc_binding *b;
+ NTSTATUS status;
+ const char *r = smbXcli_conn_remote_name(conn);
+ char *str;
+ SMB_ASSERT(r != NULL);
+ str = talloc_asprintf(p, "ncacn_np:%s", r);
+ if (str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcerpc_parse_binding(p, str, &b);
+ talloc_free(str);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ p->binding = b;
+ }
+
+ ctx = dcerpc_pipe_open_smb_send(p->conn,
+ conn, session, tcon,
+ DCERPC_REQUEST_TIMEOUT * 1000,
+ pipe_name);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return dcerpc_pipe_open_smb_recv(ctx);
+}
+
+_PUBLIC_ NTSTATUS dcerpc_pipe_open_smb2(struct dcerpc_pipe *p,
+ struct smb2_tree *t,
+ const char *pipe_name)
+{
+ struct smbXcli_conn *conn;
+ struct smbXcli_session *session;
+ struct smbXcli_tcon *tcon;
+ struct composite_context *ctx;
+
+ conn = t->session->transport->conn;
+ session = t->session->smbXcli;
+ tcon = t->smbXcli;
+
+ /* if we don't have a binding on this pipe yet, then create one */
+ if (p->binding == NULL) {
+ struct dcerpc_binding *b;
+ NTSTATUS status;
+ const char *r = smbXcli_conn_remote_name(conn);
+ char *str;
+ SMB_ASSERT(r != NULL);
+ str = talloc_asprintf(p, "ncacn_np:%s", r);
+ if (str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcerpc_parse_binding(p, str, &b);
+ talloc_free(str);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ p->binding = b;
+ }
+
+ ctx = dcerpc_pipe_open_smb_send(p->conn,
+ conn, session, tcon,
+ DCERPC_REQUEST_TIMEOUT * 1000,
+ pipe_name);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return dcerpc_pipe_open_smb_recv(ctx);
+}
+
+struct composite_context *dcerpc_secondary_smb_send(struct dcecli_connection *c1,
+ struct dcecli_connection *c2,
+ const char *pipe_name)
+{
+ struct smb_private *smb;
+
+ if (c1->transport.transport != NCACN_NP) return NULL;
+
+ smb = talloc_get_type(c1->transport.private_data, struct smb_private);
+ if (!smb) return NULL;
+
+ return dcerpc_pipe_open_smb_send(c2,
+ smb->conn,
+ smb->session,
+ smb->tcon,
+ smb->timeout_msec,
+ pipe_name);
+}
+
+NTSTATUS dcerpc_secondary_smb_recv(struct composite_context *c)
+{
+ return dcerpc_pipe_open_smb_recv(c);
+}
diff --git a/source4/librpc/rpc/dcerpc_sock.c b/source4/librpc/rpc/dcerpc_sock.c
new file mode 100644
index 0000000..ec5a5ca
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_sock.c
@@ -0,0 +1,499 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc over standard sockets transport
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Rafal Szczesniak 2006
+
+ 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/filesys.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/composite/composite.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/rpc/rpc_common.h"
+
+struct pipe_open_socket_state {
+ struct dcecli_connection *conn;
+ struct socket_context *socket_ctx;
+ struct socket_address *localaddr;
+ struct socket_address *server;
+ const char *target_hostname;
+ enum dcerpc_transport_t transport;
+ struct socket_address *client;
+};
+
+
+static void continue_socket_connect(struct composite_context *ctx)
+{
+ struct dcecli_connection *conn;
+ struct composite_context *c = talloc_get_type_abort(
+ ctx->async.private_data, struct composite_context);
+ struct pipe_open_socket_state *s = talloc_get_type_abort(
+ c->private_data, struct pipe_open_socket_state);
+ int rc;
+ int sock_fd;
+
+ /* make it easier to write a function calls */
+ conn = s->conn;
+
+ c->status = socket_connect_recv(ctx);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DBG_NOTICE("Failed to connect host %s on port %d - %s\n",
+ s->server->addr, s->server->port,
+ nt_errstr(c->status));
+ composite_error(c, c->status);
+ return;
+ }
+
+ s->client = socket_get_my_addr(s->socket_ctx, s);
+ if (s->client == NULL) {
+ TALLOC_FREE(s->socket_ctx);
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ sock_fd = socket_get_fd(s->socket_ctx);
+ if (sock_fd == -1) {
+ TALLOC_FREE(s->socket_ctx);
+ composite_error(c, NT_STATUS_INVALID_HANDLE);
+ return;
+ }
+ socket_set_flags(s->socket_ctx, SOCKET_FLAG_NOCLOSE);
+ TALLOC_FREE(s->socket_ctx);
+
+ /*
+ fill in the transport methods
+ */
+ conn->transport.transport = s->transport;
+ conn->transport.private_data = NULL;
+
+ /*
+ * Windows uses 5840 for ncacn_ip_tcp,
+ * so we also use it (for every transport which uses bsd sockets)
+ */
+ conn->srv_max_xmit_frag = 5840;
+ conn->srv_max_recv_frag = 5840;
+
+ conn->transport.pending_reads = 0;
+ conn->server_name = strupper_talloc(conn, s->target_hostname);
+
+ rc = tstream_bsd_existing_socket(conn, sock_fd,
+ &conn->transport.stream);
+ if (rc < 0) {
+ close(sock_fd);
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ conn->transport.write_queue =
+ tevent_queue_create(conn, "dcerpc sock write queue");
+ if (conn->transport.write_queue == NULL) {
+ TALLOC_FREE(conn->transport.stream);
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ /* ensure we don't get SIGPIPE */
+ BlockSignals(true, SIGPIPE);
+
+ composite_done(c);
+}
+
+
+static struct composite_context *dcerpc_pipe_open_socket_send(TALLOC_CTX *mem_ctx,
+ struct dcecli_connection *cn,
+ struct socket_address *localaddr,
+ struct socket_address *server,
+ const char *target_hostname,
+ const char *full_path,
+ enum dcerpc_transport_t transport)
+{
+ struct composite_context *c;
+ struct pipe_open_socket_state *s;
+ struct composite_context *conn_req;
+
+ c = composite_create(mem_ctx, cn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_open_socket_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->conn = cn;
+ s->transport = transport;
+ if (localaddr) {
+ s->localaddr = socket_address_copy(s, localaddr);
+ if (composite_nomem(s->localaddr, c)) return c;
+ }
+ s->server = socket_address_copy(s, server);
+ if (composite_nomem(s->server, c)) return c;
+ if (target_hostname) {
+ s->target_hostname = talloc_strdup(s, target_hostname);
+ if (composite_nomem(s->target_hostname, c)) return c;
+ }
+
+ c->status = socket_create(s, server->family, SOCKET_TYPE_STREAM,
+ &s->socket_ctx, 0);
+ if (!composite_is_ok(c)) return c;
+
+ conn_req = socket_connect_send(s->socket_ctx, s->localaddr, s->server, 0,
+ c->event_ctx);
+ composite_continue(c, conn_req, continue_socket_connect, c);
+ return c;
+}
+
+static NTSTATUS dcerpc_pipe_open_socket_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address **localaddr)
+{
+ NTSTATUS status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct pipe_open_socket_state *s =
+ talloc_get_type_abort(c->private_data,
+ struct pipe_open_socket_state);
+
+ if (localaddr != NULL) {
+ *localaddr = talloc_move(mem_ctx, &s->client);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+struct pipe_tcp_state {
+ const char *server;
+ const char *target_hostname;
+ const char **addresses;
+ uint32_t index;
+ uint32_t port;
+ struct socket_address *localaddr;
+ struct socket_address *srvaddr;
+ struct resolve_context *resolve_ctx;
+ struct dcecli_connection *conn;
+ struct nbt_name name;
+ char *local_address;
+ char *remote_address;
+};
+
+
+static void continue_ip_open_socket(struct composite_context *ctx);
+static void continue_ip_resolve_name(struct composite_context *ctx);
+
+static void continue_ip_resolve_name(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type_abort(
+ ctx->async.private_data, struct composite_context);
+ struct pipe_tcp_state *s = talloc_get_type_abort(
+ c->private_data, struct pipe_tcp_state);
+ struct composite_context *sock_ip_req;
+
+ c->status = resolve_name_multiple_recv(ctx, s, &s->addresses);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare server address using host ip:port and transport name */
+ s->srvaddr = socket_address_from_strings(s->conn, "ip", s->addresses[s->index], s->port);
+ s->index++;
+ if (composite_nomem(s->srvaddr, c)) return;
+
+ sock_ip_req = dcerpc_pipe_open_socket_send(c, s->conn, s->localaddr,
+ s->srvaddr, s->target_hostname,
+ NULL,
+ NCACN_IP_TCP);
+ composite_continue(c, sock_ip_req, continue_ip_open_socket, c);
+}
+
+
+/*
+ Stage 2 of dcerpc_pipe_open_tcp_send: receive result of pipe open request
+ on IP transport.
+*/
+static void continue_ip_open_socket(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type_abort(
+ ctx->async.private_data, struct composite_context);
+ struct pipe_tcp_state *s = talloc_get_type_abort(
+ c->private_data, struct pipe_tcp_state);
+ struct socket_address *localaddr = NULL;
+
+ /* receive result socket open request */
+ c->status = dcerpc_pipe_open_socket_recv(ctx, s, &localaddr);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ /* something went wrong... */
+ DBG_NOTICE("Failed to connect host %s (%s) on port %d - %s.\n",
+ s->addresses[s->index - 1], s->target_hostname,
+ s->port, nt_errstr(c->status));
+ if (s->addresses[s->index]) {
+ struct composite_context *sock_ip_req;
+ talloc_free(s->srvaddr);
+ /* prepare server address using host ip:port and transport name */
+ s->srvaddr = socket_address_from_strings(s->conn, "ip", s->addresses[s->index], s->port);
+ s->index++;
+ if (composite_nomem(s->srvaddr, c)) return;
+
+ sock_ip_req = dcerpc_pipe_open_socket_send(c, s->conn, s->localaddr,
+ s->srvaddr, s->target_hostname,
+ NULL,
+ NCACN_IP_TCP);
+ composite_continue(c, sock_ip_req, continue_ip_open_socket, c);
+
+ return;
+ } else {
+ composite_error(c, c->status);
+ return;
+ }
+ }
+
+ s->local_address = talloc_strdup(s, localaddr->addr);
+ if (composite_nomem(s->local_address, c)) return;
+ s->remote_address = talloc_strdup(s, s->addresses[s->index - 1]);
+ if (composite_nomem(s->remote_address, c)) return;
+
+ composite_done(c);
+}
+
+/*
+ Send rpc pipe open request to given host:port using
+ tcp/ip transport
+*/
+struct composite_context* dcerpc_pipe_open_tcp_send(struct dcecli_connection *conn,
+ const char *localaddr,
+ const char *server,
+ const char *target_hostname,
+ uint32_t port,
+ struct resolve_context *resolve_ctx)
+{
+ struct composite_context *c;
+ struct pipe_tcp_state *s;
+ struct composite_context *resolve_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(conn, conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_tcp_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store input parameters in state structure */
+ s->server = talloc_strdup(c, server);
+ if (composite_nomem(s->server, c)) return c;
+ if (target_hostname) {
+ s->target_hostname = talloc_strdup(c, target_hostname);
+ if (composite_nomem(s->target_hostname, c)) return c;
+ }
+ s->port = port;
+ s->conn = conn;
+ s->resolve_ctx = resolve_ctx;
+ if (localaddr) {
+ s->localaddr = socket_address_from_strings(s, "ip", localaddr, 0);
+ /* if there is no localaddr, we pass NULL for
+ s->localaddr, which is handled by the socket libraries as
+ meaning no local binding address specified */
+ }
+
+ make_nbt_name_server(&s->name, s->server);
+ resolve_req = resolve_name_send(resolve_ctx, s, &s->name, c->event_ctx);
+ composite_continue(c, resolve_req, continue_ip_resolve_name, c);
+ return c;
+}
+
+/*
+ Receive result of pipe open request on tcp/ip
+*/
+NTSTATUS dcerpc_pipe_open_tcp_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ char **localaddr,
+ char **remoteaddr)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct pipe_tcp_state *s = talloc_get_type_abort(
+ c->private_data, struct pipe_tcp_state);
+
+ if (localaddr != NULL) {
+ *localaddr = talloc_move(mem_ctx, &s->local_address);
+ }
+ if (remoteaddr != NULL) {
+ *remoteaddr = talloc_move(mem_ctx, &s->remote_address);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_unix_state {
+ const char *path;
+ struct socket_address *srvaddr;
+ struct dcecli_connection *conn;
+};
+
+
+/*
+ Stage 2 of dcerpc_pipe_open_unix_stream_send: receive result of pipe open
+ request on unix socket.
+*/
+static void continue_unix_open_socket(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type_abort(
+ ctx->async.private_data, struct composite_context);
+
+ c->status = dcerpc_pipe_open_socket_recv(ctx, NULL, NULL);
+ if (NT_STATUS_IS_OK(c->status)) {
+ composite_done(c);
+ return;
+ }
+
+ composite_error(c, c->status);
+}
+
+
+/*
+ Send pipe open request on unix socket
+*/
+struct composite_context *dcerpc_pipe_open_unix_stream_send(struct dcecli_connection *conn,
+ const char *path)
+{
+ struct composite_context *c;
+ struct composite_context *sock_unix_req;
+ struct pipe_unix_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(conn, conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_unix_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in state structure */
+ s->path = talloc_strdup(c, path);
+ if (composite_nomem(s->path, c)) return c;
+ s->conn = conn;
+
+ /* prepare server address using socket path and transport name */
+ s->srvaddr = socket_address_from_strings(conn, "unix", s->path, 0);
+ if (composite_nomem(s->srvaddr, c)) return c;
+
+ /* send socket open request */
+ sock_unix_req = dcerpc_pipe_open_socket_send(c, s->conn, NULL,
+ s->srvaddr, NULL,
+ s->path,
+ NCALRPC);
+ composite_continue(c, sock_unix_req, continue_unix_open_socket, c);
+ return c;
+}
+
+
+/*
+ Receive result of pipe open request on unix socket
+*/
+NTSTATUS dcerpc_pipe_open_unix_stream_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Stage 2 of dcerpc_pipe_open_pipe_send: receive socket open request
+*/
+static void continue_np_open_socket(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type_abort(
+ ctx->async.private_data, struct composite_context);
+
+ c->status = dcerpc_pipe_open_socket_recv(ctx, NULL, NULL);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Send pipe open request on ncalrpc
+*/
+struct composite_context* dcerpc_pipe_open_pipe_send(struct dcecli_connection *conn,
+ const char *ncalrpc_dir,
+ const char *identifier)
+{
+ char *canon = NULL;
+
+ struct composite_context *c;
+ struct composite_context *sock_np_req;
+ struct pipe_unix_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(conn, conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_unix_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in state structure */
+ canon = talloc_strdup(s, identifier);
+ if (composite_nomem(canon, c)) return c;
+ s->conn = conn;
+
+ string_replace(canon, '/', '\\');
+ s->path = talloc_asprintf(canon, "%s/%s", ncalrpc_dir, canon);
+ if (composite_nomem(s->path, c)) return c;
+
+ /* prepare server address using path and transport name */
+ s->srvaddr = socket_address_from_strings(conn, "unix", s->path, 0);
+ if (composite_nomem(s->srvaddr, c)) return c;
+
+ /* send socket open request */
+ sock_np_req = dcerpc_pipe_open_socket_send(c, s->conn, NULL, s->srvaddr, NULL, s->path, NCALRPC);
+ composite_continue(c, sock_np_req, continue_np_open_socket, c);
+ return c;
+}
+
+
+/*
+ Receive result of pipe open request on ncalrpc
+*/
+NTSTATUS dcerpc_pipe_open_pipe_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Open a rpc pipe on a named pipe - sync version
+*/
+NTSTATUS dcerpc_pipe_open_pipe(struct dcecli_connection *conn, const char *ncalrpc_dir, const char *identifier)
+{
+ struct composite_context *c = dcerpc_pipe_open_pipe_send(conn, ncalrpc_dir, identifier);
+ return dcerpc_pipe_open_pipe_recv(c);
+}
diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c
new file mode 100644
index 0000000..6ea27a8
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_util.c
@@ -0,0 +1,811 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc utility functions
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Rafal Szczesniak 2006
+
+ 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 "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "librpc/rpc/rpc_common.h"
+
+/*
+ find a dcerpc call on an interface by name
+*/
+const struct ndr_interface_call *dcerpc_iface_find_call(const struct ndr_interface_table *iface,
+ const char *name)
+{
+ int i;
+ for (i=0;i<iface->num_calls;i++) {
+ if (strcmp(iface->calls[i].name, name) == 0) {
+ return &iface->calls[i];
+ }
+ }
+ return NULL;
+}
+
+struct epm_map_binding_state {
+ struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct dcerpc_pipe *pipe;
+ struct policy_handle handle;
+ struct GUID object;
+ struct epm_twr_t twr;
+ struct epm_twr_t *twr_r;
+ uint32_t num_towers;
+ struct epm_Map r;
+};
+
+
+static void continue_epm_recv_binding(struct composite_context *ctx);
+static void continue_epm_map(struct tevent_req *subreq);
+
+
+/*
+ Stage 2 of epm_map_binding: Receive connected rpc pipe and send endpoint
+ mapping rpc request
+*/
+static void continue_epm_recv_binding(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct epm_map_binding_state *s = talloc_get_type(c->private_data,
+ struct epm_map_binding_state);
+ struct tevent_req *subreq;
+
+ /* receive result of rpc pipe connect request */
+ c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->pipe);
+ if (!composite_is_ok(c)) return;
+
+ c->status = dcerpc_binding_build_tower(s->pipe, s->binding, &s->twr.tower);
+ if (!composite_is_ok(c)) return;
+
+ /* with some nice pretty paper around it of course */
+ s->r.in.object = &s->object;
+ s->r.in.map_tower = &s->twr;
+ s->r.in.entry_handle = &s->handle;
+ s->r.in.max_towers = 1;
+ s->r.out.entry_handle = &s->handle;
+ s->r.out.num_towers = &s->num_towers;
+
+ /* send request for an endpoint mapping - a rpc request on connected pipe */
+ subreq = dcerpc_epm_Map_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->r);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_epm_map, c);
+}
+
+
+/*
+ Stage 3 of epm_map_binding: Receive endpoint mapping and provide binding details
+*/
+static void continue_epm_map(struct tevent_req *subreq)
+{
+ struct composite_context *c = tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct epm_map_binding_state *s = talloc_get_type(c->private_data,
+ struct epm_map_binding_state);
+ const char *endpoint;
+
+ /* receive result of a rpc request */
+ c->status = dcerpc_epm_Map_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* check the details */
+ if (s->r.out.result != 0 || *s->r.out.num_towers != 1) {
+ composite_error(c, NT_STATUS_PORT_UNREACHABLE);
+ return;
+ }
+
+ s->twr_r = s->r.out.towers[0].twr;
+ if (s->twr_r == NULL) {
+ composite_error(c, NT_STATUS_PORT_UNREACHABLE);
+ return;
+ }
+
+ if (s->twr_r->tower.num_floors != s->twr.tower.num_floors ||
+ s->twr_r->tower.floors[3].lhs.protocol != s->twr.tower.floors[3].lhs.protocol) {
+ composite_error(c, NT_STATUS_PORT_UNREACHABLE);
+ return;
+ }
+
+ /* get received endpoint */
+ endpoint = dcerpc_floor_get_rhs_data(s, &s->twr_r->tower.floors[3]);
+ if (composite_nomem(endpoint, c)) return;
+
+ c->status = dcerpc_binding_set_string_option(s->binding,
+ "endpoint",
+ endpoint);
+ if (!composite_is_ok(c)) {
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ Request for endpoint mapping of dcerpc binding - try to request for endpoint
+ unless there is default one.
+*/
+struct composite_context *dcerpc_epm_map_binding_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *creds,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct epm_map_binding_state *s;
+ struct composite_context *pipe_connect_req;
+ NTSTATUS status;
+ struct dcerpc_binding *epmapper_binding;
+ int i;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) {
+ return NULL;
+ }
+
+ s = talloc_zero(c, struct epm_map_binding_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->binding = binding;
+ s->object = dcerpc_binding_get_object(binding);
+ s->table = table;
+
+ c->status = dcerpc_binding_set_abstract_syntax(binding,
+ &table->syntax_id);
+ if (!composite_is_ok(c)) {
+ return c;
+ }
+
+ /*
+ First, check if there is a default endpoint specified in the IDL
+ */
+ for (i = 0; i < table->endpoints->count; i++) {
+ struct dcerpc_binding *default_binding;
+ enum dcerpc_transport_t transport;
+ enum dcerpc_transport_t dtransport;
+ const char *dendpoint = NULL;
+
+ status = dcerpc_parse_binding(s,
+ table->endpoints->names[i],
+ &default_binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+
+ transport = dcerpc_binding_get_transport(binding);
+ dtransport = dcerpc_binding_get_transport(default_binding);
+ if (transport == NCA_UNKNOWN) {
+ c->status = dcerpc_binding_set_transport(binding,
+ dtransport);
+ if (!composite_is_ok(c)) {
+ return c;
+ }
+ transport = dtransport;
+ }
+
+ if (transport != dtransport) {
+ TALLOC_FREE(default_binding);
+ continue;
+ }
+
+ dendpoint = dcerpc_binding_get_string_option(default_binding,
+ "endpoint");
+ if (dendpoint == NULL) {
+ TALLOC_FREE(default_binding);
+ continue;
+ }
+
+ c->status = dcerpc_binding_set_string_option(binding,
+ "endpoint",
+ dendpoint);
+ if (!composite_is_ok(c)) {
+ return c;
+ }
+
+ TALLOC_FREE(default_binding);
+ composite_done(c);
+ return c;
+ }
+
+ epmapper_binding = dcerpc_binding_dup(s, binding);
+ if (composite_nomem(epmapper_binding, c)) return c;
+
+ /* basic endpoint mapping data */
+ c->status = dcerpc_binding_set_string_option(epmapper_binding,
+ "endpoint", NULL);
+ if (!composite_is_ok(c)) {
+ return c;
+ }
+ c->status = dcerpc_binding_set_flags(epmapper_binding, 0, UINT32_MAX);
+ if (!composite_is_ok(c)) {
+ return c;
+ }
+ c->status = dcerpc_binding_set_assoc_group_id(epmapper_binding, 0);
+ if (!composite_is_ok(c)) {
+ return c;
+ }
+ c->status = dcerpc_binding_set_object(epmapper_binding, GUID_zero());
+ if (!composite_is_ok(c)) {
+ return c;
+ }
+
+ /* initiate rpc pipe connection */
+ pipe_connect_req = dcerpc_pipe_connect_b_send(s, epmapper_binding,
+ &ndr_table_epmapper,
+ creds, c->event_ctx,
+ lp_ctx);
+ if (composite_nomem(pipe_connect_req, c)) return c;
+
+ composite_continue(c, pipe_connect_req, continue_epm_recv_binding, c);
+ return c;
+}
+
+
+/*
+ Receive result of endpoint mapping request
+ */
+NTSTATUS dcerpc_epm_map_binding_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Get endpoint mapping for rpc connection
+*/
+_PUBLIC_ NTSTATUS dcerpc_epm_map_binding(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table, struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct cli_credentials *epm_creds;
+
+ epm_creds = cli_credentials_init_anon(mem_ctx);
+ if (epm_creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ c = dcerpc_epm_map_binding_send(mem_ctx, binding, table, epm_creds, ev, lp_ctx);
+ if (c == NULL) {
+ talloc_free(epm_creds);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_steal(c, epm_creds);
+ return dcerpc_epm_map_binding_recv(c);
+}
+
+
+struct pipe_auth_state {
+ struct dcerpc_pipe *pipe;
+ const struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct loadparm_context *lp_ctx;
+ struct cli_credentials *credentials;
+ unsigned int logon_retries;
+};
+
+
+static void continue_auth_schannel(struct composite_context *ctx);
+static void continue_auth(struct composite_context *ctx);
+static void continue_auth_none(struct composite_context *ctx);
+static void continue_ntlmssp_connection(struct composite_context *ctx);
+static void continue_spnego_after_wrong_pass(struct composite_context *ctx);
+
+
+/*
+ Stage 2 of pipe_auth: Receive result of schannel bind request
+*/
+static void continue_auth_schannel(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_bind_auth_schannel_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Stage 2 of pipe_auth: Receive result of authenticated bind request
+*/
+static void continue_auth(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_bind_auth_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+/*
+ Stage 2 of pipe_auth: Receive result of authenticated bind request, but handle fallbacks:
+ SPNEGO -> NTLMSSP
+*/
+static void continue_auth_auto(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_auth_state *s = talloc_get_type(c->private_data, struct pipe_auth_state);
+ struct composite_context *sec_conn_req;
+
+ c->status = dcerpc_bind_auth_recv(ctx);
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_INVALID_PARAMETER)) {
+ /*
+ * Retry with NTLMSSP auth as fallback
+ * send a request for secondary rpc connection
+ */
+ sec_conn_req = dcerpc_secondary_connection_send(s->pipe,
+ s->binding);
+ composite_continue(c, sec_conn_req, continue_ntlmssp_connection, c);
+ return;
+ } else if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_UNSUCCESSFUL)) {
+ /*
+ try a second time on any error. We don't just do it
+ on LOGON_FAILURE as some servers will give a
+ NT_STATUS_UNSUCCESSFUL on a authentication error on RPC
+ */
+ const char *principal;
+ const char *endpoint;
+
+ principal = gensec_get_target_principal(s->pipe->conn->security_state.generic_state);
+ if (principal == NULL) {
+ const char *hostname = gensec_get_target_hostname(s->pipe->conn->security_state.generic_state);
+ const char *service = gensec_get_target_service(s->pipe->conn->security_state.generic_state);
+ if (hostname != NULL && service != NULL) {
+ principal = talloc_asprintf(c, "%s/%s", service, hostname);
+ }
+ }
+
+ endpoint = dcerpc_binding_get_string_option(s->binding, "endpoint");
+
+ if ((cli_credentials_failed_kerberos_login(s->credentials, principal, &s->logon_retries) ||
+ cli_credentials_wrong_password(s->credentials)) &&
+ endpoint != NULL) {
+ /*
+ * Retry SPNEGO with a better password
+ * send a request for secondary rpc connection
+ */
+ sec_conn_req = dcerpc_secondary_connection_send(s->pipe,
+ s->binding);
+ composite_continue(c, sec_conn_req, continue_spnego_after_wrong_pass, c);
+ return;
+ }
+ }
+
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+/*
+ Stage 3 of pipe_auth (fallback to NTLMSSP case): Receive secondary
+ rpc connection (the first one can't be used any more, due to the
+ bind nak) and perform authenticated bind request
+*/
+static void continue_ntlmssp_connection(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct pipe_auth_state *s;
+ struct composite_context *auth_req;
+ struct dcerpc_pipe *p2;
+ void *pp;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct pipe_auth_state);
+
+ /* receive secondary rpc connection */
+ c->status = dcerpc_secondary_connection_recv(ctx, &p2);
+ if (!composite_is_ok(c)) return;
+
+
+ /* this is a rather strange situation. When
+ we come into the routine, s is a child of s->pipe, and
+ when we created p2 above, it also became a child of
+ s->pipe.
+
+ Now we want p2 to be a parent of s->pipe, and we want s to
+ be a parent of both of them! If we don't do this very
+ carefully we end up creating a talloc loop
+ */
+
+ /* we need the new contexts to hang off the same context
+ that s->pipe is on, but the only way to get that is
+ via talloc_parent() */
+ pp = talloc_parent(s->pipe);
+
+ /* promote s to be at the top */
+ talloc_steal(pp, s);
+
+ /* and put p2 under s */
+ talloc_steal(s, p2);
+
+ /* now put s->pipe under p2 */
+ talloc_steal(p2, s->pipe);
+
+ s->pipe = p2;
+
+ /* initiate a authenticated bind */
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lpcfg_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ dcerpc_auth_level(s->pipe->conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth, c);
+}
+
+/*
+ Stage 3 of pipe_auth (retry on wrong password): Receive secondary
+ rpc connection (the first one can't be used any more, due to the
+ bind nak) and perform authenticated bind request
+*/
+static void continue_spnego_after_wrong_pass(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct pipe_auth_state *s;
+ struct composite_context *auth_req;
+ struct dcerpc_pipe *p2;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct pipe_auth_state);
+
+ /* receive secondary rpc connection */
+ c->status = dcerpc_secondary_connection_recv(ctx, &p2);
+ if (!composite_is_ok(c)) return;
+
+ talloc_steal(s, p2);
+ talloc_steal(p2, s->pipe);
+ s->pipe = p2;
+
+ /* initiate a authenticated bind */
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lpcfg_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_SPNEGO,
+ dcerpc_auth_level(s->pipe->conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth, c);
+}
+
+
+/*
+ Stage 2 of pipe_auth: Receive result of non-authenticated bind request
+*/
+static void continue_auth_none(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_bind_auth_none_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Request to perform an authenticated bind if required. Authentication
+ is determined using credentials passed and binding flags.
+*/
+struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_auth_state *s;
+ struct composite_context *auth_schannel_req;
+ struct composite_context *auth_req;
+ struct composite_context *auth_none_req;
+ struct dcecli_connection *conn;
+ uint8_t auth_type;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_auth_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in state structure */
+ s->binding = binding;
+ s->table = table;
+ s->credentials = credentials;
+ s->pipe = p;
+ s->lp_ctx = lp_ctx;
+
+ conn = s->pipe->conn;
+ conn->flags = dcerpc_binding_get_flags(binding);
+
+ if (DEBUGLVL(100)) {
+ conn->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ }
+
+ if (conn->transport.transport == NCALRPC) {
+ const char *v = dcerpc_binding_get_string_option(binding,
+ "auth_type");
+
+ if (v != NULL && strcmp(v, "ncalrpc_as_system") == 0) {
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lpcfg_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM,
+ DCERPC_AUTH_LEVEL_CONNECT,
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth, c);
+ return c;
+ }
+ }
+
+ if (cli_credentials_is_anonymous(s->credentials)) {
+ auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe, s->table);
+ composite_continue(c, auth_none_req, continue_auth_none, c);
+ return c;
+ }
+
+ if ((conn->flags & DCERPC_SCHANNEL) &&
+ !cli_credentials_get_netlogon_creds(s->credentials)) {
+ /* If we don't already have netlogon credentials for
+ * the schannel bind, then we have to get these
+ * first */
+ auth_schannel_req = dcerpc_bind_auth_schannel_send(c, s->pipe, s->table,
+ s->credentials, s->lp_ctx,
+ dcerpc_auth_level(conn));
+ composite_continue(c, auth_schannel_req, continue_auth_schannel, c);
+ return c;
+ }
+
+ /*
+ * we rely on the already authenticated CIFS connection
+ * if not doing sign or seal
+ */
+ if (conn->transport.transport == NCACN_NP &&
+ !(conn->flags & (DCERPC_PACKET|DCERPC_SIGN|DCERPC_SEAL))) {
+ auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe, s->table);
+ composite_continue(c, auth_none_req, continue_auth_none, c);
+ return c;
+ }
+
+
+ /* Perform an authenticated DCE-RPC bind
+ */
+ if (!(conn->flags & (DCERPC_CONNECT|DCERPC_SEAL|DCERPC_PACKET))) {
+ /*
+ we are doing an authenticated connection,
+ which needs to use [connect], [sign] or [seal].
+ If nothing is specified, we default to [sign] now.
+ This give roughly the same protection as
+ ncacn_np with smb signing.
+ */
+ conn->flags |= DCERPC_SIGN;
+ }
+
+ if (conn->flags & DCERPC_AUTH_SPNEGO) {
+ auth_type = DCERPC_AUTH_TYPE_SPNEGO;
+
+ } else if (conn->flags & DCERPC_AUTH_KRB5) {
+ auth_type = DCERPC_AUTH_TYPE_KRB5;
+
+ } else if (conn->flags & DCERPC_SCHANNEL) {
+ auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
+
+ } else if (conn->flags & DCERPC_AUTH_NTLM) {
+ auth_type = DCERPC_AUTH_TYPE_NTLMSSP;
+
+ } else {
+ /* try SPNEGO with fallback to NTLMSSP */
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lpcfg_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_SPNEGO,
+ dcerpc_auth_level(conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth_auto, c);
+ return c;
+ }
+
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lpcfg_gensec_settings(c, s->lp_ctx),
+ auth_type,
+ dcerpc_auth_level(conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth, c);
+ return c;
+}
+
+
+/*
+ Receive result of authenticated bind request on dcerpc pipe
+
+ This returns *p, which may be different to the one originally
+ supllied, as it rebinds to a new pipe due to authentication fallback
+
+*/
+NTSTATUS dcerpc_pipe_auth_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p)
+{
+ NTSTATUS status;
+
+ struct pipe_auth_state *s = talloc_get_type(c->private_data,
+ struct pipe_auth_state);
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *uuid_str = GUID_string(s->pipe, &s->table->syntax_id.uuid);
+ DEBUG(0, ("Failed to bind to uuid %s for %s %s\n", uuid_str,
+ dcerpc_binding_string(uuid_str, s->binding), nt_errstr(status)));
+ talloc_free(uuid_str);
+ } else {
+ talloc_steal(mem_ctx, s->pipe);
+ *p = s->pipe;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Perform an authenticated bind if needed - sync version
+
+ This may change *p, as it rebinds to a new pipe due to authentication fallback
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_auth(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p,
+ const struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+
+ c = dcerpc_pipe_auth_send(*p, binding, table, credentials, lp_ctx);
+ return dcerpc_pipe_auth_recv(c, mem_ctx, p);
+}
+
+
+NTSTATUS dcecli_generic_session_key(struct dcecli_connection *c,
+ DATA_BLOB *session_key)
+{
+ if (c != NULL) {
+ if (c->transport.transport != NCALRPC &&
+ c->transport.transport != NCACN_UNIX_STREAM)
+ {
+ return NT_STATUS_LOCAL_USER_SESSION_KEY;
+ }
+ }
+
+ return dcerpc_generic_session_key(session_key);
+}
+
+/*
+ fetch the user session key - may be default (above) or the SMB session key
+
+ The key is always truncated to 16 bytes
+*/
+_PUBLIC_ NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS status;
+ status = p->conn->security_state.session_key(p->conn, session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ session_key->length = MIN(session_key->length, 16);
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ bool dcerpc_transport_encrypted(struct dcerpc_pipe *p)
+{
+ if (p == NULL) {
+ return false;
+ }
+
+ if (p->conn == NULL) {
+ return false;
+ }
+
+ return p->conn->transport.encrypted;
+}
+
+/*
+ create a secondary context from a primary connection
+
+ this uses dcerpc_alter_context() to create a new dcerpc context_id
+*/
+_PUBLIC_ NTSTATUS dcerpc_secondary_context(struct dcerpc_pipe *p,
+ struct dcerpc_pipe **pp2,
+ const struct ndr_interface_table *table)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p2;
+ struct GUID *object = NULL;
+
+ p2 = talloc_zero(p, struct dcerpc_pipe);
+ if (p2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ p2->conn = talloc_reference(p2, p->conn);
+ p2->request_timeout = p->request_timeout;
+
+ p2->context_id = ++p->conn->next_context_id;
+
+ p2->syntax = table->syntax_id;
+
+ p2->transfer_syntax = p->transfer_syntax;
+
+ p2->binding = dcerpc_binding_dup(p2, p->binding);
+ if (p2->binding == NULL) {
+ talloc_free(p2);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p2->object = dcerpc_binding_get_object(p2->binding);
+ if (!GUID_all_zero(&p2->object)) {
+ object = &p2->object;
+ }
+
+ p2->binding_handle = dcerpc_pipe_binding_handle(p2, object, table);
+ if (p2->binding_handle == NULL) {
+ talloc_free(p2);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_alter_context(p2, p2, &p2->syntax, &p2->transfer_syntax);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(p2);
+ return status;
+ }
+
+ *pp2 = p2;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/librpc/rpc/pyrpc.c b/source4/librpc/rpc/pyrpc.c
new file mode 100644
index 0000000..309a6d7
--- /dev/null
+++ b/source4/librpc/rpc/pyrpc.c
@@ -0,0 +1,653 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+
+ 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 <Python.h>
+#include "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include <structmember.h>
+#include "librpc/rpc/pyrpc.h"
+#include "lib/events/events.h"
+#include "param/pyparam.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include "auth/credentials/pycredentials.h"
+#include "auth/gensec/gensec.h"
+
+void initbase(void);
+
+static PyTypeObject dcerpc_InterfaceType;
+
+static PyTypeObject *BaseObject_Type;
+
+static PyTypeObject *ndr_syntax_id_Type;
+
+static bool PyString_AsGUID(PyObject *object, struct GUID *uuid)
+{
+ NTSTATUS status;
+ status = GUID_from_string(PyUnicode_AsUTF8(object), uuid);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ return false;
+ }
+ return true;
+}
+
+static bool ndr_syntax_from_py_object(PyObject *object, struct ndr_syntax_id *syntax_id)
+{
+ ZERO_STRUCTP(syntax_id);
+
+ if (PyUnicode_Check(object)) {
+ return PyString_AsGUID(object, &syntax_id->uuid);
+ } else if (PyTuple_Check(object)) {
+ PyObject *item = NULL;
+ if (PyTuple_Size(object) < 1 || PyTuple_Size(object) > 2) {
+ PyErr_SetString(PyExc_ValueError, "Syntax ID tuple has invalid size");
+ return false;
+ }
+
+ item = PyTuple_GetItem(object, 0);
+ if (!PyUnicode_Check(item)) {
+ PyErr_SetString(PyExc_ValueError, "Expected GUID as first element in tuple");
+ return false;
+ }
+
+ if (!PyString_AsGUID(item, &syntax_id->uuid)) {
+ return false;
+ }
+
+ item = PyTuple_GetItem(object, 1);
+ if (!PyLong_Check(item)) {
+ PyErr_SetString(PyExc_ValueError, "Expected version as second element in tuple");
+ return false;
+ }
+
+ syntax_id->if_version = PyLong_AsLong(item);
+ return true;
+ }
+
+ PyErr_SetString(PyExc_TypeError, "Expected UUID or syntax id tuple");
+ return false;
+}
+
+static PyObject *py_iface_server_name(PyObject *obj, void *closure)
+{
+ const char *server_name;
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+
+ server_name = dcerpc_server_name(iface->pipe);
+ if (server_name == NULL)
+ Py_RETURN_NONE;
+
+ return PyUnicode_FromString(server_name);
+}
+
+static PyObject *py_ndr_syntax_id(struct ndr_syntax_id *syntax_id)
+{
+ PyObject *ret;
+ char *uuid_str;
+
+ uuid_str = GUID_string(NULL, &syntax_id->uuid);
+ if (uuid_str == NULL)
+ return NULL;
+
+ ret = Py_BuildValue("(s,i)", uuid_str, syntax_id->if_version);
+
+ talloc_free(uuid_str);
+
+ return ret;
+}
+
+static PyObject *py_iface_abstract_syntax(PyObject *obj, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+
+ return py_ndr_syntax_id(&iface->pipe->syntax);
+}
+
+static PyObject *py_iface_transfer_syntax(PyObject *obj, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+
+ return py_ndr_syntax_id(&iface->pipe->transfer_syntax);
+}
+
+static PyObject *py_iface_session_key(PyObject *obj, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+ DATA_BLOB session_key;
+
+ NTSTATUS status = dcerpc_fetch_session_key(iface->pipe, &session_key);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ return PyBytes_FromStringAndSize((const char *)session_key.data, session_key.length);
+}
+
+static PyObject *py_iface_user_session_key(PyObject *obj, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct gensec_security *security = NULL;
+ DATA_BLOB session_key = data_blob_null;
+ static PyObject *session_key_obj = NULL;
+
+ if (iface->pipe == NULL) {
+ PyErr_SetNTSTATUS(NT_STATUS_NO_USER_SESSION_KEY);
+ return NULL;
+ }
+
+ if (iface->pipe->conn == NULL) {
+ PyErr_SetNTSTATUS(NT_STATUS_NO_USER_SESSION_KEY);
+ return NULL;
+ }
+
+ if (iface->pipe->conn->security_state.generic_state == NULL) {
+ PyErr_SetNTSTATUS(NT_STATUS_NO_USER_SESSION_KEY);
+ return NULL;
+ }
+
+ security = iface->pipe->conn->security_state.generic_state;
+
+ mem_ctx = talloc_new(NULL);
+
+ status = gensec_session_key(security, mem_ctx, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ session_key_obj = PyBytes_FromStringAndSize((const char *)session_key.data,
+ session_key.length);
+ talloc_free(mem_ctx);
+ return session_key_obj;
+}
+
+static PyObject *py_iface_get_timeout(PyObject *obj, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+ uint32_t timeout;
+
+ timeout = dcerpc_binding_handle_set_timeout(iface->binding_handle, 0);
+ dcerpc_binding_handle_set_timeout(iface->binding_handle, timeout);
+
+ return PyLong_FromUnsignedLong(timeout);
+}
+
+static int py_iface_set_timeout(PyObject *obj, PyObject *value, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+ uint32_t timeout;
+
+ timeout = PyLong_AsUnsignedLong(value);
+ if (PyErr_Occurred() != NULL) {
+ return -1;
+ }
+
+ dcerpc_binding_handle_set_timeout(iface->binding_handle, timeout);
+ return 0;
+}
+
+static PyGetSetDef dcerpc_interface_getsetters[] = {
+ {
+ .name = discard_const_p(char, "server_name"),
+ .get = py_iface_server_name,
+ .doc = discard_const_p(char, "name of the server, if connected over SMB"),
+ },
+ {
+ .name = discard_const_p(char, "abstract_syntax"),
+ .get = py_iface_abstract_syntax,
+ .doc = discard_const_p(char, "syntax id of the abstract syntax"),
+ },
+ {
+ .name = discard_const_p(char, "transfer_syntax"),
+ .get = py_iface_transfer_syntax,
+ .doc = discard_const_p(char, "syntax id of the transfer syntax"),
+ },
+ {
+ .name = discard_const_p(char, "session_key"),
+ .get = py_iface_session_key,
+ .doc = discard_const_p(char, "session key (as used for blob encryption on LSA and SAMR)"),
+ },
+ {
+ .name = discard_const_p(char, "user_session_key"),
+ .get = py_iface_user_session_key,
+ .doc = discard_const_p(char, "user_session key (as used for blob encryption on DRSUAPI)"),
+ },
+ {
+ .name = discard_const_p(char, "request_timeout"),
+ .get = py_iface_get_timeout,
+ .set = py_iface_set_timeout,
+ .doc = discard_const_p(char, "request timeout, in seconds"),
+ },
+ { .name = NULL }
+};
+
+static PyObject *py_iface_request(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
+ int opnum;
+ DATA_BLOB data_in, data_out;
+ NTSTATUS status;
+ char *in_data;
+ Py_ssize_t in_length;
+ PyObject *ret;
+ PyObject *object = NULL;
+ struct GUID object_guid;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ uint32_t out_flags = 0;
+ const char *kwnames[] = { "opnum", "data", "object", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "is#|O:request",
+ discard_const_p(char *, kwnames), &opnum, &in_data, &in_length, &object)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ data_in.data = (uint8_t *)talloc_memdup(mem_ctx, in_data, in_length);
+ data_in.length = in_length;
+
+ ZERO_STRUCT(data_out);
+
+ if (object != NULL && !PyString_AsGUID(object, &object_guid)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ status = dcerpc_binding_handle_raw_call(iface->binding_handle,
+ object?&object_guid:NULL,
+ opnum,
+ 0, /* in_flags */
+ data_in.data,
+ data_in.length,
+ mem_ctx,
+ &data_out.data,
+ &data_out.length,
+ &out_flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetDCERPCStatus(iface->pipe, status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret = PyBytes_FromStringAndSize((char *)data_out.data, data_out.length);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static PyObject *py_iface_transport_encrypted(PyObject *self)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
+
+ if (dcerpc_transport_encrypted(iface->pipe)) {
+ Py_RETURN_TRUE;
+ }
+
+ Py_RETURN_FALSE;
+}
+
+static PyMethodDef dcerpc_interface_methods[] = {
+ { "request", PY_DISCARD_FUNC_SIG(PyCFunction, py_iface_request),
+ METH_VARARGS|METH_KEYWORDS,
+ "S.request(opnum, data, object=None) -> data\n"
+ "Make a raw request" },
+ { "transport_encrypted", PY_DISCARD_FUNC_SIG(PyCFunction, py_iface_transport_encrypted),
+ METH_NOARGS,
+ "Check if the DCE transport is encrypted" },
+ { NULL, NULL, 0, NULL },
+};
+
+static void dcerpc_interface_dealloc(PyObject* self)
+{
+ dcerpc_InterfaceObject *interface = (dcerpc_InterfaceObject *)self;
+
+ struct tevent_context *ev_save = talloc_reparent(
+ interface->mem_ctx, NULL, interface->ev);
+ SMB_ASSERT(ev_save != NULL);
+
+ interface->binding_handle = NULL;
+ interface->pipe = NULL;
+
+ /*
+ * Free everything *except* the event context, which must go
+ * away last
+ */
+ TALLOC_FREE(interface->mem_ctx);
+
+ /*
+ * Now wish a fond goodbye to the event context itself
+ */
+ talloc_unlink(NULL, ev_save);
+ self->ob_type->tp_free(self);
+}
+
+static PyObject *dcerpc_interface_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *ret;
+ const char *binding_string = NULL;
+ PyObject *py_lp_ctx = Py_None;
+ PyObject *py_credentials = Py_None;
+ PyObject *syntax = Py_None;
+ PyObject *py_basis = Py_None;
+ const char *kwnames[] = {
+ "binding", "syntax", "lp_ctx", "credentials", "basis_connection", NULL
+ };
+ static struct ndr_interface_table dummy_table;
+ static struct ndr_interface_string_array dummy_endpoints;
+ PyObject *args2 = Py_None;
+ PyObject *kwargs2 = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OOO:connect", discard_const_p(char *, kwnames), &binding_string, &syntax, &py_lp_ctx, &py_credentials, &py_basis)) {
+ return NULL;
+ }
+
+ if (strncmp(binding_string, "irpc:", 5) == 0) {
+ PyErr_SetString(PyExc_ValueError, "irpc: transport not supported");
+ return NULL;
+ }
+
+ /*
+ * Fill a dummy interface table struct. TODO: In the future, we should
+ * rather just allow connecting without requiring an interface table.
+ *
+ * We just fill the syntax during the connect, but keep the memory valid
+ * the whole time.
+ */
+ if (!ndr_syntax_from_py_object(syntax, &dummy_table.syntax_id)) {
+ return NULL;
+ }
+
+ /*
+ * Initialise the endpoints list in dummy_table if required
+ */
+ if (dummy_table.endpoints == NULL) {
+ dummy_table.endpoints = &dummy_endpoints;
+ }
+
+ args2 = Py_BuildValue("(s)", binding_string);
+ if (args2 == NULL) {
+ return NULL;
+ }
+
+ kwargs2 = Py_BuildValue("{s:O,s:O,s:O}",
+ "lp_ctx", py_lp_ctx,
+ "credentials", py_credentials,
+ "basis_connection", py_basis);
+ if (kwargs2 == NULL) {
+ Py_DECREF(args2);
+ return NULL;
+ }
+
+ ret = py_dcerpc_interface_init_helper(type, args2, kwargs2, &dummy_table);
+ ZERO_STRUCT(dummy_table.syntax_id);
+ Py_DECREF(args2);
+ Py_DECREF(kwargs2);
+ return ret;
+}
+
+static PyTypeObject dcerpc_InterfaceType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "dcerpc.ClientConnection",
+ .tp_basicsize = sizeof(dcerpc_InterfaceObject),
+ .tp_dealloc = dcerpc_interface_dealloc,
+ .tp_getset = dcerpc_interface_getsetters,
+ .tp_methods = dcerpc_interface_methods,
+ .tp_doc = "ClientConnection(binding, syntax, lp_ctx=None, credentials=None) -> connection\n"
+"\n"
+"binding should be a DCE/RPC binding string (for example: ncacn_ip_tcp:127.0.0.1)\n"
+"syntax should be a tuple with a GUID and version number of an interface\n"
+"lp_ctx should be a path to a smb.conf file or a param.LoadParm object\n"
+"credentials should be a credentials.Credentials object.\n\n",
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = dcerpc_interface_new,
+};
+
+static PyObject *py_transfer_syntax_ndr_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ return py_dcerpc_syntax_init_helper(type, args, kwargs, &ndr_transfer_syntax_ndr);
+}
+
+static PyTypeObject py_transfer_syntax_ndr_SyntaxType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "base.transfer_syntax_ndr",
+ .tp_doc = "transfer_syntax_ndr()\n",
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = py_transfer_syntax_ndr_new,
+};
+
+static PyObject *py_transfer_syntax_ndr64_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ return py_dcerpc_syntax_init_helper(type, args, kwargs, &ndr_transfer_syntax_ndr64);
+}
+
+static PyTypeObject py_transfer_syntax_ndr64_SyntaxType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "base.transfer_syntax_ndr64",
+ .tp_doc = "transfer_syntax_ndr64()\n",
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = py_transfer_syntax_ndr64_new,
+};
+
+static PyObject *py_bind_time_features_syntax_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = {
+ "features", NULL
+ };
+ unsigned long long features = 0;
+ struct ndr_syntax_id syntax;
+ PyObject *args2 = Py_None;
+ PyObject *kwargs2 = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "K:features", discard_const_p(char *, kwnames), &features)) {
+ return NULL;
+ }
+
+ args2 = Py_BuildValue("()");
+ if (args2 == NULL) {
+ return NULL;
+ }
+
+ kwargs2 = Py_BuildValue("{}");
+ if (kwargs2 == NULL) {
+ Py_DECREF(args2);
+ return NULL;
+ }
+
+ syntax = dcerpc_construct_bind_time_features(features);
+
+ return py_dcerpc_syntax_init_helper(type, args2, kwargs2, &syntax);
+}
+
+static PyTypeObject py_bind_time_features_syntax_SyntaxType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "base.bind_time_features_syntax",
+ .tp_doc = "bind_time_features_syntax(features)\n",
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = py_bind_time_features_syntax_new,
+};
+
+struct py_dcerpc_ndr_pointer {
+ PyObject *value;
+};
+
+static void py_dcerpc_ndr_pointer_dealloc(PyObject* self)
+{
+ struct py_dcerpc_ndr_pointer *obj =
+ pytalloc_get_type(self, struct py_dcerpc_ndr_pointer);
+
+ Py_DECREF(obj->value);
+ obj->value = NULL;
+
+ self->ob_type->tp_free(self);
+}
+
+static PyObject *py_dcerpc_ndr_pointer_get_value(PyObject *self, void *closure)
+{
+ struct py_dcerpc_ndr_pointer *obj =
+ pytalloc_get_type(self, struct py_dcerpc_ndr_pointer);
+
+ Py_INCREF(obj->value);
+ return obj->value;
+}
+
+static int py_dcerpc_ndr_pointer_set_value(PyObject *self, PyObject *value, void *closure)
+{
+ struct py_dcerpc_ndr_pointer *obj =
+ pytalloc_get_type(self, struct py_dcerpc_ndr_pointer);
+
+ Py_DECREF(obj->value);
+ obj->value = value;
+ Py_INCREF(obj->value);
+ return 0;
+}
+
+static PyGetSetDef py_dcerpc_ndr_pointer_getsetters[] = {
+ {
+ .name = discard_const_p(char, "value"),
+ .get = py_dcerpc_ndr_pointer_get_value,
+ .set = py_dcerpc_ndr_pointer_set_value,
+ .doc = discard_const_p(char, "the value store by the pointer"),
+ },
+ {
+ .name = NULL,
+ },
+};
+
+static PyObject *py_dcerpc_ndr_pointer_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *ret = NULL;
+ struct py_dcerpc_ndr_pointer *obj = NULL;
+ const char *kwnames[] = { "value", NULL };
+ PyObject *value = NULL;
+ bool ok;
+
+ ok = PyArg_ParseTupleAndKeywords(args, kwargs, "O:value",
+ discard_const_p(char *, kwnames),
+ &value);
+ if (!ok) {
+ return NULL;
+ }
+
+ ret = pytalloc_new(struct py_dcerpc_ndr_pointer, type);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ obj = pytalloc_get_type(ret, struct py_dcerpc_ndr_pointer);
+ *obj = (struct py_dcerpc_ndr_pointer) {
+ .value = value,
+ };
+
+ Py_INCREF(obj->value);
+ return ret;
+}
+
+static PyTypeObject py_dcerpc_ndr_pointer_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "base.ndr_pointer",
+ .tp_dealloc = py_dcerpc_ndr_pointer_dealloc,
+ .tp_getset = py_dcerpc_ndr_pointer_getsetters,
+ .tp_doc = "ndr_pointer(value)\n",
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = py_dcerpc_ndr_pointer_new,
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "base",
+ .m_doc = "DCE/RPC protocol implementation",
+ .m_size = -1,
+};
+
+MODULE_INIT_FUNC(base)
+{
+ PyObject *m;
+ PyObject *dep_talloc;
+ PyObject *dep_samba_dcerpc_misc;
+
+ dep_talloc = PyImport_ImportModule("talloc");
+ if (dep_talloc == NULL)
+ return NULL;
+
+ BaseObject_Type = (PyTypeObject *)PyObject_GetAttrString(dep_talloc, "BaseObject");
+ if (BaseObject_Type == NULL) {
+ Py_CLEAR(dep_talloc);
+ return NULL;
+ }
+
+ Py_CLEAR(dep_talloc);
+ dep_samba_dcerpc_misc = PyImport_ImportModule("samba.dcerpc.misc");
+ if (dep_samba_dcerpc_misc == NULL) {
+ return NULL;
+ }
+
+ ndr_syntax_id_Type = (PyTypeObject *)PyObject_GetAttrString(dep_samba_dcerpc_misc, "ndr_syntax_id");
+ Py_CLEAR(dep_samba_dcerpc_misc);
+ if (ndr_syntax_id_Type == NULL) {
+ return NULL;
+ }
+
+ py_transfer_syntax_ndr_SyntaxType.tp_base = ndr_syntax_id_Type;
+ py_transfer_syntax_ndr_SyntaxType.tp_basicsize = pytalloc_BaseObject_size();
+ py_transfer_syntax_ndr64_SyntaxType.tp_base = ndr_syntax_id_Type;
+ py_transfer_syntax_ndr64_SyntaxType.tp_basicsize = pytalloc_BaseObject_size();
+ py_bind_time_features_syntax_SyntaxType.tp_base = ndr_syntax_id_Type;
+ py_bind_time_features_syntax_SyntaxType.tp_basicsize = pytalloc_BaseObject_size();
+
+ py_dcerpc_ndr_pointer_type.tp_base = BaseObject_Type;
+ py_dcerpc_ndr_pointer_type.tp_basicsize = pytalloc_BaseObject_size();
+
+ if (PyType_Ready(&dcerpc_InterfaceType) < 0) {
+ return NULL;
+ }
+
+ if (PyType_Ready(&py_transfer_syntax_ndr_SyntaxType) < 0) {
+ return NULL;
+ }
+ if (PyType_Ready(&py_transfer_syntax_ndr64_SyntaxType) < 0) {
+ return NULL;
+ }
+ if (PyType_Ready(&py_bind_time_features_syntax_SyntaxType) < 0) {
+ return NULL;
+ }
+
+ if (PyType_Ready(&py_dcerpc_ndr_pointer_type) < 0) {
+ return NULL;
+ }
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL) {
+ return NULL;
+ }
+
+ Py_INCREF((PyObject *)&dcerpc_InterfaceType);
+ PyModule_AddObject(m, "ClientConnection", (PyObject *)&dcerpc_InterfaceType);
+
+ Py_INCREF((PyObject *)(void *)&py_transfer_syntax_ndr_SyntaxType);
+ PyModule_AddObject(m, "transfer_syntax_ndr", (PyObject *)(void *)&py_transfer_syntax_ndr_SyntaxType);
+ Py_INCREF((PyObject *)(void *)&py_transfer_syntax_ndr64_SyntaxType);
+ PyModule_AddObject(m, "transfer_syntax_ndr64", (PyObject *)(void *)&py_transfer_syntax_ndr64_SyntaxType);
+ Py_INCREF((PyObject *)(void *)&py_bind_time_features_syntax_SyntaxType);
+ PyModule_AddObject(m, "bind_time_features_syntax", (PyObject *)(void *)&py_bind_time_features_syntax_SyntaxType);
+ Py_INCREF((PyObject *)(void *)&py_dcerpc_ndr_pointer_type);
+ PyModule_AddObject(m, "ndr_pointer", (PyObject *)(void *)&py_dcerpc_ndr_pointer_type);
+ return m;
+}
diff --git a/source4/librpc/rpc/pyrpc.h b/source4/librpc/rpc/pyrpc.h
new file mode 100644
index 0000000..311ba2d
--- /dev/null
+++ b/source4/librpc/rpc/pyrpc.h
@@ -0,0 +1,60 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+
+ 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 _PYRPC_H_
+#define _PYRPC_H_
+
+#include "libcli/util/pyerrors.h"
+
+#define PY_CHECK_TYPE(type, var, fail) \
+ if (var == NULL) { \
+ PyErr_Format(PyExc_TypeError, \
+ __location__ \
+ ": Expected type '%s' for '%s', got NULL", \
+ (type)->tp_name, #var); \
+ fail; \
+ } else if (!PyObject_TypeCheck(var, type)) { \
+ PyErr_Format(PyExc_TypeError, \
+ __location__ \
+ ": Expected type '%s' for '%s' of type '%s'", \
+ (type)->tp_name, #var, Py_TYPE(var)->tp_name); \
+ fail; \
+ }
+
+#define dom_sid0_Type dom_sid_Type
+#define dom_sid2_Type dom_sid_Type
+#define dom_sid28_Type dom_sid_Type
+#define dom_sid0_Check dom_sid_Check
+#define dom_sid2_Check dom_sid_Check
+#define dom_sid28_Check dom_sid_Check
+
+typedef struct {
+ PyObject_HEAD
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding_handle *binding_handle;
+ struct tevent_context *ev;
+} dcerpc_InterfaceObject;
+
+
+#ifndef NDR_DCERPC_REQUEST_OBJECT_PRESENT
+#define NDR_DCERPC_REQUEST_OBJECT_PRESENT LIBNDR_FLAG_OBJECT_PRESENT
+#endif /* NDR_DCERPC_REQUEST_OBJECT_PRESENT */
+
+#endif /* _PYRPC_H_ */
diff --git a/source4/librpc/rpc/pyrpc_util.c b/source4/librpc/rpc/pyrpc_util.c
new file mode 100644
index 0000000..0d6a165
--- /dev/null
+++ b/source4/librpc/rpc/pyrpc_util.c
@@ -0,0 +1,506 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Python interface to DCE/RPC library - utility functions.
+
+ Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
+ Copyright (C) 2010 Andrew Tridgell <tridge@samba.org>
+
+ 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 <Python.h>
+#include "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/pyrpc.h"
+#include "param/pyparam.h"
+#include "auth/credentials/pycredentials.h"
+#include "lib/events/events.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+
+bool py_check_dcerpc_type(PyObject *obj, const char *module, const char *type_name)
+{
+ PyObject *mod;
+ PyTypeObject *type;
+ bool ret;
+
+ mod = PyImport_ImportModule(module);
+
+ if (mod == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "Unable to import %s to check type %s",
+ module, type_name);
+ return false;
+ }
+
+ type = (PyTypeObject *)PyObject_GetAttrString(mod, type_name);
+ Py_DECREF(mod);
+ if (type == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "Unable to find type %s in module %s",
+ module, type_name);
+ return false;
+ }
+
+ ret = PyObject_TypeCheck(obj, type);
+ Py_DECREF(type);
+
+ if (!ret)
+ PyErr_Format(PyExc_TypeError, "Expected type %s.%s, got %s",
+ module, type_name, Py_TYPE(obj)->tp_name);
+
+ return ret;
+}
+
+/*
+ connect to a IRPC pipe from python
+ */
+static NTSTATUS pyrpc_irpc_connect(TALLOC_CTX *mem_ctx, const char *irpc_server,
+ const struct ndr_interface_table *table,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcerpc_binding_handle **binding_handle)
+{
+ struct imessaging_context *msg;
+
+ msg = imessaging_client_init(mem_ctx, lp_ctx, event_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ *binding_handle = irpc_binding_handle_by_name(mem_ctx, msg, irpc_server, table);
+ if (*binding_handle == NULL) {
+ talloc_free(msg);
+ return NT_STATUS_INVALID_PIPE_STATE;
+ }
+
+ /*
+ * Note: this allows nested event loops to happen,
+ * but as there's no top level event loop it's not that critical.
+ */
+ dcerpc_binding_handle_set_sync_ev(*binding_handle, event_ctx);
+
+ return NT_STATUS_OK;
+}
+
+PyObject *py_dcerpc_interface_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs,
+ const struct ndr_interface_table *table)
+{
+ dcerpc_InterfaceObject *ret;
+ const char *binding_string;
+ PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None, *py_basis = Py_None;
+ NTSTATUS status;
+ unsigned int timeout = (unsigned int)-1;
+ const char *kwnames[] = {
+ "binding", "lp_ctx", "credentials", "timeout", "basis_connection", NULL
+ };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOIO:samr", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials, &timeout, &py_basis)) {
+ return NULL;
+ }
+
+ status = dcerpc_init();
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ ret = PyObject_New(dcerpc_InterfaceObject, type);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ ret->pipe = NULL;
+ ret->binding_handle = NULL;
+ ret->ev = NULL;
+ ret->mem_ctx = talloc_new(NULL);
+ if (ret->mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (strncmp(binding_string, "irpc:", 5) == 0) {
+ struct loadparm_context *lp_ctx;
+
+ ret->ev = s4_event_context_init(ret->mem_ctx);
+ if (ret->ev == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Unable to initialise event context");
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(ret->ev, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ status = pyrpc_irpc_connect(ret->mem_ctx, binding_string+5, table,
+ ret->ev, lp_ctx, &ret->binding_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ Py_DECREF(ret);
+ return NULL;
+ }
+ } else if (py_basis != Py_None) {
+ struct dcerpc_pipe *base_pipe;
+ PyObject *py_base;
+ PyTypeObject *ClientConnection_Type;
+
+ py_base = PyImport_ImportModule("samba.dcerpc.base");
+ if (py_base == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ ClientConnection_Type = (PyTypeObject *)PyObject_GetAttrString(py_base, "ClientConnection");
+ if (ClientConnection_Type == NULL) {
+ PyErr_SetNone(PyExc_TypeError);
+ Py_DECREF(ret);
+ Py_DECREF(py_base);
+ return NULL;
+ }
+
+ if (!PyObject_TypeCheck(py_basis, ClientConnection_Type)) {
+ PyErr_SetString(PyExc_TypeError, "basis_connection must be a DCE/RPC connection");
+ Py_DECREF(ret);
+ Py_DECREF(py_base);
+ Py_DECREF(ClientConnection_Type);
+ return NULL;
+ }
+
+ base_pipe = talloc_reference(ret->mem_ctx,
+ ((dcerpc_InterfaceObject *)py_basis)->pipe);
+ if (base_pipe == NULL) {
+ PyErr_NoMemory();
+ Py_DECREF(ret);
+ Py_DECREF(py_base);
+ Py_DECREF(ClientConnection_Type);
+ return NULL;
+ }
+
+ ret->ev = talloc_reference(
+ ret->mem_ctx,
+ ((dcerpc_InterfaceObject *)py_basis)->ev);
+ if (ret->ev == NULL) {
+ PyErr_NoMemory();
+ Py_DECREF(ret);
+ Py_DECREF(py_base);
+ Py_DECREF(ClientConnection_Type);
+ return NULL;
+ }
+
+ status = dcerpc_secondary_context(base_pipe, &ret->pipe, table);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ Py_DECREF(ret);
+ Py_DECREF(py_base);
+ Py_DECREF(ClientConnection_Type);
+ return NULL;
+ }
+
+ ret->pipe = talloc_steal(ret->mem_ctx, ret->pipe);
+ Py_XDECREF(ClientConnection_Type);
+ Py_XDECREF(py_base);
+ } else {
+ struct loadparm_context *lp_ctx;
+ struct cli_credentials *credentials;
+
+ ret->ev = s4_event_context_init(ret->mem_ctx);
+ if (ret->ev == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(ret->ev, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ Py_DECREF(ret);
+ return NULL;
+ }
+ status = dcerpc_pipe_connect(ret->mem_ctx, &ret->pipe, binding_string,
+ table, credentials, ret->ev, lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ Py_DECREF(ret);
+ return NULL;
+ }
+ }
+
+ if (ret->pipe) {
+ ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+ ret->binding_handle = ret->pipe->binding_handle;
+ }
+
+ /* reset timeout for the handle */
+ if ((timeout != ((unsigned int)-1)) && (ret->binding_handle != NULL)) {
+ dcerpc_binding_handle_set_timeout(ret->binding_handle, timeout);
+ }
+
+ return (PyObject *)ret;
+}
+
+static PyObject *py_dcerpc_run_function(dcerpc_InterfaceObject *iface,
+ const struct PyNdrRpcMethodDef *md,
+ PyObject *args, PyObject *kwargs)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ void *r;
+ PyObject *result = Py_None;
+
+ if (md->pack_in_data == NULL || md->unpack_out_data == NULL) {
+ PyErr_SetString(PyExc_NotImplementedError, "No marshalling code available yet");
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ r = talloc_zero_size(mem_ctx, md->table->calls[md->opnum].struct_size);
+ if (r == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (!md->pack_in_data(args, kwargs, r)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ status = md->call(iface->binding_handle, mem_ctx, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetDCERPCStatus(iface->pipe, status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ result = md->unpack_out_data(r);
+
+ talloc_free(mem_ctx);
+ return result;
+}
+
+static PyObject *py_dcerpc_call_wrapper(PyObject *self, PyObject *args, void *wrapped, PyObject *kwargs)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
+ const struct PyNdrRpcMethodDef *md = (const struct PyNdrRpcMethodDef *)wrapped;
+
+ return py_dcerpc_run_function(iface, md, args, kwargs);
+}
+
+bool PyInterface_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpcMethodDef *mds)
+{
+ int i;
+ for (i = 0; mds[i].name; i++) {
+ PyObject *ret;
+ struct wrapperbase *wb = (struct wrapperbase *)calloc(sizeof(struct wrapperbase), 1);
+
+ if (wb == NULL) {
+ return false;
+ }
+ wb->name = discard_const_p(char, mds[i].name);
+ wb->flags = PyWrapperFlag_KEYWORDS;
+ wb->wrapper = PY_DISCARD_FUNC_SIG(wrapperfunc,
+ py_dcerpc_call_wrapper);
+ wb->doc = discard_const_p(char, mds[i].doc);
+
+ ret = PyDescr_NewWrapper(ifacetype, wb, discard_const_p(void, &mds[i]));
+
+ PyDict_SetItemString(ifacetype->tp_dict, mds[i].name,
+ (PyObject *)ret);
+ Py_CLEAR(ret);
+ }
+
+ return true;
+}
+
+PyObject *py_dcerpc_syntax_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs,
+ const struct ndr_syntax_id *syntax)
+{
+ PyObject *ret;
+ struct ndr_syntax_id *obj;
+ const char *kwnames[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, ":abstract_syntax", discard_const_p(char *, kwnames))) {
+ return NULL;
+ }
+
+ ret = pytalloc_new(struct ndr_syntax_id, type);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ obj = pytalloc_get_type(ret, struct ndr_syntax_id);
+ *obj = *syntax;
+
+ return ret;
+}
+
+void PyErr_SetDCERPCStatus(struct dcerpc_pipe *p, NTSTATUS status)
+{
+ if (p && NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ status = dcerpc_fault_to_nt_status(p->last_fault_code);
+ }
+ PyErr_SetNTSTATUS(status);
+}
+
+
+/*
+ take a NDR structure that has a type in a python module and return
+ it as a python object
+
+ r is the NDR structure pointer (a C structure)
+
+ r_ctx is the context that is a parent of r. It will be referenced by
+ the resulting python object
+
+ This MUST only be used by objects that are based on pytalloc_Object
+ otherwise the pytalloc_reference_ex() will fail.
+ */
+PyObject *py_return_ndr_struct(const char *module_name, const char *type_name,
+ TALLOC_CTX *r_ctx, void *r)
+{
+ PyTypeObject *py_type;
+ PyObject *module;
+ PyObject *result = NULL;
+
+ if (r == NULL) {
+ Py_RETURN_NONE;
+ }
+
+ module = PyImport_ImportModule(module_name);
+ if (module == NULL) {
+ return NULL;
+ }
+
+ py_type = (PyTypeObject *)PyObject_GetAttrString(module, type_name);
+ if (py_type == NULL) {
+ Py_DECREF(module);
+ return NULL;
+ }
+
+ result = pytalloc_reference_ex(py_type, r_ctx, r);
+ Py_CLEAR(module);
+ Py_CLEAR(py_type);
+ return result;
+}
+
+PyObject *PyString_FromStringOrNULL(const char *str)
+{
+ if (str == NULL) {
+ Py_RETURN_NONE;
+ }
+ return PyUnicode_FromString(str);
+}
+
+PyObject *pyrpc_import_union(PyTypeObject *type, TALLOC_CTX *mem_ctx, int level,
+ const void *in, const char *typename)
+{
+ PyObject *mem_ctx_obj = NULL;
+ PyObject *in_obj = NULL;
+ PyObject *ret = NULL;
+
+ mem_ctx_obj = pytalloc_GenericObject_reference(mem_ctx);
+ if (mem_ctx_obj == NULL) {
+ return NULL;
+ }
+
+ in_obj = pytalloc_GenericObject_reference_ex(mem_ctx, discard_const(in));
+ if (in_obj == NULL) {
+ Py_XDECREF(mem_ctx_obj);
+ return NULL;
+ }
+
+ ret = PyObject_CallMethod((PyObject *)type,
+ discard_const_p(char, "__import__"),
+ discard_const_p(char, "OiO"),
+ mem_ctx_obj, level, in_obj);
+ Py_XDECREF(mem_ctx_obj);
+ Py_XDECREF(in_obj);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ return ret;
+}
+
+void *pyrpc_export_union(PyTypeObject *type, TALLOC_CTX *mem_ctx, int level,
+ PyObject *in, const char *typename)
+{
+ PyObject *mem_ctx_obj = NULL;
+ PyObject *ret_obj = NULL;
+ void *ret = NULL;
+
+ mem_ctx_obj = pytalloc_GenericObject_reference(mem_ctx);
+ if (mem_ctx_obj == NULL) {
+ return NULL;
+ }
+
+ ret_obj = PyObject_CallMethod((PyObject *)type,
+ discard_const_p(char, "__export__"),
+ discard_const_p(char, "OiO"),
+ mem_ctx_obj, level, in);
+ Py_XDECREF(mem_ctx_obj);
+ if (ret_obj == NULL) {
+ return NULL;
+ }
+
+ ret = _pytalloc_get_type(ret_obj, typename);
+ Py_XDECREF(ret_obj);
+ return ret;
+}
+
+PyObject *py_dcerpc_ndr_pointer_deref(PyTypeObject *type, PyObject *obj)
+{
+ if (!PyObject_TypeCheck(obj, type)) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected type '%s' but got type '%s'",
+ (type)->tp_name, Py_TYPE(obj)->tp_name);
+ return NULL;
+ }
+
+ return PyObject_GetAttrString(obj, discard_const_p(char, "value"));
+}
+
+PyObject *py_dcerpc_ndr_pointer_wrap(PyTypeObject *type, PyObject *obj)
+{
+ PyObject *args = NULL;
+ PyObject *ret_obj = NULL;
+
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ return NULL;
+ }
+ Py_XINCREF(obj);
+ PyTuple_SetItem(args, 0, obj);
+
+ ret_obj = PyObject_Call((PyObject *)type, args, NULL);
+ Py_XDECREF(args);
+ return ret_obj;
+}
diff --git a/source4/librpc/rpc/pyrpc_util.h b/source4/librpc/rpc/pyrpc_util.h
new file mode 100644
index 0000000..5a5f14d
--- /dev/null
+++ b/source4/librpc/rpc/pyrpc_util.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Python interface to DCE/RPC library - utility functions.
+
+ Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
+ Copyright (C) 2010 Andrew Tridgell <tridge@samba.org>
+
+ 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 __PYRPC_UTIL_H__
+#define __PYRPC_UTIL_H__
+
+#include "librpc/rpc/pyrpc.h"
+
+#define PyErr_FromNdrError(err) Py_BuildValue("(is)", err, ndr_map_error2string(err))
+
+#define PyErr_SetNdrError(err) \
+ PyErr_SetObject(PyExc_RuntimeError, PyErr_FromNdrError(err))
+
+void PyErr_SetDCERPCStatus(struct dcerpc_pipe *p, NTSTATUS status);
+
+typedef NTSTATUS (*py_dcerpc_call_fn) (struct dcerpc_binding_handle *, TALLOC_CTX *, void *);
+typedef bool (*py_data_pack_fn) (PyObject *args, PyObject *kwargs, void *r);
+typedef PyObject *(*py_data_unpack_fn) (void *r);
+
+struct PyNdrRpcMethodDef {
+ const char *name;
+ const char *doc;
+ py_dcerpc_call_fn call;
+ py_data_pack_fn pack_in_data;
+ py_data_unpack_fn unpack_out_data;
+ uint32_t opnum;
+ const struct ndr_interface_table *table;
+};
+
+bool py_check_dcerpc_type(PyObject *obj, const char *module, const char *type_name);
+bool PyInterface_AddNdrRpcMethods(PyTypeObject *object, const struct PyNdrRpcMethodDef *mds);
+PyObject *py_dcerpc_interface_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs, const struct ndr_interface_table *table);
+
+struct ndr_syntax_id;
+PyObject *py_dcerpc_syntax_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs,
+ const struct ndr_syntax_id *syntax);
+
+PyObject *py_return_ndr_struct(const char *module_name, const char *type_name,
+ TALLOC_CTX *r_ctx, void *r);
+
+PyObject *PyString_FromStringOrNULL(const char *str);
+
+PyObject *pyrpc_import_union(PyTypeObject *type, TALLOC_CTX *mem_ctx, int level,
+ const void *in, const char *typename);
+void *pyrpc_export_union(PyTypeObject *type, TALLOC_CTX *mem_ctx, int level,
+ PyObject *in, const char *typename);
+
+PyObject *py_dcerpc_ndr_pointer_deref(PyTypeObject *type, PyObject *obj);
+PyObject *py_dcerpc_ndr_pointer_wrap(PyTypeObject *type, PyObject *obj);
+
+#endif /* __PYRPC_UTIL_H__ */