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