r23792: convert Samba4 to GPLv3
[mdw/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 "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         if (!dcesrv_auth_bind_ack(call, &pkt)) {
624                 return dcesrv_bind_nak(call, 0);
625         }
626
627         if (iface) {
628                 status = iface->bind(call, iface);
629                 if (!NT_STATUS_IS_OK(status)) {
630                         char *uuid_str = GUID_string(call, &uuid);
631                         DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n", 
632                                  uuid_str, if_version, nt_errstr(status)));
633                         talloc_free(uuid_str);
634                         return dcesrv_bind_nak(call, 0);
635                 }
636         }
637
638         rep = talloc(call, struct data_blob_list_item);
639         if (!rep) {
640                 return NT_STATUS_NO_MEMORY;
641         }
642
643         status = ncacn_push_auth(&rep->blob, call, &pkt, 
644                                   call->conn->auth_state.auth_info);
645         if (!NT_STATUS_IS_OK(status)) {
646                 return status;
647         }
648
649         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
650
651         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
652         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
653
654         return NT_STATUS_OK;
655 }
656
657
658 /*
659   handle a auth3 request
660 */
661 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
662 {
663         /* handle the auth3 in the auth code */
664         if (!dcesrv_auth_auth3(call)) {
665                 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
666         }
667
668         talloc_free(call);
669
670         /* we don't send a reply to a auth3 request, except by a
671            fault */
672         return NT_STATUS_OK;
673 }
674
675
676 /*
677   handle a bind request
678 */
679 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
680 {
681         uint32_t if_version, transfer_syntax_version;
682         struct dcesrv_connection_context *context;
683         const struct dcesrv_interface *iface;
684         struct GUID uuid, *transfer_syntax_uuid;
685
686         if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
687         uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
688
689         transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
690         transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
691         if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
692             ndr_transfer_syntax.if_version != transfer_syntax_version) {
693                 /* we only do NDR encoded dcerpc */
694                 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
695         }
696
697         iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
698         if (iface == NULL) {
699                 char *uuid_str = GUID_string(call, &uuid);
700                 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
701                 talloc_free(uuid_str);
702                 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
703         }
704
705         /* add this context to the list of available context_ids */
706         context = talloc(call->conn, struct dcesrv_connection_context);
707         if (context == NULL) {
708                 return NT_STATUS_NO_MEMORY;
709         }
710         context->conn = call->conn;
711         context->iface = iface;
712         context->context_id = context_id;
713         context->private = NULL;
714         context->handles = NULL;
715         DLIST_ADD(call->conn->contexts, context);
716         call->context = context;
717
718         return NT_STATUS_OK;
719 }
720
721
722 /*
723   handle a alter context request
724 */
725 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
726 {
727         struct ncacn_packet pkt;
728         struct data_blob_list_item *rep;
729         NTSTATUS status;
730         uint32_t result=0, reason=0;
731         uint32_t context_id;
732
733         /* handle any authentication that is being requested */
734         if (!dcesrv_auth_alter(call)) {
735                 /* TODO: work out the right reject code */
736                 result = DCERPC_BIND_PROVIDER_REJECT;
737                 reason = DCERPC_BIND_REASON_ASYNTAX;            
738         }
739
740         context_id = call->pkt.u.alter.ctx_list[0].context_id;
741
742         /* see if they are asking for a new interface */
743         if (result == 0 &&
744             dcesrv_find_context(call->conn, context_id) == NULL) {
745                 status = dcesrv_alter_new_context(call, context_id);
746                 if (!NT_STATUS_IS_OK(status)) {
747                         result = DCERPC_BIND_PROVIDER_REJECT;
748                         reason = DCERPC_BIND_REASON_ASYNTAX;            
749                 }
750         }
751
752         /* setup a alter_resp */
753         dcesrv_init_hdr(&pkt);
754         pkt.auth_length = 0;
755         pkt.call_id = call->pkt.call_id;
756         pkt.ptype = DCERPC_PKT_ALTER_RESP;
757         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
758         pkt.u.alter_resp.max_xmit_frag = 0x2000;
759         pkt.u.alter_resp.max_recv_frag = 0x2000;
760         pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
761         pkt.u.alter_resp.num_results = 1;
762         pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
763         if (!pkt.u.alter_resp.ctx_list) {
764                 return NT_STATUS_NO_MEMORY;
765         }
766         pkt.u.alter_resp.ctx_list[0].result = result;
767         pkt.u.alter_resp.ctx_list[0].reason = reason;
768         pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
769         pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
770         pkt.u.alter_resp.secondary_address = "";
771
772         if (!dcesrv_auth_alter_ack(call, &pkt)) {
773                 return dcesrv_bind_nak(call, 0);
774         }
775
776         rep = talloc(call, struct data_blob_list_item);
777         if (!rep) {
778                 return NT_STATUS_NO_MEMORY;
779         }
780
781         status = ncacn_push_auth(&rep->blob, call, &pkt, 
782                                   call->conn->auth_state.auth_info);
783         if (!NT_STATUS_IS_OK(status)) {
784                 return status;
785         }
786
787         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
788
789         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
790         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
791
792         return NT_STATUS_OK;
793 }
794
795 /*
796   handle a dcerpc request packet
797 */
798 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
799 {
800         struct ndr_pull *pull;
801         NTSTATUS status;
802         struct dcesrv_connection_context *context;
803
804         /* if authenticated, and the mech we use can't do async replies, don't use them... */
805         if (call->conn->auth_state.gensec_security && 
806             !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
807                 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
808         }
809
810         context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
811         if (context == NULL) {
812                 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
813         }
814
815         pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
816         NT_STATUS_HAVE_NO_MEMORY(pull);
817
818         pull->flags |= LIBNDR_FLAG_REF_ALLOC;
819
820         call->context   = context;
821         call->ndr_pull  = pull;
822
823         if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
824                 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
825         }
826
827         if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
828                 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
829         }
830
831         /* unravel the NDR for the packet */
832         status = context->iface->ndr_pull(call, call, pull, &call->r);
833         if (!NT_STATUS_IS_OK(status)) {
834                 return dcesrv_fault(call, call->fault_code);
835         }
836
837         if (pull->offset != pull->data_size) {
838                 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n", 
839                          pull->data_size - pull->offset));
840                 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
841         }
842
843         /* call the dispatch function */
844         status = context->iface->dispatch(call, call, call->r);
845         if (!NT_STATUS_IS_OK(status)) {
846                 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
847                          context->iface->name, 
848                          call->pkt.u.request.opnum,
849                          dcerpc_errstr(pull, call->fault_code)));
850                 return dcesrv_fault(call, call->fault_code);
851         }
852
853         /* add the call to the pending list */
854         dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
855
856         if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
857                 return NT_STATUS_OK;
858         }
859
860         return dcesrv_reply(call);
861 }
862
863 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
864 {
865         struct ndr_push *push;
866         NTSTATUS status;
867         DATA_BLOB stub;
868         uint32_t total_length;
869         struct dcesrv_connection_context *context = call->context;
870
871         /* call the reply function */
872         status = context->iface->reply(call, call, call->r);
873         if (!NT_STATUS_IS_OK(status)) {
874                 return dcesrv_fault(call, call->fault_code);
875         }
876
877         /* form the reply NDR */
878         push = ndr_push_init_ctx(call);
879         NT_STATUS_HAVE_NO_MEMORY(push);
880
881         /* carry over the pointer count to the reply in case we are
882            using full pointer. See NDR specification for full
883            pointers */
884         push->ptr_count = call->ndr_pull->ptr_count;
885
886         if (lp_rpc_big_endian()) {
887                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
888         }
889
890         status = context->iface->ndr_push(call, call, push, call->r);
891         if (!NT_STATUS_IS_OK(status)) {
892                 return dcesrv_fault(call, call->fault_code);
893         }
894
895         stub = ndr_push_blob(push);
896
897         total_length = stub.length;
898
899         do {
900                 uint32_t length;
901                 struct data_blob_list_item *rep;
902                 struct ncacn_packet pkt;
903
904                 rep = talloc(call, struct data_blob_list_item);
905                 NT_STATUS_HAVE_NO_MEMORY(rep);
906
907                 length = stub.length;
908                 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
909                         /* the 32 is to cope with signing data */
910                         length = call->conn->cli_max_recv_frag - 
911                                 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
912                 }
913
914                 /* form the dcerpc response packet */
915                 dcesrv_init_hdr(&pkt);
916                 pkt.auth_length = 0;
917                 pkt.call_id = call->pkt.call_id;
918                 pkt.ptype = DCERPC_PKT_RESPONSE;
919                 pkt.pfc_flags = 0;
920                 if (stub.length == total_length) {
921                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
922                 }
923                 if (length == stub.length) {
924                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
925                 }
926                 pkt.u.response.alloc_hint = stub.length;
927                 pkt.u.response.context_id = call->pkt.u.request.context_id;
928                 pkt.u.response.cancel_count = 0;
929                 pkt.u.response.stub_and_verifier.data = stub.data;
930                 pkt.u.response.stub_and_verifier.length = length;
931
932                 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
933                         return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
934                 }
935
936                 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
937
938                 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
939                 
940                 stub.data += length;
941                 stub.length -= length;
942         } while (stub.length != 0);
943
944         /* move the call from the pending to the finished calls list */
945         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
946
947         if (call->conn->call_list && call->conn->call_list->replies) {
948                 if (call->conn->transport.report_output_data) {
949                         call->conn->transport.report_output_data(call->conn);
950                 }
951         }
952
953         return NT_STATUS_OK;
954 }
955
956 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
957 {
958         if (!conn->transport.get_my_addr) {
959                 return NULL;
960         }
961
962         return conn->transport.get_my_addr(conn, mem_ctx);
963 }
964
965 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
966 {
967         if (!conn->transport.get_peer_addr) {
968                 return NULL;
969         }
970
971         return conn->transport.get_peer_addr(conn, mem_ctx);
972 }
973
974 /*
975   work out if we have a full packet yet
976 */
977 static BOOL dce_full_packet(const DATA_BLOB *data)
978 {
979         if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
980                 return False;
981         }
982         if (dcerpc_get_frag_length(data) > data->length) {
983                 return False;
984         }
985         return True;
986 }
987
988 /*
989   we might have consumed only part of our input - advance past that part
990 */
991 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
992 {
993         DATA_BLOB blob;
994
995         if (dce_conn->partial_input.length == offset) {
996                 data_blob_free(&dce_conn->partial_input);
997                 return;
998         }
999
1000         blob = dce_conn->partial_input;
1001         dce_conn->partial_input = data_blob(blob.data + offset,
1002                                             blob.length - offset);
1003         data_blob_free(&blob);
1004 }
1005
1006 /*
1007   remove the call from the right list when freed
1008  */
1009 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1010 {
1011         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1012         return 0;
1013 }
1014
1015 /*
1016   process some input to a dcerpc endpoint server.
1017 */
1018 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1019 {
1020         struct ndr_pull *ndr;
1021         NTSTATUS status;
1022         struct dcesrv_call_state *call;
1023         DATA_BLOB blob;
1024
1025         call = talloc_zero(dce_conn, struct dcesrv_call_state);
1026         if (!call) {
1027                 talloc_free(dce_conn->partial_input.data);
1028                 return NT_STATUS_NO_MEMORY;
1029         }
1030         call->conn              = dce_conn;
1031         call->event_ctx         = dce_conn->event_ctx;
1032         call->msg_ctx           = dce_conn->msg_ctx;
1033         call->state_flags       = call->conn->state_flags;
1034         call->time              = timeval_current();
1035         call->list              = DCESRV_LIST_NONE;
1036
1037         talloc_set_destructor(call, dcesrv_call_dequeue);
1038
1039         blob = dce_conn->partial_input;
1040         blob.length = dcerpc_get_frag_length(&blob);
1041
1042         ndr = ndr_pull_init_blob(&blob, call);
1043         if (!ndr) {
1044                 talloc_free(dce_conn->partial_input.data);
1045                 talloc_free(call);
1046                 return NT_STATUS_NO_MEMORY;
1047         }
1048
1049         if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1050                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1051         }
1052
1053         status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1054         if (!NT_STATUS_IS_OK(status)) {
1055                 talloc_free(dce_conn->partial_input.data);
1056                 talloc_free(call);
1057                 return status;
1058         }
1059
1060         /* we have to check the signing here, before combining the
1061            pdus */
1062         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1063             !dcesrv_auth_request(call, &blob)) {
1064                 dce_partial_advance(dce_conn, blob.length);
1065                 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);          
1066         }
1067
1068         dce_partial_advance(dce_conn, blob.length);
1069
1070         /* see if this is a continued packet */
1071         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1072             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1073                 struct dcesrv_call_state *call2 = call;
1074                 uint32_t alloc_size;
1075
1076                 /* we only allow fragmented requests, no other packet types */
1077                 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1078                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1079                 }
1080
1081                 /* this is a continuation of an existing call - find the call then
1082                    tack it on the end */
1083                 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1084                 if (!call) {
1085                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1086                 }
1087
1088                 if (call->pkt.ptype != call2->pkt.ptype) {
1089                         /* trying to play silly buggers are we? */
1090                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1091                 }
1092
1093                 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1094                         call2->pkt.u.request.stub_and_verifier.length;
1095                 if (call->pkt.u.request.alloc_hint > alloc_size) {
1096                         alloc_size = call->pkt.u.request.alloc_hint;
1097                 }
1098
1099                 call->pkt.u.request.stub_and_verifier.data = 
1100                         talloc_realloc(call, 
1101                                        call->pkt.u.request.stub_and_verifier.data, 
1102                                        uint8_t, alloc_size);
1103                 if (!call->pkt.u.request.stub_and_verifier.data) {
1104                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1105                 }
1106                 memcpy(call->pkt.u.request.stub_and_verifier.data +
1107                        call->pkt.u.request.stub_and_verifier.length,
1108                        call2->pkt.u.request.stub_and_verifier.data,
1109                        call2->pkt.u.request.stub_and_verifier.length);
1110                 call->pkt.u.request.stub_and_verifier.length += 
1111                         call2->pkt.u.request.stub_and_verifier.length;
1112
1113                 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1114
1115                 talloc_free(call2);
1116         }
1117
1118         /* this may not be the last pdu in the chain - if its isn't then
1119            just put it on the incoming_fragmented_call_list and wait for the rest */
1120         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1121             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1122                 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1123                 return NT_STATUS_OK;
1124         } 
1125         
1126         /* This removes any fragments we may have had stashed away */
1127         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1128
1129         switch (call->pkt.ptype) {
1130         case DCERPC_PKT_BIND:
1131                 status = dcesrv_bind(call);
1132                 break;
1133         case DCERPC_PKT_AUTH3:
1134                 status = dcesrv_auth3(call);
1135                 break;
1136         case DCERPC_PKT_ALTER:
1137                 status = dcesrv_alter(call);
1138                 break;
1139         case DCERPC_PKT_REQUEST:
1140                 status = dcesrv_request(call);
1141                 break;
1142         default:
1143                 status = NT_STATUS_INVALID_PARAMETER;
1144                 break;
1145         }
1146
1147         /* if we are going to be sending a reply then add
1148            it to the list of pending calls. We add it to the end to keep the call
1149            list in the order we will answer */
1150         if (!NT_STATUS_IS_OK(status)) {
1151                 talloc_free(call);
1152         }
1153
1154         return status;
1155 }
1156
1157
1158 /*
1159   provide some input to a dcerpc endpoint server. This passes data
1160   from a dcerpc client into the server
1161 */
1162 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1163 {
1164         NTSTATUS status;
1165
1166         dce_conn->partial_input.data = talloc_realloc(dce_conn,
1167                                                       dce_conn->partial_input.data,
1168                                                       uint8_t,
1169                                                       dce_conn->partial_input.length + data->length);
1170         if (!dce_conn->partial_input.data) {
1171                 return NT_STATUS_NO_MEMORY;
1172         }
1173         memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1174                data->data, data->length);
1175         dce_conn->partial_input.length += data->length;
1176
1177         while (dce_full_packet(&dce_conn->partial_input)) {
1178                 status = dcesrv_input_process(dce_conn);
1179                 if (!NT_STATUS_IS_OK(status)) {
1180                         return status;
1181                 }
1182         }
1183
1184         return NT_STATUS_OK;
1185 }
1186
1187 /*
1188   retrieve some output from a dcerpc server
1189   The caller supplies a function that will be called to do the
1190   actual output. 
1191
1192   The first argument to write_fn() will be 'private', the second will
1193   be a pointer to a buffer containing the data to be sent and the 3rd
1194   will be a pointer to a size_t variable that will be set to the
1195   number of bytes that are consumed from the output.
1196
1197   from the current fragment
1198 */
1199 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
1200                        void *private_data,
1201                        NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1202 {
1203         NTSTATUS status;
1204         struct dcesrv_call_state *call;
1205         struct data_blob_list_item *rep;
1206         size_t nwritten;
1207
1208         call = dce_conn->call_list;
1209         if (!call || !call->replies) {
1210                 if (dce_conn->pending_call_list) {
1211                         /* TODO: we need to say act async here
1212                          *       as we know we have pending requests
1213                          *       which will be finished at a time
1214                          */
1215                         return NT_STATUS_FOOBAR;
1216                 }
1217                 return NT_STATUS_FOOBAR;
1218         }
1219         rep = call->replies;
1220
1221         status = write_fn(private_data, &rep->blob, &nwritten);
1222         NT_STATUS_IS_ERR_RETURN(status);
1223
1224         rep->blob.length -= nwritten;
1225         rep->blob.data += nwritten;
1226
1227         if (rep->blob.length == 0) {
1228                 /* we're done with this section of the call */
1229                 DLIST_REMOVE(call->replies, rep);
1230         }
1231
1232         if (call->replies == NULL) {
1233                 /* we're done with the whole call */
1234                 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1235                 talloc_free(call);
1236         }
1237
1238         return status;
1239 }
1240
1241 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1242 {
1243         NTSTATUS status;
1244         struct dcesrv_context *dce_ctx;
1245         int i;
1246
1247         if (!endpoint_servers) {
1248                 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1249                 return NT_STATUS_INTERNAL_ERROR;
1250         }
1251
1252         dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1253         NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1254         dce_ctx->endpoint_list  = NULL;
1255
1256         for (i=0;endpoint_servers[i];i++) {
1257                 const struct dcesrv_endpoint_server *ep_server;
1258
1259                 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1260                 if (!ep_server) {
1261                         DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1262                         return NT_STATUS_INTERNAL_ERROR;
1263                 }
1264
1265                 status = ep_server->init_server(dce_ctx, ep_server);
1266                 if (!NT_STATUS_IS_OK(status)) {
1267                         DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1268                                 nt_errstr(status)));
1269                         return status;
1270                 }
1271         }
1272
1273         *_dce_ctx = dce_ctx;
1274         return NT_STATUS_OK;
1275 }
1276
1277 /*
1278   initialise the dcerpc server context for ncacn_np based services
1279 */
1280 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1281 {
1282         NTSTATUS status;
1283         struct dcesrv_context *dce_ctx;
1284
1285         status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), &dce_ctx);
1286         NT_STATUS_NOT_OK_RETURN(status);
1287
1288         *_dce_ctx = dce_ctx;
1289         return NT_STATUS_OK;
1290 }
1291
1292 /* the list of currently registered DCERPC endpoint servers.
1293  */
1294 static struct ep_server {
1295         struct dcesrv_endpoint_server *ep_server;
1296 } *ep_servers = NULL;
1297 static int num_ep_servers;
1298
1299 /*
1300   register a DCERPC endpoint server. 
1301
1302   The 'name' can be later used by other backends to find the operations
1303   structure for this backend.  
1304
1305   The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1306 */
1307 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1308 {
1309         const struct dcesrv_endpoint_server *ep_server = _ep_server;
1310         
1311         if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1312                 /* its already registered! */
1313                 DEBUG(0,("DCERPC endpoint server '%s' already registered\n", 
1314                          ep_server->name));
1315                 return NT_STATUS_OBJECT_NAME_COLLISION;
1316         }
1317
1318         ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1319         if (!ep_servers) {
1320                 smb_panic("out of memory in dcerpc_register");
1321         }
1322
1323         ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1324         ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1325
1326         num_ep_servers++;
1327
1328         DEBUG(3,("DCERPC endpoint server '%s' registered\n", 
1329                  ep_server->name));
1330
1331         return NT_STATUS_OK;
1332 }
1333
1334 /*
1335   return the operations structure for a named backend of the specified type
1336 */
1337 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1338 {
1339         int i;
1340
1341         for (i=0;i<num_ep_servers;i++) {
1342                 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1343                         return ep_servers[i].ep_server;
1344                 }
1345         }
1346
1347         return NULL;
1348 }
1349
1350 /*
1351   return the DCERPC module version, and the size of some critical types
1352   This can be used by endpoint server modules to either detect compilation errors, or provide
1353   multiple implementations for different smbd compilation options in one module
1354 */
1355 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1356 {
1357         static const struct dcesrv_critical_sizes critical_sizes = {
1358                 DCERPC_MODULE_VERSION,
1359                 sizeof(struct dcesrv_context),
1360                 sizeof(struct dcesrv_endpoint),
1361                 sizeof(struct dcesrv_endpoint_server),
1362                 sizeof(struct dcesrv_interface),
1363                 sizeof(struct dcesrv_if_list),
1364                 sizeof(struct dcesrv_connection),
1365                 sizeof(struct dcesrv_call_state),
1366                 sizeof(struct dcesrv_auth),
1367                 sizeof(struct dcesrv_handle)
1368         };
1369
1370         return &critical_sizes;
1371 }
1372
1373 /*
1374   open the dcerpc server sockets
1375 */
1376 static void dcesrv_task_init(struct task_server *task)
1377 {
1378         NTSTATUS status;
1379         struct dcesrv_context *dce_ctx;
1380         struct dcesrv_endpoint *e;
1381
1382         task_server_set_title(task, "task[dcesrv]");
1383
1384         status = dcesrv_init_context(task->event_ctx,
1385                                      lp_dcerpc_endpoint_servers(),
1386                                      &dce_ctx);
1387         if (!NT_STATUS_IS_OK(status)) goto failed;
1388
1389         /* Make sure the directory for NCALRPC exists */
1390         if (!directory_exist(lp_ncalrpc_dir())) {
1391                 mkdir(lp_ncalrpc_dir(), 0755);
1392         }
1393
1394         for (e=dce_ctx->endpoint_list;e;e=e->next) {
1395                 switch (e->ep_description->transport) {
1396                 case NCACN_UNIX_STREAM:
1397                         status = dcesrv_add_ep_unix(dce_ctx, e, task->event_ctx, task->model_ops);
1398                         if (!NT_STATUS_IS_OK(status)) goto failed;
1399                         break;
1400                 
1401                 case NCALRPC:
1402                         status = dcesrv_add_ep_ncalrpc(dce_ctx, e, task->event_ctx, task->model_ops);
1403                         if (!NT_STATUS_IS_OK(status)) goto failed;
1404                         break;
1405
1406                 case NCACN_IP_TCP:
1407                         status = dcesrv_add_ep_tcp(dce_ctx, e, task->event_ctx, task->model_ops);
1408                         if (!NT_STATUS_IS_OK(status)) goto failed;
1409                         break;
1410                         
1411                 case NCACN_NP:
1412                         status = dcesrv_add_ep_np(dce_ctx, e, task->event_ctx, task->model_ops);
1413                         if (!NT_STATUS_IS_OK(status)) goto failed;
1414                         break;
1415
1416                 default:
1417                         status = NT_STATUS_NOT_SUPPORTED;
1418                         if (!NT_STATUS_IS_OK(status)) goto failed;
1419                 }
1420         }
1421
1422         return;
1423 failed:
1424         task_server_terminate(task, "Failed to startup dcerpc server task");    
1425 }
1426
1427 /*
1428   called on startup of the smb server service It's job is to start
1429   listening on all configured sockets
1430 */
1431 static NTSTATUS dcesrv_init(struct event_context *event_context, 
1432                             const struct model_ops *model_ops)
1433 {       
1434         return task_server_startup(event_context, model_ops, dcesrv_task_init);
1435 }
1436
1437 NTSTATUS server_service_rpc_init(void)
1438 {
1439         init_module_fn static_init[] = STATIC_dcerpc_server_MODULES;
1440         init_module_fn *shared_init = load_samba_modules(NULL, "dcerpc_server");
1441
1442         run_init_functions(static_init);
1443         run_init_functions(shared_init);
1444
1445         talloc_free(shared_init);
1446         
1447         return register_server_service("rpc", dcesrv_init);
1448 }