remove needless rpath stuff for default paths as early as possible
[Samba/gebeck_regimport.git] / source4 / rpc_server / dcerpc_server.c
blobd7bd7601f7c9ece8057f83a78668beb6cbbedf88
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 tevent_context *event_ctx,
299 struct messaging_context *msg_ctx,
300 struct server_id server_id,
301 uint32_t state_flags,
302 struct dcesrv_connection **_p)
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 tevent_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_data = 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;
653 make it possible for iface->bind() to specify the assoc_group_id
654 This helps the openchange mapiproxy plugin to work correctly.
656 metze
658 if (call->context) {
659 pkt.u.bind_ack.assoc_group_id = call->context->assoc_group_id;
660 } else {
661 /* we better pick something - this chosen so as to send a non zero assoc_group_id (matching windows), it also matches samba3 */
662 pkt.u.bind_ack.assoc_group_id = SAMBA_ASSOC_GROUP;
665 if (iface) {
666 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
667 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
668 } else {
669 pkt.u.bind_ack.secondary_address = "";
671 pkt.u.bind_ack.num_results = 1;
672 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
673 if (!pkt.u.bind_ack.ctx_list) {
674 talloc_free(call->context);
675 call->context = NULL;
676 return NT_STATUS_NO_MEMORY;
678 pkt.u.bind_ack.ctx_list[0].result = result;
679 pkt.u.bind_ack.ctx_list[0].reason = reason;
680 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
681 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
683 status = dcesrv_auth_bind_ack(call, &pkt);
684 if (!NT_STATUS_IS_OK(status)) {
685 talloc_free(call->context);
686 call->context = NULL;
687 return dcesrv_bind_nak(call, 0);
690 rep = talloc(call, struct data_blob_list_item);
691 if (!rep) {
692 talloc_free(call->context);
693 call->context = NULL;
694 return NT_STATUS_NO_MEMORY;
697 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
698 if (!NT_STATUS_IS_OK(status)) {
699 talloc_free(call->context);
700 call->context = NULL;
701 return status;
704 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
706 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
707 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
709 return NT_STATUS_OK;
714 handle a auth3 request
716 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
718 /* handle the auth3 in the auth code */
719 if (!dcesrv_auth_auth3(call)) {
720 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
723 talloc_free(call);
725 /* we don't send a reply to a auth3 request, except by a
726 fault */
727 return NT_STATUS_OK;
732 handle a bind request
734 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
736 uint32_t if_version, transfer_syntax_version;
737 struct dcesrv_connection_context *context;
738 const struct dcesrv_interface *iface;
739 struct GUID uuid, *transfer_syntax_uuid;
740 NTSTATUS status;
742 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
743 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
745 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
746 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
747 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
748 ndr_transfer_syntax.if_version != transfer_syntax_version) {
749 /* we only do NDR encoded dcerpc */
750 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
753 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
754 if (iface == NULL) {
755 char *uuid_str = GUID_string(call, &uuid);
756 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
757 talloc_free(uuid_str);
758 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
761 /* add this context to the list of available context_ids */
762 context = talloc(call->conn, struct dcesrv_connection_context);
763 if (context == NULL) {
764 return NT_STATUS_NO_MEMORY;
766 context->conn = call->conn;
767 context->iface = iface;
768 context->context_id = context_id;
769 context->assoc_group_id = SAMBA_ASSOC_GROUP;
770 context->private_data = NULL;
771 context->handles = NULL;
772 DLIST_ADD(call->conn->contexts, context);
773 call->context = context;
774 talloc_set_destructor(context, dcesrv_connection_context_destructor);
776 status = iface->bind(call, iface);
777 if (!NT_STATUS_IS_OK(status)) {
778 /* we don't want to trigger the iface->unbind() hook */
779 context->iface = NULL;
780 talloc_free(context);
781 call->context = NULL;
782 return status;
785 return NT_STATUS_OK;
790 handle a alter context request
792 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
794 struct ncacn_packet pkt;
795 struct data_blob_list_item *rep;
796 NTSTATUS status;
797 uint32_t result=0, reason=0;
798 uint32_t context_id;
800 /* handle any authentication that is being requested */
801 if (!dcesrv_auth_alter(call)) {
802 /* TODO: work out the right reject code */
803 result = DCERPC_BIND_PROVIDER_REJECT;
804 reason = DCERPC_BIND_REASON_ASYNTAX;
807 context_id = call->pkt.u.alter.ctx_list[0].context_id;
809 /* see if they are asking for a new interface */
810 if (result == 0) {
811 call->context = dcesrv_find_context(call->conn, context_id);
812 if (!call->context) {
813 status = dcesrv_alter_new_context(call, context_id);
814 if (!NT_STATUS_IS_OK(status)) {
815 result = DCERPC_BIND_PROVIDER_REJECT;
816 reason = DCERPC_BIND_REASON_ASYNTAX;
821 if (result == 0 &&
822 call->pkt.u.alter.assoc_group_id != 0 &&
823 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
824 call->pkt.u.alter.assoc_group_id != call->context->assoc_group_id) {
825 /* TODO: work out what to return here */
826 result = DCERPC_BIND_PROVIDER_REJECT;
827 reason = DCERPC_BIND_REASON_ASYNTAX;
830 /* setup a alter_resp */
831 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
832 pkt.auth_length = 0;
833 pkt.call_id = call->pkt.call_id;
834 pkt.ptype = DCERPC_PKT_ALTER_RESP;
835 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
836 pkt.u.alter_resp.max_xmit_frag = 0x2000;
837 pkt.u.alter_resp.max_recv_frag = 0x2000;
838 if (result == 0) {
839 pkt.u.alter_resp.assoc_group_id = call->context->assoc_group_id;
840 } else {
841 pkt.u.alter_resp.assoc_group_id = 0;
843 pkt.u.alter_resp.num_results = 1;
844 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
845 if (!pkt.u.alter_resp.ctx_list) {
846 return NT_STATUS_NO_MEMORY;
848 pkt.u.alter_resp.ctx_list[0].result = result;
849 pkt.u.alter_resp.ctx_list[0].reason = reason;
850 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
851 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
852 pkt.u.alter_resp.secondary_address = "";
854 status = dcesrv_auth_alter_ack(call, &pkt);
855 if (!NT_STATUS_IS_OK(status)) {
856 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
857 || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
858 || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
859 || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
860 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
862 return dcesrv_fault(call, 0);
865 rep = talloc(call, struct data_blob_list_item);
866 if (!rep) {
867 return NT_STATUS_NO_MEMORY;
870 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
871 if (!NT_STATUS_IS_OK(status)) {
872 return status;
875 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
877 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
878 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
880 return NT_STATUS_OK;
884 handle a dcerpc request packet
886 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
888 struct ndr_pull *pull;
889 NTSTATUS status;
890 struct dcesrv_connection_context *context;
892 /* if authenticated, and the mech we use can't do async replies, don't use them... */
893 if (call->conn->auth_state.gensec_security &&
894 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
895 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
898 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
899 if (context == NULL) {
900 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
903 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
904 lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
905 NT_STATUS_HAVE_NO_MEMORY(pull);
907 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
909 call->context = context;
910 call->ndr_pull = pull;
912 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
913 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
916 /* unravel the NDR for the packet */
917 status = context->iface->ndr_pull(call, call, pull, &call->r);
918 if (!NT_STATUS_IS_OK(status)) {
919 return dcesrv_fault(call, call->fault_code);
922 if (pull->offset != pull->data_size) {
923 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
924 pull->data_size - pull->offset));
925 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
928 /* call the dispatch function */
929 status = context->iface->dispatch(call, call, call->r);
930 if (!NT_STATUS_IS_OK(status)) {
931 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
932 context->iface->name,
933 call->pkt.u.request.opnum,
934 dcerpc_errstr(pull, call->fault_code)));
935 return dcesrv_fault(call, call->fault_code);
938 /* add the call to the pending list */
939 dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
941 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
942 return NT_STATUS_OK;
945 return dcesrv_reply(call);
948 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
950 struct ndr_push *push;
951 NTSTATUS status;
952 DATA_BLOB stub;
953 uint32_t total_length, chunk_size;
954 struct dcesrv_connection_context *context = call->context;
955 size_t sig_size = 0;
957 /* call the reply function */
958 status = context->iface->reply(call, call, call->r);
959 if (!NT_STATUS_IS_OK(status)) {
960 return dcesrv_fault(call, call->fault_code);
963 /* form the reply NDR */
964 push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
965 NT_STATUS_HAVE_NO_MEMORY(push);
967 /* carry over the pointer count to the reply in case we are
968 using full pointer. See NDR specification for full
969 pointers */
970 push->ptr_count = call->ndr_pull->ptr_count;
972 if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
973 push->flags |= LIBNDR_FLAG_BIGENDIAN;
976 status = context->iface->ndr_push(call, call, push, call->r);
977 if (!NT_STATUS_IS_OK(status)) {
978 return dcesrv_fault(call, call->fault_code);
981 stub = ndr_push_blob(push);
983 total_length = stub.length;
985 /* we can write a full max_recv_frag size, minus the dcerpc
986 request header size */
987 chunk_size = call->conn->cli_max_recv_frag;
988 chunk_size -= DCERPC_REQUEST_LENGTH;
989 if (call->conn->auth_state.auth_info &&
990 call->conn->auth_state.gensec_security) {
991 sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
992 call->conn->cli_max_recv_frag);
993 if (sig_size) {
994 chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
995 chunk_size -= sig_size;
998 chunk_size -= (chunk_size % 16);
1000 do {
1001 uint32_t length;
1002 struct data_blob_list_item *rep;
1003 struct ncacn_packet pkt;
1005 rep = talloc(call, struct data_blob_list_item);
1006 NT_STATUS_HAVE_NO_MEMORY(rep);
1008 length = MIN(chunk_size, stub.length);
1010 /* form the dcerpc response packet */
1011 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
1012 pkt.auth_length = 0;
1013 pkt.call_id = call->pkt.call_id;
1014 pkt.ptype = DCERPC_PKT_RESPONSE;
1015 pkt.pfc_flags = 0;
1016 if (stub.length == total_length) {
1017 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
1019 if (length == stub.length) {
1020 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
1022 pkt.u.response.alloc_hint = stub.length;
1023 pkt.u.response.context_id = call->pkt.u.request.context_id;
1024 pkt.u.response.cancel_count = 0;
1025 pkt.u.response.stub_and_verifier.data = stub.data;
1026 pkt.u.response.stub_and_verifier.length = length;
1028 if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
1029 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
1032 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
1034 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
1036 stub.data += length;
1037 stub.length -= length;
1038 } while (stub.length != 0);
1040 /* move the call from the pending to the finished calls list */
1041 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
1043 if (call->conn->call_list && call->conn->call_list->replies) {
1044 if (call->conn->transport.report_output_data) {
1045 call->conn->transport.report_output_data(call->conn);
1049 return NT_STATUS_OK;
1052 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1054 if (!conn->transport.get_my_addr) {
1055 return NULL;
1058 return conn->transport.get_my_addr(conn, mem_ctx);
1061 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1063 if (!conn->transport.get_peer_addr) {
1064 return NULL;
1067 return conn->transport.get_peer_addr(conn, mem_ctx);
1071 work out if we have a full packet yet
1073 static bool dce_full_packet(const DATA_BLOB *data)
1075 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
1076 return false;
1078 if (dcerpc_get_frag_length(data) > data->length) {
1079 return false;
1081 return true;
1085 we might have consumed only part of our input - advance past that part
1087 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1089 DATA_BLOB blob;
1091 if (dce_conn->partial_input.length == offset) {
1092 data_blob_free(&dce_conn->partial_input);
1093 return;
1096 blob = dce_conn->partial_input;
1097 dce_conn->partial_input = data_blob(blob.data + offset,
1098 blob.length - offset);
1099 data_blob_free(&blob);
1103 remove the call from the right list when freed
1105 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1107 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1108 return 0;
1112 process some input to a dcerpc endpoint server.
1114 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1116 struct ndr_pull *ndr;
1117 enum ndr_err_code ndr_err;
1118 NTSTATUS status;
1119 struct dcesrv_call_state *call;
1120 DATA_BLOB blob;
1122 call = talloc_zero(dce_conn, struct dcesrv_call_state);
1123 if (!call) {
1124 talloc_free(dce_conn->partial_input.data);
1125 return NT_STATUS_NO_MEMORY;
1127 call->conn = dce_conn;
1128 call->event_ctx = dce_conn->event_ctx;
1129 call->msg_ctx = dce_conn->msg_ctx;
1130 call->state_flags = call->conn->state_flags;
1131 call->time = timeval_current();
1132 call->list = DCESRV_LIST_NONE;
1134 talloc_set_destructor(call, dcesrv_call_dequeue);
1136 blob = dce_conn->partial_input;
1137 blob.length = dcerpc_get_frag_length(&blob);
1139 ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
1140 if (!ndr) {
1141 talloc_free(dce_conn->partial_input.data);
1142 talloc_free(call);
1143 return NT_STATUS_NO_MEMORY;
1146 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1147 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1150 if (CVAL(blob.data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
1151 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
1154 ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1155 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1156 talloc_free(dce_conn->partial_input.data);
1157 talloc_free(call);
1158 return ndr_map_error2ntstatus(ndr_err);
1161 /* we have to check the signing here, before combining the
1162 pdus */
1163 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1164 !dcesrv_auth_request(call, &blob)) {
1165 dce_partial_advance(dce_conn, blob.length);
1166 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
1169 dce_partial_advance(dce_conn, blob.length);
1171 /* see if this is a continued packet */
1172 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1173 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1174 struct dcesrv_call_state *call2 = call;
1175 uint32_t alloc_size;
1177 /* we only allow fragmented requests, no other packet types */
1178 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1179 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1182 /* this is a continuation of an existing call - find the call then
1183 tack it on the end */
1184 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1185 if (!call) {
1186 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1189 if (call->pkt.ptype != call2->pkt.ptype) {
1190 /* trying to play silly buggers are we? */
1191 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1194 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1195 call2->pkt.u.request.stub_and_verifier.length;
1196 if (call->pkt.u.request.alloc_hint > alloc_size) {
1197 alloc_size = call->pkt.u.request.alloc_hint;
1200 call->pkt.u.request.stub_and_verifier.data =
1201 talloc_realloc(call,
1202 call->pkt.u.request.stub_and_verifier.data,
1203 uint8_t, alloc_size);
1204 if (!call->pkt.u.request.stub_and_verifier.data) {
1205 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1207 memcpy(call->pkt.u.request.stub_and_verifier.data +
1208 call->pkt.u.request.stub_and_verifier.length,
1209 call2->pkt.u.request.stub_and_verifier.data,
1210 call2->pkt.u.request.stub_and_verifier.length);
1211 call->pkt.u.request.stub_and_verifier.length +=
1212 call2->pkt.u.request.stub_and_verifier.length;
1214 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1216 talloc_free(call2);
1219 /* this may not be the last pdu in the chain - if its isn't then
1220 just put it on the incoming_fragmented_call_list and wait for the rest */
1221 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1222 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1223 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1224 return NT_STATUS_OK;
1227 /* This removes any fragments we may have had stashed away */
1228 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1230 switch (call->pkt.ptype) {
1231 case DCERPC_PKT_BIND:
1232 status = dcesrv_bind(call);
1233 break;
1234 case DCERPC_PKT_AUTH3:
1235 status = dcesrv_auth3(call);
1236 break;
1237 case DCERPC_PKT_ALTER:
1238 status = dcesrv_alter(call);
1239 break;
1240 case DCERPC_PKT_REQUEST:
1241 status = dcesrv_request(call);
1242 break;
1243 default:
1244 status = NT_STATUS_INVALID_PARAMETER;
1245 break;
1248 /* if we are going to be sending a reply then add
1249 it to the list of pending calls. We add it to the end to keep the call
1250 list in the order we will answer */
1251 if (!NT_STATUS_IS_OK(status)) {
1252 talloc_free(call);
1255 return status;
1260 provide some input to a dcerpc endpoint server. This passes data
1261 from a dcerpc client into the server
1263 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1265 NTSTATUS status;
1267 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1268 dce_conn->partial_input.data,
1269 uint8_t,
1270 dce_conn->partial_input.length + data->length);
1271 if (!dce_conn->partial_input.data) {
1272 return NT_STATUS_NO_MEMORY;
1274 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1275 data->data, data->length);
1276 dce_conn->partial_input.length += data->length;
1278 while (dce_full_packet(&dce_conn->partial_input)) {
1279 status = dcesrv_input_process(dce_conn);
1280 if (!NT_STATUS_IS_OK(status)) {
1281 return status;
1285 return NT_STATUS_OK;
1289 retrieve some output from a dcerpc server
1290 The caller supplies a function that will be called to do the
1291 actual output.
1293 The first argument to write_fn() will be 'private', the second will
1294 be a pointer to a buffer containing the data to be sent and the 3rd
1295 will be a pointer to a size_t variable that will be set to the
1296 number of bytes that are consumed from the output.
1298 from the current fragment
1300 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1301 void *private_data,
1302 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1304 NTSTATUS status;
1305 struct dcesrv_call_state *call;
1306 struct data_blob_list_item *rep;
1307 size_t nwritten;
1309 call = dce_conn->call_list;
1310 if (!call || !call->replies) {
1311 if (dce_conn->pending_call_list) {
1312 /* TODO: we need to say act async here
1313 * as we know we have pending requests
1314 * which will be finished at a time
1316 return NT_STATUS_FOOBAR;
1318 return NT_STATUS_FOOBAR;
1320 rep = call->replies;
1322 status = write_fn(private_data, &rep->blob, &nwritten);
1323 NT_STATUS_IS_ERR_RETURN(status);
1325 rep->blob.length -= nwritten;
1326 rep->blob.data += nwritten;
1328 if (rep->blob.length == 0) {
1329 /* we're done with this section of the call */
1330 DLIST_REMOVE(call->replies, rep);
1333 if (call->replies == NULL) {
1334 /* we're done with the whole call */
1335 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1336 talloc_free(call);
1339 return status;
1342 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
1343 struct loadparm_context *lp_ctx,
1344 const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1346 NTSTATUS status;
1347 struct dcesrv_context *dce_ctx;
1348 int i;
1350 if (!endpoint_servers) {
1351 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1352 return NT_STATUS_INTERNAL_ERROR;
1355 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1356 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1357 dce_ctx->endpoint_list = NULL;
1358 dce_ctx->lp_ctx = lp_ctx;
1360 for (i=0;endpoint_servers[i];i++) {
1361 const struct dcesrv_endpoint_server *ep_server;
1363 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1364 if (!ep_server) {
1365 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1366 return NT_STATUS_INTERNAL_ERROR;
1369 status = ep_server->init_server(dce_ctx, ep_server);
1370 if (!NT_STATUS_IS_OK(status)) {
1371 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1372 nt_errstr(status)));
1373 return status;
1377 *_dce_ctx = dce_ctx;
1378 return NT_STATUS_OK;
1381 /* the list of currently registered DCERPC endpoint servers.
1383 static struct ep_server {
1384 struct dcesrv_endpoint_server *ep_server;
1385 } *ep_servers = NULL;
1386 static int num_ep_servers;
1389 register a DCERPC endpoint server.
1391 The 'name' can be later used by other backends to find the operations
1392 structure for this backend.
1394 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1396 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1398 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1400 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1401 /* its already registered! */
1402 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1403 ep_server->name));
1404 return NT_STATUS_OBJECT_NAME_COLLISION;
1407 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1408 if (!ep_servers) {
1409 smb_panic("out of memory in dcerpc_register");
1412 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1413 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1415 num_ep_servers++;
1417 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1418 ep_server->name));
1420 return NT_STATUS_OK;
1424 return the operations structure for a named backend of the specified type
1426 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1428 int i;
1430 for (i=0;i<num_ep_servers;i++) {
1431 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1432 return ep_servers[i].ep_server;
1436 return NULL;
1439 void dcerpc_server_init(struct loadparm_context *lp_ctx)
1441 static bool initialized;
1442 extern NTSTATUS dcerpc_server_wkssvc_init(void);
1443 extern NTSTATUS dcerpc_server_drsuapi_init(void);
1444 extern NTSTATUS dcerpc_server_winreg_init(void);
1445 extern NTSTATUS dcerpc_server_spoolss_init(void);
1446 extern NTSTATUS dcerpc_server_epmapper_init(void);
1447 extern NTSTATUS dcerpc_server_srvsvc_init(void);
1448 extern NTSTATUS dcerpc_server_netlogon_init(void);
1449 extern NTSTATUS dcerpc_server_rpcecho_init(void);
1450 extern NTSTATUS dcerpc_server_unixinfo_init(void);
1451 extern NTSTATUS dcerpc_server_samr_init(void);
1452 extern NTSTATUS dcerpc_server_remote_init(void);
1453 extern NTSTATUS dcerpc_server_lsa_init(void);
1454 extern NTSTATUS dcerpc_server_browser_init(void);
1455 init_module_fn static_init[] = { STATIC_dcerpc_server_MODULES };
1456 init_module_fn *shared_init;
1458 if (initialized) {
1459 return;
1461 initialized = true;
1463 shared_init = load_samba_modules(NULL, lp_ctx, "dcerpc_server");
1465 run_init_functions(static_init);
1466 run_init_functions(shared_init);
1468 talloc_free(shared_init);
1472 return the DCERPC module version, and the size of some critical types
1473 This can be used by endpoint server modules to either detect compilation errors, or provide
1474 multiple implementations for different smbd compilation options in one module
1476 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1478 static const struct dcesrv_critical_sizes critical_sizes = {
1479 DCERPC_MODULE_VERSION,
1480 sizeof(struct dcesrv_context),
1481 sizeof(struct dcesrv_endpoint),
1482 sizeof(struct dcesrv_endpoint_server),
1483 sizeof(struct dcesrv_interface),
1484 sizeof(struct dcesrv_if_list),
1485 sizeof(struct dcesrv_connection),
1486 sizeof(struct dcesrv_call_state),
1487 sizeof(struct dcesrv_auth),
1488 sizeof(struct dcesrv_handle)
1491 return &critical_sizes;
1495 initialise the dcerpc server context for ncacn_np based services
1497 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1498 struct dcesrv_context **_dce_ctx)
1500 NTSTATUS status;
1501 struct dcesrv_context *dce_ctx;
1503 dcerpc_server_init(lp_ctx);
1505 status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1506 NT_STATUS_NOT_OK_RETURN(status);
1508 *_dce_ctx = dce_ctx;
1509 return NT_STATUS_OK;