Use ntvfs session close to close downstream session
[Samba/vfs_proxy.git] / source4 / rpc_server / dcerpc_server.c
blob2c218bdc8d77922246e46a5e54c43ed8913d4288
1 /*
2 Unix SMB/CIFS implementation.
4 server side dcerpc core code
6 Copyright (C) Andrew Tridgell 2003-2005
7 Copyright (C) Stefan (metze) Metzmacher 2004-2005
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.
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.
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/>.
23 #include "includes.h"
24 #include "librpc/gen_ndr/ndr_dcerpc.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "../lib/util/dlinklist.h"
28 #include "rpc_server/dcerpc_server.h"
29 #include "rpc_server/dcerpc_server_proto.h"
30 #include "librpc/rpc/dcerpc_proto.h"
31 #include "lib/events/events.h"
32 #include "smbd/service_task.h"
33 #include "smbd/service_stream.h"
34 #include "smbd/service.h"
35 #include "system/filesys.h"
36 #include "libcli/security/security.h"
37 #include "param/param.h"
39 #define SAMBA_ASSOC_GROUP 0x12345678
41 extern const struct dcesrv_interface dcesrv_mgmt_interface;
44 see if two endpoints match
46 static bool endpoints_match(const struct dcerpc_binding *ep1,
47 const struct dcerpc_binding *ep2)
49 if (ep1->transport != ep2->transport) {
50 return false;
53 if (!ep1->endpoint || !ep2->endpoint) {
54 return ep1->endpoint == ep2->endpoint;
57 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
58 return false;
60 return true;
64 find an endpoint in the dcesrv_context
66 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
67 const struct dcerpc_binding *ep_description)
69 struct dcesrv_endpoint *ep;
70 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
71 if (endpoints_match(ep->ep_description, ep_description)) {
72 return ep;
75 return NULL;
79 find a registered context_id from a bind or alter_context
81 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
82 uint32_t context_id)
84 struct dcesrv_connection_context *c;
85 for (c=conn->contexts;c;c=c->next) {
86 if (c->context_id == context_id) return c;
88 return NULL;
92 see if a uuid and if_version match to an interface
94 static bool interface_match(const struct dcesrv_interface *if1,
95 const struct dcesrv_interface *if2)
97 return (if1->syntax_id.if_version == if2->syntax_id.if_version &&
98 GUID_equal(&if1->syntax_id.uuid, &if2->syntax_id.uuid));
102 find the interface operations on an endpoint
104 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
105 const struct dcesrv_interface *iface)
107 struct dcesrv_if_list *ifl;
108 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
109 if (interface_match(&(ifl->iface), iface)) {
110 return &(ifl->iface);
113 return NULL;
117 see if a uuid and if_version match to an interface
119 static bool interface_match_by_uuid(const struct dcesrv_interface *iface,
120 const struct GUID *uuid, uint32_t if_version)
122 return (iface->syntax_id.if_version == if_version &&
123 GUID_equal(&iface->syntax_id.uuid, uuid));
127 find the interface operations on an endpoint by uuid
129 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
130 const struct GUID *uuid, uint32_t if_version)
132 struct dcesrv_if_list *ifl;
133 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
134 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
135 return &(ifl->iface);
138 return NULL;
142 find the earlier parts of a fragmented call awaiting reassembily
144 static struct dcesrv_call_state *dcesrv_find_fragmented_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
146 struct dcesrv_call_state *c;
147 for (c=dce_conn->incoming_fragmented_call_list;c;c=c->next) {
148 if (c->pkt.call_id == call_id) {
149 return c;
152 return NULL;
156 register an interface on an endpoint
158 _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
159 const char *ep_name,
160 const struct dcesrv_interface *iface,
161 const struct security_descriptor *sd)
163 struct dcesrv_endpoint *ep;
164 struct dcesrv_if_list *ifl;
165 struct dcerpc_binding *binding;
166 bool add_ep = false;
167 NTSTATUS status;
169 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
171 if (NT_STATUS_IS_ERR(status)) {
172 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
173 return status;
176 /* check if this endpoint exists
178 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
179 ep = talloc(dce_ctx, struct dcesrv_endpoint);
180 if (!ep) {
181 return NT_STATUS_NO_MEMORY;
183 ZERO_STRUCTP(ep);
184 ep->ep_description = talloc_reference(ep, binding);
185 add_ep = true;
187 /* add mgmt interface */
188 ifl = talloc(dce_ctx, struct dcesrv_if_list);
189 if (!ifl) {
190 return NT_STATUS_NO_MEMORY;
193 memcpy(&(ifl->iface), &dcesrv_mgmt_interface,
194 sizeof(struct dcesrv_interface));
196 DLIST_ADD(ep->interface_list, ifl);
199 /* see if the interface is already registered on te endpoint */
200 if (find_interface(ep, iface)!=NULL) {
201 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
202 iface->name, ep_name));
203 return NT_STATUS_OBJECT_NAME_COLLISION;
206 /* talloc a new interface list element */
207 ifl = talloc(dce_ctx, struct dcesrv_if_list);
208 if (!ifl) {
209 return NT_STATUS_NO_MEMORY;
212 /* copy the given interface struct to the one on the endpoints interface list */
213 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
215 /* if we have a security descriptor given,
216 * we should see if we can set it up on the endpoint
218 if (sd != NULL) {
219 /* if there's currently no security descriptor given on the endpoint
220 * we try to set it
222 if (ep->sd == NULL) {
223 ep->sd = security_descriptor_copy(dce_ctx, sd);
226 /* if now there's no security descriptor given on the endpoint
227 * something goes wrong, either we failed to copy the security descriptor
228 * or there was already one on the endpoint
230 if (ep->sd != NULL) {
231 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
232 " on endpoint '%s'\n",
233 iface->name, ep_name));
234 if (add_ep) free(ep);
235 free(ifl);
236 return NT_STATUS_OBJECT_NAME_COLLISION;
240 /* finally add the interface on the endpoint */
241 DLIST_ADD(ep->interface_list, ifl);
243 /* if it's a new endpoint add it to the dcesrv_context */
244 if (add_ep) {
245 DLIST_ADD(dce_ctx->endpoint_list, ep);
248 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
249 iface->name, ep_name));
251 return NT_STATUS_OK;
254 NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
255 DATA_BLOB *session_key)
257 if (p->auth_state.session_info->session_key.length) {
258 *session_key = p->auth_state.session_info->session_key;
259 return NT_STATUS_OK;
261 return NT_STATUS_NO_USER_SESSION_KEY;
264 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
265 DATA_BLOB *session_key)
267 /* this took quite a few CPU cycles to find ... */
268 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
269 session_key->length = 16;
270 return NT_STATUS_OK;
274 fetch the user session key - may be default (above) or the SMB session key
276 The key is always truncated to 16 bytes
278 _PUBLIC_ NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
279 DATA_BLOB *session_key)
281 NTSTATUS status = p->auth_state.session_key(p, session_key);
282 if (!NT_STATUS_IS_OK(status)) {
283 return status;
286 session_key->length = MIN(session_key->length, 16);
288 return NT_STATUS_OK;
292 connect to a dcerpc endpoint
294 _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
295 TALLOC_CTX *mem_ctx,
296 const struct dcesrv_endpoint *ep,
297 struct auth_session_info *session_info,
298 struct event_context *event_ctx,
299 struct messaging_context *msg_ctx,
300 struct server_id server_id,
301 uint32_t state_flags,
302 struct dcesrv_connection **_p)
304 struct dcesrv_connection *p;
306 if (!session_info) {
307 return NT_STATUS_ACCESS_DENIED;
310 p = talloc(mem_ctx, struct dcesrv_connection);
311 NT_STATUS_HAVE_NO_MEMORY(p);
313 if (!talloc_reference(p, session_info)) {
314 talloc_free(p);
315 return NT_STATUS_NO_MEMORY;
318 p->dce_ctx = dce_ctx;
319 p->endpoint = ep;
320 p->contexts = NULL;
321 p->call_list = NULL;
322 p->packet_log_dir = lp_lockdir(dce_ctx->lp_ctx);
323 p->incoming_fragmented_call_list = NULL;
324 p->pending_call_list = NULL;
325 p->cli_max_recv_frag = 0;
326 p->partial_input = data_blob(NULL, 0);
327 p->auth_state.auth_info = NULL;
328 p->auth_state.gensec_security = NULL;
329 p->auth_state.session_info = session_info;
330 p->auth_state.session_key = dcesrv_generic_session_key;
331 p->event_ctx = event_ctx;
332 p->msg_ctx = msg_ctx;
333 p->server_id = server_id;
334 p->processing = false;
335 p->state_flags = state_flags;
336 ZERO_STRUCT(p->transport);
338 *_p = p;
339 return NT_STATUS_OK;
343 search and connect to a dcerpc endpoint
345 _PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
346 TALLOC_CTX *mem_ctx,
347 const struct dcerpc_binding *ep_description,
348 struct auth_session_info *session_info,
349 struct event_context *event_ctx,
350 struct messaging_context *msg_ctx,
351 struct server_id server_id,
352 uint32_t state_flags,
353 struct dcesrv_connection **dce_conn_p)
355 NTSTATUS status;
356 const struct dcesrv_endpoint *ep;
358 /* make sure this endpoint exists */
359 ep = find_endpoint(dce_ctx, ep_description);
360 if (!ep) {
361 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
364 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
365 event_ctx, msg_ctx, server_id,
366 state_flags, dce_conn_p);
367 NT_STATUS_NOT_OK_RETURN(status);
369 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
371 /* TODO: check security descriptor of the endpoint here
372 * if it's a smb named pipe
373 * if it's failed free dce_conn_p
376 return NT_STATUS_OK;
380 static void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
382 pkt->rpc_vers = 5;
383 pkt->rpc_vers_minor = 0;
384 if (bigendian) {
385 pkt->drep[0] = 0;
386 } else {
387 pkt->drep[0] = DCERPC_DREP_LE;
389 pkt->drep[1] = 0;
390 pkt->drep[2] = 0;
391 pkt->drep[3] = 0;
395 move a call from an existing linked list to the specified list. This
396 prevents bugs where we forget to remove the call from a previous
397 list when moving it.
399 static void dcesrv_call_set_list(struct dcesrv_call_state *call,
400 enum dcesrv_call_list list)
402 switch (call->list) {
403 case DCESRV_LIST_NONE:
404 break;
405 case DCESRV_LIST_CALL_LIST:
406 DLIST_REMOVE(call->conn->call_list, call);
407 break;
408 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
409 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
410 break;
411 case DCESRV_LIST_PENDING_CALL_LIST:
412 DLIST_REMOVE(call->conn->pending_call_list, call);
413 break;
415 call->list = list;
416 switch (list) {
417 case DCESRV_LIST_NONE:
418 break;
419 case DCESRV_LIST_CALL_LIST:
420 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
421 break;
422 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
423 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
424 break;
425 case DCESRV_LIST_PENDING_CALL_LIST:
426 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
427 break;
432 return a dcerpc fault
434 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
436 struct ncacn_packet pkt;
437 struct data_blob_list_item *rep;
438 uint8_t zeros[4];
439 NTSTATUS status;
441 /* setup a bind_ack */
442 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
443 pkt.auth_length = 0;
444 pkt.call_id = call->pkt.call_id;
445 pkt.ptype = DCERPC_PKT_FAULT;
446 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
447 pkt.u.fault.alloc_hint = 0;
448 pkt.u.fault.context_id = 0;
449 pkt.u.fault.cancel_count = 0;
450 pkt.u.fault.status = fault_code;
452 ZERO_STRUCT(zeros);
453 pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
455 rep = talloc(call, struct data_blob_list_item);
456 if (!rep) {
457 return NT_STATUS_NO_MEMORY;
460 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
461 if (!NT_STATUS_IS_OK(status)) {
462 return status;
465 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
467 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
468 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
470 return NT_STATUS_OK;
475 return a dcerpc bind_nak
477 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
479 struct ncacn_packet pkt;
480 struct data_blob_list_item *rep;
481 NTSTATUS status;
483 /* setup a bind_nak */
484 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
485 pkt.auth_length = 0;
486 pkt.call_id = call->pkt.call_id;
487 pkt.ptype = DCERPC_PKT_BIND_NAK;
488 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
489 pkt.u.bind_nak.reject_reason = reason;
490 if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
491 pkt.u.bind_nak.versions.v.num_versions = 0;
494 rep = talloc(call, struct data_blob_list_item);
495 if (!rep) {
496 return NT_STATUS_NO_MEMORY;
499 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
500 if (!NT_STATUS_IS_OK(status)) {
501 return status;
504 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
506 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
507 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
509 return NT_STATUS_OK;
512 static int dcesrv_connection_context_destructor(struct dcesrv_connection_context *c)
514 DLIST_REMOVE(c->conn->contexts, c);
516 if (c->iface) {
517 c->iface->unbind(c, c->iface);
520 return 0;
524 handle a bind request
526 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
528 uint32_t if_version, transfer_syntax_version;
529 struct GUID uuid, *transfer_syntax_uuid;
530 struct ncacn_packet pkt;
531 struct data_blob_list_item *rep;
532 NTSTATUS status;
533 uint32_t result=0, reason=0;
534 uint32_t context_id;
535 const struct dcesrv_interface *iface;
536 uint32_t extra_flags = 0;
539 * Association groups allow policy handles to be shared across
540 * multiple client connections. We don't implement this yet.
542 * So we just allow 0 if the client wants to create a new
543 * association group.
545 * And we allow the 0x12345678 value, we give away as
546 * assoc_group_id back to the clients
548 if (call->pkt.u.bind.assoc_group_id != 0 &&
549 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
550 call->pkt.u.bind.assoc_group_id != SAMBA_ASSOC_GROUP) {
551 return dcesrv_bind_nak(call, 0);
554 if (call->pkt.u.bind.num_contexts < 1 ||
555 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
556 return dcesrv_bind_nak(call, 0);
559 context_id = call->pkt.u.bind.ctx_list[0].context_id;
561 /* you can't bind twice on one context */
562 if (dcesrv_find_context(call->conn, context_id) != NULL) {
563 return dcesrv_bind_nak(call, 0);
566 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
567 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
569 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
570 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
571 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
572 ndr_transfer_syntax.if_version != transfer_syntax_version) {
573 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
574 /* we only do NDR encoded dcerpc */
575 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
576 talloc_free(uuid_str);
577 return dcesrv_bind_nak(call, 0);
580 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
581 if (iface == NULL) {
582 char *uuid_str = GUID_string(call, &uuid);
583 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
584 talloc_free(uuid_str);
586 /* we don't know about that interface */
587 result = DCERPC_BIND_PROVIDER_REJECT;
588 reason = DCERPC_BIND_REASON_ASYNTAX;
591 if (iface) {
592 /* add this context to the list of available context_ids */
593 struct dcesrv_connection_context *context = talloc(call->conn,
594 struct dcesrv_connection_context);
595 if (context == NULL) {
596 return dcesrv_bind_nak(call, 0);
598 context->conn = call->conn;
599 context->iface = iface;
600 context->context_id = context_id;
602 * we need to send a non zero assoc_group_id here to make longhorn happy,
603 * it also matches samba3
605 context->assoc_group_id = SAMBA_ASSOC_GROUP;
606 context->private = NULL;
607 context->handles = NULL;
608 DLIST_ADD(call->conn->contexts, context);
609 call->context = context;
610 talloc_set_destructor(context, dcesrv_connection_context_destructor);
612 status = iface->bind(call, iface);
613 if (!NT_STATUS_IS_OK(status)) {
614 char *uuid_str = GUID_string(call, &uuid);
615 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
616 uuid_str, if_version, nt_errstr(status)));
617 talloc_free(uuid_str);
618 /* we don't want to trigger the iface->unbind() hook */
619 context->iface = NULL;
620 talloc_free(call->context);
621 call->context = NULL;
622 return dcesrv_bind_nak(call, 0);
626 if (call->conn->cli_max_recv_frag == 0) {
627 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
630 if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
631 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
632 call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
633 extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
636 /* handle any authentication that is being requested */
637 if (!dcesrv_auth_bind(call)) {
638 talloc_free(call->context);
639 call->context = NULL;
640 return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
643 /* setup a bind_ack */
644 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
645 pkt.auth_length = 0;
646 pkt.call_id = call->pkt.call_id;
647 pkt.ptype = DCERPC_PKT_BIND_ACK;
648 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
649 pkt.u.bind_ack.max_xmit_frag = 0x2000;
650 pkt.u.bind_ack.max_recv_frag = 0x2000;
651 pkt.u.bind_ack.assoc_group_id = call->context->assoc_group_id;
652 if (iface) {
653 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
654 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
655 } else {
656 pkt.u.bind_ack.secondary_address = "";
658 pkt.u.bind_ack.num_results = 1;
659 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
660 if (!pkt.u.bind_ack.ctx_list) {
661 talloc_free(call->context);
662 call->context = NULL;
663 return NT_STATUS_NO_MEMORY;
665 pkt.u.bind_ack.ctx_list[0].result = result;
666 pkt.u.bind_ack.ctx_list[0].reason = reason;
667 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
668 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
670 status = dcesrv_auth_bind_ack(call, &pkt);
671 if (!NT_STATUS_IS_OK(status)) {
672 talloc_free(call->context);
673 call->context = NULL;
674 return dcesrv_bind_nak(call, 0);
677 rep = talloc(call, struct data_blob_list_item);
678 if (!rep) {
679 talloc_free(call->context);
680 call->context = NULL;
681 return NT_STATUS_NO_MEMORY;
684 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
685 if (!NT_STATUS_IS_OK(status)) {
686 talloc_free(call->context);
687 call->context = NULL;
688 return status;
691 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
693 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
694 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
696 return NT_STATUS_OK;
701 handle a auth3 request
703 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
705 /* handle the auth3 in the auth code */
706 if (!dcesrv_auth_auth3(call)) {
707 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
710 talloc_free(call);
712 /* we don't send a reply to a auth3 request, except by a
713 fault */
714 return NT_STATUS_OK;
719 handle a bind request
721 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
723 uint32_t if_version, transfer_syntax_version;
724 struct dcesrv_connection_context *context;
725 const struct dcesrv_interface *iface;
726 struct GUID uuid, *transfer_syntax_uuid;
727 NTSTATUS status;
729 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
730 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
732 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
733 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
734 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
735 ndr_transfer_syntax.if_version != transfer_syntax_version) {
736 /* we only do NDR encoded dcerpc */
737 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
740 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
741 if (iface == NULL) {
742 char *uuid_str = GUID_string(call, &uuid);
743 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
744 talloc_free(uuid_str);
745 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
748 /* add this context to the list of available context_ids */
749 context = talloc(call->conn, struct dcesrv_connection_context);
750 if (context == NULL) {
751 return NT_STATUS_NO_MEMORY;
753 context->conn = call->conn;
754 context->iface = iface;
755 context->context_id = context_id;
756 context->assoc_group_id = SAMBA_ASSOC_GROUP;
757 context->private = NULL;
758 context->handles = NULL;
759 DLIST_ADD(call->conn->contexts, context);
760 call->context = context;
761 talloc_set_destructor(context, dcesrv_connection_context_destructor);
763 status = iface->bind(call, iface);
764 if (!NT_STATUS_IS_OK(status)) {
765 /* we don't want to trigger the iface->unbind() hook */
766 context->iface = NULL;
767 talloc_free(context);
768 call->context = NULL;
769 return status;
772 return NT_STATUS_OK;
777 handle a alter context request
779 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
781 struct ncacn_packet pkt;
782 struct data_blob_list_item *rep;
783 NTSTATUS status;
784 uint32_t result=0, reason=0;
785 uint32_t context_id;
787 /* handle any authentication that is being requested */
788 if (!dcesrv_auth_alter(call)) {
789 /* TODO: work out the right reject code */
790 result = DCERPC_BIND_PROVIDER_REJECT;
791 reason = DCERPC_BIND_REASON_ASYNTAX;
794 context_id = call->pkt.u.alter.ctx_list[0].context_id;
796 /* see if they are asking for a new interface */
797 if (result == 0) {
798 call->context = dcesrv_find_context(call->conn, context_id);
799 if (!call->context) {
800 status = dcesrv_alter_new_context(call, context_id);
801 if (!NT_STATUS_IS_OK(status)) {
802 result = DCERPC_BIND_PROVIDER_REJECT;
803 reason = DCERPC_BIND_REASON_ASYNTAX;
808 if (result == 0 &&
809 call->pkt.u.alter.assoc_group_id != 0 &&
810 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
811 call->pkt.u.alter.assoc_group_id != call->context->assoc_group_id) {
812 /* TODO: work out what to return here */
813 result = DCERPC_BIND_PROVIDER_REJECT;
814 reason = DCERPC_BIND_REASON_ASYNTAX;
817 /* setup a alter_resp */
818 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
819 pkt.auth_length = 0;
820 pkt.call_id = call->pkt.call_id;
821 pkt.ptype = DCERPC_PKT_ALTER_RESP;
822 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
823 pkt.u.alter_resp.max_xmit_frag = 0x2000;
824 pkt.u.alter_resp.max_recv_frag = 0x2000;
825 if (result == 0) {
826 pkt.u.alter_resp.assoc_group_id = call->context->assoc_group_id;
827 } else {
828 pkt.u.alter_resp.assoc_group_id = 0;
830 pkt.u.alter_resp.num_results = 1;
831 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
832 if (!pkt.u.alter_resp.ctx_list) {
833 return NT_STATUS_NO_MEMORY;
835 pkt.u.alter_resp.ctx_list[0].result = result;
836 pkt.u.alter_resp.ctx_list[0].reason = reason;
837 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
838 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
839 pkt.u.alter_resp.secondary_address = "";
841 status = dcesrv_auth_alter_ack(call, &pkt);
842 if (!NT_STATUS_IS_OK(status)) {
843 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
844 || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
845 || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
846 || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
847 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
849 return dcesrv_fault(call, 0);
852 rep = talloc(call, struct data_blob_list_item);
853 if (!rep) {
854 return NT_STATUS_NO_MEMORY;
857 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
858 if (!NT_STATUS_IS_OK(status)) {
859 return status;
862 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
864 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
865 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
867 return NT_STATUS_OK;
871 handle a dcerpc request packet
873 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
875 struct ndr_pull *pull;
876 NTSTATUS status;
877 struct dcesrv_connection_context *context;
879 /* if authenticated, and the mech we use can't do async replies, don't use them... */
880 if (call->conn->auth_state.gensec_security &&
881 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
882 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
885 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
886 if (context == NULL) {
887 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
890 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
891 lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
892 NT_STATUS_HAVE_NO_MEMORY(pull);
894 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
896 call->context = context;
897 call->ndr_pull = pull;
899 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
900 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
903 /* unravel the NDR for the packet */
904 status = context->iface->ndr_pull(call, call, pull, &call->r);
905 if (!NT_STATUS_IS_OK(status)) {
906 return dcesrv_fault(call, call->fault_code);
909 if (pull->offset != pull->data_size) {
910 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
911 pull->data_size - pull->offset));
912 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
915 /* call the dispatch function */
916 status = context->iface->dispatch(call, call, call->r);
917 if (!NT_STATUS_IS_OK(status)) {
918 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
919 context->iface->name,
920 call->pkt.u.request.opnum,
921 dcerpc_errstr(pull, call->fault_code)));
922 return dcesrv_fault(call, call->fault_code);
925 /* add the call to the pending list */
926 dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
928 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
929 return NT_STATUS_OK;
932 return dcesrv_reply(call);
935 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
937 struct ndr_push *push;
938 NTSTATUS status;
939 DATA_BLOB stub;
940 uint32_t total_length, chunk_size;
941 struct dcesrv_connection_context *context = call->context;
942 size_t sig_size = 0;
944 /* call the reply function */
945 status = context->iface->reply(call, call, call->r);
946 if (!NT_STATUS_IS_OK(status)) {
947 return dcesrv_fault(call, call->fault_code);
950 /* form the reply NDR */
951 push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
952 NT_STATUS_HAVE_NO_MEMORY(push);
954 /* carry over the pointer count to the reply in case we are
955 using full pointer. See NDR specification for full
956 pointers */
957 push->ptr_count = call->ndr_pull->ptr_count;
959 if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
960 push->flags |= LIBNDR_FLAG_BIGENDIAN;
963 status = context->iface->ndr_push(call, call, push, call->r);
964 if (!NT_STATUS_IS_OK(status)) {
965 return dcesrv_fault(call, call->fault_code);
968 stub = ndr_push_blob(push);
970 total_length = stub.length;
972 /* we can write a full max_recv_frag size, minus the dcerpc
973 request header size */
974 chunk_size = call->conn->cli_max_recv_frag;
975 chunk_size -= DCERPC_REQUEST_LENGTH;
976 if (call->conn->auth_state.auth_info &&
977 call->conn->auth_state.gensec_security) {
978 sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
979 call->conn->cli_max_recv_frag);
980 if (sig_size) {
981 chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
982 chunk_size -= sig_size;
985 chunk_size -= (chunk_size % 16);
987 do {
988 uint32_t length;
989 struct data_blob_list_item *rep;
990 struct ncacn_packet pkt;
992 rep = talloc(call, struct data_blob_list_item);
993 NT_STATUS_HAVE_NO_MEMORY(rep);
995 length = MIN(chunk_size, stub.length);
997 /* form the dcerpc response packet */
998 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
999 pkt.auth_length = 0;
1000 pkt.call_id = call->pkt.call_id;
1001 pkt.ptype = DCERPC_PKT_RESPONSE;
1002 pkt.pfc_flags = 0;
1003 if (stub.length == total_length) {
1004 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
1006 if (length == stub.length) {
1007 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
1009 pkt.u.response.alloc_hint = stub.length;
1010 pkt.u.response.context_id = call->pkt.u.request.context_id;
1011 pkt.u.response.cancel_count = 0;
1012 pkt.u.response.stub_and_verifier.data = stub.data;
1013 pkt.u.response.stub_and_verifier.length = length;
1015 if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
1016 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
1019 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
1021 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
1023 stub.data += length;
1024 stub.length -= length;
1025 } while (stub.length != 0);
1027 /* move the call from the pending to the finished calls list */
1028 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
1030 if (call->conn->call_list && call->conn->call_list->replies) {
1031 if (call->conn->transport.report_output_data) {
1032 call->conn->transport.report_output_data(call->conn);
1036 return NT_STATUS_OK;
1039 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1041 if (!conn->transport.get_my_addr) {
1042 return NULL;
1045 return conn->transport.get_my_addr(conn, mem_ctx);
1048 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1050 if (!conn->transport.get_peer_addr) {
1051 return NULL;
1054 return conn->transport.get_peer_addr(conn, mem_ctx);
1058 work out if we have a full packet yet
1060 static bool dce_full_packet(const DATA_BLOB *data)
1062 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
1063 return false;
1065 if (dcerpc_get_frag_length(data) > data->length) {
1066 return false;
1068 return true;
1072 we might have consumed only part of our input - advance past that part
1074 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1076 DATA_BLOB blob;
1078 if (dce_conn->partial_input.length == offset) {
1079 data_blob_free(&dce_conn->partial_input);
1080 return;
1083 blob = dce_conn->partial_input;
1084 dce_conn->partial_input = data_blob(blob.data + offset,
1085 blob.length - offset);
1086 data_blob_free(&blob);
1090 remove the call from the right list when freed
1092 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1094 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1095 return 0;
1099 process some input to a dcerpc endpoint server.
1101 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1103 struct ndr_pull *ndr;
1104 enum ndr_err_code ndr_err;
1105 NTSTATUS status;
1106 struct dcesrv_call_state *call;
1107 DATA_BLOB blob;
1109 call = talloc_zero(dce_conn, struct dcesrv_call_state);
1110 if (!call) {
1111 talloc_free(dce_conn->partial_input.data);
1112 return NT_STATUS_NO_MEMORY;
1114 call->conn = dce_conn;
1115 call->event_ctx = dce_conn->event_ctx;
1116 call->msg_ctx = dce_conn->msg_ctx;
1117 call->state_flags = call->conn->state_flags;
1118 call->time = timeval_current();
1119 call->list = DCESRV_LIST_NONE;
1121 talloc_set_destructor(call, dcesrv_call_dequeue);
1123 blob = dce_conn->partial_input;
1124 blob.length = dcerpc_get_frag_length(&blob);
1126 ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
1127 if (!ndr) {
1128 talloc_free(dce_conn->partial_input.data);
1129 talloc_free(call);
1130 return NT_STATUS_NO_MEMORY;
1133 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1134 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1137 if (CVAL(blob.data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
1138 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
1141 ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1142 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1143 talloc_free(dce_conn->partial_input.data);
1144 talloc_free(call);
1145 return ndr_map_error2ntstatus(ndr_err);
1148 /* we have to check the signing here, before combining the
1149 pdus */
1150 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1151 !dcesrv_auth_request(call, &blob)) {
1152 dce_partial_advance(dce_conn, blob.length);
1153 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
1156 dce_partial_advance(dce_conn, blob.length);
1158 /* see if this is a continued packet */
1159 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1160 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1161 struct dcesrv_call_state *call2 = call;
1162 uint32_t alloc_size;
1164 /* we only allow fragmented requests, no other packet types */
1165 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1166 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1169 /* this is a continuation of an existing call - find the call then
1170 tack it on the end */
1171 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1172 if (!call) {
1173 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1176 if (call->pkt.ptype != call2->pkt.ptype) {
1177 /* trying to play silly buggers are we? */
1178 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1181 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1182 call2->pkt.u.request.stub_and_verifier.length;
1183 if (call->pkt.u.request.alloc_hint > alloc_size) {
1184 alloc_size = call->pkt.u.request.alloc_hint;
1187 call->pkt.u.request.stub_and_verifier.data =
1188 talloc_realloc(call,
1189 call->pkt.u.request.stub_and_verifier.data,
1190 uint8_t, alloc_size);
1191 if (!call->pkt.u.request.stub_and_verifier.data) {
1192 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1194 memcpy(call->pkt.u.request.stub_and_verifier.data +
1195 call->pkt.u.request.stub_and_verifier.length,
1196 call2->pkt.u.request.stub_and_verifier.data,
1197 call2->pkt.u.request.stub_and_verifier.length);
1198 call->pkt.u.request.stub_and_verifier.length +=
1199 call2->pkt.u.request.stub_and_verifier.length;
1201 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1203 talloc_free(call2);
1206 /* this may not be the last pdu in the chain - if its isn't then
1207 just put it on the incoming_fragmented_call_list and wait for the rest */
1208 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1209 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1210 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1211 return NT_STATUS_OK;
1214 /* This removes any fragments we may have had stashed away */
1215 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1217 switch (call->pkt.ptype) {
1218 case DCERPC_PKT_BIND:
1219 status = dcesrv_bind(call);
1220 break;
1221 case DCERPC_PKT_AUTH3:
1222 status = dcesrv_auth3(call);
1223 break;
1224 case DCERPC_PKT_ALTER:
1225 status = dcesrv_alter(call);
1226 break;
1227 case DCERPC_PKT_REQUEST:
1228 status = dcesrv_request(call);
1229 break;
1230 default:
1231 status = NT_STATUS_INVALID_PARAMETER;
1232 break;
1235 /* if we are going to be sending a reply then add
1236 it to the list of pending calls. We add it to the end to keep the call
1237 list in the order we will answer */
1238 if (!NT_STATUS_IS_OK(status)) {
1239 talloc_free(call);
1242 return status;
1247 provide some input to a dcerpc endpoint server. This passes data
1248 from a dcerpc client into the server
1250 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1252 NTSTATUS status;
1254 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1255 dce_conn->partial_input.data,
1256 uint8_t,
1257 dce_conn->partial_input.length + data->length);
1258 if (!dce_conn->partial_input.data) {
1259 return NT_STATUS_NO_MEMORY;
1261 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1262 data->data, data->length);
1263 dce_conn->partial_input.length += data->length;
1265 while (dce_full_packet(&dce_conn->partial_input)) {
1266 status = dcesrv_input_process(dce_conn);
1267 if (!NT_STATUS_IS_OK(status)) {
1268 return status;
1272 return NT_STATUS_OK;
1276 retrieve some output from a dcerpc server
1277 The caller supplies a function that will be called to do the
1278 actual output.
1280 The first argument to write_fn() will be 'private', the second will
1281 be a pointer to a buffer containing the data to be sent and the 3rd
1282 will be a pointer to a size_t variable that will be set to the
1283 number of bytes that are consumed from the output.
1285 from the current fragment
1287 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1288 void *private_data,
1289 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1291 NTSTATUS status;
1292 struct dcesrv_call_state *call;
1293 struct data_blob_list_item *rep;
1294 size_t nwritten;
1296 call = dce_conn->call_list;
1297 if (!call || !call->replies) {
1298 if (dce_conn->pending_call_list) {
1299 /* TODO: we need to say act async here
1300 * as we know we have pending requests
1301 * which will be finished at a time
1303 return NT_STATUS_FOOBAR;
1305 return NT_STATUS_FOOBAR;
1307 rep = call->replies;
1309 status = write_fn(private_data, &rep->blob, &nwritten);
1310 NT_STATUS_IS_ERR_RETURN(status);
1312 rep->blob.length -= nwritten;
1313 rep->blob.data += nwritten;
1315 if (rep->blob.length == 0) {
1316 /* we're done with this section of the call */
1317 DLIST_REMOVE(call->replies, rep);
1320 if (call->replies == NULL) {
1321 /* we're done with the whole call */
1322 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1323 talloc_free(call);
1326 return status;
1329 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
1330 struct loadparm_context *lp_ctx,
1331 const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1333 NTSTATUS status;
1334 struct dcesrv_context *dce_ctx;
1335 int i;
1337 if (!endpoint_servers) {
1338 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1339 return NT_STATUS_INTERNAL_ERROR;
1342 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1343 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1344 dce_ctx->endpoint_list = NULL;
1345 dce_ctx->lp_ctx = lp_ctx;
1347 for (i=0;endpoint_servers[i];i++) {
1348 const struct dcesrv_endpoint_server *ep_server;
1350 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1351 if (!ep_server) {
1352 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1353 return NT_STATUS_INTERNAL_ERROR;
1356 status = ep_server->init_server(dce_ctx, ep_server);
1357 if (!NT_STATUS_IS_OK(status)) {
1358 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1359 nt_errstr(status)));
1360 return status;
1364 *_dce_ctx = dce_ctx;
1365 return NT_STATUS_OK;
1368 /* the list of currently registered DCERPC endpoint servers.
1370 static struct ep_server {
1371 struct dcesrv_endpoint_server *ep_server;
1372 } *ep_servers = NULL;
1373 static int num_ep_servers;
1376 register a DCERPC endpoint server.
1378 The 'name' can be later used by other backends to find the operations
1379 structure for this backend.
1381 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1383 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1385 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1387 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1388 /* its already registered! */
1389 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1390 ep_server->name));
1391 return NT_STATUS_OBJECT_NAME_COLLISION;
1394 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1395 if (!ep_servers) {
1396 smb_panic("out of memory in dcerpc_register");
1399 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1400 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1402 num_ep_servers++;
1404 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1405 ep_server->name));
1407 return NT_STATUS_OK;
1411 return the operations structure for a named backend of the specified type
1413 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1415 int i;
1417 for (i=0;i<num_ep_servers;i++) {
1418 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1419 return ep_servers[i].ep_server;
1423 return NULL;
1426 void dcerpc_server_init(struct loadparm_context *lp_ctx)
1428 static bool initialized;
1429 extern NTSTATUS dcerpc_server_wkssvc_init(void);
1430 extern NTSTATUS dcerpc_server_drsuapi_init(void);
1431 extern NTSTATUS dcerpc_server_winreg_init(void);
1432 extern NTSTATUS dcerpc_server_spoolss_init(void);
1433 extern NTSTATUS dcerpc_server_epmapper_init(void);
1434 extern NTSTATUS dcerpc_server_srvsvc_init(void);
1435 extern NTSTATUS dcerpc_server_netlogon_init(void);
1436 extern NTSTATUS dcerpc_server_rpcecho_init(void);
1437 extern NTSTATUS dcerpc_server_unixinfo_init(void);
1438 extern NTSTATUS dcerpc_server_samr_init(void);
1439 extern NTSTATUS dcerpc_server_remote_init(void);
1440 extern NTSTATUS dcerpc_server_lsa_init(void);
1441 extern NTSTATUS dcerpc_server_browser_init(void);
1442 init_module_fn static_init[] = { STATIC_dcerpc_server_MODULES };
1443 init_module_fn *shared_init;
1445 if (initialized) {
1446 return;
1448 initialized = true;
1450 shared_init = load_samba_modules(NULL, lp_ctx, "dcerpc_server");
1452 run_init_functions(static_init);
1453 run_init_functions(shared_init);
1455 talloc_free(shared_init);
1459 return the DCERPC module version, and the size of some critical types
1460 This can be used by endpoint server modules to either detect compilation errors, or provide
1461 multiple implementations for different smbd compilation options in one module
1463 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1465 static const struct dcesrv_critical_sizes critical_sizes = {
1466 DCERPC_MODULE_VERSION,
1467 sizeof(struct dcesrv_context),
1468 sizeof(struct dcesrv_endpoint),
1469 sizeof(struct dcesrv_endpoint_server),
1470 sizeof(struct dcesrv_interface),
1471 sizeof(struct dcesrv_if_list),
1472 sizeof(struct dcesrv_connection),
1473 sizeof(struct dcesrv_call_state),
1474 sizeof(struct dcesrv_auth),
1475 sizeof(struct dcesrv_handle)
1478 return &critical_sizes;
1482 initialise the dcerpc server context for ncacn_np based services
1484 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1485 struct dcesrv_context **_dce_ctx)
1487 NTSTATUS status;
1488 struct dcesrv_context *dce_ctx;
1490 dcerpc_server_init(lp_ctx);
1492 status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1493 NT_STATUS_NOT_OK_RETURN(status);
1495 *_dce_ctx = dce_ctx;
1496 return NT_STATUS_OK;