added the dcerpc remote management interfaces as mgmt.idl, and wrote a
[nivanova/samba-autobuild/.git] / source4 / librpc / rpc / dcerpc.c
1 /* 
2    Unix SMB/CIFS implementation.
3    raw dcerpc operations
4
5    Copyright (C) Tim Potter 2003
6    Copyright (C) Andrew Tridgell 2003
7    
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.
12    
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.
17    
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.
21 */
22
23 #include "includes.h"
24
25 /* initialise a dcerpc pipe. This currently assumes a SMB named pipe
26    transport */
27 struct dcerpc_pipe *dcerpc_pipe_init(struct cli_tree *tree)
28 {
29         struct dcerpc_pipe *p;
30
31         TALLOC_CTX *mem_ctx = talloc_init("dcerpc_tree");
32         if (mem_ctx == NULL)
33                 return NULL;
34
35         p = talloc(mem_ctx, sizeof(*p));
36         if (!p) {
37                 talloc_destroy(mem_ctx);
38                 return NULL;
39         }
40
41         p->reference_count = 0;
42         p->mem_ctx = mem_ctx;
43         p->tree = tree;
44         p->tree->reference_count++;
45         p->call_id = 1;
46         p->fnum = 0;
47
48         return p;
49 }
50
51 /* close down a dcerpc over SMB pipe */
52 void dcerpc_pipe_close(struct dcerpc_pipe *p)
53 {
54         if (!p) return;
55         p->reference_count--;
56         if (p->reference_count <= 0) {
57                 cli_tree_close(p->tree);
58                 talloc_destroy(p->mem_ctx);
59         }
60 }
61
62
63 /*
64   build a GUID from a string
65 */
66 static NTSTATUS guid_from_string(const char *s, struct GUID *guid)
67 {
68         uint32 time_low;
69         uint32 time_mid, time_hi_and_version;
70         uint32 clock_seq_hi_and_reserved;
71         uint32 clock_seq_low;
72         uint32 node[6];
73         int i;
74
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;
80         }
81
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);
87         for (i=0;i<6;i++) {
88                 SCVAL(guid->info, 10 + i, node[i]);
89         }
90
91         return NT_STATUS_OK;
92 }
93
94 /* 
95    parse a data blob into a dcerpc_packet structure. This handles both
96    input and output packets
97 */
98 NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
99 {
100         struct ndr_pull *ndr;
101
102         ndr = ndr_pull_init_blob(blob, mem_ctx);
103         if (!ndr) {
104                 return NT_STATUS_NO_MEMORY;
105         }
106
107         return ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
108 }
109
110
111 /* 
112    push a dcerpc_packet into a blob. This handles both input and
113    output packets
114 */
115 NTSTATUS dcerpc_push(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
116 {
117         struct ndr_push *ndr;
118         NTSTATUS status;
119
120         ndr = ndr_push_init_ctx(mem_ctx);
121         if (!ndr) {
122                 return NT_STATUS_NO_MEMORY;
123         }
124
125         status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
126         if (!NT_STATUS_IS_OK(status)) {
127                 return status;
128         }
129
130         *blob = ndr_push_blob(ndr);
131
132         /* fill in the frag length */
133         SSVAL(blob->data, 8, blob->length);
134
135         return status;
136 }
137
138
139 /* 
140    fill in the fixed values in a dcerpc header 
141 */
142 static void init_dcerpc_hdr(struct dcerpc_packet *pkt)
143 {
144         pkt->rpc_vers = 5;
145         pkt->rpc_vers_minor = 0;
146         pkt->drep[0] = 0x10; /* Little endian */
147         pkt->drep[1] = 0;
148         pkt->drep[2] = 0;
149         pkt->drep[3] = 0;
150 }
151
152
153 /* 
154    perform a bind using the given syntax 
155 */
156 NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 
157                      const struct dcerpc_syntax_id *syntax,
158                      const struct dcerpc_syntax_id *transfer_syntax)
159 {
160         TALLOC_CTX *mem_ctx;
161         struct dcerpc_packet pkt;
162         NTSTATUS status;
163         DATA_BLOB blob;
164         DATA_BLOB blob_out;
165         struct dcerpc_syntax_id tsyntax;
166
167         mem_ctx = talloc_init("dcerpc_bind");
168         if (!mem_ctx) {
169                 return NT_STATUS_NO_MEMORY;
170         }
171
172         init_dcerpc_hdr(&pkt);
173
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++;
177         pkt.auth_length = 0;
178
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;
187         }
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);
194
195         status = dcerpc_push(&blob, mem_ctx, &pkt);
196         if (!NT_STATUS_IS_OK(status)) {
197                 talloc_destroy(mem_ctx);
198                 return status;
199         }
200
201         status = dcerpc_raw_packet(p, mem_ctx, &blob, &blob_out);
202         if (!NT_STATUS_IS_OK(status)) {
203                 talloc_destroy(mem_ctx);
204                 return status;
205         }
206
207         status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
208         if (!NT_STATUS_IS_OK(status)) {
209                 talloc_destroy(mem_ctx);
210                 return status;
211         }
212
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;
217         }
218
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;
221
222         talloc_destroy(mem_ctx);
223
224         return status;  
225 }
226
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)
230 {
231         struct dcerpc_syntax_id syntax;
232         struct dcerpc_syntax_id transfer_syntax;
233         NTSTATUS status;
234
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"));
238                 return status;
239         }
240         syntax.major_version = version;
241         syntax.minor_version = 0;
242
243         status = guid_from_string("8a885d04-1ceb-11c9-9fe8-08002b104860", 
244                                    &transfer_syntax.uuid);
245         if (!NT_STATUS_IS_OK(status)) {
246                 return status;
247         }
248
249         transfer_syntax.major_version = 2;
250         transfer_syntax.minor_version = 0;
251
252         return dcerpc_bind(p, &syntax, &transfer_syntax);
253 }
254
255 /*
256   perform a full request/response pair on a dcerpc pipe
257 */
258 NTSTATUS dcerpc_request(struct dcerpc_pipe *p, 
259                         uint16 opnum,
260                         TALLOC_CTX *mem_ctx,
261                         DATA_BLOB *stub_data_in,
262                         DATA_BLOB *stub_data_out)
263 {
264         
265         struct dcerpc_packet pkt;
266         NTSTATUS status;
267         DATA_BLOB blob_in, blob_out, payload;
268         uint32 remaining, chunk_size;
269
270         init_dcerpc_hdr(&pkt);
271
272         remaining = stub_data_in->length;
273
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;
277
278         pkt.ptype = DCERPC_PKT_REQUEST;
279         pkt.call_id = p->call_id++;
280         pkt.auth_length = 0;
281         pkt.u.request.alloc_hint = remaining;
282         pkt.u.request.context_id = 0;
283         pkt.u.request.opnum = opnum;
284
285         /* we send a series of pdus without waiting for a reply until
286            the last pdu */
287         while (remaining > chunk_size) {
288                 if (remaining == stub_data_in->length) {
289                         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST;
290                 } else {
291                         pkt.pfc_flags = 0;
292                 }
293
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;
297
298                 status = dcerpc_push(&blob_in, mem_ctx, &pkt);
299                 if (!NT_STATUS_IS_OK(status)) {
300                         return status;
301                 }
302                 
303                 status = dcerpc_raw_packet_initial(p, mem_ctx, &blob_in);
304                 if (!NT_STATUS_IS_OK(status)) {
305                         return status;
306                 }               
307
308                 remaining -= chunk_size;
309         }
310
311         /* now we send a pdu with LAST_FRAG sent and get the first
312            part of the reply */
313         if (remaining == stub_data_in->length) {
314                 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
315         } else {
316                 pkt.pfc_flags = DCERPC_PFC_FLAG_LAST;
317         }
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;
321
322         status = dcerpc_push(&blob_in, mem_ctx, &pkt);
323         if (!NT_STATUS_IS_OK(status)) {
324                 return status;
325         }
326
327         /* send the pdu and get the initial response pdu */
328         status = dcerpc_raw_packet(p, mem_ctx, &blob_in, &blob_out);
329
330         status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
331         if (!NT_STATUS_IS_OK(status)) {
332                 return status;
333         }
334
335         if (pkt.ptype == DCERPC_PKT_FAULT) {
336                 return NT_STATUS_NET_WRITE_FAULT;
337         }
338
339         if (pkt.ptype != DCERPC_PKT_RESPONSE) {
340                 return NT_STATUS_UNSUCCESSFUL;
341         }
342
343         if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
344                 /* something is badly wrong! */
345                 return NT_STATUS_UNSUCCESSFUL;
346         }
347
348         payload = pkt.u.response.stub_and_verifier;
349
350         /* continue receiving fragments */
351         while (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
352                 uint32 length;
353
354                 status = dcerpc_raw_packet_secondary(p, mem_ctx, &blob_out);
355                 if (!NT_STATUS_IS_OK(status)) {
356                         return status;
357                 }
358
359                 status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
360                 if (!NT_STATUS_IS_OK(status)) {
361                         return status;
362                 }
363
364                 if (pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
365                         /* start of another packet!? */
366                         return NT_STATUS_UNSUCCESSFUL;
367                 }
368
369                 if (pkt.ptype != DCERPC_PKT_RESPONSE) {
370                         return NT_STATUS_UNSUCCESSFUL;
371                 }
372
373                 length = pkt.u.response.stub_and_verifier.length;
374
375                 payload.data = talloc_realloc(mem_ctx, 
376                                               payload.data, 
377                                               payload.length + length);
378                 if (!payload.data) {
379                         return NT_STATUS_NO_MEMORY;
380                 }
381
382                 memcpy(payload.data + payload.length,
383                        pkt.u.response.stub_and_verifier.data,
384                        length);
385
386                 payload.length += length;
387         }
388
389         if (stub_data_out) {
390                 *stub_data_out = payload;
391         }
392
393         return status;
394 }
395
396
397 /*
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
402 */
403 static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
404                                        DATA_BLOB blob,
405                                        size_t struct_size,
406                                        NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
407                                        NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
408 {
409         void *st;
410         struct ndr_pull *pull;
411         struct ndr_push *push;
412         NTSTATUS status;
413         DATA_BLOB blob2;
414
415         st = talloc(mem_ctx, struct_size);
416         if (!st) {
417                 return NT_STATUS_NO_MEMORY;
418         }
419
420         pull = ndr_pull_init_blob(&blob, mem_ctx);
421         if (!pull) {
422                 return NT_STATUS_NO_MEMORY;
423         }
424
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",
429                                       nt_errstr(status));
430         }
431
432         push = ndr_push_init_ctx(mem_ctx);
433         if (!push) {
434                 return NT_STATUS_NO_MEMORY;
435         }       
436
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",
441                                       nt_errstr(status));
442         }
443
444         blob2 = ndr_push_blob(push);
445
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",
453                                       nt_errstr(status));
454         }
455
456         return NT_STATUS_OK;
457 }
458
459 /*
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
465 */
466 static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
467                                         void *struct_ptr,
468                                         size_t struct_size,
469                                         NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
470                                         NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
471 {
472         void *st;
473         struct ndr_pull *pull;
474         struct ndr_push *push;
475         NTSTATUS status;
476         DATA_BLOB blob, blob2;
477
478         st = talloc(mem_ctx, struct_size);
479         if (!st) {
480                 return NT_STATUS_NO_MEMORY;
481         }
482         memcpy(st, struct_ptr, struct_size);
483
484         push = ndr_push_init_ctx(mem_ctx);
485         if (!push) {
486                 return NT_STATUS_NO_MEMORY;
487         }       
488
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",
493                                       nt_errstr(status));
494         }
495
496         blob = ndr_push_blob(push);
497
498         pull = ndr_pull_init_blob(&blob, mem_ctx);
499         if (!pull) {
500                 return NT_STATUS_NO_MEMORY;
501         }
502
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",
508                                       nt_errstr(status));
509         }
510
511         push = ndr_push_init_ctx(mem_ctx);
512         if (!push) {
513                 return NT_STATUS_NO_MEMORY;
514         }       
515
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",
520                                       nt_errstr(status));
521         }
522
523         blob2 = ndr_push_blob(push);
524
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",
532                                       nt_errstr(status));
533         }
534
535         return NT_STATUS_OK;
536 }
537
538 /*
539   a useful helper function for synchronous rpc requests 
540
541   this can be used when you have ndr push/pull functions in the
542   standard format
543 */
544 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
545                             uint32 opnum,
546                             TALLOC_CTX *mem_ctx,
547                             NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
548                             NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *),
549                             void *struct_ptr,
550                             size_t struct_size)
551 {
552         struct ndr_push *push;
553         struct ndr_pull *pull;
554         NTSTATUS status;
555         DATA_BLOB request, response;
556
557         /* setup for a ndr_push_* call */
558         push = ndr_push_init();
559         if (!push) {
560                 talloc_destroy(mem_ctx);
561                 return NT_STATUS_NO_MEMORY;
562         }
563
564         /* push the structure into a blob */
565         status = ndr_push(push, NDR_IN, struct_ptr);
566         if (!NT_STATUS_IS_OK(status)) {
567                 goto failed;
568         }
569
570         /* retrieve the blob */
571         request = ndr_push_blob(push);
572
573         if (p->flags & DCERPC_DEBUG_VALIDATE_IN) {
574                 status = dcerpc_ndr_validate_in(mem_ctx, request, struct_size, 
575                                                 ndr_push, ndr_pull);
576                 if (!NT_STATUS_IS_OK(status)) {
577                         goto failed;
578                 }
579         }
580
581         DEBUG(10,("rpc request data:\n"));
582         dump_data(10, request.data, request.length);
583
584         /* make the actual dcerpc request */
585         status = dcerpc_request(p, opnum, mem_ctx, &request, &response);
586         if (!NT_STATUS_IS_OK(status)) {
587                 goto failed;
588         }
589
590         /* prepare for ndr_pull_* */
591         pull = ndr_pull_init_blob(&response, mem_ctx);
592         if (!pull) {
593                 goto failed;
594         }
595
596         DEBUG(10,("rpc reply data:\n"));
597         dump_data(10, pull->data, pull->data_size);
598
599         /* pull the structure from the blob */
600         status = ndr_pull(pull, NDR_OUT, struct_ptr);
601         if (!NT_STATUS_IS_OK(status)) {
602                 goto failed;
603         }
604
605         if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) {
606                 status = dcerpc_ndr_validate_out(mem_ctx, struct_ptr, struct_size, 
607                                                  ndr_push, ndr_pull);
608                 if (!NT_STATUS_IS_OK(status)) {
609                         goto failed;
610                 }
611         }
612
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;
616                 goto failed;
617         }
618
619 failed:
620         ndr_push_free(push);
621         return status;
622 }
623
624
625 /*
626   a useful function for retrieving the server name we connected to
627 */
628 const char *dcerpc_server_name(struct dcerpc_pipe *p)
629 {
630         return p->tree->session->transport->called.name;
631 }