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