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