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 2 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, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "lib/util/dlinklist.h"
26 #include "lib/events/events.h"
27 #include "librpc/rpc/dcerpc.h"
28 #include "librpc/gen_ndr/ndr_misc.h"
29 #include "librpc/gen_ndr/ndr_dcerpc.h"
30 #include "libcli/composite/composite.h"
31 #include "auth/gensec/gensec.h"
33 NTSTATUS dcerpc_init(void)
40 static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);
41 static void dcerpc_ship_next_request(struct dcerpc_connection *c);
43 /* destroy a dcerpc connection */
44 static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
47 conn->free_skipped = True;
50 dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
55 /* initialise a dcerpc connection.
56 the event context is optional
58 static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
59 struct event_context *ev)
61 struct dcerpc_connection *c;
63 c = talloc_zero(mem_ctx, struct dcerpc_connection);
69 ev = event_context_init(c);
78 if (!talloc_reference(c, ev)) {
83 c->security_state.auth_info = NULL;
84 c->security_state.session_key = dcerpc_generic_session_key;
85 c->security_state.generic_state = NULL;
86 c->binding_string = NULL;
88 c->srv_max_xmit_frag = 0;
89 c->srv_max_recv_frag = 0;
92 talloc_set_destructor(c, dcerpc_connection_destructor);
97 /* initialise a dcerpc pipe. */
98 struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *ev)
100 struct dcerpc_pipe *p;
102 p = talloc(mem_ctx, struct dcerpc_pipe);
107 p->conn = dcerpc_connection_init(p, ev);
108 if (p->conn == NULL) {
113 p->last_fault_code = 0;
115 p->request_timeout = DCERPC_REQUEST_TIMEOUT;
118 ZERO_STRUCT(p->syntax);
119 ZERO_STRUCT(p->transfer_syntax);
126 choose the next call id to use
128 static uint32_t next_call_id(struct dcerpc_connection *c)
131 if (c->call_id == 0) {
137 /* we need to be able to get/set the fragment length without doing a full
139 void dcerpc_set_frag_length(DATA_BLOB *blob, uint16_t v)
141 if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
142 SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
144 RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
148 uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob)
150 if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
151 return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
153 return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
157 void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
159 if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
160 SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
162 RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
168 setup for a ndr pull, also setting up any flags from the binding string
170 static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c,
171 DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
173 struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
175 if (ndr == NULL) return ndr;
177 if (c->flags & DCERPC_DEBUG_PAD_CHECK) {
178 ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
181 if (c->flags & DCERPC_NDR_REF_ALLOC) {
182 ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
189 parse a data blob into a ncacn_packet structure. This handles both
190 input and output packets
192 static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
193 struct ncacn_packet *pkt)
195 struct ndr_pull *ndr;
197 ndr = ndr_pull_init_flags(c, blob, mem_ctx);
199 return NT_STATUS_NO_MEMORY;
202 if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
203 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
206 return ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
210 generate a CONNECT level verifier
212 static NTSTATUS dcerpc_connect_verifier(TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
214 *blob = data_blob_talloc(mem_ctx, NULL, 16);
215 if (blob->data == NULL) {
216 return NT_STATUS_NO_MEMORY;
218 SIVAL(blob->data, 0, 1);
219 memset(blob->data+4, 0, 12);
224 check a CONNECT level verifier
226 static NTSTATUS dcerpc_check_connect_verifier(DATA_BLOB *blob)
228 if (blob->length != 16 ||
229 IVAL(blob->data, 0) != 1) {
230 return NT_STATUS_ACCESS_DENIED;
236 parse the authentication information on a dcerpc response packet
238 static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX *mem_ctx,
239 DATA_BLOB *raw_packet,
240 struct ncacn_packet *pkt)
242 struct ndr_pull *ndr;
244 struct dcerpc_auth auth;
247 if (pkt->auth_length == 0 &&
248 c->security_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
252 auth_blob.length = 8 + pkt->auth_length;
254 /* check for a valid length */
255 if (pkt->u.response.stub_and_verifier.length < auth_blob.length) {
256 return NT_STATUS_INFO_LENGTH_MISMATCH;
260 pkt->u.response.stub_and_verifier.data +
261 pkt->u.response.stub_and_verifier.length - auth_blob.length;
262 pkt->u.response.stub_and_verifier.length -= auth_blob.length;
264 /* pull the auth structure */
265 ndr = ndr_pull_init_flags(c, &auth_blob, mem_ctx);
267 return NT_STATUS_NO_MEMORY;
270 if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
271 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
274 status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
275 if (!NT_STATUS_IS_OK(status)) {
279 /* check signature or unseal the packet */
280 switch (c->security_state.auth_info->auth_level) {
281 case DCERPC_AUTH_LEVEL_PRIVACY:
282 status = gensec_unseal_packet(c->security_state.generic_state,
284 raw_packet->data + DCERPC_REQUEST_LENGTH,
285 pkt->u.response.stub_and_verifier.length,
287 raw_packet->length - auth.credentials.length,
289 memcpy(pkt->u.response.stub_and_verifier.data,
290 raw_packet->data + DCERPC_REQUEST_LENGTH,
291 pkt->u.response.stub_and_verifier.length);
294 case DCERPC_AUTH_LEVEL_INTEGRITY:
295 status = gensec_check_packet(c->security_state.generic_state,
297 pkt->u.response.stub_and_verifier.data,
298 pkt->u.response.stub_and_verifier.length,
300 raw_packet->length - auth.credentials.length,
304 case DCERPC_AUTH_LEVEL_CONNECT:
305 status = dcerpc_check_connect_verifier(&auth.credentials);
308 case DCERPC_AUTH_LEVEL_NONE:
312 status = NT_STATUS_INVALID_LEVEL;
316 /* remove the indicated amount of paddiing */
317 if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
318 return NT_STATUS_INFO_LENGTH_MISMATCH;
320 pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
327 push a dcerpc request packet into a blob, possibly signing it.
329 static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
330 DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
331 struct ncacn_packet *pkt)
334 struct ndr_push *ndr;
336 size_t payload_length;
338 /* non-signed packets are simpler */
339 if (!c->security_state.auth_info ||
340 !c->security_state.generic_state) {
341 return ncacn_push_auth(blob, mem_ctx, pkt, c->security_state.auth_info);
344 ndr = ndr_push_init_ctx(mem_ctx);
346 return NT_STATUS_NO_MEMORY;
349 if (c->flags & DCERPC_PUSH_BIGENDIAN) {
350 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
353 if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
354 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
357 status = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
358 if (!NT_STATUS_IS_OK(status)) {
362 /* pad to 16 byte multiple in the payload portion of the
363 packet. This matches what w2k3 does */
364 c->security_state.auth_info->auth_pad_length =
365 (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
366 ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
368 payload_length = pkt->u.request.stub_and_verifier.length +
369 c->security_state.auth_info->auth_pad_length;
371 /* sign or seal the packet */
372 switch (c->security_state.auth_info->auth_level) {
373 case DCERPC_AUTH_LEVEL_PRIVACY:
374 case DCERPC_AUTH_LEVEL_INTEGRITY:
375 /* We hope this length is accruate. If must be if the
376 * GENSEC mech does AEAD signing of the packet
378 c->security_state.auth_info->credentials
379 = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(c->security_state.generic_state,
381 data_blob_clear(&c->security_state.auth_info->credentials);
384 case DCERPC_AUTH_LEVEL_CONNECT:
385 status = dcerpc_connect_verifier(mem_ctx, &c->security_state.auth_info->credentials);
388 case DCERPC_AUTH_LEVEL_NONE:
389 c->security_state.auth_info->credentials = data_blob(NULL, 0);
393 status = NT_STATUS_INVALID_LEVEL;
397 if (!NT_STATUS_IS_OK(status)) {
401 /* add the auth verifier */
402 status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
403 if (!NT_STATUS_IS_OK(status)) {
407 /* extract the whole packet as a blob */
408 *blob = ndr_push_blob(ndr);
410 /* fill in the fragment length and auth_length, we can't fill
411 in these earlier as we don't know the signature length (it
412 could be variable length) */
413 dcerpc_set_frag_length(blob, blob->length);
414 /* We hope this value is accruate. If must be if the GENSEC
415 * mech does AEAD signing of the packet headers */
416 dcerpc_set_auth_length(blob, c->security_state.auth_info->credentials.length);
418 /* sign or seal the packet */
419 switch (c->security_state.auth_info->auth_level) {
420 case DCERPC_AUTH_LEVEL_PRIVACY:
421 status = gensec_seal_packet(c->security_state.generic_state,
423 blob->data + DCERPC_REQUEST_LENGTH,
427 c->security_state.auth_info->credentials.length,
429 if (!NT_STATUS_IS_OK(status)) {
432 blob->length -= c->security_state.auth_info->credentials.length;
433 status = data_blob_append(mem_ctx, blob,
434 creds2.data, creds2.length);
435 if (!NT_STATUS_IS_OK(status)) {
438 dcerpc_set_auth_length(blob, creds2.length);
439 if (c->security_state.auth_info->credentials.length == 0) {
440 /* this is needed for krb5 only, to correct the total packet
442 dcerpc_set_frag_length(blob,
443 dcerpc_get_frag_length(blob)
448 case DCERPC_AUTH_LEVEL_INTEGRITY:
449 status = gensec_sign_packet(c->security_state.generic_state,
451 blob->data + DCERPC_REQUEST_LENGTH,
455 c->security_state.auth_info->credentials.length,
457 if (!NT_STATUS_IS_OK(status)) {
460 blob->length -= c->security_state.auth_info->credentials.length;
461 status = data_blob_append(mem_ctx, blob,
462 creds2.data, creds2.length);
463 if (!NT_STATUS_IS_OK(status)) {
466 dcerpc_set_auth_length(blob, creds2.length);
467 if (c->security_state.auth_info->credentials.length == 0) {
468 /* this is needed for krb5 only, to correct the total packet
470 dcerpc_set_frag_length(blob,
471 dcerpc_get_frag_length(blob)
476 case DCERPC_AUTH_LEVEL_CONNECT:
479 case DCERPC_AUTH_LEVEL_NONE:
480 c->security_state.auth_info->credentials = data_blob(NULL, 0);
484 status = NT_STATUS_INVALID_LEVEL;
488 data_blob_free(&c->security_state.auth_info->credentials);
495 fill in the fixed values in a dcerpc header
497 static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt)
500 pkt->rpc_vers_minor = 0;
501 if (c->flags & DCERPC_PUSH_BIGENDIAN) {
504 pkt->drep[0] = DCERPC_DREP_LE;
512 map a bind nak reason to a NTSTATUS
514 static NTSTATUS dcerpc_map_reason(uint16_t reason)
517 case DCERPC_BIND_REASON_ASYNTAX:
518 return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
519 case DCERPC_BIND_REASON_INVALID_AUTH_TYPE:
520 return NT_STATUS_INVALID_PARAMETER;
522 return NT_STATUS_UNSUCCESSFUL;
526 a bind or alter context has failed
528 static void dcerpc_composite_fail(struct rpc_request *req)
530 struct composite_context *c = talloc_get_type(req->async.private_data,
531 struct composite_context);
532 composite_error(c, req->status);
536 remove requests from the pending or queued queues
538 static int dcerpc_req_dequeue(struct rpc_request *req)
540 switch (req->state) {
541 case RPC_REQUEST_QUEUED:
542 DLIST_REMOVE(req->p->conn->request_queue, req);
544 case RPC_REQUEST_PENDING:
545 DLIST_REMOVE(req->p->conn->pending, req);
547 case RPC_REQUEST_DONE:
555 mark the dcerpc connection dead. All outstanding requests get an error
557 static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
559 if (conn->dead) return;
563 if (conn->transport.shutdown_pipe) {
564 conn->transport.shutdown_pipe(conn, status);
567 /* all pending requests get the error */
568 while (conn->pending) {
569 struct rpc_request *req = conn->pending;
570 dcerpc_req_dequeue(req);
571 req->state = RPC_REQUEST_DONE;
572 req->status = status;
573 if (req->async.callback) {
574 req->async.callback(req);
578 talloc_set_destructor(conn, NULL);
579 if (conn->free_skipped) {
585 forward declarations of the recv_data handlers for the types of
586 packets we need to handle
588 static void dcerpc_request_recv_data(struct dcerpc_connection *c,
589 DATA_BLOB *raw_packet, struct ncacn_packet *pkt);
592 receive a dcerpc reply from the transport. Here we work out what
593 type of reply it is (normal request, bind or alter context) and
594 dispatch to the appropriate handler
596 static void dcerpc_recv_data(struct dcerpc_connection *conn, DATA_BLOB *blob, NTSTATUS status)
598 struct ncacn_packet pkt;
600 if (NT_STATUS_IS_OK(status) && blob->length == 0) {
601 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
604 /* the transport may be telling us of a severe error, such as
606 if (!NT_STATUS_IS_OK(status)) {
607 data_blob_free(blob);
608 dcerpc_connection_dead(conn, status);
612 /* parse the basic packet to work out what type of response this is */
613 status = ncacn_pull(conn, blob, blob->data, &pkt);
614 if (!NT_STATUS_IS_OK(status)) {
615 data_blob_free(blob);
616 dcerpc_connection_dead(conn, status);
619 dcerpc_request_recv_data(conn, blob, &pkt);
624 Receive a bind reply from the transport
626 static void dcerpc_bind_recv_handler(struct rpc_request *req,
627 DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
629 struct composite_context *c;
630 struct dcerpc_connection *conn;
632 c = talloc_get_type(req->async.private_data, struct composite_context);
634 if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
635 DEBUG(2,("dcerpc: bind_nak reason %d\n",
636 pkt->u.bind_nak.reject_reason));
637 composite_error(c, dcerpc_map_reason(pkt->u.bind_nak.
642 if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
643 (pkt->u.bind_ack.num_results == 0) ||
644 (pkt->u.bind_ack.ctx_list[0].result != 0)) {
645 composite_error(c, NT_STATUS_NET_WRITE_FAULT);
651 conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
652 conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
654 /* the bind_ack might contain a reply set of credentials */
655 if (conn->security_state.auth_info &&
656 pkt->u.bind_ack.auth_info.length) {
657 c->status = ndr_pull_struct_blob(
658 &pkt->u.bind_ack.auth_info, conn,
659 conn->security_state.auth_info,
660 (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
661 if (!composite_is_ok(c)) return;
664 req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
670 handle timeouts of individual dcerpc requests
672 static void dcerpc_timeout_handler(struct event_context *ev, struct timed_event *te,
673 struct timeval t, void *private)
675 struct rpc_request *req = talloc_get_type(private, struct rpc_request);
677 if (req->ignore_timeout) {
678 dcerpc_req_dequeue(req);
679 req->state = RPC_REQUEST_DONE;
680 req->status = NT_STATUS_IO_TIMEOUT;
681 if (req->async.callback) {
682 req->async.callback(req);
687 dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
691 send a async dcerpc bind request
693 struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
695 const struct dcerpc_syntax_id *syntax,
696 const struct dcerpc_syntax_id *transfer_syntax)
698 struct composite_context *c;
699 struct ncacn_packet pkt;
701 struct rpc_request *req;
703 c = composite_create(mem_ctx,p->conn->event_ctx);
704 if (c == NULL) return NULL;
709 p->transfer_syntax = *transfer_syntax;
711 init_ncacn_hdr(p->conn, &pkt);
713 pkt.ptype = DCERPC_PKT_BIND;
714 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
715 pkt.call_id = p->conn->call_id;
718 if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
719 pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
722 pkt.u.bind.max_xmit_frag = 5840;
723 pkt.u.bind.max_recv_frag = 5840;
724 pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
725 pkt.u.bind.num_contexts = 1;
726 pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
727 if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
728 pkt.u.bind.ctx_list[0].context_id = p->context_id;
729 pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
730 pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
731 pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
732 pkt.u.bind.auth_info = data_blob(NULL, 0);
734 /* construct the NDR form of the packet */
735 c->status = ncacn_push_auth(&blob, c, &pkt,
736 p->conn->security_state.auth_info);
737 if (!composite_is_ok(c)) return c;
739 p->conn->transport.recv_data = dcerpc_recv_data;
742 * we allocate a dcerpc_request so we can be in the same
743 * request queue as normal requests
745 req = talloc_zero(c, struct rpc_request);
746 if (composite_nomem(req, c)) return c;
748 req->state = RPC_REQUEST_PENDING;
749 req->call_id = pkt.call_id;
750 req->async.private_data = c;
751 req->async.callback = dcerpc_composite_fail;
753 req->recv_handler = dcerpc_bind_recv_handler;
754 DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
755 talloc_set_destructor(req, dcerpc_req_dequeue);
757 c->status = p->conn->transport.send_request(p->conn, &blob,
759 if (!composite_is_ok(c)) return c;
761 event_add_timed(c->event_ctx, req,
762 timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
763 dcerpc_timeout_handler, req);
769 recv side of async dcerpc bind request
771 NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
773 NTSTATUS result = composite_wait(ctx);
779 perform a continued bind (and auth3)
781 NTSTATUS dcerpc_auth3(struct dcerpc_connection *c,
784 struct ncacn_packet pkt;
788 init_ncacn_hdr(c, &pkt);
790 pkt.ptype = DCERPC_PKT_AUTH3;
791 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
792 pkt.call_id = next_call_id(c);
794 pkt.u.auth3._pad = 0;
795 pkt.u.auth3.auth_info = data_blob(NULL, 0);
797 /* construct the NDR form of the packet */
798 status = ncacn_push_auth(&blob, mem_ctx, &pkt, c->security_state.auth_info);
799 if (!NT_STATUS_IS_OK(status)) {
803 /* send it on its way */
804 status = c->transport.send_request(c, &blob, False);
805 if (!NT_STATUS_IS_OK(status)) {
814 process a fragment received from the transport layer during a
817 This function frees the data
819 static void dcerpc_request_recv_data(struct dcerpc_connection *c,
820 DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
822 struct rpc_request *req;
824 NTSTATUS status = NT_STATUS_OK;
827 if this is an authenticated connection then parse and check
828 the auth info. We have to do this before finding the
829 matching packet, as the request structure might have been
830 removed due to a timeout, but if it has been we still need
831 to run the auth routines so that we don't get the sign/seal
832 info out of step with the server
834 if (c->security_state.auth_info && c->security_state.generic_state &&
835 pkt->ptype == DCERPC_PKT_RESPONSE) {
836 status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt);
839 /* find the matching request */
840 for (req=c->pending;req;req=req->next) {
841 if (pkt->call_id == req->call_id) break;
845 /* useful for testing certain vendors RPC servers */
846 if (req == NULL && c->pending && pkt->call_id == 0) {
847 DEBUG(0,("HACK FOR INCORRECT CALL ID\n"));
853 DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id));
854 data_blob_free(raw_packet);
858 talloc_steal(req, raw_packet->data);
860 if (req->recv_handler != NULL) {
861 dcerpc_req_dequeue(req);
862 req->state = RPC_REQUEST_DONE;
863 req->recv_handler(req, raw_packet, pkt);
867 if (pkt->ptype == DCERPC_PKT_FAULT) {
868 DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
869 req->fault_code = pkt->u.fault.status;
870 req->status = NT_STATUS_NET_WRITE_FAULT;
874 if (pkt->ptype != DCERPC_PKT_RESPONSE) {
875 DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
877 req->fault_code = DCERPC_FAULT_OTHER;
878 req->status = NT_STATUS_NET_WRITE_FAULT;
882 /* now check the status from the auth routines, and if it failed then fail
883 this request accordingly */
884 if (!NT_STATUS_IS_OK(status)) {
885 req->status = status;
889 length = pkt->u.response.stub_and_verifier.length;
892 req->payload.data = talloc_realloc(req,
895 req->payload.length + length);
896 if (!req->payload.data) {
897 req->status = NT_STATUS_NO_MEMORY;
900 memcpy(req->payload.data+req->payload.length,
901 pkt->u.response.stub_and_verifier.data, length);
902 req->payload.length += length;
905 if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
906 c->transport.send_read(c);
910 if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
911 req->flags |= DCERPC_PULL_BIGENDIAN;
913 req->flags &= ~DCERPC_PULL_BIGENDIAN;
918 /* we've got the full payload */
919 req->state = RPC_REQUEST_DONE;
920 DLIST_REMOVE(c->pending, req);
922 if (c->request_queue != NULL) {
923 /* We have to look at shipping further requests before calling
924 * the async function, that one might close the pipe */
925 dcerpc_ship_next_request(c);
928 if (req->async.callback) {
929 req->async.callback(req);
934 perform the send side of a async dcerpc request
936 static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
937 const struct GUID *object,
940 DATA_BLOB *stub_data)
942 struct rpc_request *req;
944 p->conn->transport.recv_data = dcerpc_recv_data;
946 req = talloc(p, struct rpc_request);
952 req->call_id = next_call_id(p->conn);
953 req->status = NT_STATUS_OK;
954 req->state = RPC_REQUEST_QUEUED;
955 req->payload = data_blob(NULL, 0);
958 req->async_call = async;
959 req->ignore_timeout = False;
960 req->async.callback = NULL;
961 req->async.private_data = NULL;
962 req->recv_handler = NULL;
964 if (object != NULL) {
965 req->object = talloc_memdup(req, object, sizeof(*object));
966 if (req->object == NULL) {
975 req->request_data.length = stub_data->length;
976 req->request_data.data = talloc_reference(req, stub_data->data);
977 if (req->request_data.length && req->request_data.data == NULL) {
981 DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
982 talloc_set_destructor(req, dcerpc_req_dequeue);
984 dcerpc_ship_next_request(p->conn);
986 if (p->request_timeout) {
987 event_add_timed(dcerpc_event_context(p), req,
988 timeval_current_ofs(p->request_timeout, 0),
989 dcerpc_timeout_handler, req);
996 Send a request using the transport
999 static void dcerpc_ship_next_request(struct dcerpc_connection *c)
1001 struct rpc_request *req;
1002 struct dcerpc_pipe *p;
1003 DATA_BLOB *stub_data;
1004 struct ncacn_packet pkt;
1006 uint32_t remaining, chunk_size;
1007 BOOL first_packet = True;
1009 req = c->request_queue;
1015 stub_data = &req->request_data;
1017 if (!req->async_call && (c->pending != NULL)) {
1021 DLIST_REMOVE(c->request_queue, req);
1022 DLIST_ADD(c->pending, req);
1023 req->state = RPC_REQUEST_PENDING;
1025 init_ncacn_hdr(p->conn, &pkt);
1027 remaining = stub_data->length;
1029 /* we can write a full max_recv_frag size, minus the dcerpc
1030 request header size */
1031 chunk_size = p->conn->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
1033 pkt.ptype = DCERPC_PKT_REQUEST;
1034 pkt.call_id = req->call_id;
1035 pkt.auth_length = 0;
1037 pkt.u.request.alloc_hint = remaining;
1038 pkt.u.request.context_id = p->context_id;
1039 pkt.u.request.opnum = req->opnum;
1042 pkt.u.request.object.object = *req->object;
1043 pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
1044 chunk_size -= ndr_size_GUID(req->object,0);
1047 /* we send a series of pdus without waiting for a reply */
1048 while (remaining > 0 || first_packet) {
1049 uint32_t chunk = MIN(chunk_size, remaining);
1050 BOOL last_frag = False;
1052 first_packet = False;
1053 pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST);
1055 if (remaining == stub_data->length) {
1056 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
1058 if (chunk == remaining) {
1059 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
1063 pkt.u.request.stub_and_verifier.data = stub_data->data +
1064 (stub_data->length - remaining);
1065 pkt.u.request.stub_and_verifier.length = chunk;
1067 req->status = ncacn_push_request_sign(p->conn, &blob, req, &pkt);
1068 if (!NT_STATUS_IS_OK(req->status)) {
1069 req->state = RPC_REQUEST_DONE;
1070 DLIST_REMOVE(p->conn->pending, req);
1074 req->status = p->conn->transport.send_request(p->conn, &blob, last_frag);
1075 if (!NT_STATUS_IS_OK(req->status)) {
1076 req->state = RPC_REQUEST_DONE;
1077 DLIST_REMOVE(p->conn->pending, req);
1086 return the event context for a dcerpc pipe
1087 used by callers who wish to operate asynchronously
1089 struct event_context *dcerpc_event_context(struct dcerpc_pipe *p)
1091 return p->conn->event_ctx;
1097 perform the receive side of a async dcerpc request
1099 NTSTATUS dcerpc_request_recv(struct rpc_request *req,
1100 TALLOC_CTX *mem_ctx,
1101 DATA_BLOB *stub_data)
1105 while (req->state != RPC_REQUEST_DONE) {
1106 struct event_context *ctx = dcerpc_event_context(req->p);
1107 if (event_loop_once(ctx) != 0) {
1108 return NT_STATUS_CONNECTION_DISCONNECTED;
1111 *stub_data = req->payload;
1112 status = req->status;
1113 if (stub_data->data) {
1114 stub_data->data = talloc_steal(mem_ctx, stub_data->data);
1116 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
1117 req->p->last_fault_code = req->fault_code;
1124 perform a full request/response pair on a dcerpc pipe
1126 NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
1127 struct GUID *object,
1130 TALLOC_CTX *mem_ctx,
1131 DATA_BLOB *stub_data_in,
1132 DATA_BLOB *stub_data_out)
1134 struct rpc_request *req;
1136 req = dcerpc_request_send(p, object, opnum, async, stub_data_in);
1138 return NT_STATUS_NO_MEMORY;
1141 return dcerpc_request_recv(req, mem_ctx, stub_data_out);
1146 this is a paranoid NDR validator. For every packet we push onto the wire
1147 we pull it back again, then push it again. Then we compare the raw NDR data
1148 for that to the NDR we initially generated. If they don't match then we know
1149 we must have a bug in either the pull or push side of our code
1151 static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c,
1152 TALLOC_CTX *mem_ctx,
1155 ndr_push_flags_fn_t ndr_push,
1156 ndr_pull_flags_fn_t ndr_pull)
1159 struct ndr_pull *pull;
1160 struct ndr_push *push;
1164 st = talloc_size(mem_ctx, struct_size);
1166 return NT_STATUS_NO_MEMORY;
1169 pull = ndr_pull_init_flags(c, &blob, mem_ctx);
1171 return NT_STATUS_NO_MEMORY;
1173 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
1175 status = ndr_pull(pull, NDR_IN, st);
1176 if (!NT_STATUS_IS_OK(status)) {
1177 return ndr_pull_error(pull, NDR_ERR_VALIDATE,
1178 "failed input validation pull - %s",
1182 push = ndr_push_init_ctx(mem_ctx);
1184 return NT_STATUS_NO_MEMORY;
1187 status = ndr_push(push, NDR_IN, st);
1188 if (!NT_STATUS_IS_OK(status)) {
1189 return ndr_push_error(push, NDR_ERR_VALIDATE,
1190 "failed input validation push - %s",
1194 blob2 = ndr_push_blob(push);
1196 if (!data_blob_equal(&blob, &blob2)) {
1197 DEBUG(3,("original:\n"));
1198 dump_data(3, blob.data, blob.length);
1199 DEBUG(3,("secondary:\n"));
1200 dump_data(3, blob2.data, blob2.length);
1201 return ndr_push_error(push, NDR_ERR_VALIDATE,
1202 "failed input validation data - %s",
1206 return NT_STATUS_OK;
1210 this is a paranoid NDR input validator. For every packet we pull
1211 from the wire we push it back again then pull and push it
1212 again. Then we compare the raw NDR data for that to the NDR we
1213 initially generated. If they don't match then we know we must have a
1214 bug in either the pull or push side of our code
1216 static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
1217 struct ndr_pull *pull_in,
1220 ndr_push_flags_fn_t ndr_push,
1221 ndr_pull_flags_fn_t ndr_pull,
1222 ndr_print_function_t ndr_print)
1225 struct ndr_pull *pull;
1226 struct ndr_push *push;
1228 DATA_BLOB blob, blob2;
1229 TALLOC_CTX *mem_ctx = pull_in;
1232 st = talloc_size(mem_ctx, struct_size);
1234 return NT_STATUS_NO_MEMORY;
1236 memcpy(st, struct_ptr, struct_size);
1238 push = ndr_push_init_ctx(mem_ctx);
1240 return NT_STATUS_NO_MEMORY;
1243 status = ndr_push(push, NDR_OUT, struct_ptr);
1244 if (!NT_STATUS_IS_OK(status)) {
1245 return ndr_push_error(push, NDR_ERR_VALIDATE,
1246 "failed output validation push - %s",
1250 blob = ndr_push_blob(push);
1252 pull = ndr_pull_init_flags(c, &blob, mem_ctx);
1254 return NT_STATUS_NO_MEMORY;
1257 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
1258 status = ndr_pull(pull, NDR_OUT, st);
1259 if (!NT_STATUS_IS_OK(status)) {
1260 return ndr_pull_error(pull, NDR_ERR_VALIDATE,
1261 "failed output validation pull - %s",
1265 push = ndr_push_init_ctx(mem_ctx);
1267 return NT_STATUS_NO_MEMORY;
1270 status = ndr_push(push, NDR_OUT, st);
1271 if (!NT_STATUS_IS_OK(status)) {
1272 return ndr_push_error(push, NDR_ERR_VALIDATE,
1273 "failed output validation push2 - %s",
1277 blob2 = ndr_push_blob(push);
1279 if (!data_blob_equal(&blob, &blob2)) {
1280 DEBUG(3,("original:\n"));
1281 dump_data(3, blob.data, blob.length);
1282 DEBUG(3,("secondary:\n"));
1283 dump_data(3, blob2.data, blob2.length);
1284 return ndr_push_error(push, NDR_ERR_VALIDATE,
1285 "failed output validation data - %s",
1289 /* this checks the printed forms of the two structures, which effectively
1290 tests all of the value() attributes */
1291 s1 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
1292 NDR_OUT, struct_ptr);
1293 s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
1295 if (strcmp(s1, s2) != 0) {
1297 printf("VALIDATE ERROR:\nWIRE:\n%s\n GEN:\n%s\n", s1, s2);
1299 /* this is sometimes useful */
1300 printf("VALIDATE ERROR\n");
1301 file_save("wire.dat", s1, strlen(s1));
1302 file_save("gen.dat", s2, strlen(s2));
1303 system("diff -u wire.dat gen.dat");
1307 return NT_STATUS_OK;
1312 send a rpc request given a dcerpc_call structure
1314 struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
1315 const struct GUID *object,
1316 const struct dcerpc_interface_table *table,
1318 TALLOC_CTX *mem_ctx,
1321 const struct dcerpc_interface_call *call;
1322 struct ndr_push *push;
1325 struct rpc_request *req;
1327 call = &table->calls[opnum];
1329 /* setup for a ndr_push_* call */
1330 push = ndr_push_init_ctx(mem_ctx);
1335 if (p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
1336 push->flags |= LIBNDR_FLAG_BIGENDIAN;
1339 /* push the structure into a blob */
1340 status = call->ndr_push(push, NDR_IN, r);
1341 if (!NT_STATUS_IS_OK(status)) {
1342 DEBUG(2,("Unable to ndr_push structure in dcerpc_ndr_request_send - %s\n",
1343 nt_errstr(status)));
1348 /* retrieve the blob */
1349 request = ndr_push_blob(push);
1351 if (p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
1352 status = dcerpc_ndr_validate_in(p->conn, push, request, call->struct_size,
1353 call->ndr_push, call->ndr_pull);
1354 if (!NT_STATUS_IS_OK(status)) {
1355 DEBUG(2,("Validation failed in dcerpc_ndr_request_send - %s\n",
1356 nt_errstr(status)));
1362 DEBUG(10,("rpc request data:\n"));
1363 dump_data(10, request.data, request.length);
1365 /* make the actual dcerpc request */
1366 req = dcerpc_request_send(p, object, opnum, table->calls[opnum].async,
1370 req->ndr.table = table;
1371 req->ndr.opnum = opnum;
1372 req->ndr.struct_ptr = r;
1373 req->ndr.mem_ctx = mem_ctx;
1382 receive the answer from a dcerpc_ndr_request_send()
1384 _PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
1386 struct dcerpc_pipe *p = req->p;
1389 struct ndr_pull *pull;
1391 TALLOC_CTX *mem_ctx = req->ndr.mem_ctx;
1392 void *r = req->ndr.struct_ptr;
1393 uint32_t opnum = req->ndr.opnum;
1394 const struct dcerpc_interface_table *table = req->ndr.table;
1395 const struct dcerpc_interface_call *call = &table->calls[opnum];
1397 /* make sure the recv code doesn't free the request, as we
1398 need to grab the flags element before it is freed */
1399 if (talloc_reference(p, req) == NULL) {
1400 return NT_STATUS_NO_MEMORY;
1403 status = dcerpc_request_recv(req, mem_ctx, &response);
1404 if (!NT_STATUS_IS_OK(status)) {
1405 talloc_unlink(p, req);
1411 /* prepare for ndr_pull_* */
1412 pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
1414 talloc_unlink(p, req);
1415 return NT_STATUS_NO_MEMORY;
1419 pull->data = talloc_steal(pull, pull->data);
1421 talloc_unlink(p, req);
1423 if (flags & DCERPC_PULL_BIGENDIAN) {
1424 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
1427 DEBUG(10,("rpc reply data:\n"));
1428 dump_data(10, pull->data, pull->data_size);
1430 /* pull the structure from the blob */
1431 status = call->ndr_pull(pull, NDR_OUT, r);
1432 if (!NT_STATUS_IS_OK(status)) {
1433 dcerpc_log_packet(table, opnum, NDR_OUT,
1438 if (p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
1439 status = dcerpc_ndr_validate_out(p->conn, pull, r, call->struct_size,
1440 call->ndr_push, call->ndr_pull,
1442 if (!NT_STATUS_IS_OK(status)) {
1443 dcerpc_log_packet(table, opnum, NDR_OUT,
1449 if (pull->offset != pull->data_size) {
1450 DEBUG(0,("Warning! ignoring %d unread bytes in rpc packet!\n",
1451 pull->data_size - pull->offset));
1452 /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
1453 but it turns out that early versions of NT
1454 (specifically NT3.1) add junk onto the end of rpc
1455 packets, so if we want to interoperate at all with
1456 those versions then we need to ignore this error */
1459 /* TODO: make pull context independent from the output mem_ctx and free the pull context */
1461 return NT_STATUS_OK;
1466 a useful helper function for synchronous rpc requests
1468 this can be used when you have ndr push/pull functions in the
1471 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
1472 const struct GUID *object,
1473 const struct dcerpc_interface_table *table,
1475 TALLOC_CTX *mem_ctx,
1478 struct rpc_request *req;
1480 req = dcerpc_ndr_request_send(p, object, table, opnum, mem_ctx, r);
1482 return NT_STATUS_NO_MEMORY;
1485 return dcerpc_ndr_request_recv(req);
1490 a useful function for retrieving the server name we connected to
1492 const char *dcerpc_server_name(struct dcerpc_pipe *p)
1494 if (!p->conn->transport.peer_name) {
1497 return p->conn->transport.peer_name(p->conn);
1502 get the dcerpc auth_level for a open connection
1504 uint32_t dcerpc_auth_level(struct dcerpc_connection *c)
1508 if (c->flags & DCERPC_SEAL) {
1509 auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
1510 } else if (c->flags & DCERPC_SIGN) {
1511 auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
1512 } else if (c->flags & DCERPC_CONNECT) {
1513 auth_level = DCERPC_AUTH_LEVEL_CONNECT;
1515 auth_level = DCERPC_AUTH_LEVEL_NONE;
1521 Receive an alter reply from the transport
1523 static void dcerpc_alter_recv_handler(struct rpc_request *req,
1524 DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
1526 struct composite_context *c;
1527 struct dcerpc_pipe *recv_pipe;
1529 c = talloc_get_type(req->async.private_data, struct composite_context);
1530 recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
1532 if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
1533 pkt->u.alter_resp.num_results == 1 &&
1534 pkt->u.alter_resp.ctx_list[0].result != 0) {
1535 DEBUG(2,("dcerpc: alter_resp failed - reason %d\n",
1536 pkt->u.alter_resp.ctx_list[0].reason));
1537 composite_error(c, dcerpc_map_reason(pkt->u.alter_resp.ctx_list[0].reason));
1541 if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
1542 pkt->u.alter_resp.num_results == 0 ||
1543 pkt->u.alter_resp.ctx_list[0].result != 0) {
1544 composite_error(c, NT_STATUS_NET_WRITE_FAULT);
1548 /* the alter_resp might contain a reply set of credentials */
1549 if (recv_pipe->conn->security_state.auth_info &&
1550 pkt->u.alter_resp.auth_info.length) {
1551 c->status = ndr_pull_struct_blob(
1552 &pkt->u.alter_resp.auth_info, recv_pipe,
1553 recv_pipe->conn->security_state.auth_info,
1554 (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
1555 if (!composite_is_ok(c)) return;
1562 send a dcerpc alter_context request
1564 struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
1565 TALLOC_CTX *mem_ctx,
1566 const struct dcerpc_syntax_id *syntax,
1567 const struct dcerpc_syntax_id *transfer_syntax)
1569 struct composite_context *c;
1570 struct ncacn_packet pkt;
1572 struct rpc_request *req;
1574 c = composite_create(mem_ctx, p->conn->event_ctx);
1575 if (c == NULL) return NULL;
1577 c->private_data = p;
1579 p->syntax = *syntax;
1580 p->transfer_syntax = *transfer_syntax;
1582 init_ncacn_hdr(p->conn, &pkt);
1584 pkt.ptype = DCERPC_PKT_ALTER;
1585 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
1586 pkt.call_id = p->conn->call_id;
1587 pkt.auth_length = 0;
1589 if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
1590 pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
1593 pkt.u.alter.max_xmit_frag = 5840;
1594 pkt.u.alter.max_recv_frag = 5840;
1595 pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
1596 pkt.u.alter.num_contexts = 1;
1597 pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
1598 if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
1599 pkt.u.alter.ctx_list[0].context_id = p->context_id;
1600 pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
1601 pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
1602 pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
1603 pkt.u.alter.auth_info = data_blob(NULL, 0);
1605 /* construct the NDR form of the packet */
1606 c->status = ncacn_push_auth(&blob, mem_ctx, &pkt,
1607 p->conn->security_state.auth_info);
1608 if (!composite_is_ok(c)) return c;
1610 p->conn->transport.recv_data = dcerpc_recv_data;
1613 * we allocate a dcerpc_request so we can be in the same
1614 * request queue as normal requests
1616 req = talloc_zero(c, struct rpc_request);
1617 if (composite_nomem(req, c)) return c;
1619 req->state = RPC_REQUEST_PENDING;
1620 req->call_id = pkt.call_id;
1621 req->async.private_data = c;
1622 req->async.callback = dcerpc_composite_fail;
1624 req->recv_handler = dcerpc_alter_recv_handler;
1625 DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
1626 talloc_set_destructor(req, dcerpc_req_dequeue);
1628 c->status = p->conn->transport.send_request(p->conn, &blob, True);
1629 if (!composite_is_ok(c)) return c;
1631 event_add_timed(c->event_ctx, req,
1632 timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
1633 dcerpc_timeout_handler, req);
1638 NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
1640 NTSTATUS result = composite_wait(ctx);
1646 send a dcerpc alter_context request
1648 NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
1649 TALLOC_CTX *mem_ctx,
1650 const struct dcerpc_syntax_id *syntax,
1651 const struct dcerpc_syntax_id *transfer_syntax)
1653 struct composite_context *creq;
1654 creq = dcerpc_alter_context_send(p, mem_ctx, syntax, transfer_syntax);
1655 return dcerpc_alter_context_recv(creq);