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