Merge v4.0-test
[tprouty/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 "param/param.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, bool bigendian)
389 {
390         pkt->rpc_vers = 5;
391         pkt->rpc_vers_minor = 0;
392         if (bigendian) {
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, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
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, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &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, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
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, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &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, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
599         pkt.auth_length = 0;
600         pkt.call_id = call->pkt.call_id;
601         pkt.ptype = DCERPC_PKT_BIND_ACK;
602         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
603         pkt.u.bind_ack.max_xmit_frag = 0x2000;
604         pkt.u.bind_ack.max_recv_frag = 0x2000;
605         /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
606         pkt.u.bind_ack.assoc_group_id = 0x12345678;
607         if (iface) {
608                 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
609                 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
610         } else {
611                 pkt.u.bind_ack.secondary_address = "";
612         }
613         pkt.u.bind_ack.num_results = 1;
614         pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
615         if (!pkt.u.bind_ack.ctx_list) {
616                 return NT_STATUS_NO_MEMORY;
617         }
618         pkt.u.bind_ack.ctx_list[0].result = result;
619         pkt.u.bind_ack.ctx_list[0].reason = reason;
620         pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
621         pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
622
623         status = dcesrv_auth_bind_ack(call, &pkt);
624         if (!NT_STATUS_IS_OK(status)) {
625                 return dcesrv_bind_nak(call, 0);
626         }
627
628         if (iface) {
629                 status = iface->bind(call, iface);
630                 if (!NT_STATUS_IS_OK(status)) {
631                         char *uuid_str = GUID_string(call, &uuid);
632                         DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n", 
633                                  uuid_str, if_version, nt_errstr(status)));
634                         talloc_free(uuid_str);
635                         return dcesrv_bind_nak(call, 0);
636                 }
637         }
638
639         rep = talloc(call, struct data_blob_list_item);
640         if (!rep) {
641                 return NT_STATUS_NO_MEMORY;
642         }
643
644         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, 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, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
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         status = dcesrv_auth_alter_ack(call, &pkt);
773         if (!NT_STATUS_IS_OK(status)) {
774                 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
775                     || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
776                     || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
777                     || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
778                         return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
779                 }
780                 return dcesrv_fault(call, 0);
781         }
782
783         rep = talloc(call, struct data_blob_list_item);
784         if (!rep) {
785                 return NT_STATUS_NO_MEMORY;
786         }
787
788         status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
789         if (!NT_STATUS_IS_OK(status)) {
790                 return status;
791         }
792
793         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
794
795         DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
796         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
797
798         return NT_STATUS_OK;
799 }
800
801 /*
802   handle a dcerpc request packet
803 */
804 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
805 {
806         struct ndr_pull *pull;
807         NTSTATUS status;
808         struct dcesrv_connection_context *context;
809
810         /* if authenticated, and the mech we use can't do async replies, don't use them... */
811         if (call->conn->auth_state.gensec_security && 
812             !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
813                 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
814         }
815
816         context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
817         if (context == NULL) {
818                 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
819         }
820
821         pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
822                                   lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
823         NT_STATUS_HAVE_NO_MEMORY(pull);
824
825         pull->flags |= LIBNDR_FLAG_REF_ALLOC;
826
827         call->context   = context;
828         call->ndr_pull  = pull;
829
830         if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
831                 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
832         }
833
834         if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
835                 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
836         }
837
838         /* unravel the NDR for the packet */
839         status = context->iface->ndr_pull(call, call, pull, &call->r);
840         if (!NT_STATUS_IS_OK(status)) {
841                 return dcesrv_fault(call, call->fault_code);
842         }
843
844         if (pull->offset != pull->data_size) {
845                 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n", 
846                          pull->data_size - pull->offset));
847                 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
848         }
849
850         /* call the dispatch function */
851         status = context->iface->dispatch(call, call, call->r);
852         if (!NT_STATUS_IS_OK(status)) {
853                 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
854                          context->iface->name, 
855                          call->pkt.u.request.opnum,
856                          dcerpc_errstr(pull, call->fault_code)));
857                 return dcesrv_fault(call, call->fault_code);
858         }
859
860         /* add the call to the pending list */
861         dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
862
863         if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
864                 return NT_STATUS_OK;
865         }
866
867         return dcesrv_reply(call);
868 }
869
870 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
871 {
872         struct ndr_push *push;
873         NTSTATUS status;
874         DATA_BLOB stub;
875         uint32_t total_length;
876         struct dcesrv_connection_context *context = call->context;
877
878         /* call the reply function */
879         status = context->iface->reply(call, call, call->r);
880         if (!NT_STATUS_IS_OK(status)) {
881                 return dcesrv_fault(call, call->fault_code);
882         }
883
884         /* form the reply NDR */
885         push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
886         NT_STATUS_HAVE_NO_MEMORY(push);
887
888         /* carry over the pointer count to the reply in case we are
889            using full pointer. See NDR specification for full
890            pointers */
891         push->ptr_count = call->ndr_pull->ptr_count;
892
893         if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
894                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
895         }
896
897         status = context->iface->ndr_push(call, call, push, call->r);
898         if (!NT_STATUS_IS_OK(status)) {
899                 return dcesrv_fault(call, call->fault_code);
900         }
901
902         stub = ndr_push_blob(push);
903
904         total_length = stub.length;
905
906         do {
907                 uint32_t length;
908                 struct data_blob_list_item *rep;
909                 struct ncacn_packet pkt;
910
911                 rep = talloc(call, struct data_blob_list_item);
912                 NT_STATUS_HAVE_NO_MEMORY(rep);
913
914                 length = stub.length;
915                 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
916                         /* the 32 is to cope with signing data */
917                         length = call->conn->cli_max_recv_frag - 
918                                 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
919                 }
920
921                 /* form the dcerpc response packet */
922                 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
923                 pkt.auth_length = 0;
924                 pkt.call_id = call->pkt.call_id;
925                 pkt.ptype = DCERPC_PKT_RESPONSE;
926                 pkt.pfc_flags = 0;
927                 if (stub.length == total_length) {
928                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
929                 }
930                 if (length == stub.length) {
931                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
932                 }
933                 pkt.u.response.alloc_hint = stub.length;
934                 pkt.u.response.context_id = call->pkt.u.request.context_id;
935                 pkt.u.response.cancel_count = 0;
936                 pkt.u.response.stub_and_verifier.data = stub.data;
937                 pkt.u.response.stub_and_verifier.length = length;
938
939                 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
940                         return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
941                 }
942
943                 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
944
945                 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
946                 
947                 stub.data += length;
948                 stub.length -= length;
949         } while (stub.length != 0);
950
951         /* move the call from the pending to the finished calls list */
952         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
953
954         if (call->conn->call_list && call->conn->call_list->replies) {
955                 if (call->conn->transport.report_output_data) {
956                         call->conn->transport.report_output_data(call->conn);
957                 }
958         }
959
960         return NT_STATUS_OK;
961 }
962
963 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
964 {
965         if (!conn->transport.get_my_addr) {
966                 return NULL;
967         }
968
969         return conn->transport.get_my_addr(conn, mem_ctx);
970 }
971
972 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
973 {
974         if (!conn->transport.get_peer_addr) {
975                 return NULL;
976         }
977
978         return conn->transport.get_peer_addr(conn, mem_ctx);
979 }
980
981 /*
982   work out if we have a full packet yet
983 */
984 static bool dce_full_packet(const DATA_BLOB *data)
985 {
986         if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
987                 return false;
988         }
989         if (dcerpc_get_frag_length(data) > data->length) {
990                 return false;
991         }
992         return true;
993 }
994
995 /*
996   we might have consumed only part of our input - advance past that part
997 */
998 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
999 {
1000         DATA_BLOB blob;
1001
1002         if (dce_conn->partial_input.length == offset) {
1003                 data_blob_free(&dce_conn->partial_input);
1004                 return;
1005         }
1006
1007         blob = dce_conn->partial_input;
1008         dce_conn->partial_input = data_blob(blob.data + offset,
1009                                             blob.length - offset);
1010         data_blob_free(&blob);
1011 }
1012
1013 /*
1014   remove the call from the right list when freed
1015  */
1016 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1017 {
1018         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1019         return 0;
1020 }
1021
1022 /*
1023   process some input to a dcerpc endpoint server.
1024 */
1025 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1026 {
1027         struct ndr_pull *ndr;
1028         enum ndr_err_code ndr_err;
1029         NTSTATUS status;
1030         struct dcesrv_call_state *call;
1031         DATA_BLOB blob;
1032
1033         call = talloc_zero(dce_conn, struct dcesrv_call_state);
1034         if (!call) {
1035                 talloc_free(dce_conn->partial_input.data);
1036                 return NT_STATUS_NO_MEMORY;
1037         }
1038         call->conn              = dce_conn;
1039         call->event_ctx         = dce_conn->event_ctx;
1040         call->msg_ctx           = dce_conn->msg_ctx;
1041         call->state_flags       = call->conn->state_flags;
1042         call->time              = timeval_current();
1043         call->list              = DCESRV_LIST_NONE;
1044
1045         talloc_set_destructor(call, dcesrv_call_dequeue);
1046
1047         blob = dce_conn->partial_input;
1048         blob.length = dcerpc_get_frag_length(&blob);
1049
1050         ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
1051         if (!ndr) {
1052                 talloc_free(dce_conn->partial_input.data);
1053                 talloc_free(call);
1054                 return NT_STATUS_NO_MEMORY;
1055         }
1056
1057         if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1058                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1059         }
1060
1061         ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1062         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1063                 talloc_free(dce_conn->partial_input.data);
1064                 talloc_free(call);
1065                 return ndr_map_error2ntstatus(ndr_err);
1066         }
1067
1068         /* we have to check the signing here, before combining the
1069            pdus */
1070         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1071             !dcesrv_auth_request(call, &blob)) {
1072                 dce_partial_advance(dce_conn, blob.length);
1073                 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);          
1074         }
1075
1076         dce_partial_advance(dce_conn, blob.length);
1077
1078         /* see if this is a continued packet */
1079         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1080             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1081                 struct dcesrv_call_state *call2 = call;
1082                 uint32_t alloc_size;
1083
1084                 /* we only allow fragmented requests, no other packet types */
1085                 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1086                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1087                 }
1088
1089                 /* this is a continuation of an existing call - find the call then
1090                    tack it on the end */
1091                 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1092                 if (!call) {
1093                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1094                 }
1095
1096                 if (call->pkt.ptype != call2->pkt.ptype) {
1097                         /* trying to play silly buggers are we? */
1098                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1099                 }
1100
1101                 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1102                         call2->pkt.u.request.stub_and_verifier.length;
1103                 if (call->pkt.u.request.alloc_hint > alloc_size) {
1104                         alloc_size = call->pkt.u.request.alloc_hint;
1105                 }
1106
1107                 call->pkt.u.request.stub_and_verifier.data = 
1108                         talloc_realloc(call, 
1109                                        call->pkt.u.request.stub_and_verifier.data, 
1110                                        uint8_t, alloc_size);
1111                 if (!call->pkt.u.request.stub_and_verifier.data) {
1112                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1113                 }
1114                 memcpy(call->pkt.u.request.stub_and_verifier.data +
1115                        call->pkt.u.request.stub_and_verifier.length,
1116                        call2->pkt.u.request.stub_and_verifier.data,
1117                        call2->pkt.u.request.stub_and_verifier.length);
1118                 call->pkt.u.request.stub_and_verifier.length += 
1119                         call2->pkt.u.request.stub_and_verifier.length;
1120
1121                 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1122
1123                 talloc_free(call2);
1124         }
1125
1126         /* this may not be the last pdu in the chain - if its isn't then
1127            just put it on the incoming_fragmented_call_list and wait for the rest */
1128         if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1129             !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1130                 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1131                 return NT_STATUS_OK;
1132         } 
1133         
1134         /* This removes any fragments we may have had stashed away */
1135         dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1136
1137         switch (call->pkt.ptype) {
1138         case DCERPC_PKT_BIND:
1139                 status = dcesrv_bind(call);
1140                 break;
1141         case DCERPC_PKT_AUTH3:
1142                 status = dcesrv_auth3(call);
1143                 break;
1144         case DCERPC_PKT_ALTER:
1145                 status = dcesrv_alter(call);
1146                 break;
1147         case DCERPC_PKT_REQUEST:
1148                 status = dcesrv_request(call);
1149                 break;
1150         default:
1151                 status = NT_STATUS_INVALID_PARAMETER;
1152                 break;
1153         }
1154
1155         /* if we are going to be sending a reply then add
1156            it to the list of pending calls. We add it to the end to keep the call
1157            list in the order we will answer */
1158         if (!NT_STATUS_IS_OK(status)) {
1159                 talloc_free(call);
1160         }
1161
1162         return status;
1163 }
1164
1165
1166 /*
1167   provide some input to a dcerpc endpoint server. This passes data
1168   from a dcerpc client into the server
1169 */
1170 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1171 {
1172         NTSTATUS status;
1173
1174         dce_conn->partial_input.data = talloc_realloc(dce_conn,
1175                                                       dce_conn->partial_input.data,
1176                                                       uint8_t,
1177                                                       dce_conn->partial_input.length + data->length);
1178         if (!dce_conn->partial_input.data) {
1179                 return NT_STATUS_NO_MEMORY;
1180         }
1181         memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1182                data->data, data->length);
1183         dce_conn->partial_input.length += data->length;
1184
1185         while (dce_full_packet(&dce_conn->partial_input)) {
1186                 status = dcesrv_input_process(dce_conn);
1187                 if (!NT_STATUS_IS_OK(status)) {
1188                         return status;
1189                 }
1190         }
1191
1192         return NT_STATUS_OK;
1193 }
1194
1195 /*
1196   retrieve some output from a dcerpc server
1197   The caller supplies a function that will be called to do the
1198   actual output. 
1199
1200   The first argument to write_fn() will be 'private', the second will
1201   be a pointer to a buffer containing the data to be sent and the 3rd
1202   will be a pointer to a size_t variable that will be set to the
1203   number of bytes that are consumed from the output.
1204
1205   from the current fragment
1206 */
1207 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
1208                        void *private_data,
1209                        NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1210 {
1211         NTSTATUS status;
1212         struct dcesrv_call_state *call;
1213         struct data_blob_list_item *rep;
1214         size_t nwritten;
1215
1216         call = dce_conn->call_list;
1217         if (!call || !call->replies) {
1218                 if (dce_conn->pending_call_list) {
1219                         /* TODO: we need to say act async here
1220                          *       as we know we have pending requests
1221                          *       which will be finished at a time
1222                          */
1223                         return NT_STATUS_FOOBAR;
1224                 }
1225                 return NT_STATUS_FOOBAR;
1226         }
1227         rep = call->replies;
1228
1229         status = write_fn(private_data, &rep->blob, &nwritten);
1230         NT_STATUS_IS_ERR_RETURN(status);
1231
1232         rep->blob.length -= nwritten;
1233         rep->blob.data += nwritten;
1234
1235         if (rep->blob.length == 0) {
1236                 /* we're done with this section of the call */
1237                 DLIST_REMOVE(call->replies, rep);
1238         }
1239
1240         if (call->replies == NULL) {
1241                 /* we're done with the whole call */
1242                 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1243                 talloc_free(call);
1244         }
1245
1246         return status;
1247 }
1248
1249 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, 
1250                                       struct loadparm_context *lp_ctx,
1251                                       const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1252 {
1253         NTSTATUS status;
1254         struct dcesrv_context *dce_ctx;
1255         int i;
1256
1257         if (!endpoint_servers) {
1258                 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1259                 return NT_STATUS_INTERNAL_ERROR;
1260         }
1261
1262         dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1263         NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1264         dce_ctx->endpoint_list  = NULL;
1265         dce_ctx->lp_ctx = lp_ctx;
1266
1267         for (i=0;endpoint_servers[i];i++) {
1268                 const struct dcesrv_endpoint_server *ep_server;
1269
1270                 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1271                 if (!ep_server) {
1272                         DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1273                         return NT_STATUS_INTERNAL_ERROR;
1274                 }
1275
1276                 status = ep_server->init_server(dce_ctx, ep_server);
1277                 if (!NT_STATUS_IS_OK(status)) {
1278                         DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1279                                 nt_errstr(status)));
1280                         return status;
1281                 }
1282         }
1283
1284         *_dce_ctx = dce_ctx;
1285         return NT_STATUS_OK;
1286 }
1287
1288 /* the list of currently registered DCERPC endpoint servers.
1289  */
1290 static struct ep_server {
1291         struct dcesrv_endpoint_server *ep_server;
1292 } *ep_servers = NULL;
1293 static int num_ep_servers;
1294
1295 /*
1296   register a DCERPC endpoint server. 
1297
1298   The 'name' can be later used by other backends to find the operations
1299   structure for this backend.  
1300
1301   The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1302 */
1303 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1304 {
1305         const struct dcesrv_endpoint_server *ep_server = _ep_server;
1306         
1307         if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1308                 /* its already registered! */
1309                 DEBUG(0,("DCERPC endpoint server '%s' already registered\n", 
1310                          ep_server->name));
1311                 return NT_STATUS_OBJECT_NAME_COLLISION;
1312         }
1313
1314         ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1315         if (!ep_servers) {
1316                 smb_panic("out of memory in dcerpc_register");
1317         }
1318
1319         ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1320         ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1321
1322         num_ep_servers++;
1323
1324         DEBUG(3,("DCERPC endpoint server '%s' registered\n", 
1325                  ep_server->name));
1326
1327         return NT_STATUS_OK;
1328 }
1329
1330 /*
1331   return the operations structure for a named backend of the specified type
1332 */
1333 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1334 {
1335         int i;
1336
1337         for (i=0;i<num_ep_servers;i++) {
1338                 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1339                         return ep_servers[i].ep_server;
1340                 }
1341         }
1342
1343         return NULL;
1344 }
1345
1346 /*
1347   return the DCERPC module version, and the size of some critical types
1348   This can be used by endpoint server modules to either detect compilation errors, or provide
1349   multiple implementations for different smbd compilation options in one module
1350 */
1351 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1352 {
1353         static const struct dcesrv_critical_sizes critical_sizes = {
1354                 DCERPC_MODULE_VERSION,
1355                 sizeof(struct dcesrv_context),
1356                 sizeof(struct dcesrv_endpoint),
1357                 sizeof(struct dcesrv_endpoint_server),
1358                 sizeof(struct dcesrv_interface),
1359                 sizeof(struct dcesrv_if_list),
1360                 sizeof(struct dcesrv_connection),
1361                 sizeof(struct dcesrv_call_state),
1362                 sizeof(struct dcesrv_auth),
1363                 sizeof(struct dcesrv_handle)
1364         };
1365
1366         return &critical_sizes;
1367 }
1368
1369 /*
1370   initialise the dcerpc server context for ncacn_np based services
1371 */
1372 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1373                                           struct dcesrv_context **_dce_ctx)
1374 {
1375         NTSTATUS status;
1376         struct dcesrv_context *dce_ctx;
1377
1378         status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1379         NT_STATUS_NOT_OK_RETURN(status);
1380
1381         *_dce_ctx = dce_ctx;
1382         return NT_STATUS_OK;
1383 }
1384
1385