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