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