rpc_server: add support for DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN
[samba.git] / source4 / rpc_server / dcerpc_server.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    server side dcerpc core code
5
6    Copyright (C) Andrew Tridgell 2003-2005
7    Copyright (C) Stefan (metze) Metzmacher 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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "librpc/gen_ndr/ndr_dcerpc.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "lib/util/dlinklist.h"
28 #include "rpc_server/dcerpc_server.h"
29 #include "rpc_server/dcerpc_server_proto.h"
30 #include "librpc/rpc/dcerpc_proto.h"
31 #include "lib/events/events.h"
32 #include "smbd/service_task.h"
33 #include "smbd/service_stream.h"
34 #include "smbd/service.h"
35 #include "system/filesys.h"
36 #include "libcli/security/security.h"
37 #include "param/param.h"
38
39 #define SAMBA_ACCOC_GROUP 0x12345678
40
41 extern const struct dcesrv_interface dcesrv_mgmt_interface;
42
43 /*
44   see if two endpoints match
45 */
46 static bool endpoints_match(const struct dcerpc_binding *ep1,
47                             const struct dcerpc_binding *ep2)
48 {
49         if (ep1->transport != ep2->transport) {
50                 return false;
51         }
52
53         if (!ep1->endpoint || !ep2->endpoint) {
54                 return ep1->endpoint == ep2->endpoint;
55         }
56
57         if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0) 
58                 return false;
59
60         return true;
61 }
62
63 /*
64   find an endpoint in the dcesrv_context
65 */
66 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
67                                              const struct dcerpc_binding *ep_description)
68 {
69         struct dcesrv_endpoint *ep;
70         for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
71                 if (endpoints_match(ep->ep_description, ep_description)) {
72                         return ep;
73                 }
74         }
75         return NULL;
76 }
77
78 /*
79   find a registered context_id from a bind or alter_context
80 */
81 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn, 
82                                                                    uint32_t context_id)
83 {
84         struct dcesrv_connection_context *c;
85         for (c=conn->contexts;c;c=c->next) {
86                 if (c->context_id == context_id) return c;
87         }
88         return NULL;
89 }
90
91 /*
92   see if a uuid and if_version match to an interface
93 */
94 static bool interface_match(const struct dcesrv_interface *if1,
95                                                         const struct dcesrv_interface *if2)
96 {
97         return (if1->syntax_id.if_version == if2->syntax_id.if_version && 
98                         GUID_equal(&if1->syntax_id.uuid, &if2->syntax_id.uuid));
99 }
100
101 /*
102   find the interface operations on an endpoint
103 */
104 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
105                                                      const struct dcesrv_interface *iface)
106 {
107         struct dcesrv_if_list *ifl;
108         for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
109                 if (interface_match(&(ifl->iface), iface)) {
110                         return &(ifl->iface);
111                 }
112         }
113         return NULL;
114 }
115
116 /*
117   see if a uuid and if_version match to an interface
118 */
119 static bool interface_match_by_uuid(const struct dcesrv_interface *iface,
120                                     const struct GUID *uuid, uint32_t if_version)
121 {
122         return (iface->syntax_id.if_version == if_version && 
123                         GUID_equal(&iface->syntax_id.uuid, uuid));
124 }
125
126 /*
127   find the interface operations on an endpoint by uuid
128 */
129 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
130                                                              const struct GUID *uuid, uint32_t if_version)
131 {
132         struct dcesrv_if_list *ifl;
133         for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
134                 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
135                         return &(ifl->iface);
136                 }
137         }
138         return NULL;
139 }
140
141 /*
142   find the earlier parts of a fragmented call awaiting reassembily
143 */
144 static struct dcesrv_call_state *dcesrv_find_fragmented_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
145 {
146         struct dcesrv_call_state *c;
147         for (c=dce_conn->incoming_fragmented_call_list;c;c=c->next) {
148                 if (c->pkt.call_id == call_id) {
149                         return c;
150                 }
151         }
152         return NULL;
153 }
154
155 /*
156   register an interface on an endpoint
157 */
158 _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
159                                    const char *ep_name,
160                                    const struct dcesrv_interface *iface,
161                                    const struct security_descriptor *sd)
162 {
163         struct dcesrv_endpoint *ep;
164         struct dcesrv_if_list *ifl;
165         struct dcerpc_binding *binding;
166         bool add_ep = false;
167         NTSTATUS status;
168         
169         status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
170
171         if (NT_STATUS_IS_ERR(status)) {
172                 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
173                 return status;
174         }
175
176         /* check if this endpoint exists
177          */
178         if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
179                 ep = talloc(dce_ctx, struct dcesrv_endpoint);
180                 if (!ep) {
181                         return NT_STATUS_NO_MEMORY;
182                 }
183                 ZERO_STRUCTP(ep);
184                 ep->ep_description = talloc_reference(ep, binding);
185                 add_ep = true;
186
187                 /* add mgmt interface */
188                 ifl = talloc(dce_ctx, struct dcesrv_if_list);
189                 if (!ifl) {
190                         return NT_STATUS_NO_MEMORY;
191                 }
192
193                 memcpy(&(ifl->iface), &dcesrv_mgmt_interface, 
194                            sizeof(struct dcesrv_interface));
195
196                 DLIST_ADD(ep->interface_list, ifl);
197         }
198
199         /* see if the interface is already registered on te endpoint */
200         if (find_interface(ep, iface)!=NULL) {
201                 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
202                         iface->name, ep_name));
203                 return NT_STATUS_OBJECT_NAME_COLLISION;
204         }
205
206         /* talloc a new interface list element */
207         ifl = talloc(dce_ctx, struct dcesrv_if_list);
208         if (!ifl) {
209                 return NT_STATUS_NO_MEMORY;
210         }
211
212         /* copy the given interface struct to the one on the endpoints interface list */
213         memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
214
215         /* if we have a security descriptor given,
216          * we should see if we can set it up on the endpoint
217          */
218         if (sd != NULL) {
219                 /* if there's currently no security descriptor given on the endpoint
220                  * we try to set it
221                  */
222                 if (ep->sd == NULL) {
223                         ep->sd = security_descriptor_copy(dce_ctx, sd);
224                 }
225
226                 /* if now there's no security descriptor given on the endpoint
227                  * something goes wrong, either we failed to copy the security descriptor
228                  * or there was already one on the endpoint
229                  */
230                 if (ep->sd != NULL) {
231                         DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
232                                  "                           on endpoint '%s'\n",
233                                 iface->name, ep_name));
234                         if (add_ep) free(ep);
235                         free(ifl);
236                         return NT_STATUS_OBJECT_NAME_COLLISION;
237                 }
238         }
239
240         /* finally add the interface on the endpoint */
241         DLIST_ADD(ep->interface_list, ifl);
242
243         /* if it's a new endpoint add it to the dcesrv_context */
244         if (add_ep) {
245                 DLIST_ADD(dce_ctx->endpoint_list, ep);
246         }
247
248         DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
249                 iface->name, ep_name));
250
251         return NT_STATUS_OK;
252 }
253
254 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
255                                               DATA_BLOB *session_key)
256 {
257         if (p->auth_state.session_info->session_key.length) {
258                 *session_key = p->auth_state.session_info->session_key;
259                 return NT_STATUS_OK;
260         }
261         return NT_STATUS_NO_USER_SESSION_KEY;
262 }
263
264 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
265                                     DATA_BLOB *session_key)
266 {
267         /* this took quite a few CPU cycles to find ... */
268         session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
269         session_key->length = 16;
270         return NT_STATUS_OK;
271 }
272
273 /*
274   fetch the user session key - may be default (above) or the SMB session key
275
276   The key is always truncated to 16 bytes 
277 */
278 _PUBLIC_ NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
279                                   DATA_BLOB *session_key)
280 {
281         NTSTATUS status = p->auth_state.session_key(p, session_key);
282         if (!NT_STATUS_IS_OK(status)) {
283                 return status;
284         }
285
286         session_key->length = MIN(session_key->length, 16);
287
288         return NT_STATUS_OK;
289 }
290
291
292 /*
293   destroy a link to an endpoint
294 */
295 static int dcesrv_endpoint_destructor(struct dcesrv_connection *p)
296 {
297         while (p->contexts) {
298                 struct dcesrv_connection_context *c = p->contexts;
299
300                 DLIST_REMOVE(p->contexts, c);
301
302                 if (c->iface) {
303                         c->iface->unbind(c, c->iface);
304                 }
305         }
306
307         return 0;
308 }
309
310
311 /*
312   connect to a dcerpc endpoint
313 */
314 _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
315                                  TALLOC_CTX *mem_ctx,
316                                  const struct dcesrv_endpoint *ep,
317                                  struct auth_session_info *session_info,
318                                  struct event_context *event_ctx,
319                                  struct messaging_context *msg_ctx,
320                                  struct server_id server_id,
321                                  uint32_t state_flags,
322                                  struct dcesrv_connection **_p)
323 {
324         struct dcesrv_connection *p;
325
326         if (!session_info) {
327                 return NT_STATUS_ACCESS_DENIED;
328         }
329
330         p = talloc(mem_ctx, struct dcesrv_connection);
331         NT_STATUS_HAVE_NO_MEMORY(p);
332
333         if (!talloc_reference(p, session_info)) {
334                 talloc_free(p);
335                 return NT_STATUS_NO_MEMORY;
336         }
337
338         p->dce_ctx = dce_ctx;
339         p->endpoint = ep;
340         p->contexts = NULL;
341         p->call_list = NULL;
342         p->incoming_fragmented_call_list = NULL;
343         p->pending_call_list = NULL;
344         p->cli_max_recv_frag = 0;
345         p->partial_input = data_blob(NULL, 0);
346         p->auth_state.auth_info = NULL;
347         p->auth_state.gensec_security = NULL;
348         p->auth_state.session_info = session_info;
349         p->auth_state.session_key = dcesrv_generic_session_key;
350         p->event_ctx = event_ctx;
351         p->msg_ctx = msg_ctx;
352         p->server_id = server_id;
353         p->processing = false;
354         p->state_flags = state_flags;
355         ZERO_STRUCT(p->transport);
356
357         talloc_set_destructor(p, dcesrv_endpoint_destructor);
358
359         *_p = p;
360         return NT_STATUS_OK;
361 }
362
363 /*
364   search and connect to a dcerpc endpoint
365 */
366 _PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
367                                         TALLOC_CTX *mem_ctx,
368                                         const struct dcerpc_binding *ep_description,
369                                         struct auth_session_info *session_info,
370                                         struct event_context *event_ctx,
371                                         struct messaging_context *msg_ctx,
372                                         struct server_id server_id,
373                                         uint32_t state_flags,
374                                         struct dcesrv_connection **dce_conn_p)
375 {
376         NTSTATUS status;
377         const struct dcesrv_endpoint *ep;
378
379         /* make sure this endpoint exists */
380         ep = find_endpoint(dce_ctx, ep_description);
381         if (!ep) {
382                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
383         }
384
385         status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
386                                          event_ctx, msg_ctx, server_id,
387                                          state_flags, dce_conn_p);
388         NT_STATUS_NOT_OK_RETURN(status);
389
390         (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
391
392         /* TODO: check security descriptor of the endpoint here 
393          *       if it's a smb named pipe
394          *       if it's failed free dce_conn_p
395          */
396
397         return NT_STATUS_OK;
398 }
399
400
401 static void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
402 {
403         pkt->rpc_vers = 5;
404         pkt->rpc_vers_minor = 0;
405         if (bigendian) {
406                 pkt->drep[0] = 0;
407         } else {
408                 pkt->drep[0] = DCERPC_DREP_LE;
409         }
410         pkt->drep[1] = 0;
411         pkt->drep[2] = 0;
412         pkt->drep[3] = 0;
413 }
414
415 /*
416   move a call from an existing linked list to the specified list. This
417   prevents bugs where we forget to remove the call from a previous
418   list when moving it.
419  */
420 static void dcesrv_call_set_list(struct dcesrv_call_state *call, 
421                                  enum dcesrv_call_list list)
422 {
423         switch (call->list) {
424         case DCESRV_LIST_NONE:
425                 break;
426         case DCESRV_LIST_CALL_LIST:
427                 DLIST_REMOVE(call->conn->call_list, call);
428                 break;
429         case DCESRV_LIST_FRAGMENTED_CALL_LIST:
430                 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
431                 break;
432         case DCESRV_LIST_PENDING_CALL_LIST:
433                 DLIST_REMOVE(call->conn->pending_call_list, call);
434                 break;
435         }
436         call->list = list;
437         switch (list) {
438         case DCESRV_LIST_NONE:
439                 break;
440         case DCESRV_LIST_CALL_LIST:
441                 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
442                 break;
443         case DCESRV_LIST_FRAGMENTED_CALL_LIST:
444                 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
445                 break;
446         case DCESRV_LIST_PENDING_CALL_LIST:
447                 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
448                 break;
449         }
450 }
451
452 /*
453   return a dcerpc fault
454 */
455 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
456 {
457         struct ncacn_packet pkt;
458         struct data_blob_list_item *rep;
459         uint8_t zeros[4];
460         NTSTATUS status;
461
462         /* setup a bind_ack */
463         dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
464         pkt.auth_length = 0;
465         pkt.call_id = call->pkt.call_id;
466         pkt.ptype = DCERPC_PKT_FAULT;
467         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
468         pkt.u.fault.alloc_hint = 0;
469         pkt.u.fault.context_id = 0;
470         pkt.u.fault.cancel_count = 0;
471         pkt.u.fault.status = fault_code;
472
473         ZERO_STRUCT(zeros);
474         pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
475
476         rep = talloc(call, struct data_blob_list_item);
477         if (!rep) {
478                 return NT_STATUS_NO_MEMORY;
479         }
480
481         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
482         if (!NT_STATUS_IS_OK(status)) {
483                 return status;
484         }
485
486         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
487
488         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
489         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
490
491         return NT_STATUS_OK;    
492 }
493
494
495 /*
496   return a dcerpc bind_nak
497 */
498 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
499 {
500         struct ncacn_packet pkt;
501         struct data_blob_list_item *rep;
502         NTSTATUS status;
503
504         /* setup a bind_nak */
505         dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
506         pkt.auth_length = 0;
507         pkt.call_id = call->pkt.call_id;
508         pkt.ptype = DCERPC_PKT_BIND_NAK;
509         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
510         pkt.u.bind_nak.reject_reason = reason;
511         if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
512                 pkt.u.bind_nak.versions.v.num_versions = 0;
513         }
514
515         rep = talloc(call, struct data_blob_list_item);
516         if (!rep) {
517                 return NT_STATUS_NO_MEMORY;
518         }
519
520         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
521         if (!NT_STATUS_IS_OK(status)) {
522                 return status;
523         }
524
525         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
526
527         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
528         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
529
530         return NT_STATUS_OK;    
531 }
532
533
534 /*
535   handle a bind request
536 */
537 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
538 {
539         uint32_t if_version, transfer_syntax_version;
540         struct GUID uuid, *transfer_syntax_uuid;
541         struct ncacn_packet pkt;
542         struct data_blob_list_item *rep;
543         NTSTATUS status;
544         uint32_t result=0, reason=0;
545         uint32_t context_id;
546         const struct dcesrv_interface *iface;
547         uint32_t extra_flags = 0;
548
549         /*
550          * Association groups allow policy handles to be shared across
551          * multiple client connections.  We don't implement this yet.
552          *
553          * So we just allow 0 if the client wants to create a new
554          * association group.
555          *
556          * And we allow the 0x12345678 value, we give away as
557          * assoc_group_id back to the clients
558          */
559         if (call->pkt.u.bind.assoc_group_id != 0 &&
560             call->pkt.u.bind.assoc_group_id != SAMBA_ACCOC_GROUP) {
561                 return dcesrv_bind_nak(call, 0);        
562         }
563
564         if (call->pkt.u.bind.num_contexts < 1 ||
565             call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
566                 return dcesrv_bind_nak(call, 0);
567         }
568
569         context_id = call->pkt.u.bind.ctx_list[0].context_id;
570
571         /* you can't bind twice on one context */
572         if (dcesrv_find_context(call->conn, context_id) != NULL) {
573                 return dcesrv_bind_nak(call, 0);
574         }
575
576         if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
577         uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
578
579         transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
580         transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
581         if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
582             ndr_transfer_syntax.if_version != transfer_syntax_version) {
583                 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
584                 /* we only do NDR encoded dcerpc */
585                 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
586                 talloc_free(uuid_str);
587                 return dcesrv_bind_nak(call, 0);
588         }
589
590         iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
591         if (iface == NULL) {
592                 char *uuid_str = GUID_string(call, &uuid);
593                 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
594                 talloc_free(uuid_str);
595
596                 /* we don't know about that interface */
597                 result = DCERPC_BIND_PROVIDER_REJECT;
598                 reason = DCERPC_BIND_REASON_ASYNTAX;            
599         }
600
601         if (iface) {
602                 /* add this context to the list of available context_ids */
603                 struct dcesrv_connection_context *context = talloc(call->conn, 
604                                                                    struct dcesrv_connection_context);
605                 if (context == NULL) {
606                         return dcesrv_bind_nak(call, 0);
607                 }
608                 context->conn = call->conn;
609                 context->iface = iface;
610                 context->context_id = context_id;
611                 context->private = NULL;
612                 context->handles = NULL;
613                 DLIST_ADD(call->conn->contexts, context);
614                 call->context = context;
615         }
616
617         if (call->conn->cli_max_recv_frag == 0) {
618                 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
619         }
620
621         if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
622             lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
623                 call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
624                 extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
625         }
626
627         /* handle any authentication that is being requested */
628         if (!dcesrv_auth_bind(call)) {
629                 return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
630         }
631
632         /* setup a bind_ack */
633         dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
634         pkt.auth_length = 0;
635         pkt.call_id = call->pkt.call_id;
636         pkt.ptype = DCERPC_PKT_BIND_ACK;
637         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
638         pkt.u.bind_ack.max_xmit_frag = 0x2000;
639         pkt.u.bind_ack.max_recv_frag = 0x2000;
640         /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
641         pkt.u.bind_ack.assoc_group_id = SAMBA_ACCOC_GROUP;
642         if (iface) {
643                 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
644                 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
645         } else {
646                 pkt.u.bind_ack.secondary_address = "";
647         }
648         pkt.u.bind_ack.num_results = 1;
649         pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
650         if (!pkt.u.bind_ack.ctx_list) {
651                 return NT_STATUS_NO_MEMORY;
652         }
653         pkt.u.bind_ack.ctx_list[0].result = result;
654         pkt.u.bind_ack.ctx_list[0].reason = reason;
655         pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
656         pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
657
658         status = dcesrv_auth_bind_ack(call, &pkt);
659         if (!NT_STATUS_IS_OK(status)) {
660                 return dcesrv_bind_nak(call, 0);
661         }
662
663         if (iface) {
664                 status = iface->bind(call, iface);
665                 if (!NT_STATUS_IS_OK(status)) {
666                         char *uuid_str = GUID_string(call, &uuid);
667                         DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n", 
668                                  uuid_str, if_version, nt_errstr(status)));
669                         talloc_free(uuid_str);
670                         return dcesrv_bind_nak(call, 0);
671                 }
672         }
673
674         rep = talloc(call, struct data_blob_list_item);
675         if (!rep) {
676                 return NT_STATUS_NO_MEMORY;
677         }
678
679         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
680         if (!NT_STATUS_IS_OK(status)) {
681                 return status;
682         }
683
684         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
685
686         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
687         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
688
689         return NT_STATUS_OK;
690 }
691
692
693 /*
694   handle a auth3 request
695 */
696 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
697 {
698         /* handle the auth3 in the auth code */
699         if (!dcesrv_auth_auth3(call)) {
700                 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
701         }
702
703         talloc_free(call);
704
705         /* we don't send a reply to a auth3 request, except by a
706            fault */
707         return NT_STATUS_OK;
708 }
709
710
711 /*
712   handle a bind request
713 */
714 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
715 {
716         uint32_t if_version, transfer_syntax_version;
717         struct dcesrv_connection_context *context;
718         const struct dcesrv_interface *iface;
719         struct GUID uuid, *transfer_syntax_uuid;
720         NTSTATUS status;
721
722         if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
723         uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
724
725         transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
726         transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
727         if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
728             ndr_transfer_syntax.if_version != transfer_syntax_version) {
729                 /* we only do NDR encoded dcerpc */
730                 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
731         }
732
733         iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
734         if (iface == NULL) {
735                 char *uuid_str = GUID_string(call, &uuid);
736                 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
737                 talloc_free(uuid_str);
738                 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
739         }
740
741         /* add this context to the list of available context_ids */
742         context = talloc(call->conn, struct dcesrv_connection_context);
743         if (context == NULL) {
744                 return NT_STATUS_NO_MEMORY;
745         }
746         context->conn = call->conn;
747         context->iface = iface;
748         context->context_id = context_id;
749         context->private = NULL;
750         context->handles = NULL;
751         DLIST_ADD(call->conn->contexts, context);
752         call->context = context;
753
754         if (iface) {
755                 status = iface->bind(call, iface);
756                 if (!NT_STATUS_IS_OK(status)) {
757                         return status;
758                 }
759         }
760
761         return NT_STATUS_OK;
762 }
763
764
765 /*
766   handle a alter context request
767 */
768 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
769 {
770         struct ncacn_packet pkt;
771         struct data_blob_list_item *rep;
772         NTSTATUS status;
773         uint32_t result=0, reason=0;
774         uint32_t context_id;
775
776         /* handle any authentication that is being requested */
777         if (!dcesrv_auth_alter(call)) {
778                 /* TODO: work out the right reject code */
779                 result = DCERPC_BIND_PROVIDER_REJECT;
780                 reason = DCERPC_BIND_REASON_ASYNTAX;            
781         }
782
783         context_id = call->pkt.u.alter.ctx_list[0].context_id;
784
785         /* see if they are asking for a new interface */
786         if (result == 0 &&
787             dcesrv_find_context(call->conn, context_id) == NULL) {
788                 status = dcesrv_alter_new_context(call, context_id);
789                 if (!NT_STATUS_IS_OK(status)) {
790                         result = DCERPC_BIND_PROVIDER_REJECT;
791                         reason = DCERPC_BIND_REASON_ASYNTAX;            
792                 }
793         }
794
795         /* setup a alter_resp */
796         dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
797         pkt.auth_length = 0;
798         pkt.call_id = call->pkt.call_id;
799         pkt.ptype = DCERPC_PKT_ALTER_RESP;
800         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
801         pkt.u.alter_resp.max_xmit_frag = 0x2000;
802         pkt.u.alter_resp.max_recv_frag = 0x2000;
803         pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
804         pkt.u.alter_resp.num_results = 1;
805         pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
806         if (!pkt.u.alter_resp.ctx_list) {
807                 return NT_STATUS_NO_MEMORY;
808         }
809         pkt.u.alter_resp.ctx_list[0].result = result;
810         pkt.u.alter_resp.ctx_list[0].reason = reason;
811         pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
812         pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
813         pkt.u.alter_resp.secondary_address = "";
814
815         status = dcesrv_auth_alter_ack(call, &pkt);
816         if (!NT_STATUS_IS_OK(status)) {
817                 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
818                     || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
819                     || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
820                     || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
821                         return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
822                 }
823                 return dcesrv_fault(call, 0);
824         }
825
826         rep = talloc(call, struct data_blob_list_item);
827         if (!rep) {
828                 return NT_STATUS_NO_MEMORY;
829         }
830
831         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
832         if (!NT_STATUS_IS_OK(status)) {
833                 return status;
834         }
835
836         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
837
838         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
839         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
840
841         return NT_STATUS_OK;
842 }
843
844 /*
845   handle a dcerpc request packet
846 */
847 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
848 {
849         struct ndr_pull *pull;
850         NTSTATUS status;
851         struct dcesrv_connection_context *context;
852
853         /* if authenticated, and the mech we use can't do async replies, don't use them... */
854         if (call->conn->auth_state.gensec_security && 
855             !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
856                 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
857         }
858
859         context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
860         if (context == NULL) {
861                 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
862         }
863
864         pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
865                                   lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
866         NT_STATUS_HAVE_NO_MEMORY(pull);
867
868         pull->flags |= LIBNDR_FLAG_REF_ALLOC;
869
870         call->context   = context;
871         call->ndr_pull  = pull;
872
873         if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
874                 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
875         }
876
877         if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
878                 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
879         }
880
881         /* unravel the NDR for the packet */
882         status = context->iface->ndr_pull(call, call, pull, &call->r);
883         if (!NT_STATUS_IS_OK(status)) {
884                 return dcesrv_fault(call, call->fault_code);
885         }
886
887         if (pull->offset != pull->data_size) {
888                 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n", 
889                          pull->data_size - pull->offset));
890                 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
891         }
892
893         /* call the dispatch function */
894         status = context->iface->dispatch(call, call, call->r);
895         if (!NT_STATUS_IS_OK(status)) {
896                 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
897                          context->iface->name, 
898                          call->pkt.u.request.opnum,
899                          dcerpc_errstr(pull, call->fault_code)));
900                 return dcesrv_fault(call, call->fault_code);
901         }
902
903         /* add the call to the pending list */
904         dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
905
906         if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
907                 return NT_STATUS_OK;
908         }
909
910         return dcesrv_reply(call);
911 }
912
913 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
914 {
915         struct ndr_push *push;
916         NTSTATUS status;
917         DATA_BLOB stub;
918         uint32_t total_length, chunk_size;
919         struct dcesrv_connection_context *context = call->context;
920
921         /* call the reply function */
922         status = context->iface->reply(call, call, call->r);
923         if (!NT_STATUS_IS_OK(status)) {
924                 return dcesrv_fault(call, call->fault_code);
925         }
926
927         /* form the reply NDR */
928         push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
929         NT_STATUS_HAVE_NO_MEMORY(push);
930
931         /* carry over the pointer count to the reply in case we are
932            using full pointer. See NDR specification for full
933            pointers */
934         push->ptr_count = call->ndr_pull->ptr_count;
935
936         if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
937                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
938         }
939
940         status = context->iface->ndr_push(call, call, push, call->r);
941         if (!NT_STATUS_IS_OK(status)) {
942                 return dcesrv_fault(call, call->fault_code);
943         }
944
945         stub = ndr_push_blob(push);
946
947         total_length = stub.length;
948
949         /* we can write a full max_recv_frag size, minus the dcerpc
950            request header size */
951         chunk_size = call->conn->cli_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
952
953         do {
954                 uint32_t length;
955                 struct data_blob_list_item *rep;
956                 struct ncacn_packet pkt;
957
958                 rep = talloc(call, struct data_blob_list_item);
959                 NT_STATUS_HAVE_NO_MEMORY(rep);
960
961                 length = MIN(chunk_size, stub.length);
962
963                 /* form the dcerpc response packet */
964                 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
965                 pkt.auth_length = 0;
966                 pkt.call_id = call->pkt.call_id;
967                 pkt.ptype = DCERPC_PKT_RESPONSE;
968                 pkt.pfc_flags = 0;
969                 if (stub.length == total_length) {
970                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
971                 }
972                 if (length == stub.length) {
973                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
974                 }
975                 pkt.u.response.alloc_hint = stub.length;
976                 pkt.u.response.context_id = call->pkt.u.request.context_id;
977                 pkt.u.response.cancel_count = 0;
978                 pkt.u.response.stub_and_verifier.data = stub.data;
979                 pkt.u.response.stub_and_verifier.length = length;
980
981                 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
982                         return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
983                 }
984
985                 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
986
987                 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
988                 
989                 stub.data += length;
990                 stub.length -= length;
991         } while (stub.length != 0);
992
993         /* move the call from the pending to the finished calls list */
994         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
995
996         if (call->conn->call_list && call->conn->call_list->replies) {
997                 if (call->conn->transport.report_output_data) {
998                         call->conn->transport.report_output_data(call->conn);
999                 }
1000         }
1001
1002         return NT_STATUS_OK;
1003 }
1004
1005 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1006 {
1007         if (!conn->transport.get_my_addr) {
1008                 return NULL;
1009         }
1010
1011         return conn->transport.get_my_addr(conn, mem_ctx);
1012 }
1013
1014 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1015 {
1016         if (!conn->transport.get_peer_addr) {
1017                 return NULL;
1018         }
1019
1020         return conn->transport.get_peer_addr(conn, mem_ctx);
1021 }
1022
1023 /*
1024   work out if we have a full packet yet
1025 */
1026 static bool dce_full_packet(const DATA_BLOB *data)
1027 {
1028         if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
1029                 return false;
1030         }
1031         if (dcerpc_get_frag_length(data) > data->length) {
1032                 return false;
1033         }
1034         return true;
1035 }
1036
1037 /*
1038   we might have consumed only part of our input - advance past that part
1039 */
1040 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1041 {
1042         DATA_BLOB blob;
1043
1044         if (dce_conn->partial_input.length == offset) {
1045                 data_blob_free(&dce_conn->partial_input);
1046                 return;
1047         }
1048
1049         blob = dce_conn->partial_input;
1050         dce_conn->partial_input = data_blob(blob.data + offset,
1051                                             blob.length - offset);
1052         data_blob_free(&blob);
1053 }
1054
1055 /*
1056   remove the call from the right list when freed
1057  */
1058 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1059 {
1060         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1061         return 0;
1062 }
1063
1064 /*
1065   process some input to a dcerpc endpoint server.
1066 */
1067 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1068 {
1069         struct ndr_pull *ndr;
1070         enum ndr_err_code ndr_err;
1071         NTSTATUS status;
1072         struct dcesrv_call_state *call;
1073         DATA_BLOB blob;
1074
1075         call = talloc_zero(dce_conn, struct dcesrv_call_state);
1076         if (!call) {
1077                 talloc_free(dce_conn->partial_input.data);
1078                 return NT_STATUS_NO_MEMORY;
1079         }
1080         call->conn              = dce_conn;
1081         call->event_ctx         = dce_conn->event_ctx;
1082         call->msg_ctx           = dce_conn->msg_ctx;
1083         call->state_flags       = call->conn->state_flags;
1084         call->time              = timeval_current();
1085         call->list              = DCESRV_LIST_NONE;
1086
1087         talloc_set_destructor(call, dcesrv_call_dequeue);
1088
1089         blob = dce_conn->partial_input;
1090         blob.length = dcerpc_get_frag_length(&blob);
1091
1092         ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
1093         if (!ndr) {
1094                 talloc_free(dce_conn->partial_input.data);
1095                 talloc_free(call);
1096                 return NT_STATUS_NO_MEMORY;
1097         }
1098
1099         if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1100                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1101         }
1102
1103         ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1104         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1105                 talloc_free(dce_conn->partial_input.data);
1106                 talloc_free(call);
1107                 return ndr_map_error2ntstatus(ndr_err);
1108         }
1109
1110         /* we have to check the signing here, before combining the
1111            pdus */
1112         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1113             !dcesrv_auth_request(call, &blob)) {
1114                 dce_partial_advance(dce_conn, blob.length);
1115                 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);          
1116         }
1117
1118         dce_partial_advance(dce_conn, blob.length);
1119
1120         /* see if this is a continued packet */
1121         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1122             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1123                 struct dcesrv_call_state *call2 = call;
1124                 uint32_t alloc_size;
1125
1126                 /* we only allow fragmented requests, no other packet types */
1127                 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1128                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1129                 }
1130
1131                 /* this is a continuation of an existing call - find the call then
1132                    tack it on the end */
1133                 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1134                 if (!call) {
1135                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1136                 }
1137
1138                 if (call->pkt.ptype != call2->pkt.ptype) {
1139                         /* trying to play silly buggers are we? */
1140                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1141                 }
1142
1143                 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1144                         call2->pkt.u.request.stub_and_verifier.length;
1145                 if (call->pkt.u.request.alloc_hint > alloc_size) {
1146                         alloc_size = call->pkt.u.request.alloc_hint;
1147                 }
1148
1149                 call->pkt.u.request.stub_and_verifier.data = 
1150                         talloc_realloc(call, 
1151                                        call->pkt.u.request.stub_and_verifier.data, 
1152                                        uint8_t, alloc_size);
1153                 if (!call->pkt.u.request.stub_and_verifier.data) {
1154                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1155                 }
1156                 memcpy(call->pkt.u.request.stub_and_verifier.data +
1157                        call->pkt.u.request.stub_and_verifier.length,
1158                        call2->pkt.u.request.stub_and_verifier.data,
1159                        call2->pkt.u.request.stub_and_verifier.length);
1160                 call->pkt.u.request.stub_and_verifier.length += 
1161                         call2->pkt.u.request.stub_and_verifier.length;
1162
1163                 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1164
1165                 talloc_free(call2);
1166         }
1167
1168         /* this may not be the last pdu in the chain - if its isn't then
1169            just put it on the incoming_fragmented_call_list and wait for the rest */
1170         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1171             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1172                 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1173                 return NT_STATUS_OK;
1174         } 
1175         
1176         /* This removes any fragments we may have had stashed away */
1177         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1178
1179         switch (call->pkt.ptype) {
1180         case DCERPC_PKT_BIND:
1181                 status = dcesrv_bind(call);
1182                 break;
1183         case DCERPC_PKT_AUTH3:
1184                 status = dcesrv_auth3(call);
1185                 break;
1186         case DCERPC_PKT_ALTER:
1187                 status = dcesrv_alter(call);
1188                 break;
1189         case DCERPC_PKT_REQUEST:
1190                 status = dcesrv_request(call);
1191                 break;
1192         default:
1193                 status = NT_STATUS_INVALID_PARAMETER;
1194                 break;
1195         }
1196
1197         /* if we are going to be sending a reply then add
1198            it to the list of pending calls. We add it to the end to keep the call
1199            list in the order we will answer */
1200         if (!NT_STATUS_IS_OK(status)) {
1201                 talloc_free(call);
1202         }
1203
1204         return status;
1205 }
1206
1207
1208 /*
1209   provide some input to a dcerpc endpoint server. This passes data
1210   from a dcerpc client into the server
1211 */
1212 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1213 {
1214         NTSTATUS status;
1215
1216         dce_conn->partial_input.data = talloc_realloc(dce_conn,
1217                                                       dce_conn->partial_input.data,
1218                                                       uint8_t,
1219                                                       dce_conn->partial_input.length + data->length);
1220         if (!dce_conn->partial_input.data) {
1221                 return NT_STATUS_NO_MEMORY;
1222         }
1223         memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1224                data->data, data->length);
1225         dce_conn->partial_input.length += data->length;
1226
1227         while (dce_full_packet(&dce_conn->partial_input)) {
1228                 status = dcesrv_input_process(dce_conn);
1229                 if (!NT_STATUS_IS_OK(status)) {
1230                         return status;
1231                 }
1232         }
1233
1234         return NT_STATUS_OK;
1235 }
1236
1237 /*
1238   retrieve some output from a dcerpc server
1239   The caller supplies a function that will be called to do the
1240   actual output. 
1241
1242   The first argument to write_fn() will be 'private', the second will
1243   be a pointer to a buffer containing the data to be sent and the 3rd
1244   will be a pointer to a size_t variable that will be set to the
1245   number of bytes that are consumed from the output.
1246
1247   from the current fragment
1248 */
1249 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
1250                        void *private_data,
1251                        NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1252 {
1253         NTSTATUS status;
1254         struct dcesrv_call_state *call;
1255         struct data_blob_list_item *rep;
1256         size_t nwritten;
1257
1258         call = dce_conn->call_list;
1259         if (!call || !call->replies) {
1260                 if (dce_conn->pending_call_list) {
1261                         /* TODO: we need to say act async here
1262                          *       as we know we have pending requests
1263                          *       which will be finished at a time
1264                          */
1265                         return NT_STATUS_FOOBAR;
1266                 }
1267                 return NT_STATUS_FOOBAR;
1268         }
1269         rep = call->replies;
1270
1271         status = write_fn(private_data, &rep->blob, &nwritten);
1272         NT_STATUS_IS_ERR_RETURN(status);
1273
1274         rep->blob.length -= nwritten;
1275         rep->blob.data += nwritten;
1276
1277         if (rep->blob.length == 0) {
1278                 /* we're done with this section of the call */
1279                 DLIST_REMOVE(call->replies, rep);
1280         }
1281
1282         if (call->replies == NULL) {
1283                 /* we're done with the whole call */
1284                 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1285                 talloc_free(call);
1286         }
1287
1288         return status;
1289 }
1290
1291 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, 
1292                                       struct loadparm_context *lp_ctx,
1293                                       const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1294 {
1295         NTSTATUS status;
1296         struct dcesrv_context *dce_ctx;
1297         int i;
1298
1299         if (!endpoint_servers) {
1300                 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1301                 return NT_STATUS_INTERNAL_ERROR;
1302         }
1303
1304         dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1305         NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1306         dce_ctx->endpoint_list  = NULL;
1307         dce_ctx->lp_ctx = lp_ctx;
1308
1309         for (i=0;endpoint_servers[i];i++) {
1310                 const struct dcesrv_endpoint_server *ep_server;
1311
1312                 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1313                 if (!ep_server) {
1314                         DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1315                         return NT_STATUS_INTERNAL_ERROR;
1316                 }
1317
1318                 status = ep_server->init_server(dce_ctx, ep_server);
1319                 if (!NT_STATUS_IS_OK(status)) {
1320                         DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1321                                 nt_errstr(status)));
1322                         return status;
1323                 }
1324         }
1325
1326         *_dce_ctx = dce_ctx;
1327         return NT_STATUS_OK;
1328 }
1329
1330 /* the list of currently registered DCERPC endpoint servers.
1331  */
1332 static struct ep_server {
1333         struct dcesrv_endpoint_server *ep_server;
1334 } *ep_servers = NULL;
1335 static int num_ep_servers;
1336
1337 /*
1338   register a DCERPC endpoint server. 
1339
1340   The 'name' can be later used by other backends to find the operations
1341   structure for this backend.  
1342
1343   The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1344 */
1345 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1346 {
1347         const struct dcesrv_endpoint_server *ep_server = _ep_server;
1348         
1349         if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1350                 /* its already registered! */
1351                 DEBUG(0,("DCERPC endpoint server '%s' already registered\n", 
1352                          ep_server->name));
1353                 return NT_STATUS_OBJECT_NAME_COLLISION;
1354         }
1355
1356         ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1357         if (!ep_servers) {
1358                 smb_panic("out of memory in dcerpc_register");
1359         }
1360
1361         ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1362         ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1363
1364         num_ep_servers++;
1365
1366         DEBUG(3,("DCERPC endpoint server '%s' registered\n", 
1367                  ep_server->name));
1368
1369         return NT_STATUS_OK;
1370 }
1371
1372 /*
1373   return the operations structure for a named backend of the specified type
1374 */
1375 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1376 {
1377         int i;
1378
1379         for (i=0;i<num_ep_servers;i++) {
1380                 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1381                         return ep_servers[i].ep_server;
1382                 }
1383         }
1384
1385         return NULL;
1386 }
1387
1388 /*
1389   return the DCERPC module version, and the size of some critical types
1390   This can be used by endpoint server modules to either detect compilation errors, or provide
1391   multiple implementations for different smbd compilation options in one module
1392 */
1393 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1394 {
1395         static const struct dcesrv_critical_sizes critical_sizes = {
1396                 DCERPC_MODULE_VERSION,
1397                 sizeof(struct dcesrv_context),
1398                 sizeof(struct dcesrv_endpoint),
1399                 sizeof(struct dcesrv_endpoint_server),
1400                 sizeof(struct dcesrv_interface),
1401                 sizeof(struct dcesrv_if_list),
1402                 sizeof(struct dcesrv_connection),
1403                 sizeof(struct dcesrv_call_state),
1404                 sizeof(struct dcesrv_auth),
1405                 sizeof(struct dcesrv_handle)
1406         };
1407
1408         return &critical_sizes;
1409 }
1410
1411 /*
1412   initialise the dcerpc server context for ncacn_np based services
1413 */
1414 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1415                                           struct dcesrv_context **_dce_ctx)
1416 {
1417         NTSTATUS status;
1418         struct dcesrv_context *dce_ctx;
1419
1420         status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1421         NT_STATUS_NOT_OK_RETURN(status);
1422
1423         *_dce_ctx = dce_ctx;
1424         return NT_STATUS_OK;
1425 }
1426
1427