Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into manpage
[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
548         /*
549          * Association groups allow policy handles to be shared across
550          * multiple client connections.  We don't implement this yet.
551          *
552          * So we just allow 0 if the client wants to create a new
553          * association group.
554          *
555          * And we allow the 0x12345678 value, we give away as
556          * assoc_group_id back to the clients
557          */
558         if (call->pkt.u.bind.assoc_group_id != 0 &&
559             call->pkt.u.bind.assoc_group_id != SAMBA_ACCOC_GROUP) {
560                 return dcesrv_bind_nak(call, 0);        
561         }
562
563         if (call->pkt.u.bind.num_contexts < 1 ||
564             call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
565                 return dcesrv_bind_nak(call, 0);
566         }
567
568         context_id = call->pkt.u.bind.ctx_list[0].context_id;
569
570         /* you can't bind twice on one context */
571         if (dcesrv_find_context(call->conn, context_id) != NULL) {
572                 return dcesrv_bind_nak(call, 0);
573         }
574
575         if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
576         uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
577
578         transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
579         transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
580         if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
581             ndr_transfer_syntax.if_version != transfer_syntax_version) {
582                 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
583                 /* we only do NDR encoded dcerpc */
584                 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
585                 talloc_free(uuid_str);
586                 return dcesrv_bind_nak(call, 0);
587         }
588
589         iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
590         if (iface == NULL) {
591                 char *uuid_str = GUID_string(call, &uuid);
592                 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
593                 talloc_free(uuid_str);
594
595                 /* we don't know about that interface */
596                 result = DCERPC_BIND_PROVIDER_REJECT;
597                 reason = DCERPC_BIND_REASON_ASYNTAX;            
598         }
599
600         if (iface) {
601                 /* add this context to the list of available context_ids */
602                 struct dcesrv_connection_context *context = talloc(call->conn, 
603                                                                    struct dcesrv_connection_context);
604                 if (context == NULL) {
605                         return dcesrv_bind_nak(call, 0);
606                 }
607                 context->conn = call->conn;
608                 context->iface = iface;
609                 context->context_id = context_id;
610                 context->private = NULL;
611                 context->handles = NULL;
612                 DLIST_ADD(call->conn->contexts, context);
613                 call->context = context;
614         }
615
616         if (call->conn->cli_max_recv_frag == 0) {
617                 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
618         }
619
620         /* handle any authentication that is being requested */
621         if (!dcesrv_auth_bind(call)) {
622                 return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
623         }
624
625         /* setup a bind_ack */
626         dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
627         pkt.auth_length = 0;
628         pkt.call_id = call->pkt.call_id;
629         pkt.ptype = DCERPC_PKT_BIND_ACK;
630         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
631         pkt.u.bind_ack.max_xmit_frag = 0x2000;
632         pkt.u.bind_ack.max_recv_frag = 0x2000;
633         /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
634         pkt.u.bind_ack.assoc_group_id = SAMBA_ACCOC_GROUP;
635         if (iface) {
636                 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
637                 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
638         } else {
639                 pkt.u.bind_ack.secondary_address = "";
640         }
641         pkt.u.bind_ack.num_results = 1;
642         pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
643         if (!pkt.u.bind_ack.ctx_list) {
644                 return NT_STATUS_NO_MEMORY;
645         }
646         pkt.u.bind_ack.ctx_list[0].result = result;
647         pkt.u.bind_ack.ctx_list[0].reason = reason;
648         pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
649         pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
650
651         status = dcesrv_auth_bind_ack(call, &pkt);
652         if (!NT_STATUS_IS_OK(status)) {
653                 return dcesrv_bind_nak(call, 0);
654         }
655
656         if (iface) {
657                 status = iface->bind(call, iface);
658                 if (!NT_STATUS_IS_OK(status)) {
659                         char *uuid_str = GUID_string(call, &uuid);
660                         DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n", 
661                                  uuid_str, if_version, nt_errstr(status)));
662                         talloc_free(uuid_str);
663                         return dcesrv_bind_nak(call, 0);
664                 }
665         }
666
667         rep = talloc(call, struct data_blob_list_item);
668         if (!rep) {
669                 return NT_STATUS_NO_MEMORY;
670         }
671
672         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
673         if (!NT_STATUS_IS_OK(status)) {
674                 return status;
675         }
676
677         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
678
679         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
680         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
681
682         return NT_STATUS_OK;
683 }
684
685
686 /*
687   handle a auth3 request
688 */
689 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
690 {
691         /* handle the auth3 in the auth code */
692         if (!dcesrv_auth_auth3(call)) {
693                 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
694         }
695
696         talloc_free(call);
697
698         /* we don't send a reply to a auth3 request, except by a
699            fault */
700         return NT_STATUS_OK;
701 }
702
703
704 /*
705   handle a bind request
706 */
707 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
708 {
709         uint32_t if_version, transfer_syntax_version;
710         struct dcesrv_connection_context *context;
711         const struct dcesrv_interface *iface;
712         struct GUID uuid, *transfer_syntax_uuid;
713         NTSTATUS status;
714
715         if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
716         uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
717
718         transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
719         transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
720         if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
721             ndr_transfer_syntax.if_version != transfer_syntax_version) {
722                 /* we only do NDR encoded dcerpc */
723                 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
724         }
725
726         iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
727         if (iface == NULL) {
728                 char *uuid_str = GUID_string(call, &uuid);
729                 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
730                 talloc_free(uuid_str);
731                 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
732         }
733
734         /* add this context to the list of available context_ids */
735         context = talloc(call->conn, struct dcesrv_connection_context);
736         if (context == NULL) {
737                 return NT_STATUS_NO_MEMORY;
738         }
739         context->conn = call->conn;
740         context->iface = iface;
741         context->context_id = context_id;
742         context->private = NULL;
743         context->handles = NULL;
744         DLIST_ADD(call->conn->contexts, context);
745         call->context = context;
746
747         if (iface) {
748                 status = iface->bind(call, iface);
749                 if (!NT_STATUS_IS_OK(status)) {
750                         return status;
751                 }
752         }
753
754         return NT_STATUS_OK;
755 }
756
757
758 /*
759   handle a alter context request
760 */
761 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
762 {
763         struct ncacn_packet pkt;
764         struct data_blob_list_item *rep;
765         NTSTATUS status;
766         uint32_t result=0, reason=0;
767         uint32_t context_id;
768
769         /* handle any authentication that is being requested */
770         if (!dcesrv_auth_alter(call)) {
771                 /* TODO: work out the right reject code */
772                 result = DCERPC_BIND_PROVIDER_REJECT;
773                 reason = DCERPC_BIND_REASON_ASYNTAX;            
774         }
775
776         context_id = call->pkt.u.alter.ctx_list[0].context_id;
777
778         /* see if they are asking for a new interface */
779         if (result == 0 &&
780             dcesrv_find_context(call->conn, context_id) == NULL) {
781                 status = dcesrv_alter_new_context(call, context_id);
782                 if (!NT_STATUS_IS_OK(status)) {
783                         result = DCERPC_BIND_PROVIDER_REJECT;
784                         reason = DCERPC_BIND_REASON_ASYNTAX;            
785                 }
786         }
787
788         /* setup a alter_resp */
789         dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
790         pkt.auth_length = 0;
791         pkt.call_id = call->pkt.call_id;
792         pkt.ptype = DCERPC_PKT_ALTER_RESP;
793         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
794         pkt.u.alter_resp.max_xmit_frag = 0x2000;
795         pkt.u.alter_resp.max_recv_frag = 0x2000;
796         pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
797         pkt.u.alter_resp.num_results = 1;
798         pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
799         if (!pkt.u.alter_resp.ctx_list) {
800                 return NT_STATUS_NO_MEMORY;
801         }
802         pkt.u.alter_resp.ctx_list[0].result = result;
803         pkt.u.alter_resp.ctx_list[0].reason = reason;
804         pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
805         pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
806         pkt.u.alter_resp.secondary_address = "";
807
808         status = dcesrv_auth_alter_ack(call, &pkt);
809         if (!NT_STATUS_IS_OK(status)) {
810                 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
811                     || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
812                     || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
813                     || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
814                         return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
815                 }
816                 return dcesrv_fault(call, 0);
817         }
818
819         rep = talloc(call, struct data_blob_list_item);
820         if (!rep) {
821                 return NT_STATUS_NO_MEMORY;
822         }
823
824         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
825         if (!NT_STATUS_IS_OK(status)) {
826                 return status;
827         }
828
829         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
830
831         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
832         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
833
834         return NT_STATUS_OK;
835 }
836
837 /*
838   handle a dcerpc request packet
839 */
840 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
841 {
842         struct ndr_pull *pull;
843         NTSTATUS status;
844         struct dcesrv_connection_context *context;
845
846         /* if authenticated, and the mech we use can't do async replies, don't use them... */
847         if (call->conn->auth_state.gensec_security && 
848             !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
849                 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
850         }
851
852         context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
853         if (context == NULL) {
854                 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
855         }
856
857         pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
858                                   lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
859         NT_STATUS_HAVE_NO_MEMORY(pull);
860
861         pull->flags |= LIBNDR_FLAG_REF_ALLOC;
862
863         call->context   = context;
864         call->ndr_pull  = pull;
865
866         if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
867                 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
868         }
869
870         if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
871                 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
872         }
873
874         /* unravel the NDR for the packet */
875         status = context->iface->ndr_pull(call, call, pull, &call->r);
876         if (!NT_STATUS_IS_OK(status)) {
877                 return dcesrv_fault(call, call->fault_code);
878         }
879
880         if (pull->offset != pull->data_size) {
881                 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n", 
882                          pull->data_size - pull->offset));
883                 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
884         }
885
886         /* call the dispatch function */
887         status = context->iface->dispatch(call, call, call->r);
888         if (!NT_STATUS_IS_OK(status)) {
889                 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
890                          context->iface->name, 
891                          call->pkt.u.request.opnum,
892                          dcerpc_errstr(pull, call->fault_code)));
893                 return dcesrv_fault(call, call->fault_code);
894         }
895
896         /* add the call to the pending list */
897         dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
898
899         if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
900                 return NT_STATUS_OK;
901         }
902
903         return dcesrv_reply(call);
904 }
905
906 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
907 {
908         struct ndr_push *push;
909         NTSTATUS status;
910         DATA_BLOB stub;
911         uint32_t total_length, chunk_size;
912         struct dcesrv_connection_context *context = call->context;
913
914         /* call the reply function */
915         status = context->iface->reply(call, call, call->r);
916         if (!NT_STATUS_IS_OK(status)) {
917                 return dcesrv_fault(call, call->fault_code);
918         }
919
920         /* form the reply NDR */
921         push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
922         NT_STATUS_HAVE_NO_MEMORY(push);
923
924         /* carry over the pointer count to the reply in case we are
925            using full pointer. See NDR specification for full
926            pointers */
927         push->ptr_count = call->ndr_pull->ptr_count;
928
929         if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
930                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
931         }
932
933         status = context->iface->ndr_push(call, call, push, call->r);
934         if (!NT_STATUS_IS_OK(status)) {
935                 return dcesrv_fault(call, call->fault_code);
936         }
937
938         stub = ndr_push_blob(push);
939
940         total_length = stub.length;
941
942         /* we can write a full max_recv_frag size, minus the dcerpc
943            request header size */
944         chunk_size = call->conn->cli_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
945
946         do {
947                 uint32_t length;
948                 struct data_blob_list_item *rep;
949                 struct ncacn_packet pkt;
950                 const uint32_t overhead = (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
951
952                 rep = talloc(call, struct data_blob_list_item);
953                 NT_STATUS_HAVE_NO_MEMORY(rep);
954
955                 length = MIN(chunk_size, stub.length);
956
957                 /* form the dcerpc response packet */
958                 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
959                 pkt.auth_length = 0;
960                 pkt.call_id = call->pkt.call_id;
961                 pkt.ptype = DCERPC_PKT_RESPONSE;
962                 pkt.pfc_flags = 0;
963                 if (stub.length == total_length) {
964                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
965                 }
966                 if (length == stub.length) {
967                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
968                 }
969                 pkt.u.response.alloc_hint = stub.length;
970                 pkt.u.response.context_id = call->pkt.u.request.context_id;
971                 pkt.u.response.cancel_count = 0;
972                 pkt.u.response.stub_and_verifier.data = stub.data;
973                 pkt.u.response.stub_and_verifier.length = length;
974
975                 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
976                         return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
977                 }
978
979                 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
980
981                 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
982                 
983                 stub.data += length;
984                 stub.length -= length;
985         } while (stub.length != 0);
986
987         /* move the call from the pending to the finished calls list */
988         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
989
990         if (call->conn->call_list && call->conn->call_list->replies) {
991                 if (call->conn->transport.report_output_data) {
992                         call->conn->transport.report_output_data(call->conn);
993                 }
994         }
995
996         return NT_STATUS_OK;
997 }
998
999 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1000 {
1001         if (!conn->transport.get_my_addr) {
1002                 return NULL;
1003         }
1004
1005         return conn->transport.get_my_addr(conn, mem_ctx);
1006 }
1007
1008 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1009 {
1010         if (!conn->transport.get_peer_addr) {
1011                 return NULL;
1012         }
1013
1014         return conn->transport.get_peer_addr(conn, mem_ctx);
1015 }
1016
1017 /*
1018   work out if we have a full packet yet
1019 */
1020 static bool dce_full_packet(const DATA_BLOB *data)
1021 {
1022         if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
1023                 return false;
1024         }
1025         if (dcerpc_get_frag_length(data) > data->length) {
1026                 return false;
1027         }
1028         return true;
1029 }
1030
1031 /*
1032   we might have consumed only part of our input - advance past that part
1033 */
1034 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1035 {
1036         DATA_BLOB blob;
1037
1038         if (dce_conn->partial_input.length == offset) {
1039                 data_blob_free(&dce_conn->partial_input);
1040                 return;
1041         }
1042
1043         blob = dce_conn->partial_input;
1044         dce_conn->partial_input = data_blob(blob.data + offset,
1045                                             blob.length - offset);
1046         data_blob_free(&blob);
1047 }
1048
1049 /*
1050   remove the call from the right list when freed
1051  */
1052 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1053 {
1054         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1055         return 0;
1056 }
1057
1058 /*
1059   process some input to a dcerpc endpoint server.
1060 */
1061 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1062 {
1063         struct ndr_pull *ndr;
1064         enum ndr_err_code ndr_err;
1065         NTSTATUS status;
1066         struct dcesrv_call_state *call;
1067         DATA_BLOB blob;
1068
1069         call = talloc_zero(dce_conn, struct dcesrv_call_state);
1070         if (!call) {
1071                 talloc_free(dce_conn->partial_input.data);
1072                 return NT_STATUS_NO_MEMORY;
1073         }
1074         call->conn              = dce_conn;
1075         call->event_ctx         = dce_conn->event_ctx;
1076         call->msg_ctx           = dce_conn->msg_ctx;
1077         call->state_flags       = call->conn->state_flags;
1078         call->time              = timeval_current();
1079         call->list              = DCESRV_LIST_NONE;
1080
1081         talloc_set_destructor(call, dcesrv_call_dequeue);
1082
1083         blob = dce_conn->partial_input;
1084         blob.length = dcerpc_get_frag_length(&blob);
1085
1086         ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
1087         if (!ndr) {
1088                 talloc_free(dce_conn->partial_input.data);
1089                 talloc_free(call);
1090                 return NT_STATUS_NO_MEMORY;
1091         }
1092
1093         if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1094                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1095         }
1096
1097         ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1098         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1099                 talloc_free(dce_conn->partial_input.data);
1100                 talloc_free(call);
1101                 return ndr_map_error2ntstatus(ndr_err);
1102         }
1103
1104         /* we have to check the signing here, before combining the
1105            pdus */
1106         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1107             !dcesrv_auth_request(call, &blob)) {
1108                 dce_partial_advance(dce_conn, blob.length);
1109                 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);          
1110         }
1111
1112         dce_partial_advance(dce_conn, blob.length);
1113
1114         /* see if this is a continued packet */
1115         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1116             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1117                 struct dcesrv_call_state *call2 = call;
1118                 uint32_t alloc_size;
1119
1120                 /* we only allow fragmented requests, no other packet types */
1121                 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1122                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1123                 }
1124
1125                 /* this is a continuation of an existing call - find the call then
1126                    tack it on the end */
1127                 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1128                 if (!call) {
1129                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1130                 }
1131
1132                 if (call->pkt.ptype != call2->pkt.ptype) {
1133                         /* trying to play silly buggers are we? */
1134                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1135                 }
1136
1137                 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1138                         call2->pkt.u.request.stub_and_verifier.length;
1139                 if (call->pkt.u.request.alloc_hint > alloc_size) {
1140                         alloc_size = call->pkt.u.request.alloc_hint;
1141                 }
1142
1143                 call->pkt.u.request.stub_and_verifier.data = 
1144                         talloc_realloc(call, 
1145                                        call->pkt.u.request.stub_and_verifier.data, 
1146                                        uint8_t, alloc_size);
1147                 if (!call->pkt.u.request.stub_and_verifier.data) {
1148                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1149                 }
1150                 memcpy(call->pkt.u.request.stub_and_verifier.data +
1151                        call->pkt.u.request.stub_and_verifier.length,
1152                        call2->pkt.u.request.stub_and_verifier.data,
1153                        call2->pkt.u.request.stub_and_verifier.length);
1154                 call->pkt.u.request.stub_and_verifier.length += 
1155                         call2->pkt.u.request.stub_and_verifier.length;
1156
1157                 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1158
1159                 talloc_free(call2);
1160         }
1161
1162         /* this may not be the last pdu in the chain - if its isn't then
1163            just put it on the incoming_fragmented_call_list and wait for the rest */
1164         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1165             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1166                 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1167                 return NT_STATUS_OK;
1168         } 
1169         
1170         /* This removes any fragments we may have had stashed away */
1171         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1172
1173         switch (call->pkt.ptype) {
1174         case DCERPC_PKT_BIND:
1175                 status = dcesrv_bind(call);
1176                 break;
1177         case DCERPC_PKT_AUTH3:
1178                 status = dcesrv_auth3(call);
1179                 break;
1180         case DCERPC_PKT_ALTER:
1181                 status = dcesrv_alter(call);
1182                 break;
1183         case DCERPC_PKT_REQUEST:
1184                 status = dcesrv_request(call);
1185                 break;
1186         default:
1187                 status = NT_STATUS_INVALID_PARAMETER;
1188                 break;
1189         }
1190
1191         /* if we are going to be sending a reply then add
1192            it to the list of pending calls. We add it to the end to keep the call
1193            list in the order we will answer */
1194         if (!NT_STATUS_IS_OK(status)) {
1195                 talloc_free(call);
1196         }
1197
1198         return status;
1199 }
1200
1201
1202 /*
1203   provide some input to a dcerpc endpoint server. This passes data
1204   from a dcerpc client into the server
1205 */
1206 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1207 {
1208         NTSTATUS status;
1209
1210         dce_conn->partial_input.data = talloc_realloc(dce_conn,
1211                                                       dce_conn->partial_input.data,
1212                                                       uint8_t,
1213                                                       dce_conn->partial_input.length + data->length);
1214         if (!dce_conn->partial_input.data) {
1215                 return NT_STATUS_NO_MEMORY;
1216         }
1217         memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1218                data->data, data->length);
1219         dce_conn->partial_input.length += data->length;
1220
1221         while (dce_full_packet(&dce_conn->partial_input)) {
1222                 status = dcesrv_input_process(dce_conn);
1223                 if (!NT_STATUS_IS_OK(status)) {
1224                         return status;
1225                 }
1226         }
1227
1228         return NT_STATUS_OK;
1229 }
1230
1231 /*
1232   retrieve some output from a dcerpc server
1233   The caller supplies a function that will be called to do the
1234   actual output. 
1235
1236   The first argument to write_fn() will be 'private', the second will
1237   be a pointer to a buffer containing the data to be sent and the 3rd
1238   will be a pointer to a size_t variable that will be set to the
1239   number of bytes that are consumed from the output.
1240
1241   from the current fragment
1242 */
1243 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
1244                        void *private_data,
1245                        NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1246 {
1247         NTSTATUS status;
1248         struct dcesrv_call_state *call;
1249         struct data_blob_list_item *rep;
1250         size_t nwritten;
1251
1252         call = dce_conn->call_list;
1253         if (!call || !call->replies) {
1254                 if (dce_conn->pending_call_list) {
1255                         /* TODO: we need to say act async here
1256                          *       as we know we have pending requests
1257                          *       which will be finished at a time
1258                          */
1259                         return NT_STATUS_FOOBAR;
1260                 }
1261                 return NT_STATUS_FOOBAR;
1262         }
1263         rep = call->replies;
1264
1265         status = write_fn(private_data, &rep->blob, &nwritten);
1266         NT_STATUS_IS_ERR_RETURN(status);
1267
1268         rep->blob.length -= nwritten;
1269         rep->blob.data += nwritten;
1270
1271         if (rep->blob.length == 0) {
1272                 /* we're done with this section of the call */
1273                 DLIST_REMOVE(call->replies, rep);
1274         }
1275
1276         if (call->replies == NULL) {
1277                 /* we're done with the whole call */
1278                 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1279                 talloc_free(call);
1280         }
1281
1282         return status;
1283 }
1284
1285 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, 
1286                                       struct loadparm_context *lp_ctx,
1287                                       const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1288 {
1289         NTSTATUS status;
1290         struct dcesrv_context *dce_ctx;
1291         int i;
1292
1293         if (!endpoint_servers) {
1294                 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1295                 return NT_STATUS_INTERNAL_ERROR;
1296         }
1297
1298         dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1299         NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1300         dce_ctx->endpoint_list  = NULL;
1301         dce_ctx->lp_ctx = lp_ctx;
1302
1303         for (i=0;endpoint_servers[i];i++) {
1304                 const struct dcesrv_endpoint_server *ep_server;
1305
1306                 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1307                 if (!ep_server) {
1308                         DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1309                         return NT_STATUS_INTERNAL_ERROR;
1310                 }
1311
1312                 status = ep_server->init_server(dce_ctx, ep_server);
1313                 if (!NT_STATUS_IS_OK(status)) {
1314                         DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1315                                 nt_errstr(status)));
1316                         return status;
1317                 }
1318         }
1319
1320         *_dce_ctx = dce_ctx;
1321         return NT_STATUS_OK;
1322 }
1323
1324 /* the list of currently registered DCERPC endpoint servers.
1325  */
1326 static struct ep_server {
1327         struct dcesrv_endpoint_server *ep_server;
1328 } *ep_servers = NULL;
1329 static int num_ep_servers;
1330
1331 /*
1332   register a DCERPC endpoint server. 
1333
1334   The 'name' can be later used by other backends to find the operations
1335   structure for this backend.  
1336
1337   The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1338 */
1339 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1340 {
1341         const struct dcesrv_endpoint_server *ep_server = _ep_server;
1342         
1343         if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1344                 /* its already registered! */
1345                 DEBUG(0,("DCERPC endpoint server '%s' already registered\n", 
1346                          ep_server->name));
1347                 return NT_STATUS_OBJECT_NAME_COLLISION;
1348         }
1349
1350         ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1351         if (!ep_servers) {
1352                 smb_panic("out of memory in dcerpc_register");
1353         }
1354
1355         ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1356         ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1357
1358         num_ep_servers++;
1359
1360         DEBUG(3,("DCERPC endpoint server '%s' registered\n", 
1361                  ep_server->name));
1362
1363         return NT_STATUS_OK;
1364 }
1365
1366 /*
1367   return the operations structure for a named backend of the specified type
1368 */
1369 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1370 {
1371         int i;
1372
1373         for (i=0;i<num_ep_servers;i++) {
1374                 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1375                         return ep_servers[i].ep_server;
1376                 }
1377         }
1378
1379         return NULL;
1380 }
1381
1382 /*
1383   return the DCERPC module version, and the size of some critical types
1384   This can be used by endpoint server modules to either detect compilation errors, or provide
1385   multiple implementations for different smbd compilation options in one module
1386 */
1387 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1388 {
1389         static const struct dcesrv_critical_sizes critical_sizes = {
1390                 DCERPC_MODULE_VERSION,
1391                 sizeof(struct dcesrv_context),
1392                 sizeof(struct dcesrv_endpoint),
1393                 sizeof(struct dcesrv_endpoint_server),
1394                 sizeof(struct dcesrv_interface),
1395                 sizeof(struct dcesrv_if_list),
1396                 sizeof(struct dcesrv_connection),
1397                 sizeof(struct dcesrv_call_state),
1398                 sizeof(struct dcesrv_auth),
1399                 sizeof(struct dcesrv_handle)
1400         };
1401
1402         return &critical_sizes;
1403 }
1404
1405 /*
1406   initialise the dcerpc server context for ncacn_np based services
1407 */
1408 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1409                                           struct dcesrv_context **_dce_ctx)
1410 {
1411         NTSTATUS status;
1412         struct dcesrv_context *dce_ctx;
1413
1414         status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1415         NT_STATUS_NOT_OK_RETURN(status);
1416
1417         *_dce_ctx = dce_ctx;
1418         return NT_STATUS_OK;
1419 }
1420
1421