2 Unix SMB/CIFS implementation.
5 Copyright (C) Tim Potter 2003
6 Copyright (C) Andrew Tridgell 2003-2005
7 Copyright (C) Jelmer Vernooij 2004-2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "lib/util/dlinklist.h"
25 #include "lib/events/events.h"
26 #include "librpc/rpc/dcerpc.h"
27 #include "librpc/gen_ndr/ndr_misc.h"
28 #include "librpc/gen_ndr/ndr_dcerpc.h"
29 #include "libcli/composite/composite.h"
30 #include "auth/gensec/gensec.h"
32 NTSTATUS dcerpc_init(void)
39 static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);
40 static void dcerpc_ship_next_request(struct dcerpc_connection *c);
42 /* destroy a dcerpc connection */
43 static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
46 conn->free_skipped = true;
49 dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
54 /* initialise a dcerpc connection.
55 the event context is optional
57 static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
58 struct event_context *ev)
60 struct dcerpc_connection *c;
62 c = talloc_zero(mem_ctx, struct dcerpc_connection);
68 ev = event_context_init(c);
77 if (!talloc_reference(c, ev)) {
82 c->security_state.auth_info = NULL;
83 c->security_state.session_key = dcerpc_generic_session_key;
84 c->security_state.generic_state = NULL;
85 c->binding_string = NULL;
87 c->srv_max_xmit_frag = 0;
88 c->srv_max_recv_frag = 0;
91 talloc_set_destructor(c, dcerpc_connection_destructor);
96 /* initialise a dcerpc pipe. */
97 struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *ev)
99 struct dcerpc_pipe *p;
101 p = talloc(mem_ctx, struct dcerpc_pipe);
106 p->conn = dcerpc_connection_init(p, ev);
107 if (p->conn == NULL) {
112 p->last_fault_code = 0;
114 p->request_timeout = DCERPC_REQUEST_TIMEOUT;
117 ZERO_STRUCT(p->syntax);
118 ZERO_STRUCT(p->transfer_syntax);
125 choose the next call id to use
127 static uint32_t next_call_id(struct dcerpc_connection *c)
130 if (c->call_id == 0) {
136 /* we need to be able to get/set the fragment length without doing a full
138 void dcerpc_set_frag_length(DATA_BLOB *blob, uint16_t v)
140 if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
141 SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
143 RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
147 uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob)
149 if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
150 return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
152 return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
156 void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
158 if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
159 SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
161 RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
167 setup for a ndr pull, also setting up any flags from the binding string
169 static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c,
170 DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
172 struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
174 if (ndr == NULL) return ndr;
176 if (c->flags & DCERPC_DEBUG_PAD_CHECK) {
177 ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
180 if (c->flags & DCERPC_NDR_REF_ALLOC) {
181 ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
188 parse a data blob into a ncacn_packet structure. This handles both
189 input and output packets
191 static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
192 struct ncacn_packet *pkt)
194 struct ndr_pull *ndr;
196 ndr = ndr_pull_init_flags(c, blob, mem_ctx);
198 return NT_STATUS_NO_MEMORY;
201 if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
202 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
205 return ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
209 generate a CONNECT level verifier
211 static NTSTATUS dcerpc_connect_verifier(TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
213 *blob = data_blob_talloc(mem_ctx, NULL, 16);
214 if (blob->data == NULL) {
215 return NT_STATUS_NO_MEMORY;
217 SIVAL(blob->data, 0, 1);
218 memset(blob->data+4, 0, 12);
223 check a CONNECT level verifier
225 static NTSTATUS dcerpc_check_connect_verifier(DATA_BLOB *blob)
227 if (blob->length != 16 ||
228 IVAL(blob->data, 0) != 1) {
229 return NT_STATUS_ACCESS_DENIED;
235 parse the authentication information on a dcerpc response packet
237 static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX *mem_ctx,
238 DATA_BLOB *raw_packet,
239 struct ncacn_packet *pkt)
241 struct ndr_pull *ndr;
243 struct dcerpc_auth auth;
246 if (pkt->auth_length == 0 &&
247 c->security_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
251 auth_blob.length = 8 + pkt->auth_length;
253 /* check for a valid length */
254 if (pkt->u.response.stub_and_verifier.length < auth_blob.length) {
255 return NT_STATUS_INFO_LENGTH_MISMATCH;
259 pkt->u.response.stub_and_verifier.data +
260 pkt->u.response.stub_and_verifier.length - auth_blob.length;
261 pkt->u.response.stub_and_verifier.length -= auth_blob.length;
263 /* pull the auth structure */
264 ndr = ndr_pull_init_flags(c, &auth_blob, mem_ctx);
266 return NT_STATUS_NO_MEMORY;
269 if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
270 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
273 status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
274 if (!NT_STATUS_IS_OK(status)) {
278 /* check signature or unseal the packet */
279 switch (c->security_state.auth_info->auth_level) {
280 case DCERPC_AUTH_LEVEL_PRIVACY:
281 status = gensec_unseal_packet(c->security_state.generic_state,
283 raw_packet->data + DCERPC_REQUEST_LENGTH,
284 pkt->u.response.stub_and_verifier.length,
286 raw_packet->length - auth.credentials.length,
288 memcpy(pkt->u.response.stub_and_verifier.data,
289 raw_packet->data + DCERPC_REQUEST_LENGTH,
290 pkt->u.response.stub_and_verifier.length);
293 case DCERPC_AUTH_LEVEL_INTEGRITY:
294 status = gensec_check_packet(c->security_state.generic_state,
296 pkt->u.response.stub_and_verifier.data,
297 pkt->u.response.stub_and_verifier.length,
299 raw_packet->length - auth.credentials.length,
303 case DCERPC_AUTH_LEVEL_CONNECT:
304 status = dcerpc_check_connect_verifier(&auth.credentials);
307 case DCERPC_AUTH_LEVEL_NONE:
311 status = NT_STATUS_INVALID_LEVEL;
315 /* remove the indicated amount of paddiing */
316 if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
317 return NT_STATUS_INFO_LENGTH_MISMATCH;
319 pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
326 push a dcerpc request packet into a blob, possibly signing it.
328 static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
329 DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
330 struct ncacn_packet *pkt)
333 struct ndr_push *ndr;
335 size_t payload_length;
337 /* non-signed packets are simpler */
338 if (!c->security_state.auth_info ||
339 !c->security_state.generic_state) {
340 return ncacn_push_auth(blob, mem_ctx, pkt, c->security_state.auth_info);
343 ndr = ndr_push_init_ctx(mem_ctx);
345 return NT_STATUS_NO_MEMORY;
348 if (c->flags & DCERPC_PUSH_BIGENDIAN) {
349 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
352 if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
353 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
356 status = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
357 if (!NT_STATUS_IS_OK(status)) {
361 /* pad to 16 byte multiple in the payload portion of the
362 packet. This matches what w2k3 does */
363 c->security_state.auth_info->auth_pad_length =
364 (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
365 ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
367 payload_length = pkt->u.request.stub_and_verifier.length +
368 c->security_state.auth_info->auth_pad_length;
370 /* sign or seal the packet */
371 switch (c->security_state.auth_info->auth_level) {
372 case DCERPC_AUTH_LEVEL_PRIVACY:
373 case DCERPC_AUTH_LEVEL_INTEGRITY:
374 /* We hope this length is accruate. If must be if the
375 * GENSEC mech does AEAD signing of the packet
377 c->security_state.auth_info->credentials
378 = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(c->security_state.generic_state,
380 data_blob_clear(&c->security_state.auth_info->credentials);
383 case DCERPC_AUTH_LEVEL_CONNECT:
384 status = dcerpc_connect_verifier(mem_ctx, &c->security_state.auth_info->credentials);
387 case DCERPC_AUTH_LEVEL_NONE:
388 c->security_state.auth_info->credentials = data_blob(NULL, 0);
392 status = NT_STATUS_INVALID_LEVEL;
396 if (!NT_STATUS_IS_OK(status)) {
400 /* add the auth verifier */
401 status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
402 if (!NT_STATUS_IS_OK(status)) {
406 /* extract the whole packet as a blob */
407 *blob = ndr_push_blob(ndr);
409 /* fill in the fragment length and auth_length, we can't fill
410 in these earlier as we don't know the signature length (it
411 could be variable length) */
412 dcerpc_set_frag_length(blob, blob->length);
413 /* We hope this value is accruate. If must be if the GENSEC
414 * mech does AEAD signing of the packet headers */
415 dcerpc_set_auth_length(blob, c->security_state.auth_info->credentials.length);
417 /* sign or seal the packet */
418 switch (c->security_state.auth_info->auth_level) {
419 case DCERPC_AUTH_LEVEL_PRIVACY:
420 status = gensec_seal_packet(c->security_state.generic_state,
422 blob->data + DCERPC_REQUEST_LENGTH,
426 c->security_state.auth_info->credentials.length,
428 if (!NT_STATUS_IS_OK(status)) {
431 blob->length -= c->security_state.auth_info->credentials.length;
432 if (!data_blob_append(mem_ctx, blob,
433 creds2.data, creds2.length)) {
434 return NT_STATUS_NO_MEMORY;
436 dcerpc_set_auth_length(blob, creds2.length);
437 if (c->security_state.auth_info->credentials.length == 0) {
438 /* this is needed for krb5 only, to correct the total packet
440 dcerpc_set_frag_length(blob,
441 dcerpc_get_frag_length(blob)
446 case DCERPC_AUTH_LEVEL_INTEGRITY:
447 status = gensec_sign_packet(c->security_state.generic_state,
449 blob->data + DCERPC_REQUEST_LENGTH,
453 c->security_state.auth_info->credentials.length,
455 if (!NT_STATUS_IS_OK(status)) {
458 blob->length -= c->security_state.auth_info->credentials.length;
459 if (!data_blob_append(mem_ctx, blob,
460 creds2.data, creds2.length)) {
461 return NT_STATUS_NO_MEMORY;
463 dcerpc_set_auth_length(blob, creds2.length);
464 if (c->security_state.auth_info->credentials.length == 0) {
465 /* this is needed for krb5 only, to correct the total packet
467 dcerpc_set_frag_length(blob,
468 dcerpc_get_frag_length(blob)
473 case DCERPC_AUTH_LEVEL_CONNECT:
476 case DCERPC_AUTH_LEVEL_NONE:
477 c->security_state.auth_info->credentials = data_blob(NULL, 0);
481 status = NT_STATUS_INVALID_LEVEL;
485 data_blob_free(&c->security_state.auth_info->credentials);
492 fill in the fixed values in a dcerpc header
494 static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt)
497 pkt->rpc_vers_minor = 0;
498 if (c->flags & DCERPC_PUSH_BIGENDIAN) {
501 pkt->drep[0] = DCERPC_DREP_LE;
509 map a bind nak reason to a NTSTATUS
511 static NTSTATUS dcerpc_map_reason(uint16_t reason)
514 case DCERPC_BIND_REASON_ASYNTAX:
515 return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
516 case DCERPC_BIND_REASON_INVALID_AUTH_TYPE:
517 return NT_STATUS_INVALID_PARAMETER;
519 return NT_STATUS_UNSUCCESSFUL;
523 a bind or alter context has failed
525 static void dcerpc_composite_fail(struct rpc_request *req)
527 struct composite_context *c = talloc_get_type(req->async.private_data,
528 struct composite_context);
529 composite_error(c, req->status);
533 remove requests from the pending or queued queues
535 static int dcerpc_req_dequeue(struct rpc_request *req)
537 switch (req->state) {
538 case RPC_REQUEST_QUEUED:
539 DLIST_REMOVE(req->p->conn->request_queue, req);
541 case RPC_REQUEST_PENDING:
542 DLIST_REMOVE(req->p->conn->pending, req);
544 case RPC_REQUEST_DONE:
552 mark the dcerpc connection dead. All outstanding requests get an error
554 static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
556 if (conn->dead) return;
560 if (conn->transport.shutdown_pipe) {
561 conn->transport.shutdown_pipe(conn, status);
564 /* all pending requests get the error */
565 while (conn->pending) {
566 struct rpc_request *req = conn->pending;
567 dcerpc_req_dequeue(req);
568 req->state = RPC_REQUEST_DONE;
569 req->status = status;
570 if (req->async.callback) {
571 req->async.callback(req);
575 talloc_set_destructor(conn, NULL);
576 if (conn->free_skipped) {
582 forward declarations of the recv_data handlers for the types of
583 packets we need to handle
585 static void dcerpc_request_recv_data(struct dcerpc_connection *c,
586 DATA_BLOB *raw_packet, struct ncacn_packet *pkt);
589 receive a dcerpc reply from the transport. Here we work out what
590 type of reply it is (normal request, bind or alter context) and
591 dispatch to the appropriate handler
593 static void dcerpc_recv_data(struct dcerpc_connection *conn, DATA_BLOB *blob, NTSTATUS status)
595 struct ncacn_packet pkt;
597 if (NT_STATUS_IS_OK(status) && blob->length == 0) {
598 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
601 /* the transport may be telling us of a severe error, such as
603 if (!NT_STATUS_IS_OK(status)) {
604 data_blob_free(blob);
605 dcerpc_connection_dead(conn, status);
609 /* parse the basic packet to work out what type of response this is */
610 status = ncacn_pull(conn, blob, blob->data, &pkt);
611 if (!NT_STATUS_IS_OK(status)) {
612 data_blob_free(blob);
613 dcerpc_connection_dead(conn, status);
616 dcerpc_request_recv_data(conn, blob, &pkt);
621 Receive a bind reply from the transport
623 static void dcerpc_bind_recv_handler(struct rpc_request *req,
624 DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
626 struct composite_context *c;
627 struct dcerpc_connection *conn;
629 c = talloc_get_type(req->async.private_data, struct composite_context);
631 if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
632 DEBUG(2,("dcerpc: bind_nak reason %d\n",
633 pkt->u.bind_nak.reject_reason));
634 composite_error(c, dcerpc_map_reason(pkt->u.bind_nak.
639 if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
640 (pkt->u.bind_ack.num_results == 0) ||
641 (pkt->u.bind_ack.ctx_list[0].result != 0)) {
642 composite_error(c, NT_STATUS_NET_WRITE_FAULT);
648 conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
649 conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
651 /* the bind_ack might contain a reply set of credentials */
652 if (conn->security_state.auth_info &&
653 pkt->u.bind_ack.auth_info.length) {
654 c->status = ndr_pull_struct_blob(
655 &pkt->u.bind_ack.auth_info, conn,
656 conn->security_state.auth_info,
657 (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
658 if (!composite_is_ok(c)) return;
661 req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
667 handle timeouts of individual dcerpc requests
669 static void dcerpc_timeout_handler(struct event_context *ev, struct timed_event *te,
670 struct timeval t, void *private)
672 struct rpc_request *req = talloc_get_type(private, struct rpc_request);
674 if (req->ignore_timeout) {
675 dcerpc_req_dequeue(req);
676 req->state = RPC_REQUEST_DONE;
677 req->status = NT_STATUS_IO_TIMEOUT;
678 if (req->async.callback) {
679 req->async.callback(req);
684 dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
688 send a async dcerpc bind request
690 struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
692 const struct ndr_syntax_id *syntax,
693 const struct ndr_syntax_id *transfer_syntax)
695 struct composite_context *c;
696 struct ncacn_packet pkt;
698 struct rpc_request *req;
700 c = composite_create(mem_ctx,p->conn->event_ctx);
701 if (c == NULL) return NULL;
706 p->transfer_syntax = *transfer_syntax;
708 init_ncacn_hdr(p->conn, &pkt);
710 pkt.ptype = DCERPC_PKT_BIND;
711 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
712 pkt.call_id = p->conn->call_id;
715 if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
716 pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
719 pkt.u.bind.max_xmit_frag = 5840;
720 pkt.u.bind.max_recv_frag = 5840;
721 pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
722 pkt.u.bind.num_contexts = 1;
723 pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
724 if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
725 pkt.u.bind.ctx_list[0].context_id = p->context_id;
726 pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
727 pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
728 pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
729 pkt.u.bind.auth_info = data_blob(NULL, 0);
731 /* construct the NDR form of the packet */
732 c->status = ncacn_push_auth(&blob, c, &pkt,
733 p->conn->security_state.auth_info);
734 if (!composite_is_ok(c)) return c;
736 p->conn->transport.recv_data = dcerpc_recv_data;
739 * we allocate a dcerpc_request so we can be in the same
740 * request queue as normal requests
742 req = talloc_zero(c, struct rpc_request);
743 if (composite_nomem(req, c)) return c;
745 req->state = RPC_REQUEST_PENDING;
746 req->call_id = pkt.call_id;
747 req->async.private_data = c;
748 req->async.callback = dcerpc_composite_fail;
750 req->recv_handler = dcerpc_bind_recv_handler;
751 DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
752 talloc_set_destructor(req, dcerpc_req_dequeue);
754 c->status = p->conn->transport.send_request(p->conn, &blob,
756 if (!composite_is_ok(c)) return c;
758 event_add_timed(c->event_ctx, req,
759 timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
760 dcerpc_timeout_handler, req);
766 recv side of async dcerpc bind request
768 NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
770 NTSTATUS result = composite_wait(ctx);
776 perform a continued bind (and auth3)
778 NTSTATUS dcerpc_auth3(struct dcerpc_connection *c,
781 struct ncacn_packet pkt;
785 init_ncacn_hdr(c, &pkt);
787 pkt.ptype = DCERPC_PKT_AUTH3;
788 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
789 pkt.call_id = next_call_id(c);
791 pkt.u.auth3._pad = 0;
792 pkt.u.auth3.auth_info = data_blob(NULL, 0);
794 /* construct the NDR form of the packet */
795 status = ncacn_push_auth(&blob, mem_ctx, &pkt, c->security_state.auth_info);
796 if (!NT_STATUS_IS_OK(status)) {
800 /* send it on its way */
801 status = c->transport.send_request(c, &blob, false);
802 if (!NT_STATUS_IS_OK(status)) {
811 process a fragment received from the transport layer during a
814 This function frees the data
816 static void dcerpc_request_recv_data(struct dcerpc_connection *c,
817 DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
819 struct rpc_request *req;
821 NTSTATUS status = NT_STATUS_OK;
824 if this is an authenticated connection then parse and check
825 the auth info. We have to do this before finding the
826 matching packet, as the request structure might have been
827 removed due to a timeout, but if it has been we still need
828 to run the auth routines so that we don't get the sign/seal
829 info out of step with the server
831 if (c->security_state.auth_info && c->security_state.generic_state &&
832 pkt->ptype == DCERPC_PKT_RESPONSE) {
833 status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt);
836 /* find the matching request */
837 for (req=c->pending;req;req=req->next) {
838 if (pkt->call_id == req->call_id) break;
842 /* useful for testing certain vendors RPC servers */
843 if (req == NULL && c->pending && pkt->call_id == 0) {
844 DEBUG(0,("HACK FOR INCORRECT CALL ID\n"));
850 DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id));
851 data_blob_free(raw_packet);
855 talloc_steal(req, raw_packet->data);
857 if (req->recv_handler != NULL) {
858 dcerpc_req_dequeue(req);
859 req->state = RPC_REQUEST_DONE;
860 req->recv_handler(req, raw_packet, pkt);
864 if (pkt->ptype == DCERPC_PKT_FAULT) {
865 DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
866 req->fault_code = pkt->u.fault.status;
867 req->status = NT_STATUS_NET_WRITE_FAULT;
871 if (pkt->ptype != DCERPC_PKT_RESPONSE) {
872 DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
874 req->fault_code = DCERPC_FAULT_OTHER;
875 req->status = NT_STATUS_NET_WRITE_FAULT;
879 /* now check the status from the auth routines, and if it failed then fail
880 this request accordingly */
881 if (!NT_STATUS_IS_OK(status)) {
882 req->status = status;
886 length = pkt->u.response.stub_and_verifier.length;
889 req->payload.data = talloc_realloc(req,
892 req->payload.length + length);
893 if (!req->payload.data) {
894 req->status = NT_STATUS_NO_MEMORY;
897 memcpy(req->payload.data+req->payload.length,
898 pkt->u.response.stub_and_verifier.data, length);
899 req->payload.length += length;
902 if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
903 c->transport.send_read(c);
907 if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
908 req->flags |= DCERPC_PULL_BIGENDIAN;
910 req->flags &= ~DCERPC_PULL_BIGENDIAN;
915 /* we've got the full payload */
916 req->state = RPC_REQUEST_DONE;
917 DLIST_REMOVE(c->pending, req);
919 if (c->request_queue != NULL) {
920 /* We have to look at shipping further requests before calling
921 * the async function, that one might close the pipe */
922 dcerpc_ship_next_request(c);
925 if (req->async.callback) {
926 req->async.callback(req);
931 perform the send side of a async dcerpc request
933 static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
934 const struct GUID *object,
937 DATA_BLOB *stub_data)
939 struct rpc_request *req;
941 p->conn->transport.recv_data = dcerpc_recv_data;
943 req = talloc(p, struct rpc_request);
949 req->call_id = next_call_id(p->conn);
950 req->status = NT_STATUS_OK;
951 req->state = RPC_REQUEST_QUEUED;
952 req->payload = data_blob(NULL, 0);
955 req->async_call = async;
956 req->ignore_timeout = false;
957 req->async.callback = NULL;
958 req->async.private_data = NULL;
959 req->recv_handler = NULL;
961 if (object != NULL) {
962 req->object = (struct GUID *)talloc_memdup(req, (const void *)object, sizeof(*object));
963 if (req->object == NULL) {
972 req->request_data.length = stub_data->length;
973 req->request_data.data = talloc_reference(req, stub_data->data);
974 if (req->request_data.length && req->request_data.data == NULL) {
978 DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
979 talloc_set_destructor(req, dcerpc_req_dequeue);
981 dcerpc_ship_next_request(p->conn);
983 if (p->request_timeout) {
984 event_add_timed(dcerpc_event_context(p), req,
985 timeval_current_ofs(p->request_timeout, 0),
986 dcerpc_timeout_handler, req);
993 Send a request using the transport
996 static void dcerpc_ship_next_request(struct dcerpc_connection *c)
998 struct rpc_request *req;
999 struct dcerpc_pipe *p;
1000 DATA_BLOB *stub_data;
1001 struct ncacn_packet pkt;
1003 uint32_t remaining, chunk_size;
1004 bool first_packet = true;
1006 req = c->request_queue;
1012 stub_data = &req->request_data;
1014 if (!req->async_call && (c->pending != NULL)) {
1018 DLIST_REMOVE(c->request_queue, req);
1019 DLIST_ADD(c->pending, req);
1020 req->state = RPC_REQUEST_PENDING;
1022 init_ncacn_hdr(p->conn, &pkt);
1024 remaining = stub_data->length;
1026 /* we can write a full max_recv_frag size, minus the dcerpc
1027 request header size */
1028 chunk_size = p->conn->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
1030 pkt.ptype = DCERPC_PKT_REQUEST;
1031 pkt.call_id = req->call_id;
1032 pkt.auth_length = 0;
1034 pkt.u.request.alloc_hint = remaining;
1035 pkt.u.request.context_id = p->context_id;
1036 pkt.u.request.opnum = req->opnum;
1039 pkt.u.request.object.object = *req->object;
1040 pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
1041 chunk_size -= ndr_size_GUID(req->object,0);
1044 /* we send a series of pdus without waiting for a reply */
1045 while (remaining > 0 || first_packet) {
1046 uint32_t chunk = MIN(chunk_size, remaining);
1047 bool last_frag = false;
1049 first_packet = false;
1050 pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST);
1052 if (remaining == stub_data->length) {
1053 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
1055 if (chunk == remaining) {
1056 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
1060 pkt.u.request.stub_and_verifier.data = stub_data->data +
1061 (stub_data->length - remaining);
1062 pkt.u.request.stub_and_verifier.length = chunk;
1064 req->status = ncacn_push_request_sign(p->conn, &blob, req, &pkt);
1065 if (!NT_STATUS_IS_OK(req->status)) {
1066 req->state = RPC_REQUEST_DONE;
1067 DLIST_REMOVE(p->conn->pending, req);
1071 req->status = p->conn->transport.send_request(p->conn, &blob, last_frag);
1072 if (!NT_STATUS_IS_OK(req->status)) {
1073 req->state = RPC_REQUEST_DONE;
1074 DLIST_REMOVE(p->conn->pending, req);
1083 return the event context for a dcerpc pipe
1084 used by callers who wish to operate asynchronously
1086 struct event_context *dcerpc_event_context(struct dcerpc_pipe *p)
1088 return p->conn->event_ctx;
1094 perform the receive side of a async dcerpc request
1096 NTSTATUS dcerpc_request_recv(struct rpc_request *req,
1097 TALLOC_CTX *mem_ctx,
1098 DATA_BLOB *stub_data)
1102 while (req->state != RPC_REQUEST_DONE) {
1103 struct event_context *ctx = dcerpc_event_context(req->p);
1104 if (event_loop_once(ctx) != 0) {
1105 return NT_STATUS_CONNECTION_DISCONNECTED;
1108 *stub_data = req->payload;
1109 status = req->status;
1110 if (stub_data->data) {
1111 stub_data->data = talloc_steal(mem_ctx, stub_data->data);
1113 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
1114 req->p->last_fault_code = req->fault_code;
1121 perform a full request/response pair on a dcerpc pipe
1123 NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
1124 struct GUID *object,
1127 TALLOC_CTX *mem_ctx,
1128 DATA_BLOB *stub_data_in,
1129 DATA_BLOB *stub_data_out)
1131 struct rpc_request *req;
1133 req = dcerpc_request_send(p, object, opnum, async, stub_data_in);
1135 return NT_STATUS_NO_MEMORY;
1138 return dcerpc_request_recv(req, mem_ctx, stub_data_out);
1143 this is a paranoid NDR validator. For every packet we push onto the wire
1144 we pull it back again, then push it again. Then we compare the raw NDR data
1145 for that to the NDR we initially generated. If they don't match then we know
1146 we must have a bug in either the pull or push side of our code
1148 static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c,
1149 TALLOC_CTX *mem_ctx,
1152 ndr_push_flags_fn_t ndr_push,
1153 ndr_pull_flags_fn_t ndr_pull)
1156 struct ndr_pull *pull;
1157 struct ndr_push *push;
1161 st = talloc_size(mem_ctx, struct_size);
1163 return NT_STATUS_NO_MEMORY;
1166 pull = ndr_pull_init_flags(c, &blob, mem_ctx);
1168 return NT_STATUS_NO_MEMORY;
1170 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
1172 status = ndr_pull(pull, NDR_IN, st);
1173 if (!NT_STATUS_IS_OK(status)) {
1174 return ndr_pull_error(pull, NDR_ERR_VALIDATE,
1175 "failed input validation pull - %s",
1179 push = ndr_push_init_ctx(mem_ctx);
1181 return NT_STATUS_NO_MEMORY;
1184 status = ndr_push(push, NDR_IN, st);
1185 if (!NT_STATUS_IS_OK(status)) {
1186 return ndr_push_error(push, NDR_ERR_VALIDATE,
1187 "failed input validation push - %s",
1191 blob2 = ndr_push_blob(push);
1193 if (data_blob_cmp(&blob, &blob2) != 0) {
1194 DEBUG(3,("original:\n"));
1195 dump_data(3, blob.data, blob.length);
1196 DEBUG(3,("secondary:\n"));
1197 dump_data(3, blob2.data, blob2.length);
1198 return ndr_push_error(push, NDR_ERR_VALIDATE,
1199 "failed input validation data - %s",
1203 return NT_STATUS_OK;
1207 this is a paranoid NDR input validator. For every packet we pull
1208 from the wire we push it back again then pull and push it
1209 again. Then we compare the raw NDR data for that to the NDR we
1210 initially generated. If they don't match then we know we must have a
1211 bug in either the pull or push side of our code
1213 static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
1214 struct ndr_pull *pull_in,
1217 ndr_push_flags_fn_t ndr_push,
1218 ndr_pull_flags_fn_t ndr_pull,
1219 ndr_print_function_t ndr_print)
1222 struct ndr_pull *pull;
1223 struct ndr_push *push;
1225 DATA_BLOB blob, blob2;
1226 TALLOC_CTX *mem_ctx = pull_in;
1229 st = talloc_size(mem_ctx, struct_size);
1231 return NT_STATUS_NO_MEMORY;
1233 memcpy(st, struct_ptr, struct_size);
1235 push = ndr_push_init_ctx(mem_ctx);
1237 return NT_STATUS_NO_MEMORY;
1240 status = ndr_push(push, NDR_OUT, struct_ptr);
1241 if (!NT_STATUS_IS_OK(status)) {
1242 return ndr_push_error(push, NDR_ERR_VALIDATE,
1243 "failed output validation push - %s",
1247 blob = ndr_push_blob(push);
1249 pull = ndr_pull_init_flags(c, &blob, mem_ctx);
1251 return NT_STATUS_NO_MEMORY;
1254 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
1255 status = ndr_pull(pull, NDR_OUT, st);
1256 if (!NT_STATUS_IS_OK(status)) {
1257 return ndr_pull_error(pull, NDR_ERR_VALIDATE,
1258 "failed output validation pull - %s",
1262 push = ndr_push_init_ctx(mem_ctx);
1264 return NT_STATUS_NO_MEMORY;
1267 status = ndr_push(push, NDR_OUT, st);
1268 if (!NT_STATUS_IS_OK(status)) {
1269 return ndr_push_error(push, NDR_ERR_VALIDATE,
1270 "failed output validation push2 - %s",
1274 blob2 = ndr_push_blob(push);
1276 if (data_blob_cmp(&blob, &blob2) != 0) {
1277 DEBUG(3,("original:\n"));
1278 dump_data(3, blob.data, blob.length);
1279 DEBUG(3,("secondary:\n"));
1280 dump_data(3, blob2.data, blob2.length);
1281 return ndr_push_error(push, NDR_ERR_VALIDATE,
1282 "failed output validation data - %s",
1286 /* this checks the printed forms of the two structures, which effectively
1287 tests all of the value() attributes */
1288 s1 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
1289 NDR_OUT, struct_ptr);
1290 s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
1292 if (strcmp(s1, s2) != 0) {
1294 DEBUG(3,("VALIDATE ERROR:\nWIRE:\n%s\n GEN:\n%s\n", s1, s2));
1296 /* this is sometimes useful */
1297 printf("VALIDATE ERROR\n");
1298 file_save("wire.dat", s1, strlen(s1));
1299 file_save("gen.dat", s2, strlen(s2));
1300 system("diff -u wire.dat gen.dat");
1302 return ndr_push_error(push, NDR_ERR_VALIDATE,
1303 "failed output validation strings doesn't match");
1306 return NT_STATUS_OK;
1311 send a rpc request given a dcerpc_call structure
1313 struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
1314 const struct GUID *object,
1315 const struct ndr_interface_table *table,
1317 TALLOC_CTX *mem_ctx,
1320 const struct ndr_interface_call *call;
1321 struct ndr_push *push;
1324 struct rpc_request *req;
1326 call = &table->calls[opnum];
1328 /* setup for a ndr_push_* call */
1329 push = ndr_push_init_ctx(mem_ctx);
1334 if (p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
1335 push->flags |= LIBNDR_FLAG_BIGENDIAN;
1338 /* push the structure into a blob */
1339 status = call->ndr_push(push, NDR_IN, r);
1340 if (!NT_STATUS_IS_OK(status)) {
1341 DEBUG(2,("Unable to ndr_push structure in dcerpc_ndr_request_send - %s\n",
1342 nt_errstr(status)));
1347 /* retrieve the blob */
1348 request = ndr_push_blob(push);
1350 if (p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
1351 status = dcerpc_ndr_validate_in(p->conn, push, request, call->struct_size,
1352 call->ndr_push, call->ndr_pull);
1353 if (!NT_STATUS_IS_OK(status)) {
1354 DEBUG(2,("Validation failed in dcerpc_ndr_request_send - %s\n",
1355 nt_errstr(status)));
1361 DEBUG(10,("rpc request data:\n"));
1362 dump_data(10, request.data, request.length);
1364 /* make the actual dcerpc request */
1365 req = dcerpc_request_send(p, object, opnum, table->calls[opnum].async,
1369 req->ndr.table = table;
1370 req->ndr.opnum = opnum;
1371 req->ndr.struct_ptr = r;
1372 req->ndr.mem_ctx = mem_ctx;
1381 receive the answer from a dcerpc_ndr_request_send()
1383 _PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
1385 struct dcerpc_pipe *p = req->p;
1388 struct ndr_pull *pull;
1390 TALLOC_CTX *mem_ctx = req->ndr.mem_ctx;
1391 void *r = req->ndr.struct_ptr;
1392 uint32_t opnum = req->ndr.opnum;
1393 const struct ndr_interface_table *table = req->ndr.table;
1394 const struct ndr_interface_call *call = &table->calls[opnum];
1396 /* make sure the recv code doesn't free the request, as we
1397 need to grab the flags element before it is freed */
1398 if (talloc_reference(p, req) == NULL) {
1399 return NT_STATUS_NO_MEMORY;
1402 status = dcerpc_request_recv(req, mem_ctx, &response);
1403 if (!NT_STATUS_IS_OK(status)) {
1404 talloc_unlink(p, req);
1410 /* prepare for ndr_pull_* */
1411 pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
1413 talloc_unlink(p, req);
1414 return NT_STATUS_NO_MEMORY;
1418 pull->data = talloc_steal(pull, pull->data);
1420 talloc_unlink(p, req);
1422 if (flags & DCERPC_PULL_BIGENDIAN) {
1423 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
1426 DEBUG(10,("rpc reply data:\n"));
1427 dump_data(10, pull->data, pull->data_size);
1429 /* pull the structure from the blob */
1430 status = call->ndr_pull(pull, NDR_OUT, r);
1431 if (!NT_STATUS_IS_OK(status)) {
1432 dcerpc_log_packet(table, opnum, NDR_OUT,
1437 if (p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
1438 status = dcerpc_ndr_validate_out(p->conn, pull, r, call->struct_size,
1439 call->ndr_push, call->ndr_pull,
1441 if (!NT_STATUS_IS_OK(status)) {
1442 dcerpc_log_packet(table, opnum, NDR_OUT,
1448 if (pull->offset != pull->data_size) {
1449 DEBUG(0,("Warning! ignoring %d unread bytes in rpc packet!\n",
1450 pull->data_size - pull->offset));
1451 /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
1452 but it turns out that early versions of NT
1453 (specifically NT3.1) add junk onto the end of rpc
1454 packets, so if we want to interoperate at all with
1455 those versions then we need to ignore this error */
1458 /* TODO: make pull context independent from the output mem_ctx and free the pull context */
1460 return NT_STATUS_OK;
1465 a useful helper function for synchronous rpc requests
1467 this can be used when you have ndr push/pull functions in the
1470 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
1471 const struct GUID *object,
1472 const struct ndr_interface_table *table,
1474 TALLOC_CTX *mem_ctx,
1477 struct rpc_request *req;
1479 req = dcerpc_ndr_request_send(p, object, table, opnum, mem_ctx, r);
1481 return NT_STATUS_NO_MEMORY;
1484 return dcerpc_ndr_request_recv(req);
1489 a useful function for retrieving the server name we connected to
1491 const char *dcerpc_server_name(struct dcerpc_pipe *p)
1493 if (!p->conn->transport.peer_name) {
1496 return p->conn->transport.peer_name(p->conn);
1501 get the dcerpc auth_level for a open connection
1503 uint32_t dcerpc_auth_level(struct dcerpc_connection *c)
1507 if (c->flags & DCERPC_SEAL) {
1508 auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
1509 } else if (c->flags & DCERPC_SIGN) {
1510 auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
1511 } else if (c->flags & DCERPC_CONNECT) {
1512 auth_level = DCERPC_AUTH_LEVEL_CONNECT;
1514 auth_level = DCERPC_AUTH_LEVEL_NONE;
1520 Receive an alter reply from the transport
1522 static void dcerpc_alter_recv_handler(struct rpc_request *req,
1523 DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
1525 struct composite_context *c;
1526 struct dcerpc_pipe *recv_pipe;
1528 c = talloc_get_type(req->async.private_data, struct composite_context);
1529 recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
1531 if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
1532 pkt->u.alter_resp.num_results == 1 &&
1533 pkt->u.alter_resp.ctx_list[0].result != 0) {
1534 DEBUG(2,("dcerpc: alter_resp failed - reason %d\n",
1535 pkt->u.alter_resp.ctx_list[0].reason));
1536 composite_error(c, dcerpc_map_reason(pkt->u.alter_resp.ctx_list[0].reason));
1540 if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
1541 pkt->u.alter_resp.num_results == 0 ||
1542 pkt->u.alter_resp.ctx_list[0].result != 0) {
1543 composite_error(c, NT_STATUS_NET_WRITE_FAULT);
1547 /* the alter_resp might contain a reply set of credentials */
1548 if (recv_pipe->conn->security_state.auth_info &&
1549 pkt->u.alter_resp.auth_info.length) {
1550 c->status = ndr_pull_struct_blob(
1551 &pkt->u.alter_resp.auth_info, recv_pipe,
1552 recv_pipe->conn->security_state.auth_info,
1553 (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
1554 if (!composite_is_ok(c)) return;
1561 send a dcerpc alter_context request
1563 struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
1564 TALLOC_CTX *mem_ctx,
1565 const struct ndr_syntax_id *syntax,
1566 const struct ndr_syntax_id *transfer_syntax)
1568 struct composite_context *c;
1569 struct ncacn_packet pkt;
1571 struct rpc_request *req;
1573 c = composite_create(mem_ctx, p->conn->event_ctx);
1574 if (c == NULL) return NULL;
1576 c->private_data = p;
1578 p->syntax = *syntax;
1579 p->transfer_syntax = *transfer_syntax;
1581 init_ncacn_hdr(p->conn, &pkt);
1583 pkt.ptype = DCERPC_PKT_ALTER;
1584 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
1585 pkt.call_id = p->conn->call_id;
1586 pkt.auth_length = 0;
1588 if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
1589 pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
1592 pkt.u.alter.max_xmit_frag = 5840;
1593 pkt.u.alter.max_recv_frag = 5840;
1594 pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
1595 pkt.u.alter.num_contexts = 1;
1596 pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
1597 if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
1598 pkt.u.alter.ctx_list[0].context_id = p->context_id;
1599 pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
1600 pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
1601 pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
1602 pkt.u.alter.auth_info = data_blob(NULL, 0);
1604 /* construct the NDR form of the packet */
1605 c->status = ncacn_push_auth(&blob, mem_ctx, &pkt,
1606 p->conn->security_state.auth_info);
1607 if (!composite_is_ok(c)) return c;
1609 p->conn->transport.recv_data = dcerpc_recv_data;
1612 * we allocate a dcerpc_request so we can be in the same
1613 * request queue as normal requests
1615 req = talloc_zero(c, struct rpc_request);
1616 if (composite_nomem(req, c)) return c;
1618 req->state = RPC_REQUEST_PENDING;
1619 req->call_id = pkt.call_id;
1620 req->async.private_data = c;
1621 req->async.callback = dcerpc_composite_fail;
1623 req->recv_handler = dcerpc_alter_recv_handler;
1624 DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
1625 talloc_set_destructor(req, dcerpc_req_dequeue);
1627 c->status = p->conn->transport.send_request(p->conn, &blob, true);
1628 if (!composite_is_ok(c)) return c;
1630 event_add_timed(c->event_ctx, req,
1631 timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
1632 dcerpc_timeout_handler, req);
1637 NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
1639 NTSTATUS result = composite_wait(ctx);
1645 send a dcerpc alter_context request
1647 NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
1648 TALLOC_CTX *mem_ctx,
1649 const struct ndr_syntax_id *syntax,
1650 const struct ndr_syntax_id *transfer_syntax)
1652 struct composite_context *creq;
1653 creq = dcerpc_alter_context_send(p, mem_ctx, syntax, transfer_syntax);
1654 return dcerpc_alter_context_recv(creq);