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);
64 build a GUID from a string
66 static NTSTATUS guid_from_string(const char *s, struct GUID *guid)
69 uint32 time_mid, time_hi_and_version;
70 uint32 clock_seq_hi_and_reserved;
75 if (11 != sscanf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
76 &time_low, &time_mid, &time_hi_and_version,
77 &clock_seq_hi_and_reserved, &clock_seq_low,
78 &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) {
79 return NT_STATUS_INVALID_PARAMETER;
82 SIVAL(guid->info, 0, time_low);
83 SSVAL(guid->info, 4, time_mid);
84 SSVAL(guid->info, 6, time_hi_and_version);
85 SCVAL(guid->info, 8, clock_seq_hi_and_reserved);
86 SCVAL(guid->info, 9, clock_seq_low);
88 SCVAL(guid->info, 10 + i, node[i]);
95 parse a data blob into a dcerpc_packet structure. This handles both
96 input and output packets
98 NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
100 struct ndr_pull *ndr;
102 ndr = ndr_pull_init_blob(blob, mem_ctx);
104 return NT_STATUS_NO_MEMORY;
107 return ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
112 push a dcerpc_packet into a blob. This handles both input and
115 NTSTATUS dcerpc_push(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
117 struct ndr_push *ndr;
120 ndr = ndr_push_init_ctx(mem_ctx);
122 return NT_STATUS_NO_MEMORY;
125 status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
126 if (!NT_STATUS_IS_OK(status)) {
130 *blob = ndr_push_blob(ndr);
132 /* fill in the frag length */
133 SSVAL(blob->data, 8, blob->length);
140 fill in the fixed values in a dcerpc header
142 static void init_dcerpc_hdr(struct dcerpc_packet *pkt)
145 pkt->rpc_vers_minor = 0;
146 pkt->drep[0] = 0x10; /* Little endian */
154 perform a bind using the given syntax
156 NTSTATUS dcerpc_bind(struct dcerpc_pipe *p,
157 const struct dcerpc_syntax_id *syntax,
158 const struct dcerpc_syntax_id *transfer_syntax)
161 struct dcerpc_packet pkt;
165 struct dcerpc_syntax_id tsyntax;
167 mem_ctx = talloc_init("dcerpc_bind");
169 return NT_STATUS_NO_MEMORY;
172 init_dcerpc_hdr(&pkt);
174 pkt.ptype = DCERPC_PKT_BIND;
175 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
176 pkt.call_id = p->call_id++;
179 pkt.u.bind.max_xmit_frag = 0x2000;
180 pkt.u.bind.max_recv_frag = 0x2000;
181 pkt.u.bind.assoc_group_id = 0;
182 pkt.u.bind.num_contexts = 1;
183 pkt.u.bind.ctx_list = talloc(mem_ctx, sizeof(pkt.u.bind.ctx_list[0]));
184 if (!pkt.u.bind.ctx_list) {
185 talloc_destroy(mem_ctx);
186 return NT_STATUS_NO_MEMORY;
188 pkt.u.bind.ctx_list[0].context_id = 0;
189 pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
190 pkt.u.bind.ctx_list[0].abstract_syntax = *syntax;
191 tsyntax = *transfer_syntax;
192 pkt.u.bind.ctx_list[0].transfer_syntaxes = &tsyntax;
193 pkt.u.bind.auth_verifier = data_blob(NULL, 0);
195 status = dcerpc_push(&blob, mem_ctx, &pkt);
196 if (!NT_STATUS_IS_OK(status)) {
197 talloc_destroy(mem_ctx);
201 status = dcerpc_raw_packet(p, mem_ctx, &blob, &blob_out);
202 if (!NT_STATUS_IS_OK(status)) {
203 talloc_destroy(mem_ctx);
207 status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
208 if (!NT_STATUS_IS_OK(status)) {
209 talloc_destroy(mem_ctx);
213 if (pkt.ptype != DCERPC_PKT_BIND_ACK ||
214 pkt.u.bind_ack.num_results == 0 ||
215 pkt.u.bind_ack.ctx_list[0].result != 0) {
216 status = NT_STATUS_UNSUCCESSFUL;
219 p->srv_max_xmit_frag = pkt.u.bind_ack.max_xmit_frag;
220 p->srv_max_recv_frag = pkt.u.bind_ack.max_recv_frag;
222 talloc_destroy(mem_ctx);
227 /* Perform a bind using the given UUID and version */
228 NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p,
229 const char *uuid, unsigned version)
231 struct dcerpc_syntax_id syntax;
232 struct dcerpc_syntax_id transfer_syntax;
235 status = guid_from_string(uuid, &syntax.uuid);
236 if (!NT_STATUS_IS_OK(status)) {
237 DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
240 syntax.major_version = version;
241 syntax.minor_version = 0;
243 status = guid_from_string("8a885d04-1ceb-11c9-9fe8-08002b104860",
244 &transfer_syntax.uuid);
245 if (!NT_STATUS_IS_OK(status)) {
249 transfer_syntax.major_version = 2;
250 transfer_syntax.minor_version = 0;
252 return dcerpc_bind(p, &syntax, &transfer_syntax);
256 perform a full request/response pair on a dcerpc pipe
258 NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
261 DATA_BLOB *stub_data_in,
262 DATA_BLOB *stub_data_out)
265 struct dcerpc_packet pkt;
267 DATA_BLOB blob_in, blob_out, payload;
268 uint32 remaining, chunk_size;
270 init_dcerpc_hdr(&pkt);
272 remaining = stub_data_in->length;
274 /* we can write a full max_recv_frag size, minus the dcerpc
275 request header size */
276 chunk_size = p->srv_max_recv_frag - 24;
278 pkt.ptype = DCERPC_PKT_REQUEST;
279 pkt.call_id = p->call_id++;
281 pkt.u.request.alloc_hint = remaining;
282 pkt.u.request.context_id = 0;
283 pkt.u.request.opnum = opnum;
285 /* we send a series of pdus without waiting for a reply until
287 while (remaining > chunk_size) {
288 if (remaining == stub_data_in->length) {
289 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST;
294 pkt.u.request.stub_and_verifier.data = stub_data_in->data +
295 (stub_data_in->length - remaining);
296 pkt.u.request.stub_and_verifier.length = chunk_size;
298 status = dcerpc_push(&blob_in, mem_ctx, &pkt);
299 if (!NT_STATUS_IS_OK(status)) {
303 status = dcerpc_raw_packet_initial(p, mem_ctx, &blob_in);
304 if (!NT_STATUS_IS_OK(status)) {
308 remaining -= chunk_size;
311 /* now we send a pdu with LAST_FRAG sent and get the first
313 if (remaining == stub_data_in->length) {
314 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
316 pkt.pfc_flags = DCERPC_PFC_FLAG_LAST;
318 pkt.u.request.stub_and_verifier.data = stub_data_in->data +
319 (stub_data_in->length - remaining);
320 pkt.u.request.stub_and_verifier.length = remaining;
322 status = dcerpc_push(&blob_in, mem_ctx, &pkt);
323 if (!NT_STATUS_IS_OK(status)) {
327 /* send the pdu and get the initial response pdu */
328 status = dcerpc_raw_packet(p, mem_ctx, &blob_in, &blob_out);
330 status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
331 if (!NT_STATUS_IS_OK(status)) {
335 if (pkt.ptype == DCERPC_PKT_FAULT) {
336 return NT_STATUS_NET_WRITE_FAULT;
339 if (pkt.ptype != DCERPC_PKT_RESPONSE) {
340 return NT_STATUS_UNSUCCESSFUL;
343 if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
344 /* something is badly wrong! */
345 return NT_STATUS_UNSUCCESSFUL;
348 payload = pkt.u.response.stub_and_verifier;
350 /* continue receiving fragments */
351 while (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
354 status = dcerpc_raw_packet_secondary(p, mem_ctx, &blob_out);
355 if (!NT_STATUS_IS_OK(status)) {
359 status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
360 if (!NT_STATUS_IS_OK(status)) {
364 if (pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
365 /* start of another packet!? */
366 return NT_STATUS_UNSUCCESSFUL;
369 if (pkt.ptype != DCERPC_PKT_RESPONSE) {
370 return NT_STATUS_UNSUCCESSFUL;
373 length = pkt.u.response.stub_and_verifier.length;
375 payload.data = talloc_realloc(mem_ctx,
377 payload.length + length);
379 return NT_STATUS_NO_MEMORY;
382 memcpy(payload.data + payload.length,
383 pkt.u.response.stub_and_verifier.data,
386 payload.length += length;
390 *stub_data_out = payload;
398 this is a paranoid NDR validator. For every packet we push onto the wire
399 we pull it back again, then push it again. Then we compare the raw NDR data
400 for that to the NDR we initially generated. If they don't match then we know
401 we must have a bug in either the pull or push side of our code
403 static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
406 NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
407 NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
410 struct ndr_pull *pull;
411 struct ndr_push *push;
415 st = talloc(mem_ctx, struct_size);
417 return NT_STATUS_NO_MEMORY;
420 pull = ndr_pull_init_blob(&blob, mem_ctx);
422 return NT_STATUS_NO_MEMORY;
425 status = ndr_pull(pull, NDR_IN, st);
426 if (!NT_STATUS_IS_OK(status)) {
427 return ndr_pull_error(pull, NDR_ERR_VALIDATE,
428 "failed input validation pull - %s",
432 push = ndr_push_init_ctx(mem_ctx);
434 return NT_STATUS_NO_MEMORY;
437 status = ndr_push(push, NDR_IN, st);
438 if (!NT_STATUS_IS_OK(status)) {
439 return ndr_push_error(push, NDR_ERR_VALIDATE,
440 "failed input validation push - %s",
444 blob2 = ndr_push_blob(push);
446 if (!data_blob_equal(&blob, &blob2)) {
447 DEBUG(3,("original:\n"));
448 dump_data(3, blob.data, blob.length);
449 DEBUG(3,("secondary:\n"));
450 dump_data(3, blob2.data, blob2.length);
451 return ndr_push_error(push, NDR_ERR_VALIDATE,
452 "failed input validation data - %s",
460 this is a paranoid NDR input validator. For every packet we pull
461 from the wire we push it back again then pull and push it
462 again. Then we compare the raw NDR data for that to the NDR we
463 initially generated. If they don't match then we know we must have a
464 bug in either the pull or push side of our code
466 static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
469 NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
470 NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
473 struct ndr_pull *pull;
474 struct ndr_push *push;
476 DATA_BLOB blob, blob2;
478 st = talloc(mem_ctx, struct_size);
480 return NT_STATUS_NO_MEMORY;
482 memcpy(st, struct_ptr, struct_size);
484 push = ndr_push_init_ctx(mem_ctx);
486 return NT_STATUS_NO_MEMORY;
489 status = ndr_push(push, NDR_OUT, struct_ptr);
490 if (!NT_STATUS_IS_OK(status)) {
491 return ndr_push_error(push, NDR_ERR_VALIDATE,
492 "failed output validation push - %s",
496 blob = ndr_push_blob(push);
498 pull = ndr_pull_init_blob(&blob, mem_ctx);
500 return NT_STATUS_NO_MEMORY;
503 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
504 status = ndr_pull(pull, NDR_OUT, st);
505 if (!NT_STATUS_IS_OK(status)) {
506 return ndr_pull_error(pull, NDR_ERR_VALIDATE,
507 "failed output validation pull - %s",
511 push = ndr_push_init_ctx(mem_ctx);
513 return NT_STATUS_NO_MEMORY;
516 status = ndr_push(push, NDR_OUT, st);
517 if (!NT_STATUS_IS_OK(status)) {
518 return ndr_push_error(push, NDR_ERR_VALIDATE,
519 "failed output validation push2 - %s",
523 blob2 = ndr_push_blob(push);
525 if (!data_blob_equal(&blob, &blob2)) {
526 DEBUG(3,("original:\n"));
527 dump_data(3, blob.data, blob.length);
528 DEBUG(3,("secondary:\n"));
529 dump_data(3, blob2.data, blob2.length);
530 return ndr_push_error(push, NDR_ERR_VALIDATE,
531 "failed output validation data - %s",
539 a useful helper function for synchronous rpc requests
541 this can be used when you have ndr push/pull functions in the
544 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
547 NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
548 NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *),
552 struct ndr_push *push;
553 struct ndr_pull *pull;
555 DATA_BLOB request, response;
557 /* setup for a ndr_push_* call */
558 push = ndr_push_init();
560 talloc_destroy(mem_ctx);
561 return NT_STATUS_NO_MEMORY;
564 /* push the structure into a blob */
565 status = ndr_push(push, NDR_IN, struct_ptr);
566 if (!NT_STATUS_IS_OK(status)) {
570 /* retrieve the blob */
571 request = ndr_push_blob(push);
573 if (p->flags & DCERPC_DEBUG_VALIDATE_IN) {
574 status = dcerpc_ndr_validate_in(mem_ctx, request, struct_size,
576 if (!NT_STATUS_IS_OK(status)) {
581 DEBUG(10,("rpc request data:\n"));
582 dump_data(10, request.data, request.length);
584 /* make the actual dcerpc request */
585 status = dcerpc_request(p, opnum, mem_ctx, &request, &response);
586 if (!NT_STATUS_IS_OK(status)) {
590 /* prepare for ndr_pull_* */
591 pull = ndr_pull_init_blob(&response, mem_ctx);
596 DEBUG(10,("rpc reply data:\n"));
597 dump_data(10, pull->data, pull->data_size);
599 /* pull the structure from the blob */
600 status = ndr_pull(pull, NDR_OUT, struct_ptr);
601 if (!NT_STATUS_IS_OK(status)) {
605 if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) {
606 status = dcerpc_ndr_validate_out(mem_ctx, struct_ptr, struct_size,
608 if (!NT_STATUS_IS_OK(status)) {
613 if (pull->offset != pull->data_size) {
614 DEBUG(0,("Warning! %d unread bytes\n", pull->data_size - pull->offset));
615 status = NT_STATUS_INFO_LENGTH_MISMATCH;
626 a useful function for retrieving the server name we connected to
628 const char *dcerpc_server_name(struct dcerpc_pipe *p)
630 return p->tree->session->transport->called.name;