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