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