2 Unix SMB/CIFS implementation.
5 Copyright (C) Tim Potter 2003
6 Copyright (C) Andrew Tridgell 2003
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 /* initialise a dcerpc pipe. This currently assumes a SMB named pipe
27 struct dcerpc_pipe *dcerpc_pipe_init(struct cli_tree *tree)
29 struct dcerpc_pipe *p;
31 TALLOC_CTX *mem_ctx = talloc_init("dcerpc_tree");
35 p = talloc(mem_ctx, sizeof(*p));
37 talloc_destroy(mem_ctx);
41 p->reference_count = 0;
44 p->tree->reference_count++;
51 /* close down a dcerpc over SMB pipe */
52 void dcerpc_pipe_close(struct dcerpc_pipe *p)
56 if (p->reference_count <= 0) {
57 cli_tree_close(p->tree);
58 talloc_destroy(p->mem_ctx);
62 #define BLOB_CHECK_BOUNDS(blob, offset, len) do { \
63 if ((offset) > blob->length || (blob->length - (offset) < (len))) { \
64 return NT_STATUS_INVALID_PARAMETER; \
68 #define DCERPC_ALIGN(offset, n) do { \
69 (offset) = ((offset) + ((n)-1)) & ~((n)-1); \
73 pull a wire format uuid into a string. This will consume 16 bytes
75 static char *dcerpc_pull_uuid(char *data, TALLOC_CTX *mem_ctx)
78 uint16 time_mid, time_hi_and_version;
79 uint8 clock_seq_hi_and_reserved;
84 time_low = IVAL(data, 0);
85 time_mid = SVAL(data, 4);
86 time_hi_and_version = SVAL(data, 6);
87 clock_seq_hi_and_reserved = CVAL(data, 8);
88 clock_seq_low = CVAL(data, 9);
90 node[i] = CVAL(data, 10 + i);
93 return talloc_asprintf(mem_ctx,
94 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
95 time_low, time_mid, time_hi_and_version,
96 clock_seq_hi_and_reserved, clock_seq_low,
97 node[0], node[1], node[2], node[3], node[4], node[5]);
101 push a uuid_str into wire format. It will consume 16 bytes
103 static NTSTATUS push_uuid_str(char *data, const char *uuid_str)
106 uint32 time_mid, time_hi_and_version;
107 uint32 clock_seq_hi_and_reserved;
108 uint32 clock_seq_low;
112 if (11 != sscanf(uuid_str, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
113 &time_low, &time_mid, &time_hi_and_version,
114 &clock_seq_hi_and_reserved, &clock_seq_low,
115 &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) {
116 return NT_STATUS_INVALID_PARAMETER;
119 SIVAL(data, 0, time_low);
120 SSVAL(data, 4, time_mid);
121 SSVAL(data, 6, time_hi_and_version);
122 SCVAL(data, 8, clock_seq_hi_and_reserved);
123 SCVAL(data, 9, clock_seq_low);
125 SCVAL(data, 10 + i, node[i]);
132 pull a dcerpc syntax id from a blob
134 static NTSTATUS dcerpc_pull_syntax_id(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
136 struct dcerpc_syntax_id *syntax)
138 syntax->uuid_str = dcerpc_pull_uuid(blob->data + (*offset), mem_ctx);
139 if (!syntax->uuid_str) {
140 return NT_STATUS_NO_MEMORY;
143 syntax->if_version = IVAL(blob->data, *offset);
149 push a syntax id onto the wire. It will consume 20 bytes
151 static NTSTATUS push_syntax_id(char *data, const struct dcerpc_syntax_id *syntax)
155 status = push_uuid_str(data, syntax->uuid_str);
156 SIVAL(data, 16, syntax->if_version);
162 pull an auth verifier from a packet
164 static NTSTATUS dcerpc_pull_auth_verifier(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
166 struct dcerpc_hdr *hdr,
169 if (hdr->auth_length == 0) {
173 BLOB_CHECK_BOUNDS(blob, *offset, hdr->auth_length);
174 *auth = data_blob_talloc(mem_ctx, blob->data + (*offset), hdr->auth_length);
176 return NT_STATUS_NO_MEMORY;
178 (*offset) += hdr->auth_length;
183 parse a struct dcerpc_response
185 static NTSTATUS dcerpc_pull_response(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
187 struct dcerpc_hdr *hdr,
188 struct dcerpc_response *pkt)
192 BLOB_CHECK_BOUNDS(blob, *offset, 8);
194 pkt->alloc_hint = IVAL(blob->data, (*offset) + 0);
195 pkt->context_id = SVAL(blob->data, (*offset) + 4);
196 pkt->cancel_count = CVAL(blob->data, (*offset) + 6);
200 stub_len = blob->length - ((*offset) + hdr->auth_length);
201 BLOB_CHECK_BOUNDS(blob, *offset, stub_len);
202 pkt->stub_data = data_blob_talloc(mem_ctx, blob->data + (*offset), stub_len);
203 if (stub_len != 0 && !pkt->stub_data.data) {
204 return NT_STATUS_NO_MEMORY;
206 (*offset) += stub_len;
208 return dcerpc_pull_auth_verifier(blob, mem_ctx, offset, hdr, &pkt->auth_verifier);
213 parse a struct bind_ack
215 static NTSTATUS dcerpc_pull_bind_ack(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
217 struct dcerpc_hdr *hdr,
218 struct dcerpc_bind_ack *pkt)
223 BLOB_CHECK_BOUNDS(blob, *offset, 10);
224 pkt->max_xmit_frag = SVAL(blob->data, (*offset) + 0);
225 pkt->max_recv_frag = SVAL(blob->data, (*offset) + 2);
226 pkt->assoc_group_id = IVAL(blob->data, (*offset) + 4);
227 len = SVAL(blob->data, (*offset) + 8);
231 BLOB_CHECK_BOUNDS(blob, *offset, len);
232 pkt->secondary_address = talloc_strndup(mem_ctx, blob->data + (*offset), len);
233 if (!pkt->secondary_address) {
234 return NT_STATUS_NO_MEMORY;
239 DCERPC_ALIGN(*offset, 4);
240 BLOB_CHECK_BOUNDS(blob, *offset, 4);
241 pkt->num_results = CVAL(blob->data, *offset);
244 if (pkt->num_results > 0) {
245 pkt->ctx_list = talloc(mem_ctx, sizeof(pkt->ctx_list[0]) * pkt->num_results);
246 if (!pkt->ctx_list) {
247 return NT_STATUS_NO_MEMORY;
251 for (i=0;i<pkt->num_results;i++) {
254 BLOB_CHECK_BOUNDS(blob, *offset, 24);
255 pkt->ctx_list[i].result = IVAL(blob->data, *offset);
257 status = dcerpc_pull_syntax_id(blob, mem_ctx, offset, &pkt->ctx_list[i].syntax);
258 if (!NT_STATUS_IS_OK(status)) {
263 return dcerpc_pull_auth_verifier(blob, mem_ctx, offset, hdr, &pkt->auth_verifier);
268 parse a dcerpc header
270 static NTSTATUS dcerpc_pull_hdr(DATA_BLOB *blob, uint32 *offset, struct dcerpc_hdr *hdr)
272 BLOB_CHECK_BOUNDS(blob, *offset, 16);
274 hdr->rpc_vers = CVAL(blob->data, (*offset) + 0);
275 hdr->rpc_vers_minor = CVAL(blob->data, (*offset) + 1);
276 hdr->ptype = CVAL(blob->data, (*offset) + 2);
277 hdr->pfc_flags = CVAL(blob->data, (*offset) + 3);
278 memcpy(hdr->drep, blob->data + (*offset) + 4, 4);
279 hdr->frag_length = SVAL(blob->data, (*offset) + 8);
280 hdr->auth_length = SVAL(blob->data, (*offset) + 10);
281 hdr->call_id = IVAL(blob->data, (*offset) + 12);
289 parse a dcerpc header. It consumes 16 bytes
291 static void dcerpc_push_hdr(char *data, struct dcerpc_hdr *hdr)
293 SCVAL(data, 0, hdr->rpc_vers);
294 SCVAL(data, 1, hdr->rpc_vers_minor);
295 SCVAL(data, 2, hdr->ptype);
296 SCVAL(data, 3, hdr->pfc_flags);
297 memcpy(data + 4, hdr->drep, 4);
298 SSVAL(data, 8, hdr->frag_length);
299 SSVAL(data, 12, hdr->call_id);
305 parse a data blob into a dcerpc_packet structure. This handles both
306 input and output packets
308 NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
313 status = dcerpc_pull_hdr(blob, &offset, &pkt->hdr);
314 if (!NT_STATUS_IS_OK(status)) {
318 switch (pkt->hdr.ptype) {
319 case DCERPC_PKT_BIND_ACK:
320 status = dcerpc_pull_bind_ack(blob, mem_ctx, &offset, &pkt->hdr, &pkt->out.bind_ack);
321 if (!NT_STATUS_IS_OK(status)) {
326 case DCERPC_PKT_RESPONSE:
327 status = dcerpc_pull_response(blob, mem_ctx, &offset, &pkt->hdr, &pkt->out.response);
328 if (!NT_STATUS_IS_OK(status)) {
334 return NT_STATUS_NET_WRITE_FAULT;
342 push a dcerpc_bind into a blob
344 static NTSTATUS dcerpc_push_bind(DATA_BLOB *blob, uint32 *offset,
345 struct dcerpc_hdr *hdr,
346 struct dcerpc_bind *pkt)
350 SSVAL(blob->data, (*offset) + 0, pkt->max_xmit_frag);
351 SSVAL(blob->data, (*offset) + 2, pkt->max_recv_frag);
352 SIVAL(blob->data, (*offset) + 4, pkt->assoc_group_id);
353 SCVAL(blob->data, (*offset) + 8, pkt->num_contexts);
356 for (i=0;i<pkt->num_contexts;i++) {
359 SSVAL(blob->data, (*offset) + 0, pkt->ctx_list[i].context_id);
360 SCVAL(blob->data, (*offset) + 2, pkt->ctx_list[i].num_transfer_syntaxes);
361 status = push_syntax_id(blob->data + (*offset) + 4, &pkt->ctx_list[i].abstract_syntax);
362 if (!NT_STATUS_IS_OK(status)) {
366 for (j=0;j<pkt->ctx_list[i].num_transfer_syntaxes;j++) {
367 status = push_syntax_id(blob->data + (*offset),
368 &pkt->ctx_list[i].transfer_syntaxes[j]);
369 if (!NT_STATUS_IS_OK(status)) {
380 push a dcerpc_request into a blob
382 static NTSTATUS dcerpc_push_request(DATA_BLOB *blob, uint32 *offset,
383 struct dcerpc_hdr *hdr,
384 struct dcerpc_request *pkt)
386 SIVAL(blob->data, (*offset) + 0, pkt->alloc_hint);
387 SSVAL(blob->data, (*offset) + 4, pkt->context_id);
388 SSVAL(blob->data, (*offset) + 6, pkt->opnum);
392 memcpy(blob->data + (*offset), pkt->stub_data.data, pkt->stub_data.length);
393 (*offset) += pkt->stub_data.length;
395 memcpy(blob->data + (*offset), pkt->auth_verifier.data, pkt->auth_verifier.length);
396 (*offset) += pkt->auth_verifier.length;
403 work out the wire size of a dcerpc packet
405 static uint32 dcerpc_wire_size(struct dcerpc_packet *pkt)
410 size += 16; /* header */
412 switch (pkt->hdr.ptype) {
413 case DCERPC_PKT_REQUEST:
415 size += pkt->in.request.stub_data.length;
416 size += pkt->in.request.auth_verifier.length;
419 case DCERPC_PKT_RESPONSE:
421 size += pkt->out.response.stub_data.length;
422 size += pkt->hdr.auth_length;
425 case DCERPC_PKT_BIND:
427 for (i=0;i<pkt->in.bind.num_contexts;i++) {
429 size += pkt->in.bind.ctx_list[i].num_transfer_syntaxes * 20;
431 size += pkt->hdr.auth_length;
434 case DCERPC_PKT_BIND_ACK:
436 if (pkt->out.bind_ack.secondary_address) {
437 size += strlen(pkt->out.bind_ack.secondary_address) + 1;
440 size += pkt->out.bind_ack.num_results * 24;
441 size += pkt->hdr.auth_length;
449 push a dcerpc_packet into a blob. This handles both input and
452 NTSTATUS dcerpc_push(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
458 /* work out how big the packet will be on the wire */
459 wire_size = dcerpc_wire_size(pkt);
461 (*blob) = data_blob_talloc(mem_ctx, NULL, wire_size);
463 return NT_STATUS_NO_MEMORY;
466 pkt->hdr.frag_length = wire_size;
468 dcerpc_push_hdr(blob->data + offset, &pkt->hdr);
471 switch (pkt->hdr.ptype) {
472 case DCERPC_PKT_BIND:
473 status = dcerpc_push_bind(blob, &offset, &pkt->hdr, &pkt->in.bind);
476 case DCERPC_PKT_REQUEST:
477 status = dcerpc_push_request(blob, &offset, &pkt->hdr, &pkt->in.request);
481 status = NT_STATUS_NET_WRITE_FAULT;
491 fill in the fixed values in a dcerpc header
493 static void init_dcerpc_hdr(struct dcerpc_hdr *hdr)
496 hdr->rpc_vers_minor = 0;
497 hdr->drep[0] = 0x10; /* Little endian */
505 perform a bind using the given syntax
507 NTSTATUS dcerpc_bind(struct dcerpc_pipe *p,
508 const struct dcerpc_syntax_id *syntax,
509 const struct dcerpc_syntax_id *transfer_syntax)
512 struct dcerpc_packet pkt;
517 mem_ctx = talloc_init("dcerpc_bind");
519 return NT_STATUS_NO_MEMORY;
522 init_dcerpc_hdr(&pkt.hdr);
524 pkt.hdr.ptype = DCERPC_PKT_BIND;
525 pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
526 pkt.hdr.call_id = p->call_id++;
527 pkt.hdr.auth_length = 0;
529 pkt.in.bind.max_xmit_frag = 0x2000;
530 pkt.in.bind.max_recv_frag = 0x2000;
531 pkt.in.bind.assoc_group_id = 0;
532 pkt.in.bind.num_contexts = 1;
533 pkt.in.bind.ctx_list = talloc(mem_ctx, sizeof(pkt.in.bind.ctx_list[0]));
534 if (!pkt.in.bind.ctx_list) {
535 talloc_destroy(mem_ctx);
536 return NT_STATUS_NO_MEMORY;
538 pkt.in.bind.ctx_list[0].context_id = 0;
539 pkt.in.bind.ctx_list[0].num_transfer_syntaxes = 1;
540 pkt.in.bind.ctx_list[0].abstract_syntax = *syntax;
541 pkt.in.bind.ctx_list[0].transfer_syntaxes = transfer_syntax;
543 pkt.in.bind.auth_verifier = data_blob(NULL, 0);
545 status = dcerpc_push(&blob, mem_ctx, &pkt);
546 if (!NT_STATUS_IS_OK(status)) {
547 talloc_destroy(mem_ctx);
551 status = dcerpc_raw_packet(p, mem_ctx, &blob, &blob_out);
552 if (!NT_STATUS_IS_OK(status)) {
553 talloc_destroy(mem_ctx);
557 status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
558 if (!NT_STATUS_IS_OK(status)) {
559 talloc_destroy(mem_ctx);
563 if (pkt.hdr.ptype != DCERPC_PKT_BIND_ACK ||
564 pkt.out.bind_ack.num_results == 0 ||
565 pkt.out.bind_ack.ctx_list[0].result != 0) {
566 status = NT_STATUS_UNSUCCESSFUL;
569 p->srv_max_xmit_frag = pkt.out.bind_ack.max_xmit_frag;
570 p->srv_max_recv_frag = pkt.out.bind_ack.max_recv_frag;
572 talloc_destroy(mem_ctx);
577 /* Perform a bind using the given UUID and version */
578 NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p,
579 const char *uuid, unsigned version)
581 struct dcerpc_syntax_id syntax;
582 struct dcerpc_syntax_id transfer_syntax;
584 syntax.uuid_str = uuid;
585 syntax.if_version = version;
587 transfer_syntax.uuid_str = "8a885d04-1ceb-11c9-9fe8-08002b104860";
588 transfer_syntax.if_version = 2;
590 return dcerpc_bind(p, &syntax, &transfer_syntax);
594 perform a full request/response pair on a dcerpc pipe
596 NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
599 DATA_BLOB *stub_data_in,
600 DATA_BLOB *stub_data_out)
603 struct dcerpc_packet pkt;
605 DATA_BLOB blob_in, blob_out, payload;
606 uint32 remaining, chunk_size;
608 init_dcerpc_hdr(&pkt.hdr);
610 remaining = stub_data_in->length;
612 /* we can write a full max_recv_frag size, minus the dcerpc
613 request header size */
614 chunk_size = p->srv_max_recv_frag - 24;
616 pkt.hdr.ptype = DCERPC_PKT_REQUEST;
617 pkt.hdr.call_id = p->call_id++;
618 pkt.hdr.auth_length = 0;
619 pkt.in.request.alloc_hint = remaining;
620 pkt.in.request.context_id = 0;
621 pkt.in.request.opnum = opnum;
622 pkt.in.request.auth_verifier = data_blob(NULL, 0);
624 /* we send a series of pdus without waiting for a reply until
626 while (remaining > chunk_size) {
627 if (remaining == stub_data_in->length) {
628 pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_FIRST;
630 pkt.hdr.pfc_flags = 0;
633 pkt.in.request.stub_data.data = stub_data_in->data +
634 (stub_data_in->length - remaining);
635 pkt.in.request.stub_data.length = chunk_size;
637 status = dcerpc_push(&blob_in, mem_ctx, &pkt);
638 if (!NT_STATUS_IS_OK(status)) {
642 status = dcerpc_raw_packet_initial(p, mem_ctx, &blob_in);
643 if (!NT_STATUS_IS_OK(status)) {
647 remaining -= chunk_size;
650 /* now we send a pdu with LAST_FRAG sent and get the first
652 if (remaining == stub_data_in->length) {
653 pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
655 pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_LAST;
657 pkt.in.request.stub_data.data = stub_data_in->data +
658 (stub_data_in->length - remaining);
659 pkt.in.request.stub_data.length = remaining;
661 status = dcerpc_push(&blob_in, mem_ctx, &pkt);
662 if (!NT_STATUS_IS_OK(status)) {
666 /* send the pdu and get the initial response pdu */
667 status = dcerpc_raw_packet(p, mem_ctx, &blob_in, &blob_out);
669 status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
670 if (!NT_STATUS_IS_OK(status)) {
674 if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
675 return NT_STATUS_UNSUCCESSFUL;
678 if (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
679 /* something is badly wrong! */
680 return NT_STATUS_UNSUCCESSFUL;
683 payload = pkt.out.response.stub_data;
685 /* continue receiving fragments */
686 while (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
689 status = dcerpc_raw_packet_secondary(p, mem_ctx, &blob_out);
690 if (!NT_STATUS_IS_OK(status)) {
694 status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
695 if (!NT_STATUS_IS_OK(status)) {
699 if (pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
700 /* start of another packet!? */
701 return NT_STATUS_UNSUCCESSFUL;
704 if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
705 return NT_STATUS_UNSUCCESSFUL;
708 length = pkt.out.response.stub_data.length;
710 payload.data = talloc_realloc(mem_ctx,
712 payload.length + length);
714 return NT_STATUS_NO_MEMORY;
717 memcpy(payload.data + payload.length,
718 pkt.out.response.stub_data.data,
721 payload.length += length;
725 *stub_data_out = payload;
733 a useful helper function for synchronous rpc requests
735 this can be used when you have ndr push/pull functions in the
738 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
741 NTSTATUS (*ndr_push)(struct ndr_push *, void *),
742 NTSTATUS (*ndr_pull)(struct ndr_pull *, void *),
745 struct ndr_push *push;
746 struct ndr_pull *pull;
748 DATA_BLOB request, response;
750 /* setup for a ndr_push_* call */
751 push = ndr_push_init();
753 talloc_destroy(mem_ctx);
754 return NT_STATUS_NO_MEMORY;
757 /* push the structure into a blob */
758 status = ndr_push(push, struct_ptr);
759 if (!NT_STATUS_IS_OK(status)) {
763 /* retrieve the blob */
764 request = ndr_push_blob(push);
766 dump_data(10, request.data, request.length);
768 /* make the actual dcerpc request */
769 status = dcerpc_request(p, opnum, mem_ctx, &request, &response);
770 if (!NT_STATUS_IS_OK(status)) {
774 /* prepare for ndr_pull_* */
775 pull = ndr_pull_init_blob(&response, mem_ctx);
780 dump_data(10, pull->data, pull->data_size);
782 /* pull the structure from the blob */
783 status = ndr_pull(pull, struct_ptr);
784 if (!NT_STATUS_IS_OK(status)) {
788 if (pull->offset != pull->data_size) {
789 DEBUG(0,("Warning! %d unread bytes\n", pull->data_size - pull->offset));
790 status = NT_STATUS_INFO_LENGTH_MISMATCH;
801 a useful function for retrieving the server name we connected to
803 const char *dcerpc_server_name(struct dcerpc_pipe *p)
805 return p->tree->session->transport->called.name;