- modified the dcerpc client security code to be generic, so ntlmssp
[samba.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(void)
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->call_id = 1;
44         p->auth_info = NULL;
45         p->security_state = NULL;
46         p->flags = 0;
47         p->srv_max_xmit_frag = 0;
48         p->srv_max_recv_frag = 0;
49
50         return p;
51 }
52
53 /* close down a dcerpc over SMB pipe */
54 void dcerpc_pipe_close(struct dcerpc_pipe *p)
55 {
56         if (!p) return;
57         p->reference_count--;
58         if (p->reference_count <= 0) {
59                 if (p->security_state) {
60                         p->security_state->security_end(p->security_state);
61                 }
62                 p->transport.shutdown_pipe(p);
63                 talloc_destroy(p->mem_ctx);
64         }
65 }
66
67 /* we need to be able to get/set the fragment length without doing a full
68    decode */
69 void dcerpc_set_frag_length(DATA_BLOB *blob, uint16 v)
70 {
71         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
72                 SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
73         } else {
74                 RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
75         }
76 }
77
78 uint16 dcerpc_get_frag_length(const DATA_BLOB *blob)
79 {
80         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
81                 return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
82         } else {
83                 return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
84         }
85 }
86
87 void dcerpc_set_auth_length(DATA_BLOB *blob, uint16 v)
88 {
89         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
90                 SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
91         } else {
92                 RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
93         }
94 }
95
96
97 /* 
98    parse a data blob into a dcerpc_packet structure. This handles both
99    input and output packets
100 */
101 static NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
102                             struct dcerpc_packet *pkt)
103 {
104         struct ndr_pull *ndr;
105
106         ndr = ndr_pull_init_blob(blob, mem_ctx);
107         if (!ndr) {
108                 return NT_STATUS_NO_MEMORY;
109         }
110
111         if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
112                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
113         }
114
115         return ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
116 }
117
118 /* 
119    parse a possibly signed blob into a dcerpc request packet structure
120 */
121 static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p, 
122                                          DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
123                                          struct dcerpc_packet *pkt)
124 {
125         struct ndr_pull *ndr;
126         NTSTATUS status;
127         struct dcerpc_auth auth;
128         DATA_BLOB auth_blob;
129
130         /* non-signed packets are simpler */
131         if (!p->auth_info || !p->security_state) {
132                 return dcerpc_pull(blob, mem_ctx, pkt);
133         }
134
135         ndr = ndr_pull_init_blob(blob, mem_ctx);
136         if (!ndr) {
137                 return NT_STATUS_NO_MEMORY;
138         }
139
140         if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
141                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
142         }
143
144         /* pull the basic packet */
145         status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
146         if (!NT_STATUS_IS_OK(status)) {
147                 return status;
148         }
149
150         if (pkt->ptype != DCERPC_PKT_RESPONSE) {
151                 return status;
152         }
153
154         auth_blob.length = 8 + pkt->auth_length;
155
156         /* check for a valid length */
157         if (pkt->u.response.stub_and_verifier.length < auth_blob.length) {
158                 return NT_STATUS_INFO_LENGTH_MISMATCH;
159         }
160
161         auth_blob.data = 
162                 pkt->u.response.stub_and_verifier.data + 
163                 pkt->u.response.stub_and_verifier.length - auth_blob.length;
164         pkt->u.response.stub_and_verifier.length -= auth_blob.length;
165
166         /* pull the auth structure */
167         ndr = ndr_pull_init_blob(&auth_blob, mem_ctx);
168         if (!ndr) {
169                 return NT_STATUS_NO_MEMORY;
170         }
171
172         if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
173                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
174         }
175
176         status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
177         if (!NT_STATUS_IS_OK(status)) {
178                 return status;
179         }
180
181
182         /* check signature or unseal the packet */
183         switch (p->auth_info->auth_level) {
184         case DCERPC_AUTH_LEVEL_PRIVACY:
185                 status = p->security_state->unseal_packet(p->security_state, 
186                                                           pkt->u.response.stub_and_verifier.data, 
187                                                           pkt->u.response.stub_and_verifier.length, 
188                                                           &auth.credentials);
189                 break;
190
191         case DCERPC_AUTH_LEVEL_INTEGRITY:
192                 status = p->security_state->check_packet(p->security_state, 
193                                                          pkt->u.response.stub_and_verifier.data, 
194                                                          pkt->u.response.stub_and_verifier.length, 
195                                                          &auth.credentials);
196                 break;
197
198         case DCERPC_AUTH_LEVEL_NONE:
199                 break;
200
201         default:
202                 status = NT_STATUS_INVALID_LEVEL;
203                 break;
204         }
205
206         /* remove the indicated amount of paddiing */
207         if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
208                 return NT_STATUS_INFO_LENGTH_MISMATCH;
209         }
210         pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
211
212         return status;
213 }
214
215
216 /* 
217    push a dcerpc request packet into a blob, possibly signing it.
218 */
219 static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, 
220                                          DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
221                                          struct dcerpc_packet *pkt)
222 {
223         NTSTATUS status;
224         struct ndr_push *ndr;
225
226         /* non-signed packets are simpler */
227         if (!p->auth_info || !p->security_state) {
228                 return dcerpc_push_auth(blob, mem_ctx, pkt, p->auth_info);
229         }
230
231         ndr = ndr_push_init_ctx(mem_ctx);
232         if (!ndr) {
233                 return NT_STATUS_NO_MEMORY;
234         }
235
236         if (p->flags & DCERPC_PUSH_BIGENDIAN) {
237                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
238         }
239
240         status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
241         if (!NT_STATUS_IS_OK(status)) {
242                 return status;
243         }
244
245         /* pad to 8 byte multiple */
246         p->auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
247         ndr_push_zero(ndr, p->auth_info->auth_pad_length);
248
249         /* sign or seal the packet */
250         switch (p->auth_info->auth_level) {
251         case DCERPC_AUTH_LEVEL_PRIVACY:
252                 status = p->security_state->seal_packet(p->security_state, 
253                                                         ndr->data + DCERPC_REQUEST_LENGTH, 
254                                                         ndr->offset - DCERPC_REQUEST_LENGTH,
255                                                         &p->auth_info->credentials);
256                 break;
257
258         case DCERPC_AUTH_LEVEL_INTEGRITY:
259                 status = p->security_state->sign_packet(p->security_state, 
260                                                         ndr->data + DCERPC_REQUEST_LENGTH, 
261                                                         ndr->offset - DCERPC_REQUEST_LENGTH,
262                                                         &p->auth_info->credentials);
263                 break;
264
265         case DCERPC_AUTH_LEVEL_NONE:
266                 p->auth_info->credentials = data_blob(NULL, 0);
267                 break;
268
269         default:
270                 status = NT_STATUS_INVALID_LEVEL;
271                 break;
272         }
273
274         if (!NT_STATUS_IS_OK(status)) {
275                 return status;
276         }       
277
278         /* add the auth verifier */
279         status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, p->auth_info);
280         if (!NT_STATUS_IS_OK(status)) {
281                 return status;
282         }
283
284         /* extract the whole packet as a blob */
285         *blob = ndr_push_blob(ndr);
286
287         /* fill in the fragment length and auth_length, we can't fill
288            in these earlier as we don't know the signature length (it
289            could be variable length) */
290         dcerpc_set_frag_length(blob, blob->length);
291         dcerpc_set_auth_length(blob, p->auth_info->credentials.length);
292
293         data_blob_free(&p->auth_info->credentials);
294
295         return NT_STATUS_OK;
296 }
297
298
299 /* 
300    fill in the fixed values in a dcerpc header 
301 */
302 static void init_dcerpc_hdr(struct dcerpc_pipe *p, struct dcerpc_packet *pkt)
303 {
304         pkt->rpc_vers = 5;
305         pkt->rpc_vers_minor = 0;
306         if (p->flags & DCERPC_PUSH_BIGENDIAN) {
307                 pkt->drep[0] = 0;
308         } else {
309                 pkt->drep[0] = DCERPC_DREP_LE;
310         }
311         pkt->drep[1] = 0;
312         pkt->drep[2] = 0;
313         pkt->drep[3] = 0;
314 }
315
316
317 /* 
318    perform a bind using the given syntax 
319
320    the auth_info structure is updated with the reply authentication info
321    on success
322 */
323 NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 
324                      TALLOC_CTX *mem_ctx,
325                      const struct dcerpc_syntax_id *syntax,
326                      const struct dcerpc_syntax_id *transfer_syntax)
327 {
328         struct dcerpc_packet pkt;
329         NTSTATUS status;
330         DATA_BLOB blob;
331         struct dcerpc_syntax_id tsyntax;
332
333         init_dcerpc_hdr(p, &pkt);
334
335         pkt.ptype = DCERPC_PKT_BIND;
336         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
337         pkt.call_id = p->call_id;
338         pkt.auth_length = 0;
339
340         pkt.u.bind.max_xmit_frag = 0x2000;
341         pkt.u.bind.max_recv_frag = 0x2000;
342         pkt.u.bind.assoc_group_id = 0;
343         pkt.u.bind.num_contexts = 1;
344         pkt.u.bind.ctx_list = talloc(mem_ctx, sizeof(pkt.u.bind.ctx_list[0]));
345         if (!pkt.u.bind.ctx_list) {
346                 return NT_STATUS_NO_MEMORY;
347         }
348         pkt.u.bind.ctx_list[0].context_id = 0;
349         pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
350         pkt.u.bind.ctx_list[0].abstract_syntax = *syntax;
351         tsyntax = *transfer_syntax;
352         pkt.u.bind.ctx_list[0].transfer_syntaxes = &tsyntax;
353         pkt.u.bind.auth_info = data_blob(NULL, 0);
354
355         /* construct the NDR form of the packet */
356         status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info);
357         if (!NT_STATUS_IS_OK(status)) {
358                 return status;
359         }
360
361         /* send it on its way */
362         status = p->transport.full_request(p, mem_ctx, &blob, &blob);
363         if (!NT_STATUS_IS_OK(status)) {
364                 return status;
365         }
366
367         /* unmarshall the NDR */
368         status = dcerpc_pull(&blob, mem_ctx, &pkt);
369         if (!NT_STATUS_IS_OK(status)) {
370                 return status;
371         }
372
373         if ((pkt.ptype != DCERPC_PKT_BIND_ACK && pkt.ptype != DCERPC_PKT_ALTER_ACK) ||
374             pkt.u.bind_ack.num_results == 0 ||
375             pkt.u.bind_ack.ctx_list[0].result != 0) {
376                 status = NT_STATUS_UNSUCCESSFUL;
377         }
378
379         if (pkt.ptype != DCERPC_PKT_ALTER_ACK) {
380                 p->srv_max_xmit_frag = pkt.u.bind_ack.max_xmit_frag;
381                 p->srv_max_recv_frag = pkt.u.bind_ack.max_recv_frag;
382         }
383
384         /* the bind_ack might contain a reply set of credentials */
385         if (p->auth_info && pkt.u.bind_ack.auth_info.length) {
386                 status = ndr_pull_struct_blob(&pkt.u.bind_ack.auth_info,
387                                               mem_ctx,
388                                               p->auth_info,
389                                               (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
390         }
391
392         return status;  
393 }
394
395 /* 
396    perform a continued bind (and auth3)
397 */
398 NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p, 
399                       TALLOC_CTX *mem_ctx)
400 {
401         struct dcerpc_packet pkt;
402         NTSTATUS status;
403         DATA_BLOB blob;
404
405         init_dcerpc_hdr(p, &pkt);
406
407         pkt.ptype = DCERPC_PKT_AUTH3;
408         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
409         pkt.call_id = p->call_id++;
410         pkt.auth_length = 0;
411         pkt.u.auth._pad = 0;
412         pkt.u.auth.auth_info = data_blob(NULL, 0);
413
414         /* construct the NDR form of the packet */
415         status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info);
416         if (!NT_STATUS_IS_OK(status)) {
417                 return status;
418         }
419
420         /* send it on its way */
421         status = p->transport.initial_request(p, mem_ctx, &blob);
422         if (!NT_STATUS_IS_OK(status)) {
423                 return status;
424         }
425
426         return status;  
427 }
428
429
430 /* perform a dcerpc bind, using the uuid as the key */
431 NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, 
432                             TALLOC_CTX *mem_ctx,
433                             const char *uuid, unsigned version)
434 {
435         struct dcerpc_syntax_id syntax;
436         struct dcerpc_syntax_id transfer_syntax;
437         NTSTATUS status;
438
439         status = GUID_from_string(uuid, &syntax.uuid);
440         if (!NT_STATUS_IS_OK(status)) {
441                 DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
442                 return status;
443         }
444         syntax.if_version = version;
445
446         status = GUID_from_string(NDR_GUID, &transfer_syntax.uuid);
447         if (!NT_STATUS_IS_OK(status)) {
448                 return status;
449         }
450         transfer_syntax.if_version = NDR_GUID_VERSION;
451
452         return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
453 }
454
455 /*
456   perform a full request/response pair on a dcerpc pipe
457 */
458 NTSTATUS dcerpc_request(struct dcerpc_pipe *p, 
459                         uint16 opnum,
460                         TALLOC_CTX *mem_ctx,
461                         DATA_BLOB *stub_data_in,
462                         DATA_BLOB *stub_data_out)
463 {
464         
465         struct dcerpc_packet pkt;
466         NTSTATUS status;
467         DATA_BLOB blob, payload;
468         uint32 remaining, chunk_size;
469
470         init_dcerpc_hdr(p, &pkt);
471
472         remaining = stub_data_in->length;
473
474         /* we can write a full max_recv_frag size, minus the dcerpc
475            request header size */
476         chunk_size = p->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
477
478         pkt.ptype = DCERPC_PKT_REQUEST;
479         pkt.call_id = p->call_id++;
480         pkt.auth_length = 0;
481         pkt.u.request.alloc_hint = remaining;
482         pkt.u.request.context_id = 0;
483         pkt.u.request.opnum = opnum;
484
485         /* we send a series of pdus without waiting for a reply until
486            the last pdu */
487         while (remaining > chunk_size) {
488                 if (remaining == stub_data_in->length) {
489                         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST;
490                 } else {
491                         pkt.pfc_flags = 0;
492                 }
493
494                 pkt.u.request.stub_and_verifier.data = stub_data_in->data + 
495                         (stub_data_in->length - remaining);
496                 pkt.u.request.stub_and_verifier.length = chunk_size;
497
498                 status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
499                 if (!NT_STATUS_IS_OK(status)) {
500                         return status;
501                 }
502                 
503                 status = p->transport.initial_request(p, mem_ctx, &blob);
504                 if (!NT_STATUS_IS_OK(status)) {
505                         return status;
506                 }               
507
508                 remaining -= chunk_size;
509         }
510
511         /* now we send a pdu with LAST_FRAG sent and get the first
512            part of the reply */
513         if (remaining == stub_data_in->length) {
514                 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
515         } else {
516                 pkt.pfc_flags = DCERPC_PFC_FLAG_LAST;
517         }
518         pkt.u.request.stub_and_verifier.data = stub_data_in->data + 
519                 (stub_data_in->length - remaining);
520         pkt.u.request.stub_and_verifier.length = remaining;
521
522         status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
523         if (!NT_STATUS_IS_OK(status)) {
524                 return status;
525         }
526
527         /* send the pdu and get the initial response pdu */
528         status = p->transport.full_request(p, mem_ctx, &blob, &blob);
529         if (!NT_STATUS_IS_OK(status)) {
530                 return status;
531         }
532
533         status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt);
534         if (!NT_STATUS_IS_OK(status)) {
535                 return status;
536         }
537
538         if (pkt.ptype == DCERPC_PKT_FAULT) {
539                 p->last_fault_code = pkt.u.fault.status;
540                 return NT_STATUS_NET_WRITE_FAULT;
541         }
542
543         if (pkt.ptype != DCERPC_PKT_RESPONSE) {
544                 return NT_STATUS_UNSUCCESSFUL;
545         }
546
547         if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
548                 /* something is badly wrong! */
549                 return NT_STATUS_UNSUCCESSFUL;
550         }
551
552         payload = pkt.u.response.stub_and_verifier;
553
554         /* continue receiving fragments */
555         while (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
556                 uint32 length;
557
558                 status = p->transport.secondary_request(p, mem_ctx, &blob);
559                 if (!NT_STATUS_IS_OK(status)) {
560                         return status;
561                 }
562
563                 status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt);
564                 if (!NT_STATUS_IS_OK(status)) {
565                         return status;
566                 }
567
568                 if (pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
569                         /* start of another packet!? */
570                         return NT_STATUS_UNSUCCESSFUL;
571                 }
572
573                 if (pkt.ptype == DCERPC_PKT_FAULT) {
574                         p->last_fault_code = pkt.u.fault.status;
575                         return NT_STATUS_NET_WRITE_FAULT;
576                 }
577
578                 if (pkt.ptype != DCERPC_PKT_RESPONSE) {
579                         return NT_STATUS_UNSUCCESSFUL;
580                 }
581
582                 length = pkt.u.response.stub_and_verifier.length;
583
584                 payload.data = talloc_realloc(mem_ctx, 
585                                               payload.data, 
586                                               payload.length + length);
587                 if (!payload.data) {
588                         return NT_STATUS_NO_MEMORY;
589                 }
590
591                 memcpy(payload.data + payload.length,
592                        pkt.u.response.stub_and_verifier.data,
593                        length);
594
595                 payload.length += length;
596         }
597
598         if (stub_data_out) {
599                 *stub_data_out = payload;
600         }
601
602         if (!(pkt.drep[0] & DCERPC_DREP_LE)) {
603                 p->flags |= DCERPC_PULL_BIGENDIAN;
604         } else {
605                 p->flags &= ~DCERPC_PULL_BIGENDIAN;
606         }
607
608         return status;
609 }
610
611
612 /*
613   this is a paranoid NDR validator. For every packet we push onto the wire
614   we pull it back again, then push it again. Then we compare the raw NDR data
615   for that to the NDR we initially generated. If they don't match then we know
616   we must have a bug in either the pull or push side of our code
617 */
618 static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
619                                        DATA_BLOB blob,
620                                        size_t struct_size,
621                                        NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
622                                        NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
623 {
624         void *st;
625         struct ndr_pull *pull;
626         struct ndr_push *push;
627         NTSTATUS status;
628         DATA_BLOB blob2;
629
630         st = talloc(mem_ctx, struct_size);
631         if (!st) {
632                 return NT_STATUS_NO_MEMORY;
633         }
634
635         pull = ndr_pull_init_blob(&blob, mem_ctx);
636         if (!pull) {
637                 return NT_STATUS_NO_MEMORY;
638         }
639
640         status = ndr_pull(pull, NDR_IN, st);
641         if (!NT_STATUS_IS_OK(status)) {
642                 return ndr_pull_error(pull, NDR_ERR_VALIDATE, 
643                                       "failed input validation pull - %s",
644                                       nt_errstr(status));
645         }
646
647         push = ndr_push_init_ctx(mem_ctx);
648         if (!push) {
649                 return NT_STATUS_NO_MEMORY;
650         }       
651
652         status = ndr_push(push, NDR_IN, st);
653         if (!NT_STATUS_IS_OK(status)) {
654                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
655                                       "failed input validation push - %s",
656                                       nt_errstr(status));
657         }
658
659         blob2 = ndr_push_blob(push);
660
661         if (!data_blob_equal(&blob, &blob2)) {
662                 DEBUG(3,("original:\n"));
663                 dump_data(3, blob.data, blob.length);
664                 DEBUG(3,("secondary:\n"));
665                 dump_data(3, blob2.data, blob2.length);
666                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
667                                       "failed input validation data - %s",
668                                       nt_errstr(status));
669         }
670
671         return NT_STATUS_OK;
672 }
673
674 /*
675   this is a paranoid NDR input validator. For every packet we pull
676   from the wire we push it back again then pull and push it
677   again. Then we compare the raw NDR data for that to the NDR we
678   initially generated. If they don't match then we know we must have a
679   bug in either the pull or push side of our code
680 */
681 static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
682                                         void *struct_ptr,
683                                         size_t struct_size,
684                                         NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
685                                         NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
686 {
687         void *st;
688         struct ndr_pull *pull;
689         struct ndr_push *push;
690         NTSTATUS status;
691         DATA_BLOB blob, blob2;
692
693         st = talloc(mem_ctx, struct_size);
694         if (!st) {
695                 return NT_STATUS_NO_MEMORY;
696         }
697         memcpy(st, struct_ptr, struct_size);
698
699         push = ndr_push_init_ctx(mem_ctx);
700         if (!push) {
701                 return NT_STATUS_NO_MEMORY;
702         }       
703
704         status = ndr_push(push, NDR_OUT, struct_ptr);
705         if (!NT_STATUS_IS_OK(status)) {
706                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
707                                       "failed output validation push - %s",
708                                       nt_errstr(status));
709         }
710
711         blob = ndr_push_blob(push);
712
713         pull = ndr_pull_init_blob(&blob, mem_ctx);
714         if (!pull) {
715                 return NT_STATUS_NO_MEMORY;
716         }
717
718         pull->flags |= LIBNDR_FLAG_REF_ALLOC;
719         status = ndr_pull(pull, NDR_OUT, st);
720         if (!NT_STATUS_IS_OK(status)) {
721                 return ndr_pull_error(pull, NDR_ERR_VALIDATE, 
722                                       "failed output validation pull - %s",
723                                       nt_errstr(status));
724         }
725
726         push = ndr_push_init_ctx(mem_ctx);
727         if (!push) {
728                 return NT_STATUS_NO_MEMORY;
729         }       
730
731         status = ndr_push(push, NDR_OUT, st);
732         if (!NT_STATUS_IS_OK(status)) {
733                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
734                                       "failed output validation push2 - %s",
735                                       nt_errstr(status));
736         }
737
738         blob2 = ndr_push_blob(push);
739
740         if (!data_blob_equal(&blob, &blob2)) {
741                 DEBUG(3,("original:\n"));
742                 dump_data(3, blob.data, blob.length);
743                 DEBUG(3,("secondary:\n"));
744                 dump_data(3, blob2.data, blob2.length);
745                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
746                                       "failed output validation data - %s",
747                                       nt_errstr(status));
748         }
749
750         return NT_STATUS_OK;
751 }
752
753 /*
754   a useful helper function for synchronous rpc requests 
755
756   this can be used when you have ndr push/pull functions in the
757   standard format
758 */
759 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
760                             uint32 opnum,
761                             TALLOC_CTX *mem_ctx,
762                             NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
763                             NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *),
764                             void *struct_ptr,
765                             size_t struct_size)
766 {
767         struct ndr_push *push;
768         struct ndr_pull *pull;
769         NTSTATUS status;
770         DATA_BLOB request, response;
771
772         /* setup for a ndr_push_* call */
773         push = ndr_push_init();
774         if (!push) {
775                 talloc_destroy(mem_ctx);
776                 return NT_STATUS_NO_MEMORY;
777         }
778
779         if (p->flags & DCERPC_PUSH_BIGENDIAN) {
780                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
781         }
782
783         /* push the structure into a blob */
784         status = ndr_push(push, NDR_IN, struct_ptr);
785         if (!NT_STATUS_IS_OK(status)) {
786                 goto failed;
787         }
788
789         /* retrieve the blob */
790         request = ndr_push_blob(push);
791
792         if (p->flags & DCERPC_DEBUG_VALIDATE_IN) {
793                 status = dcerpc_ndr_validate_in(mem_ctx, request, struct_size, 
794                                                 ndr_push, ndr_pull);
795                 if (!NT_STATUS_IS_OK(status)) {
796                         goto failed;
797                 }
798         }
799
800         DEBUG(10,("rpc request data:\n"));
801         dump_data(10, request.data, request.length);
802
803         /* make the actual dcerpc request */
804         status = dcerpc_request(p, opnum, mem_ctx, &request, &response);
805         if (!NT_STATUS_IS_OK(status)) {
806                 goto failed;
807         }
808
809         /* prepare for ndr_pull_* */
810         pull = ndr_pull_init_blob(&response, mem_ctx);
811         if (!pull) {
812                 goto failed;
813         }
814
815         if (p->flags & DCERPC_PULL_BIGENDIAN) {
816                 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
817         }
818
819         DEBUG(10,("rpc reply data:\n"));
820         dump_data(10, pull->data, pull->data_size);
821
822         /* pull the structure from the blob */
823         status = ndr_pull(pull, NDR_OUT, struct_ptr);
824         if (!NT_STATUS_IS_OK(status)) {
825                 goto failed;
826         }
827
828         /* possibly check the packet signature */
829         
830
831         if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) {
832                 status = dcerpc_ndr_validate_out(mem_ctx, struct_ptr, struct_size, 
833                                                  ndr_push, ndr_pull);
834                 if (!NT_STATUS_IS_OK(status)) {
835                         goto failed;
836                 }
837         }
838
839         if (pull->offset != pull->data_size) {
840                 DEBUG(0,("Warning! %d unread bytes\n", pull->data_size - pull->offset));
841                 status = NT_STATUS_INFO_LENGTH_MISMATCH;
842                 goto failed;
843         }
844
845 failed:
846         ndr_push_free(push);
847         return status;
848 }
849
850
851 /*
852   a useful function for retrieving the server name we connected to
853 */
854 const char *dcerpc_server_name(struct dcerpc_pipe *p)
855 {
856         if (!p->transport.peer_name) {
857                 return "";
858         }
859         return p->transport.peer_name(p);
860 }