#include "libcli/smb/tstream_smbXcli_np.h"
#include "librpc/rpc/dcerpc_connection.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,
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.
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 = dcerpc_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;
}
return false;
}
- if (hs->p->conn->dead) {
- return false;
- }
-
return dcerpc_connection_is_connected(hs->p->conn->conn);
}
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
*/
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,
- 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.
+/*
+ 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 ncacn_push_request_sign(struct dcecli_connection *c,
- DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
- size_t sig_size,
- struct ncacn_packet *pkt)
+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)
{
- 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;
+ void *st;
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ DATA_BLOB blob2;
+ enum ndr_err_code ndr_err;
- if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
- payload_offset += 16;
+ st = talloc_size(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
}
- 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;
+ pull = ndr_pull_init_flags(c, &blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
}
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
- 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;
+ pull->flags |= LIBNDR_FLAG_BIGENDIAN;
}
- 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;
+ if (c->flags & DCERPC_NDR64) {
+ pull->flags |= LIBNDR_FLAG_NDR64;
}
- 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;
+ 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);
}
- 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);
- }
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
}
- talloc_set_destructor(conn, NULL);
- if (conn->free_skipped) {
- talloc_free(conn);
+ if (c->flags & DCERPC_PUSH_BIGENDIAN) {
+ push->flags |= LIBNDR_FLAG_BIGENDIAN;
}
-}
-
-/*
- 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 (c->flags & DCERPC_NDR64) {
+ push->flags |= LIBNDR_FLAG_NDR64;
}
- if (NT_STATUS_IS_OK(status) && blob->length == 0) {
- status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ 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);
}
- /* 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;
- }
+ blob2 = ndr_push_blob(push);
- /* 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;
+ 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);
}
- dcerpc_request_recv_data(conn, blob, &pkt);
+ return NT_STATUS_OK;
}
/*
- handle timeouts of individual dcerpc requests
+ 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 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)
+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)
{
- 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);
+ 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;
- req = tevent_req_create(mem_ctx, &state,
- struct dcerpc_bind_state);
- if (req == NULL) {
- return NULL;
+ st = talloc_size(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
}
+ memcpy(st, struct_ptr, struct_size);
- 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;
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
}
- if (p->conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) {
- pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ 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);
}
- 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);
+ blob = ndr_push_blob(push);
+
+ pull = ndr_pull_init_flags(c, &blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
}
- 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 = 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);
+ 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);
}
- /*
- * 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 = 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);
/* 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",
+ s1 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
NDR_OUT, struct_ptr);
- s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
+ s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
NDR_OUT, st);
if (strcmp(s1, s2) != 0) {
#if 1