r7313: Prefix a few functions with ncacn_ rather then dcerpc_ because they are
[kai/samba.git] / source / 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-2005
7    Copyright (C) Jelmer Vernooij 2004-2005
8    
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.
13    
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.
18    
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.
22 */
23
24 #include "includes.h"
25 #include "dlinklist.h"
26 #include "lib/events/events.h"
27 #include "librpc/gen_ndr/ndr_epmapper.h"
28 #include "librpc/gen_ndr/ndr_dcerpc.h"
29
30 static struct dcerpc_interface_list *dcerpc_pipes = NULL;
31
32 /*
33   register a dcerpc client interface
34 */
35 NTSTATUS librpc_register_interface(const struct dcerpc_interface_table *interface)
36 {
37         struct dcerpc_interface_list *l = talloc(talloc_autofree_context(),
38                                                    struct dcerpc_interface_list);
39                 
40         if (idl_iface_by_name (interface->name) != NULL) {
41                 DEBUG(0, ("Attempt to register interface %s twice\n", interface->name));
42                 return NT_STATUS_OBJECT_NAME_COLLISION;
43         }
44         l->table = interface;
45
46         DLIST_ADD(dcerpc_pipes, l);
47         
48         return NT_STATUS_OK;
49 }
50
51 /*
52   return the list of registered dcerpc_pipes
53 */
54 const struct dcerpc_interface_list *librpc_dcerpc_pipes(void)
55 {
56         return dcerpc_pipes;
57 }
58
59 /* destroy a dcerpc connection */
60 static int dcerpc_connection_destructor(void *ptr)
61 {
62         struct dcerpc_connection *c = ptr;
63         if (c->transport.shutdown_pipe) {
64                 c->transport.shutdown_pipe(c);
65         }
66         return 0;
67 }
68
69
70 /* initialise a dcerpc connection. */
71 struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx)
72 {
73         struct dcerpc_connection *c;
74
75         c = talloc_zero(mem_ctx, struct dcerpc_connection);
76         if (!c) {
77                 return NULL;
78         }
79
80         c->call_id = 1;
81         c->security_state.auth_info = NULL;
82         c->security_state.session_key = dcerpc_generic_session_key;
83         c->security_state.generic_state = NULL;
84         c->binding_string = NULL;
85         c->flags = 0;
86         c->srv_max_xmit_frag = 0;
87         c->srv_max_recv_frag = 0;
88         c->pending = NULL;
89
90         talloc_set_destructor(c, dcerpc_connection_destructor);
91
92         return c;
93 }
94
95 /* initialise a dcerpc pipe. */
96 struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx)
97 {
98         struct dcerpc_pipe *p;
99
100         p = talloc(mem_ctx, struct dcerpc_pipe);
101         if (!p) {
102                 return NULL;
103         }
104
105         p->conn = dcerpc_connection_init(p);
106         if (p->conn == NULL) {
107                 talloc_free(p);
108                 return NULL;
109         }
110
111         p->last_fault_code = 0;
112         p->context_id = 0;
113
114         ZERO_STRUCT(p->syntax);
115         ZERO_STRUCT(p->transfer_syntax);
116
117         return p;
118 }
119
120
121 /* 
122    choose the next call id to use
123 */
124 static uint32_t next_call_id(struct dcerpc_connection *c)
125 {
126         c->call_id++;
127         if (c->call_id == 0) {
128                 c->call_id++;
129         }
130         return c->call_id;
131 }
132
133 /* we need to be able to get/set the fragment length without doing a full
134    decode */
135 void dcerpc_set_frag_length(DATA_BLOB *blob, uint16_t v)
136 {
137         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
138                 SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
139         } else {
140                 RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
141         }
142 }
143
144 uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob)
145 {
146         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
147                 return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
148         } else {
149                 return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
150         }
151 }
152
153 void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
154 {
155         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
156                 SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
157         } else {
158                 RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
159         }
160 }
161
162
163 /*
164   setup for a ndr pull, also setting up any flags from the binding string
165 */
166 static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c, 
167                                             DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
168 {
169         struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
170
171         if (ndr == NULL) return ndr;
172
173         if (c->flags & DCERPC_DEBUG_PAD_CHECK) {
174                 ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
175         }
176
177         if (c->flags & DCERPC_NDR_REF_ALLOC) {
178                 ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
179         }
180
181         return ndr;
182 }
183
184 /* 
185    parse a data blob into a ncacn_packet structure. This handles both
186    input and output packets
187 */
188 static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
189                             struct ncacn_packet *pkt)
190 {
191         struct ndr_pull *ndr;
192
193         ndr = ndr_pull_init_flags(c, blob, mem_ctx);
194         if (!ndr) {
195                 return NT_STATUS_NO_MEMORY;
196         }
197
198         if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
199                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
200         }
201
202         return ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
203 }
204
205 /*
206   generate a CONNECT level verifier
207 */
208 static NTSTATUS dcerpc_connect_verifier(TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
209 {
210         *blob = data_blob_talloc(mem_ctx, NULL, 16);
211         if (blob->data == NULL) {
212                 return NT_STATUS_NO_MEMORY;
213         }
214         SIVAL(blob->data, 0, 1);
215         memset(blob->data+4, 0, 12);
216         return NT_STATUS_OK;
217 }
218
219 /*
220   check a CONNECT level verifier
221 */
222 static NTSTATUS dcerpc_check_connect_verifier(DATA_BLOB *blob)
223 {
224         if (blob->length != 16 ||
225             IVAL(blob->data, 0) != 1) {
226                 return NT_STATUS_ACCESS_DENIED;
227         }
228         return NT_STATUS_OK;
229 }
230
231 /* 
232    parse a possibly signed blob into a dcerpc request packet structure
233 */
234 static NTSTATUS ncacn_pull_request_sign(struct dcerpc_connection *c, 
235                                          DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
236                                          struct ncacn_packet *pkt)
237 {
238         struct ndr_pull *ndr;
239         NTSTATUS status;
240         struct dcerpc_auth auth;
241         DATA_BLOB auth_blob;
242
243         /* non-signed packets are simpler */
244         if (!c->security_state.auth_info || 
245             !c->security_state.generic_state) {
246                 return ncacn_pull(c, blob, mem_ctx, pkt);
247         }
248
249         ndr = ndr_pull_init_flags(c, blob, mem_ctx);
250         if (!ndr) {
251                 return NT_STATUS_NO_MEMORY;
252         }
253
254         if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
255                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
256         }
257
258         /* pull the basic packet */
259         status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
260         if (!NT_STATUS_IS_OK(status)) {
261                 return status;
262         }
263
264         if (pkt->ptype != DCERPC_PKT_RESPONSE) {
265                 return status;
266         }
267
268         if (pkt->auth_length == 0 &&
269             c->security_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
270                 return NT_STATUS_OK;
271         }
272
273         auth_blob.length = 8 + pkt->auth_length;
274
275         /* check for a valid length */
276         if (pkt->u.response.stub_and_verifier.length < auth_blob.length) {
277                 return NT_STATUS_INFO_LENGTH_MISMATCH;
278         }
279
280         auth_blob.data = 
281                 pkt->u.response.stub_and_verifier.data + 
282                 pkt->u.response.stub_and_verifier.length - auth_blob.length;
283         pkt->u.response.stub_and_verifier.length -= auth_blob.length;
284
285         /* pull the auth structure */
286         ndr = ndr_pull_init_flags(c, &auth_blob, mem_ctx);
287         if (!ndr) {
288                 return NT_STATUS_NO_MEMORY;
289         }
290
291         if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
292                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
293         }
294
295         status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
296         if (!NT_STATUS_IS_OK(status)) {
297                 return status;
298         }
299         
300         
301         /* check signature or unseal the packet */
302         switch (c->security_state.auth_info->auth_level) {
303         case DCERPC_AUTH_LEVEL_PRIVACY:
304                 status = gensec_unseal_packet(c->security_state.generic_state, 
305                                               mem_ctx, 
306                                               blob->data + DCERPC_REQUEST_LENGTH,
307                                               pkt->u.response.stub_and_verifier.length, 
308                                               blob->data,
309                                               blob->length - auth.credentials.length,
310                                               &auth.credentials);
311                 memcpy(pkt->u.response.stub_and_verifier.data,
312                        blob->data + DCERPC_REQUEST_LENGTH,
313                        pkt->u.response.stub_and_verifier.length);
314                 break;
315                 
316         case DCERPC_AUTH_LEVEL_INTEGRITY:
317                 status = gensec_check_packet(c->security_state.generic_state, 
318                                              mem_ctx, 
319                                              pkt->u.response.stub_and_verifier.data, 
320                                              pkt->u.response.stub_and_verifier.length, 
321                                              blob->data,
322                                              blob->length - auth.credentials.length,
323                                              &auth.credentials);
324                 break;
325
326         case DCERPC_AUTH_LEVEL_CONNECT:
327                 status = dcerpc_check_connect_verifier(&auth.credentials);
328                 break;
329
330         case DCERPC_AUTH_LEVEL_NONE:
331                 break;
332
333         default:
334                 status = NT_STATUS_INVALID_LEVEL;
335                 break;
336         }
337         
338         /* remove the indicated amount of paddiing */
339         if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
340                 return NT_STATUS_INFO_LENGTH_MISMATCH;
341         }
342         pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
343
344         return status;
345 }
346
347
348 /* 
349    push a dcerpc request packet into a blob, possibly signing it.
350 */
351 static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, 
352                                          DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
353                                          struct ncacn_packet *pkt)
354 {
355         NTSTATUS status;
356         struct ndr_push *ndr;
357         DATA_BLOB creds2;
358
359         /* non-signed packets are simpler */
360         if (!c->security_state.auth_info || 
361             !c->security_state.generic_state) {
362                 return ncacn_push_auth(blob, mem_ctx, pkt, c->security_state.auth_info);
363         }
364
365         ndr = ndr_push_init_ctx(mem_ctx);
366         if (!ndr) {
367                 return NT_STATUS_NO_MEMORY;
368         }
369
370         if (c->flags & DCERPC_PUSH_BIGENDIAN) {
371                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
372         }
373
374         if (pkt->pfc_flags & DCERPC_PFC_FLAG_ORPC) {
375                 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
376         }
377
378         status = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
379         if (!NT_STATUS_IS_OK(status)) {
380                 return status;
381         }
382
383         /* pad to 16 byte multiple in the payload portion of the
384            packet. This matches what w2k3 does */
385         c->security_state.auth_info->auth_pad_length = 
386                 (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
387         ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
388
389         /* sign or seal the packet */
390         switch (c->security_state.auth_info->auth_level) {
391         case DCERPC_AUTH_LEVEL_PRIVACY:
392         case DCERPC_AUTH_LEVEL_INTEGRITY:
393                 c->security_state.auth_info->credentials
394                         = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(c->security_state.generic_state));
395                 data_blob_clear(&c->security_state.auth_info->credentials);
396                 break;
397
398         case DCERPC_AUTH_LEVEL_CONNECT:
399                 status = dcerpc_connect_verifier(mem_ctx, &c->security_state.auth_info->credentials);
400                 break;
401                 
402         case DCERPC_AUTH_LEVEL_NONE:
403                 c->security_state.auth_info->credentials = data_blob(NULL, 0);
404                 break;
405                 
406         default:
407                 status = NT_STATUS_INVALID_LEVEL;
408                 break;
409         }
410         
411         if (!NT_STATUS_IS_OK(status)) {
412                 return status;
413         }       
414
415         /* add the auth verifier */
416         status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
417         if (!NT_STATUS_IS_OK(status)) {
418                 return status;
419         }
420
421         /* extract the whole packet as a blob */
422         *blob = ndr_push_blob(ndr);
423
424         /* fill in the fragment length and auth_length, we can't fill
425            in these earlier as we don't know the signature length (it
426            could be variable length) */
427         dcerpc_set_frag_length(blob, blob->length);
428         dcerpc_set_auth_length(blob, c->security_state.auth_info->credentials.length);
429
430         /* sign or seal the packet */
431         switch (c->security_state.auth_info->auth_level) {
432         case DCERPC_AUTH_LEVEL_PRIVACY:
433                 status = gensec_seal_packet(c->security_state.generic_state, 
434                                             mem_ctx, 
435                                             blob->data + DCERPC_REQUEST_LENGTH, 
436                                             pkt->u.request.stub_and_verifier.length + 
437                                             c->security_state.auth_info->auth_pad_length,
438                                             blob->data,
439                                             blob->length - 
440                                             c->security_state.auth_info->credentials.length,
441                                             &creds2);
442                 if (!NT_STATUS_IS_OK(status)) {
443                         return status;
444                 }
445                 memcpy(blob->data + blob->length - creds2.length, creds2.data, creds2.length);
446                 break;
447
448         case DCERPC_AUTH_LEVEL_INTEGRITY:
449                 status = gensec_sign_packet(c->security_state.generic_state, 
450                                             mem_ctx, 
451                                             blob->data + DCERPC_REQUEST_LENGTH, 
452                                             pkt->u.request.stub_and_verifier.length + 
453                                             c->security_state.auth_info->auth_pad_length,
454                                             blob->data,
455                                             blob->length - 
456                                             c->security_state.auth_info->credentials.length,
457                                             &creds2);
458                 if (!NT_STATUS_IS_OK(status)) {
459                         return status;
460                 }
461                 memcpy(blob->data + blob->length - creds2.length, creds2.data, creds2.length);
462                 break;
463
464         case DCERPC_AUTH_LEVEL_CONNECT:
465                 break;
466
467         case DCERPC_AUTH_LEVEL_NONE:
468                 c->security_state.auth_info->credentials = data_blob(NULL, 0);
469                 break;
470
471         default:
472                 status = NT_STATUS_INVALID_LEVEL;
473                 break;
474         }
475
476         data_blob_free(&c->security_state.auth_info->credentials);
477
478         return NT_STATUS_OK;
479 }
480
481
482 /* 
483    fill in the fixed values in a dcerpc header 
484 */
485 static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt)
486 {
487         pkt->rpc_vers = 5;
488         pkt->rpc_vers_minor = 0;
489         if (c->flags & DCERPC_PUSH_BIGENDIAN) {
490                 pkt->drep[0] = 0;
491         } else {
492                 pkt->drep[0] = DCERPC_DREP_LE;
493         }
494         pkt->drep[1] = 0;
495         pkt->drep[2] = 0;
496         pkt->drep[3] = 0;
497 }
498
499 /*
500   hold the state of pending full requests
501 */
502 struct full_request_state {
503         DATA_BLOB *reply_blob;
504         NTSTATUS status;
505 };
506
507 /*
508   receive a reply to a full request
509  */
510 static void full_request_recv(struct dcerpc_connection *c, DATA_BLOB *blob, 
511                               NTSTATUS status)
512 {
513         struct full_request_state *state = c->full_request_private;
514
515         if (!NT_STATUS_IS_OK(status)) {
516                 state->status = status;
517                 return;
518         }
519         state->reply_blob[0] = data_blob_talloc(state, blob->data, blob->length);
520         state->reply_blob = NULL;
521 }
522
523 /*
524   perform a single pdu synchronous request - used for the bind code
525   this cannot be mixed with normal async requests
526 */
527 static NTSTATUS full_request(struct dcerpc_connection *c, 
528                              TALLOC_CTX *mem_ctx,
529                              DATA_BLOB *request_blob,
530                              DATA_BLOB *reply_blob)
531 {
532         struct full_request_state *state = talloc(mem_ctx, struct full_request_state);
533         NTSTATUS status;
534
535         if (state == NULL) {
536                 return NT_STATUS_NO_MEMORY;
537         }
538
539         state->reply_blob = reply_blob;
540         state->status = NT_STATUS_OK;
541
542         c->transport.recv_data = full_request_recv;
543         c->full_request_private = state;
544
545         status = c->transport.send_request(c, request_blob, True);
546         if (!NT_STATUS_IS_OK(status)) {
547                 return status;
548         }
549
550         while (NT_STATUS_IS_OK(state->status) && state->reply_blob) {
551                 struct event_context *ctx = c->transport.event_context(c);
552                 if (event_loop_once(ctx) != 0) {
553                         return NT_STATUS_CONNECTION_DISCONNECTED;
554                 }
555         }
556
557         return state->status;
558 }
559
560 /*
561   map a bind nak reason to a NTSTATUS
562 */
563 static NTSTATUS dcerpc_map_reason(uint16_t reason)
564 {
565         switch (reason) {
566         case DCERPC_BIND_REASON_ASYNTAX:
567                 return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
568         }
569         return NT_STATUS_UNSUCCESSFUL;
570 }
571
572
573 /* 
574    perform a bind using the given syntax 
575
576    the auth_info structure is updated with the reply authentication info
577    on success
578 */
579 NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 
580                      TALLOC_CTX *mem_ctx,
581                      const struct dcerpc_syntax_id *syntax,
582                      const struct dcerpc_syntax_id *transfer_syntax)
583 {
584         struct ncacn_packet pkt;
585         NTSTATUS status;
586         DATA_BLOB blob;
587
588         p->syntax = *syntax;
589         p->transfer_syntax = *transfer_syntax;
590
591         init_ncacn_hdr(p->conn, &pkt);
592
593         pkt.ptype = DCERPC_PKT_BIND;
594         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
595         pkt.call_id = p->conn->call_id;
596         pkt.auth_length = 0;
597
598         pkt.u.bind.max_xmit_frag = 5840;
599         pkt.u.bind.max_recv_frag = 5840;
600         pkt.u.bind.assoc_group_id = 0;
601         pkt.u.bind.num_contexts = 1;
602         pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
603         if (!pkt.u.bind.ctx_list) {
604                 return NT_STATUS_NO_MEMORY;
605         }
606         pkt.u.bind.ctx_list[0].context_id = p->context_id;
607         pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
608         pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
609         pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
610         pkt.u.bind.auth_info = data_blob(NULL, 0);
611
612         /* construct the NDR form of the packet */
613         status = ncacn_push_auth(&blob, mem_ctx, &pkt, p->conn->security_state.auth_info);
614         if (!NT_STATUS_IS_OK(status)) {
615                 return status;
616         }
617
618         /* send it on its way */
619         status = full_request(p->conn, mem_ctx, &blob, &blob);
620         if (!NT_STATUS_IS_OK(status)) {
621                 return status;
622         }
623
624         /* unmarshall the NDR */
625         status = ncacn_pull(p->conn, &blob, mem_ctx, &pkt);
626         if (!NT_STATUS_IS_OK(status)) {
627                 return status;
628         }
629
630         if (pkt.ptype == DCERPC_PKT_BIND_NAK) {
631                 DEBUG(2,("dcerpc: bind_nak reason %d\n", pkt.u.bind_nak.reject_reason));
632                 return dcerpc_map_reason(pkt.u.bind_nak.reject_reason);
633         }
634
635         if ((pkt.ptype != DCERPC_PKT_BIND_ACK) ||
636             pkt.u.bind_ack.num_results == 0 ||
637             pkt.u.bind_ack.ctx_list[0].result != 0) {
638                 return NT_STATUS_UNSUCCESSFUL;
639         }
640
641         p->conn->srv_max_xmit_frag = pkt.u.bind_ack.max_xmit_frag;
642         p->conn->srv_max_recv_frag = pkt.u.bind_ack.max_recv_frag;
643
644         /* the bind_ack might contain a reply set of credentials */
645         if (p->conn->security_state.auth_info && pkt.u.bind_ack.auth_info.length) {
646                 status = ndr_pull_struct_blob(&pkt.u.bind_ack.auth_info,
647                                               mem_ctx,
648                                               p->conn->security_state.auth_info,
649                                               (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
650         }
651
652         return status;  
653 }
654
655 /* 
656    perform a continued bind (and auth3)
657 */
658 NTSTATUS dcerpc_auth3(struct dcerpc_connection *c, 
659                       TALLOC_CTX *mem_ctx)
660 {
661         struct ncacn_packet pkt;
662         NTSTATUS status;
663         DATA_BLOB blob;
664
665         init_ncacn_hdr(c, &pkt);
666
667         pkt.ptype = DCERPC_PKT_AUTH3;
668         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
669         pkt.call_id = next_call_id(c);
670         pkt.auth_length = 0;
671         pkt.u.auth3._pad = 0;
672         pkt.u.auth3.auth_info = data_blob(NULL, 0);
673
674         /* construct the NDR form of the packet */
675         status = ncacn_push_auth(&blob, mem_ctx, &pkt, c->security_state.auth_info);
676         if (!NT_STATUS_IS_OK(status)) {
677                 return status;
678         }
679
680         /* send it on its way */
681         status = c->transport.send_request(c, &blob, False);
682         if (!NT_STATUS_IS_OK(status)) {
683                 return status;
684         }
685
686         return status;  
687 }
688
689
690 /* perform a dcerpc bind, using the uuid as the key */
691 NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, 
692                             TALLOC_CTX *mem_ctx,
693                             const char *uuid, uint_t version)
694 {
695         struct dcerpc_syntax_id syntax;
696         struct dcerpc_syntax_id transfer_syntax;
697         NTSTATUS status;
698
699         status = GUID_from_string(uuid, &syntax.uuid);
700         if (!NT_STATUS_IS_OK(status)) {
701                 DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
702                 return status;
703         }
704         syntax.if_version = version;
705
706         status = GUID_from_string(NDR_GUID, &transfer_syntax.uuid);
707         if (!NT_STATUS_IS_OK(status)) {
708                 return status;
709         }
710         transfer_syntax.if_version = NDR_GUID_VERSION;
711
712         return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
713 }
714
715 /*
716   process a fragment received from the transport layer during a
717   request
718 */
719 static void dcerpc_request_recv_data(struct dcerpc_connection *c, 
720                                      DATA_BLOB *data,
721                                      NTSTATUS status)
722 {
723         struct ncacn_packet pkt;
724         struct rpc_request *req;
725         uint_t length;
726         
727         if (!NT_STATUS_IS_OK(status)) {
728                 /* all pending requests get the error */
729                 while (c->pending) {
730                         req = c->pending;
731                         req->state = RPC_REQUEST_DONE;
732                         req->status = status;
733                         DLIST_REMOVE(c->pending, req);
734                         if (req->async.callback) {
735                                 req->async.callback(req);
736                         }
737                 }
738                 return;
739         }
740
741         pkt.call_id = 0;
742
743         status = ncacn_pull_request_sign(c, data, (TALLOC_CTX *)data->data, &pkt);
744
745         /* find the matching request. Notice we match before we check
746            the status.  this is ok as a pending call_id can never be
747            zero */
748         for (req=c->pending;req;req=req->next) {
749                 if (pkt.call_id == req->call_id) break;
750         }
751
752         if (req == NULL) {
753                 DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt.call_id));
754                 return;
755         }
756
757         if (!NT_STATUS_IS_OK(status)) {
758                 req->status = status;
759                 req->state = RPC_REQUEST_DONE;
760                 DLIST_REMOVE(c->pending, req);
761                 if (req->async.callback) {
762                         req->async.callback(req);
763                 }
764                 return;
765         }
766
767         if (pkt.ptype == DCERPC_PKT_FAULT) {
768                 DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt.u.fault.status)));
769                 req->fault_code = pkt.u.fault.status;
770                 req->status = NT_STATUS_NET_WRITE_FAULT;
771                 req->state = RPC_REQUEST_DONE;
772                 DLIST_REMOVE(c->pending, req);
773                 if (req->async.callback) {
774                         req->async.callback(req);
775                 }
776                 return;
777         }
778
779         if (pkt.ptype != DCERPC_PKT_RESPONSE) {
780                 DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
781                          (int)pkt.ptype)); 
782                 req->fault_code = DCERPC_FAULT_OTHER;
783                 req->status = NT_STATUS_NET_WRITE_FAULT;
784                 req->state = RPC_REQUEST_DONE;
785                 DLIST_REMOVE(c->pending, req);
786                 if (req->async.callback) {
787                         req->async.callback(req);
788                 }
789                 return;
790         }
791
792         length = pkt.u.response.stub_and_verifier.length;
793
794         if (length > 0) {
795                 req->payload.data = talloc_realloc(req, 
796                                                    req->payload.data, 
797                                                    uint8_t,
798                                                    req->payload.length + length);
799                 if (!req->payload.data) {
800                         req->status = NT_STATUS_NO_MEMORY;
801                         req->state = RPC_REQUEST_DONE;
802                         DLIST_REMOVE(c->pending, req);
803                         if (req->async.callback) {
804                                 req->async.callback(req);
805                         }
806                         return;
807                 }
808                 memcpy(req->payload.data+req->payload.length, 
809                        pkt.u.response.stub_and_verifier.data, length);
810                 req->payload.length += length;
811         }
812
813         if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
814                 c->transport.send_read(c);
815                 return;
816         }
817
818         /* we've got the full payload */
819         req->state = RPC_REQUEST_DONE;
820         DLIST_REMOVE(c->pending, req);
821
822         if (!(pkt.drep[0] & DCERPC_DREP_LE)) {
823                 req->flags |= DCERPC_PULL_BIGENDIAN;
824         } else {
825                 req->flags &= ~DCERPC_PULL_BIGENDIAN;
826         }
827
828         if (req->async.callback) {
829                 req->async.callback(req);
830         }
831 }
832
833
834 /*
835   make sure requests are cleaned up 
836  */
837 static int dcerpc_req_destructor(void *ptr)
838 {
839         struct rpc_request *req = ptr;
840         DLIST_REMOVE(req->p->conn->pending, req);
841         return 0;
842 }
843
844 /*
845   perform the send side of a async dcerpc request
846 */
847 struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p, 
848                                         const struct GUID *object,
849                                         uint16_t opnum,
850                                         DATA_BLOB *stub_data)
851 {
852         struct rpc_request *req;
853         struct ncacn_packet pkt;
854         DATA_BLOB blob;
855         uint32_t remaining, chunk_size;
856         BOOL first_packet = True;
857
858         p->conn->transport.recv_data = dcerpc_request_recv_data;
859
860         req = talloc(p, struct rpc_request);
861         if (req == NULL) {
862                 return NULL;
863         }
864
865         req->p = p;
866         req->call_id = next_call_id(p->conn);
867         req->status = NT_STATUS_OK;
868         req->state = RPC_REQUEST_PENDING;
869         req->payload = data_blob(NULL, 0);
870         req->flags = 0;
871         req->fault_code = 0;
872         req->async.callback = NULL;
873
874         init_ncacn_hdr(p->conn, &pkt);
875
876         remaining = stub_data->length;
877
878         /* we can write a full max_recv_frag size, minus the dcerpc
879            request header size */
880         chunk_size = p->conn->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
881
882         pkt.ptype = DCERPC_PKT_REQUEST;
883         pkt.call_id = req->call_id;
884         pkt.auth_length = 0;
885         pkt.pfc_flags = 0;
886         pkt.u.request.alloc_hint = remaining;
887         pkt.u.request.context_id = p->context_id;
888         pkt.u.request.opnum = opnum;
889
890         if (object) {
891                 pkt.u.request.object.object = *object;
892                 pkt.pfc_flags |= DCERPC_PFC_FLAG_ORPC;
893                 chunk_size -= ndr_size_GUID(object,0);
894         }
895
896         DLIST_ADD(p->conn->pending, req);
897
898         /* we send a series of pdus without waiting for a reply */
899         while (remaining > 0 || first_packet) {
900                 uint32_t chunk = MIN(chunk_size, remaining);
901                 BOOL last_frag = False;
902
903                 first_packet = False;
904                 pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST);
905
906                 if (remaining == stub_data->length) {
907                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
908                 }
909                 if (chunk == remaining) {
910                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
911                         last_frag = True;
912                 }
913
914                 pkt.u.request.stub_and_verifier.data = stub_data->data + 
915                         (stub_data->length - remaining);
916                 pkt.u.request.stub_and_verifier.length = chunk;
917
918                 req->status = ncacn_push_request_sign(p->conn, &blob, req, &pkt);
919                 if (!NT_STATUS_IS_OK(req->status)) {
920                         req->state = RPC_REQUEST_DONE;
921                         DLIST_REMOVE(p->conn->pending, req);
922                         return req;
923                 }
924                 
925                 req->status = p->conn->transport.send_request(p->conn, &blob, last_frag);
926                 if (!NT_STATUS_IS_OK(req->status)) {
927                         req->state = RPC_REQUEST_DONE;
928                         DLIST_REMOVE(p->conn->pending, req);
929                         return req;
930                 }               
931
932                 remaining -= chunk;
933         }
934
935         talloc_set_destructor(req, dcerpc_req_destructor);
936
937         return req;
938 }
939
940 /*
941   return the event context for a dcerpc pipe
942   used by callers who wish to operate asynchronously
943 */
944 struct event_context *dcerpc_event_context(struct dcerpc_pipe *p)
945 {
946         return p->conn->transport.event_context(p->conn);
947 }
948
949
950
951 /*
952   perform the receive side of a async dcerpc request
953 */
954 NTSTATUS dcerpc_request_recv(struct rpc_request *req,
955                              TALLOC_CTX *mem_ctx,
956                              DATA_BLOB *stub_data)
957 {
958         NTSTATUS status;
959
960         while (req->state == RPC_REQUEST_PENDING) {
961                 struct event_context *ctx = dcerpc_event_context(req->p);
962                 if (event_loop_once(ctx) != 0) {
963                         return NT_STATUS_CONNECTION_DISCONNECTED;
964                 }
965         }
966         *stub_data = req->payload;
967         status = req->status;
968         if (stub_data->data) {
969                 stub_data->data = talloc_steal(mem_ctx, stub_data->data);
970         }
971         if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
972                 req->p->last_fault_code = req->fault_code;
973         }
974         talloc_free(req);
975         return status;
976 }
977
978 /*
979   perform a full request/response pair on a dcerpc pipe
980 */
981 NTSTATUS dcerpc_request(struct dcerpc_pipe *p, 
982                         struct GUID *object,
983                         uint16_t opnum,
984                         TALLOC_CTX *mem_ctx,
985                         DATA_BLOB *stub_data_in,
986                         DATA_BLOB *stub_data_out)
987 {
988         struct rpc_request *req;
989
990         req = dcerpc_request_send(p, object, opnum, stub_data_in);
991         if (req == NULL) {
992                 return NT_STATUS_NO_MEMORY;
993         }
994
995         return dcerpc_request_recv(req, mem_ctx, stub_data_out);
996 }
997
998
999 /*
1000   this is a paranoid NDR validator. For every packet we push onto the wire
1001   we pull it back again, then push it again. Then we compare the raw NDR data
1002   for that to the NDR we initially generated. If they don't match then we know
1003   we must have a bug in either the pull or push side of our code
1004 */
1005 static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c, 
1006                                        TALLOC_CTX *mem_ctx,
1007                                        DATA_BLOB blob,
1008                                        size_t struct_size,
1009                                        NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
1010                                        NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
1011 {
1012         void *st;
1013         struct ndr_pull *pull;
1014         struct ndr_push *push;
1015         NTSTATUS status;
1016         DATA_BLOB blob2;
1017
1018         st = talloc_size(mem_ctx, struct_size);
1019         if (!st) {
1020                 return NT_STATUS_NO_MEMORY;
1021         }
1022
1023         pull = ndr_pull_init_flags(c, &blob, mem_ctx);
1024         if (!pull) {
1025                 return NT_STATUS_NO_MEMORY;
1026         }
1027         pull->flags |= LIBNDR_FLAG_REF_ALLOC;
1028
1029         status = ndr_pull(pull, NDR_IN, st);
1030         if (!NT_STATUS_IS_OK(status)) {
1031                 return ndr_pull_error(pull, NDR_ERR_VALIDATE, 
1032                                       "failed input validation pull - %s",
1033                                       nt_errstr(status));
1034         }
1035
1036         push = ndr_push_init_ctx(mem_ctx);
1037         if (!push) {
1038                 return NT_STATUS_NO_MEMORY;
1039         }       
1040
1041         status = ndr_push(push, NDR_IN, st);
1042         if (!NT_STATUS_IS_OK(status)) {
1043                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
1044                                       "failed input validation push - %s",
1045                                       nt_errstr(status));
1046         }
1047
1048         blob2 = ndr_push_blob(push);
1049
1050         if (!data_blob_equal(&blob, &blob2)) {
1051                 DEBUG(3,("original:\n"));
1052                 dump_data(3, blob.data, blob.length);
1053                 DEBUG(3,("secondary:\n"));
1054                 dump_data(3, blob2.data, blob2.length);
1055                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
1056                                       "failed input validation data - %s",
1057                                       nt_errstr(status));
1058         }
1059
1060         return NT_STATUS_OK;
1061 }
1062
1063 /*
1064   this is a paranoid NDR input validator. For every packet we pull
1065   from the wire we push it back again then pull and push it
1066   again. Then we compare the raw NDR data for that to the NDR we
1067   initially generated. If they don't match then we know we must have a
1068   bug in either the pull or push side of our code
1069 */
1070 static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
1071                                         TALLOC_CTX *mem_ctx,
1072                                         void *struct_ptr,
1073                                         size_t struct_size,
1074                                         NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
1075                                         NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
1076 {
1077         void *st;
1078         struct ndr_pull *pull;
1079         struct ndr_push *push;
1080         NTSTATUS status;
1081         DATA_BLOB blob, blob2;
1082
1083         st = talloc_size(mem_ctx, struct_size);
1084         if (!st) {
1085                 return NT_STATUS_NO_MEMORY;
1086         }
1087         memcpy(st, struct_ptr, struct_size);
1088
1089         push = ndr_push_init_ctx(mem_ctx);
1090         if (!push) {
1091                 return NT_STATUS_NO_MEMORY;
1092         }       
1093
1094         status = ndr_push(push, NDR_OUT, struct_ptr);
1095         if (!NT_STATUS_IS_OK(status)) {
1096                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
1097                                       "failed output validation push - %s",
1098                                       nt_errstr(status));
1099         }
1100
1101         blob = ndr_push_blob(push);
1102
1103         pull = ndr_pull_init_flags(c, &blob, mem_ctx);
1104         if (!pull) {
1105                 return NT_STATUS_NO_MEMORY;
1106         }
1107
1108         pull->flags |= LIBNDR_FLAG_REF_ALLOC;
1109         status = ndr_pull(pull, NDR_OUT, st);
1110         if (!NT_STATUS_IS_OK(status)) {
1111                 return ndr_pull_error(pull, NDR_ERR_VALIDATE, 
1112                                       "failed output validation pull - %s",
1113                                       nt_errstr(status));
1114         }
1115
1116         push = ndr_push_init_ctx(mem_ctx);
1117         if (!push) {
1118                 return NT_STATUS_NO_MEMORY;
1119         }       
1120
1121         status = ndr_push(push, NDR_OUT, st);
1122         if (!NT_STATUS_IS_OK(status)) {
1123                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
1124                                       "failed output validation push2 - %s",
1125                                       nt_errstr(status));
1126         }
1127
1128         blob2 = ndr_push_blob(push);
1129
1130         if (!data_blob_equal(&blob, &blob2)) {
1131                 DEBUG(3,("original:\n"));
1132                 dump_data(3, blob.data, blob.length);
1133                 DEBUG(3,("secondary:\n"));
1134                 dump_data(3, blob2.data, blob2.length);
1135                 return ndr_push_error(push, NDR_ERR_VALIDATE, 
1136                                       "failed output validation data - %s",
1137                                       nt_errstr(status));
1138         }
1139
1140         return NT_STATUS_OK;
1141 }
1142
1143
1144 /*
1145  send a rpc request given a dcerpc_call structure 
1146  */
1147 struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
1148                                                 const struct GUID *object,
1149                                                 const struct dcerpc_interface_table *table,
1150                                                 uint32_t opnum, 
1151                                                 TALLOC_CTX *mem_ctx, 
1152                                                 void *r)
1153 {
1154         const struct dcerpc_interface_call *call;
1155         struct ndr_push *push;
1156         NTSTATUS status;
1157         DATA_BLOB request;
1158         struct rpc_request *req;
1159
1160         call = &table->calls[opnum];
1161
1162         /* setup for a ndr_push_* call */
1163         push = ndr_push_init_ctx(mem_ctx);
1164         if (!push) {
1165                 return NULL;
1166         }
1167
1168         if (p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
1169                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
1170         }
1171
1172         /* push the structure into a blob */
1173         status = call->ndr_push(push, NDR_IN, r);
1174         if (!NT_STATUS_IS_OK(status)) {
1175                 DEBUG(2,("Unable to ndr_push structure in dcerpc_ndr_request_send - %s\n",
1176                          nt_errstr(status)));
1177                 talloc_free(push);
1178                 return NULL;
1179         }
1180
1181         /* retrieve the blob */
1182         request = ndr_push_blob(push);
1183
1184         if (p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
1185                 status = dcerpc_ndr_validate_in(p->conn, push, request, call->struct_size, 
1186                                                 call->ndr_push, call->ndr_pull);
1187                 if (!NT_STATUS_IS_OK(status)) {
1188                         DEBUG(2,("Validation failed in dcerpc_ndr_request_send - %s\n",
1189                                  nt_errstr(status)));
1190                         talloc_free(push);
1191                         return NULL;
1192                 }
1193         }
1194
1195         DEBUG(10,("rpc request data:\n"));
1196         dump_data(10, request.data, request.length);
1197
1198         /* make the actual dcerpc request */
1199         req = dcerpc_request_send(p, object, opnum, &request);
1200
1201         if (req != NULL) {
1202                 req->ndr.table = table;
1203                 req->ndr.opnum = opnum;
1204                 req->ndr.struct_ptr = r;
1205                 req->ndr.mem_ctx = mem_ctx;
1206         }
1207
1208         talloc_free(push);
1209
1210         return req;
1211 }
1212
1213 /*
1214   receive the answer from a dcerpc_ndr_request_send()
1215 */
1216 NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
1217 {
1218         struct dcerpc_pipe *p = req->p;
1219         NTSTATUS status;
1220         DATA_BLOB response;
1221         struct ndr_pull *pull;
1222         uint_t flags;
1223         TALLOC_CTX *mem_ctx = req->ndr.mem_ctx;
1224         void *r = req->ndr.struct_ptr;
1225         uint32_t opnum = req->ndr.opnum;
1226         const struct dcerpc_interface_table *table = req->ndr.table;
1227         const struct dcerpc_interface_call *call = &table->calls[opnum];
1228
1229         /* make sure the recv code doesn't free the request, as we
1230            need to grab the flags element before it is freed */
1231         talloc_increase_ref_count(req);
1232
1233         status = dcerpc_request_recv(req, mem_ctx, &response);
1234         if (!NT_STATUS_IS_OK(status)) {
1235                 return status;
1236         }
1237
1238         flags = req->flags;
1239
1240         /* prepare for ndr_pull_* */
1241         pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
1242         if (!pull) {
1243                 talloc_free(req);
1244                 return NT_STATUS_NO_MEMORY;
1245         }
1246
1247         if (pull->data) {
1248                 pull->data = talloc_steal(pull, pull->data);
1249         }
1250         talloc_free(req);
1251
1252         if (flags & DCERPC_PULL_BIGENDIAN) {
1253                 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
1254         }
1255
1256         DEBUG(10,("rpc reply data:\n"));
1257         dump_data(10, pull->data, pull->data_size);
1258
1259         /* pull the structure from the blob */
1260         status = call->ndr_pull(pull, NDR_OUT, r);
1261         if (!NT_STATUS_IS_OK(status)) {
1262                 dcerpc_log_packet(table, opnum, NDR_OUT, 
1263                                   &response);
1264                 return status;
1265         }
1266
1267         if (p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
1268                 status = dcerpc_ndr_validate_out(p->conn, pull, r, call->struct_size, 
1269                                                  call->ndr_push, call->ndr_pull);
1270                 if (!NT_STATUS_IS_OK(status)) {
1271                         dcerpc_log_packet(table, opnum, NDR_OUT, 
1272                                   &response);
1273                         return status;
1274                 }
1275         }
1276
1277         if (pull->offset != pull->data_size) {
1278                 DEBUG(0,("Warning! ignoring %d unread bytes in rpc packet!\n", 
1279                          pull->data_size - pull->offset));
1280                 /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
1281                    but it turns out that early versions of NT
1282                    (specifically NT3.1) add junk onto the end of rpc
1283                    packets, so if we want to interoperate at all with
1284                    those versions then we need to ignore this error */
1285         }
1286
1287         /* TODO: make pull context independent from the output mem_ctx and free the pull context */
1288
1289         return NT_STATUS_OK;
1290 }
1291
1292
1293 /*
1294   a useful helper function for synchronous rpc requests 
1295
1296   this can be used when you have ndr push/pull functions in the
1297   standard format
1298 */
1299 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
1300                             const struct GUID *object,
1301                             const struct dcerpc_interface_table *table,
1302                             uint32_t opnum, 
1303                             TALLOC_CTX *mem_ctx, 
1304                             void *r)
1305 {
1306         struct rpc_request *req;
1307
1308         req = dcerpc_ndr_request_send(p, object, table, opnum, mem_ctx, r);
1309         if (req == NULL) {
1310                 return NT_STATUS_NO_MEMORY;
1311         }
1312
1313         return dcerpc_ndr_request_recv(req);
1314 }
1315
1316
1317 /*
1318   a useful function for retrieving the server name we connected to
1319 */
1320 const char *dcerpc_server_name(struct dcerpc_pipe *p)
1321 {
1322         if (!p->conn->transport.peer_name) {
1323                 return "";
1324         }
1325         return p->conn->transport.peer_name(p->conn);
1326 }
1327
1328
1329 /*
1330   get the dcerpc auth_level for a open connection
1331 */
1332 uint32_t dcerpc_auth_level(struct dcerpc_connection *c) 
1333 {
1334         uint8_t auth_level;
1335
1336         if (c->flags & DCERPC_SEAL) {
1337                 auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
1338         } else if (c->flags & DCERPC_SIGN) {
1339                 auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
1340         } else if (c->flags & DCERPC_CONNECT) {
1341                 auth_level = DCERPC_AUTH_LEVEL_CONNECT;
1342         } else {
1343                 auth_level = DCERPC_AUTH_LEVEL_NONE;
1344         }
1345         return auth_level;
1346 }
1347
1348
1349 /* 
1350    send a dcerpc alter_context request
1351 */
1352 NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p, 
1353                               TALLOC_CTX *mem_ctx,
1354                               const struct dcerpc_syntax_id *syntax,
1355                               const struct dcerpc_syntax_id *transfer_syntax)
1356 {
1357         struct ncacn_packet pkt;
1358         NTSTATUS status;
1359         DATA_BLOB blob;
1360
1361         p->syntax = *syntax;
1362         p->transfer_syntax = *transfer_syntax;
1363
1364         init_ncacn_hdr(p->conn, &pkt);
1365
1366         pkt.ptype = DCERPC_PKT_ALTER;
1367         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
1368         pkt.call_id = p->conn->call_id;
1369         pkt.auth_length = 0;
1370
1371         pkt.u.alter.max_xmit_frag = 5840;
1372         pkt.u.alter.max_recv_frag = 5840;
1373         pkt.u.alter.assoc_group_id = 0;
1374         pkt.u.alter.num_contexts = 1;
1375         pkt.u.alter.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
1376         if (!pkt.u.alter.ctx_list) {
1377                 return NT_STATUS_NO_MEMORY;
1378         }
1379         pkt.u.alter.ctx_list[0].context_id = p->context_id;
1380         pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
1381         pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
1382         pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
1383         pkt.u.alter.auth_info = data_blob(NULL, 0);
1384
1385         /* construct the NDR form of the packet */
1386         status = ncacn_push_auth(&blob, mem_ctx, &pkt, p->conn->security_state.auth_info);
1387         if (!NT_STATUS_IS_OK(status)) {
1388                 return status;
1389         }
1390
1391         /* send it on its way */
1392         status = full_request(p->conn, mem_ctx, &blob, &blob);
1393         if (!NT_STATUS_IS_OK(status)) {
1394                 return status;
1395         }
1396
1397         /* unmarshall the NDR */
1398         status = ncacn_pull(p->conn, &blob, mem_ctx, &pkt);
1399         if (!NT_STATUS_IS_OK(status)) {
1400                 return status;
1401         }
1402
1403         if (pkt.ptype == DCERPC_PKT_ALTER_RESP &&
1404             pkt.u.alter_resp.num_results == 1 &&
1405             pkt.u.alter_resp.ctx_list[0].result != 0) {
1406                 DEBUG(2,("dcerpc: alter_resp failed - reason %d\n", 
1407                          pkt.u.alter_resp.ctx_list[0].reason));
1408                 return dcerpc_map_reason(pkt.u.alter_resp.ctx_list[0].reason);
1409         }
1410
1411         if (pkt.ptype != DCERPC_PKT_ALTER_RESP ||
1412             pkt.u.alter_resp.num_results == 0 ||
1413             pkt.u.alter_resp.ctx_list[0].result != 0) {
1414                 return NT_STATUS_UNSUCCESSFUL;
1415         }
1416
1417         /* the alter_resp might contain a reply set of credentials */
1418         if (p->conn->security_state.auth_info && pkt.u.alter_resp.auth_info.length) {
1419                 status = ndr_pull_struct_blob(&pkt.u.alter_resp.auth_info,
1420                                               mem_ctx,
1421                                               p->conn->security_state.auth_info,
1422                                               (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
1423         }
1424
1425         return status;  
1426 }