First attempt to implement dcesrv_drsuapi_DsGetNCChanges
[Samba/aatanasov.git] / source4 / rpc_server / dcerpc_server.c
blobc7f073ccd370df699ba81631813f8052a1a379b4
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 if (call->conn->call_list && call->conn->call_list->replies) {
471 if (call->conn->transport.report_output_data) {
472 call->conn->transport.report_output_data(call->conn);
476 return NT_STATUS_OK;
481 return a dcerpc bind_nak
483 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
485 struct ncacn_packet pkt;
486 struct data_blob_list_item *rep;
487 NTSTATUS status;
489 /* setup a bind_nak */
490 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
491 pkt.auth_length = 0;
492 pkt.call_id = call->pkt.call_id;
493 pkt.ptype = DCERPC_PKT_BIND_NAK;
494 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
495 pkt.u.bind_nak.reject_reason = reason;
496 if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
497 pkt.u.bind_nak.versions.v.num_versions = 0;
500 rep = talloc(call, struct data_blob_list_item);
501 if (!rep) {
502 return NT_STATUS_NO_MEMORY;
505 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
506 if (!NT_STATUS_IS_OK(status)) {
507 return status;
510 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
512 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
513 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
515 if (call->conn->call_list && call->conn->call_list->replies) {
516 if (call->conn->transport.report_output_data) {
517 call->conn->transport.report_output_data(call->conn);
521 return NT_STATUS_OK;
524 static int dcesrv_connection_context_destructor(struct dcesrv_connection_context *c)
526 DLIST_REMOVE(c->conn->contexts, c);
528 if (c->iface) {
529 c->iface->unbind(c, c->iface);
532 return 0;
536 handle a bind request
538 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
540 uint32_t if_version, transfer_syntax_version;
541 struct GUID uuid, *transfer_syntax_uuid;
542 struct ncacn_packet pkt;
543 struct data_blob_list_item *rep;
544 NTSTATUS status;
545 uint32_t result=0, reason=0;
546 uint32_t context_id;
547 const struct dcesrv_interface *iface;
548 uint32_t extra_flags = 0;
551 * Association groups allow policy handles to be shared across
552 * multiple client connections. We don't implement this yet.
554 * So we just allow 0 if the client wants to create a new
555 * association group.
557 * And we allow the 0x12345678 value, we give away as
558 * assoc_group_id back to the clients
560 if (call->pkt.u.bind.assoc_group_id != 0 &&
561 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
562 call->pkt.u.bind.assoc_group_id != SAMBA_ASSOC_GROUP) {
563 return dcesrv_bind_nak(call, 0);
566 if (call->pkt.u.bind.num_contexts < 1 ||
567 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
568 return dcesrv_bind_nak(call, 0);
571 context_id = call->pkt.u.bind.ctx_list[0].context_id;
573 /* you can't bind twice on one context */
574 if (dcesrv_find_context(call->conn, context_id) != NULL) {
575 return dcesrv_bind_nak(call, 0);
578 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
579 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
581 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
582 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
583 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
584 ndr_transfer_syntax.if_version != transfer_syntax_version) {
585 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
586 /* we only do NDR encoded dcerpc */
587 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
588 talloc_free(uuid_str);
589 return dcesrv_bind_nak(call, 0);
592 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
593 if (iface == NULL) {
594 char *uuid_str = GUID_string(call, &uuid);
595 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
596 talloc_free(uuid_str);
598 /* we don't know about that interface */
599 result = DCERPC_BIND_PROVIDER_REJECT;
600 reason = DCERPC_BIND_REASON_ASYNTAX;
603 if (iface) {
604 /* add this context to the list of available context_ids */
605 struct dcesrv_connection_context *context = talloc(call->conn,
606 struct dcesrv_connection_context);
607 if (context == NULL) {
608 return dcesrv_bind_nak(call, 0);
610 context->conn = call->conn;
611 context->iface = iface;
612 context->context_id = context_id;
614 * we need to send a non zero assoc_group_id here to make longhorn happy,
615 * it also matches samba3
617 context->assoc_group_id = SAMBA_ASSOC_GROUP;
618 context->private_data = NULL;
619 context->handles = NULL;
620 DLIST_ADD(call->conn->contexts, context);
621 call->context = context;
622 talloc_set_destructor(context, dcesrv_connection_context_destructor);
624 status = iface->bind(call, iface);
625 if (!NT_STATUS_IS_OK(status)) {
626 char *uuid_str = GUID_string(call, &uuid);
627 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
628 uuid_str, if_version, nt_errstr(status)));
629 talloc_free(uuid_str);
630 /* we don't want to trigger the iface->unbind() hook */
631 context->iface = NULL;
632 talloc_free(call->context);
633 call->context = NULL;
634 return dcesrv_bind_nak(call, 0);
638 if (call->conn->cli_max_recv_frag == 0) {
639 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
642 if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
643 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
644 call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
645 extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
648 /* handle any authentication that is being requested */
649 if (!dcesrv_auth_bind(call)) {
650 talloc_free(call->context);
651 call->context = NULL;
652 return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
655 /* setup a bind_ack */
656 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
657 pkt.auth_length = 0;
658 pkt.call_id = call->pkt.call_id;
659 pkt.ptype = DCERPC_PKT_BIND_ACK;
660 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
661 pkt.u.bind_ack.max_xmit_frag = 0x2000;
662 pkt.u.bind_ack.max_recv_frag = 0x2000;
665 make it possible for iface->bind() to specify the assoc_group_id
666 This helps the openchange mapiproxy plugin to work correctly.
668 metze
670 if (call->context) {
671 pkt.u.bind_ack.assoc_group_id = call->context->assoc_group_id;
672 } else {
673 /* we better pick something - this chosen so as to send a non zero assoc_group_id (matching windows), it also matches samba3 */
674 pkt.u.bind_ack.assoc_group_id = SAMBA_ASSOC_GROUP;
677 if (iface) {
678 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
679 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
680 } else {
681 pkt.u.bind_ack.secondary_address = "";
683 pkt.u.bind_ack.num_results = 1;
684 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
685 if (!pkt.u.bind_ack.ctx_list) {
686 talloc_free(call->context);
687 call->context = NULL;
688 return NT_STATUS_NO_MEMORY;
690 pkt.u.bind_ack.ctx_list[0].result = result;
691 pkt.u.bind_ack.ctx_list[0].reason = reason;
692 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
693 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
695 status = dcesrv_auth_bind_ack(call, &pkt);
696 if (!NT_STATUS_IS_OK(status)) {
697 talloc_free(call->context);
698 call->context = NULL;
699 return dcesrv_bind_nak(call, 0);
702 rep = talloc(call, struct data_blob_list_item);
703 if (!rep) {
704 talloc_free(call->context);
705 call->context = NULL;
706 return NT_STATUS_NO_MEMORY;
709 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
710 if (!NT_STATUS_IS_OK(status)) {
711 talloc_free(call->context);
712 call->context = NULL;
713 return status;
716 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
718 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
719 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
721 if (call->conn->call_list && call->conn->call_list->replies) {
722 if (call->conn->transport.report_output_data) {
723 call->conn->transport.report_output_data(call->conn);
727 return NT_STATUS_OK;
732 handle a auth3 request
734 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
736 /* handle the auth3 in the auth code */
737 if (!dcesrv_auth_auth3(call)) {
738 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
741 talloc_free(call);
743 /* we don't send a reply to a auth3 request, except by a
744 fault */
745 return NT_STATUS_OK;
750 handle a bind request
752 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
754 uint32_t if_version, transfer_syntax_version;
755 struct dcesrv_connection_context *context;
756 const struct dcesrv_interface *iface;
757 struct GUID uuid, *transfer_syntax_uuid;
758 NTSTATUS status;
760 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
761 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
763 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
764 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
765 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
766 ndr_transfer_syntax.if_version != transfer_syntax_version) {
767 /* we only do NDR encoded dcerpc */
768 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
771 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
772 if (iface == NULL) {
773 char *uuid_str = GUID_string(call, &uuid);
774 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
775 talloc_free(uuid_str);
776 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
779 /* add this context to the list of available context_ids */
780 context = talloc(call->conn, struct dcesrv_connection_context);
781 if (context == NULL) {
782 return NT_STATUS_NO_MEMORY;
784 context->conn = call->conn;
785 context->iface = iface;
786 context->context_id = context_id;
787 context->assoc_group_id = SAMBA_ASSOC_GROUP;
788 context->private_data = NULL;
789 context->handles = NULL;
790 DLIST_ADD(call->conn->contexts, context);
791 call->context = context;
792 talloc_set_destructor(context, dcesrv_connection_context_destructor);
794 status = iface->bind(call, iface);
795 if (!NT_STATUS_IS_OK(status)) {
796 /* we don't want to trigger the iface->unbind() hook */
797 context->iface = NULL;
798 talloc_free(context);
799 call->context = NULL;
800 return status;
803 return NT_STATUS_OK;
808 handle a alter context request
810 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
812 struct ncacn_packet pkt;
813 struct data_blob_list_item *rep;
814 NTSTATUS status;
815 uint32_t result=0, reason=0;
816 uint32_t context_id;
818 /* handle any authentication that is being requested */
819 if (!dcesrv_auth_alter(call)) {
820 /* TODO: work out the right reject code */
821 result = DCERPC_BIND_PROVIDER_REJECT;
822 reason = DCERPC_BIND_REASON_ASYNTAX;
825 context_id = call->pkt.u.alter.ctx_list[0].context_id;
827 /* see if they are asking for a new interface */
828 if (result == 0) {
829 call->context = dcesrv_find_context(call->conn, context_id);
830 if (!call->context) {
831 status = dcesrv_alter_new_context(call, context_id);
832 if (!NT_STATUS_IS_OK(status)) {
833 result = DCERPC_BIND_PROVIDER_REJECT;
834 reason = DCERPC_BIND_REASON_ASYNTAX;
839 if (result == 0 &&
840 call->pkt.u.alter.assoc_group_id != 0 &&
841 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
842 call->pkt.u.alter.assoc_group_id != call->context->assoc_group_id) {
843 /* TODO: work out what to return here */
844 result = DCERPC_BIND_PROVIDER_REJECT;
845 reason = DCERPC_BIND_REASON_ASYNTAX;
848 /* setup a alter_resp */
849 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
850 pkt.auth_length = 0;
851 pkt.call_id = call->pkt.call_id;
852 pkt.ptype = DCERPC_PKT_ALTER_RESP;
853 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
854 pkt.u.alter_resp.max_xmit_frag = 0x2000;
855 pkt.u.alter_resp.max_recv_frag = 0x2000;
856 if (result == 0) {
857 pkt.u.alter_resp.assoc_group_id = call->context->assoc_group_id;
858 } else {
859 pkt.u.alter_resp.assoc_group_id = 0;
861 pkt.u.alter_resp.num_results = 1;
862 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
863 if (!pkt.u.alter_resp.ctx_list) {
864 return NT_STATUS_NO_MEMORY;
866 pkt.u.alter_resp.ctx_list[0].result = result;
867 pkt.u.alter_resp.ctx_list[0].reason = reason;
868 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
869 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
870 pkt.u.alter_resp.secondary_address = "";
872 status = dcesrv_auth_alter_ack(call, &pkt);
873 if (!NT_STATUS_IS_OK(status)) {
874 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
875 || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
876 || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
877 || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
878 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
880 return dcesrv_fault(call, 0);
883 rep = talloc(call, struct data_blob_list_item);
884 if (!rep) {
885 return NT_STATUS_NO_MEMORY;
888 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
889 if (!NT_STATUS_IS_OK(status)) {
890 return status;
893 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
895 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
896 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
898 if (call->conn->call_list && call->conn->call_list->replies) {
899 if (call->conn->transport.report_output_data) {
900 call->conn->transport.report_output_data(call->conn);
904 return NT_STATUS_OK;
908 handle a dcerpc request packet
910 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
912 struct ndr_pull *pull;
913 NTSTATUS status;
914 struct dcesrv_connection_context *context;
916 /* if authenticated, and the mech we use can't do async replies, don't use them... */
917 if (call->conn->auth_state.gensec_security &&
918 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
919 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
922 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
923 if (context == NULL) {
924 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
927 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
928 lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
929 NT_STATUS_HAVE_NO_MEMORY(pull);
931 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
933 call->context = context;
934 call->ndr_pull = pull;
936 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
937 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
940 /* unravel the NDR for the packet */
941 status = context->iface->ndr_pull(call, call, pull, &call->r);
942 if (!NT_STATUS_IS_OK(status)) {
943 return dcesrv_fault(call, call->fault_code);
946 if (pull->offset != pull->data_size) {
947 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
948 pull->data_size - pull->offset));
949 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
952 /* call the dispatch function */
953 status = context->iface->dispatch(call, call, call->r);
954 if (!NT_STATUS_IS_OK(status)) {
955 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
956 context->iface->name,
957 call->pkt.u.request.opnum,
958 dcerpc_errstr(pull, call->fault_code)));
959 return dcesrv_fault(call, call->fault_code);
962 /* add the call to the pending list */
963 dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
965 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
966 return NT_STATUS_OK;
969 return dcesrv_reply(call);
972 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
974 struct ndr_push *push;
975 NTSTATUS status;
976 DATA_BLOB stub;
977 uint32_t total_length, chunk_size;
978 struct dcesrv_connection_context *context = call->context;
979 size_t sig_size = 0;
981 /* call the reply function */
982 status = context->iface->reply(call, call, call->r);
983 if (!NT_STATUS_IS_OK(status)) {
984 return dcesrv_fault(call, call->fault_code);
987 /* form the reply NDR */
988 push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
989 NT_STATUS_HAVE_NO_MEMORY(push);
991 /* carry over the pointer count to the reply in case we are
992 using full pointer. See NDR specification for full
993 pointers */
994 push->ptr_count = call->ndr_pull->ptr_count;
996 if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
997 push->flags |= LIBNDR_FLAG_BIGENDIAN;
1000 status = context->iface->ndr_push(call, call, push, call->r);
1001 if (!NT_STATUS_IS_OK(status)) {
1002 return dcesrv_fault(call, call->fault_code);
1005 stub = ndr_push_blob(push);
1007 total_length = stub.length;
1009 /* we can write a full max_recv_frag size, minus the dcerpc
1010 request header size */
1011 chunk_size = call->conn->cli_max_recv_frag;
1012 chunk_size -= DCERPC_REQUEST_LENGTH;
1013 if (call->conn->auth_state.auth_info &&
1014 call->conn->auth_state.gensec_security) {
1015 sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
1016 call->conn->cli_max_recv_frag);
1017 if (sig_size) {
1018 chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
1019 chunk_size -= sig_size;
1022 chunk_size -= (chunk_size % 16);
1024 do {
1025 uint32_t length;
1026 struct data_blob_list_item *rep;
1027 struct ncacn_packet pkt;
1029 rep = talloc(call, struct data_blob_list_item);
1030 NT_STATUS_HAVE_NO_MEMORY(rep);
1032 length = MIN(chunk_size, stub.length);
1034 /* form the dcerpc response packet */
1035 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
1036 pkt.auth_length = 0;
1037 pkt.call_id = call->pkt.call_id;
1038 pkt.ptype = DCERPC_PKT_RESPONSE;
1039 pkt.pfc_flags = 0;
1040 if (stub.length == total_length) {
1041 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
1043 if (length == stub.length) {
1044 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
1046 pkt.u.response.alloc_hint = stub.length;
1047 pkt.u.response.context_id = call->pkt.u.request.context_id;
1048 pkt.u.response.cancel_count = 0;
1049 pkt.u.response.stub_and_verifier.data = stub.data;
1050 pkt.u.response.stub_and_verifier.length = length;
1052 if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
1053 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
1056 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
1058 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
1060 stub.data += length;
1061 stub.length -= length;
1062 } while (stub.length != 0);
1064 /* move the call from the pending to the finished calls list */
1065 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
1067 if (call->conn->call_list && call->conn->call_list->replies) {
1068 if (call->conn->transport.report_output_data) {
1069 call->conn->transport.report_output_data(call->conn);
1073 return NT_STATUS_OK;
1076 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1078 if (!conn->transport.get_my_addr) {
1079 return NULL;
1082 return conn->transport.get_my_addr(conn, mem_ctx);
1085 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1087 if (!conn->transport.get_peer_addr) {
1088 return NULL;
1091 return conn->transport.get_peer_addr(conn, mem_ctx);
1095 work out if we have a full packet yet
1097 static bool dce_full_packet(const DATA_BLOB *data)
1099 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
1100 return false;
1102 if (dcerpc_get_frag_length(data) > data->length) {
1103 return false;
1105 return true;
1109 we might have consumed only part of our input - advance past that part
1111 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1113 DATA_BLOB blob;
1115 if (dce_conn->partial_input.length == offset) {
1116 data_blob_free(&dce_conn->partial_input);
1117 return;
1120 blob = dce_conn->partial_input;
1121 dce_conn->partial_input = data_blob(blob.data + offset,
1122 blob.length - offset);
1123 data_blob_free(&blob);
1127 remove the call from the right list when freed
1129 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1131 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1132 return 0;
1136 process some input to a dcerpc endpoint server.
1138 NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn,
1139 struct ncacn_packet *pkt,
1140 DATA_BLOB blob)
1142 NTSTATUS status;
1143 struct dcesrv_call_state *call;
1145 call = talloc_zero(dce_conn, struct dcesrv_call_state);
1146 if (!call) {
1147 data_blob_free(&blob);
1148 talloc_free(pkt);
1149 return NT_STATUS_NO_MEMORY;
1151 call->conn = dce_conn;
1152 call->event_ctx = dce_conn->event_ctx;
1153 call->msg_ctx = dce_conn->msg_ctx;
1154 call->state_flags = call->conn->state_flags;
1155 call->time = timeval_current();
1156 call->list = DCESRV_LIST_NONE;
1158 talloc_steal(call, pkt);
1159 talloc_steal(call, blob.data);
1160 call->pkt = *pkt;
1162 talloc_set_destructor(call, dcesrv_call_dequeue);
1164 /* we have to check the signing here, before combining the
1165 pdus */
1166 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1167 !dcesrv_auth_request(call, &blob)) {
1168 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
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 NTSTATUS status;
1280 struct ndr_pull *ndr;
1281 enum ndr_err_code ndr_err;
1282 DATA_BLOB blob;
1283 struct ncacn_packet *pkt;
1285 blob = dce_conn->partial_input;
1286 blob.length = dcerpc_get_frag_length(&blob);
1287 blob = data_blob_talloc(dce_conn, blob.data, blob.length);
1288 if (!blob.data) {
1289 data_blob_free(&dce_conn->partial_input);
1290 return NT_STATUS_NO_MEMORY;
1293 dce_partial_advance(dce_conn, blob.length);
1295 pkt = talloc(dce_conn, struct ncacn_packet);
1296 if (!pkt) {
1297 data_blob_free(&blob);
1298 return NT_STATUS_NO_MEMORY;
1301 ndr = ndr_pull_init_blob(&blob, pkt, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx));
1302 if (!ndr) {
1303 data_blob_free(&blob);
1304 talloc_free(pkt);
1305 return NT_STATUS_NO_MEMORY;
1308 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1309 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1312 if (CVAL(blob.data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
1313 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
1316 ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
1317 TALLOC_FREE(ndr);
1318 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1319 data_blob_free(&blob);
1320 talloc_free(pkt);
1321 return ndr_map_error2ntstatus(ndr_err);
1324 status = dcesrv_process_ncacn_packet(dce_conn, pkt, blob);
1325 if (!NT_STATUS_IS_OK(status)) {
1326 return status;
1330 return NT_STATUS_OK;
1334 retrieve some output from a dcerpc server
1335 The caller supplies a function that will be called to do the
1336 actual output.
1338 The first argument to write_fn() will be 'private', the second will
1339 be a pointer to a buffer containing the data to be sent and the 3rd
1340 will be a pointer to a size_t variable that will be set to the
1341 number of bytes that are consumed from the output.
1343 from the current fragment
1345 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1346 void *private_data,
1347 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1349 NTSTATUS status;
1350 struct dcesrv_call_state *call;
1351 struct data_blob_list_item *rep;
1352 size_t nwritten;
1354 call = dce_conn->call_list;
1355 if (!call || !call->replies) {
1356 if (dce_conn->pending_call_list) {
1357 /* TODO: we need to say act async here
1358 * as we know we have pending requests
1359 * which will be finished at a time
1361 return NT_STATUS_FOOBAR;
1363 return NT_STATUS_FOOBAR;
1365 rep = call->replies;
1367 status = write_fn(private_data, &rep->blob, &nwritten);
1368 NT_STATUS_IS_ERR_RETURN(status);
1370 rep->blob.length -= nwritten;
1371 rep->blob.data += nwritten;
1373 if (rep->blob.length == 0) {
1374 /* we're done with this section of the call */
1375 DLIST_REMOVE(call->replies, rep);
1378 if (call->replies == NULL) {
1379 /* we're done with the whole call */
1380 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1381 talloc_free(call);
1384 return status;
1387 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
1388 struct loadparm_context *lp_ctx,
1389 const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1391 NTSTATUS status;
1392 struct dcesrv_context *dce_ctx;
1393 int i;
1395 if (!endpoint_servers) {
1396 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1397 return NT_STATUS_INTERNAL_ERROR;
1400 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1401 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1402 dce_ctx->endpoint_list = NULL;
1403 dce_ctx->lp_ctx = lp_ctx;
1405 for (i=0;endpoint_servers[i];i++) {
1406 const struct dcesrv_endpoint_server *ep_server;
1408 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1409 if (!ep_server) {
1410 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1411 return NT_STATUS_INTERNAL_ERROR;
1414 status = ep_server->init_server(dce_ctx, ep_server);
1415 if (!NT_STATUS_IS_OK(status)) {
1416 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1417 nt_errstr(status)));
1418 return status;
1422 *_dce_ctx = dce_ctx;
1423 return NT_STATUS_OK;
1426 /* the list of currently registered DCERPC endpoint servers.
1428 static struct ep_server {
1429 struct dcesrv_endpoint_server *ep_server;
1430 } *ep_servers = NULL;
1431 static int num_ep_servers;
1434 register a DCERPC endpoint server.
1436 The 'name' can be later used by other backends to find the operations
1437 structure for this backend.
1439 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1441 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1443 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1445 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1446 /* its already registered! */
1447 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1448 ep_server->name));
1449 return NT_STATUS_OBJECT_NAME_COLLISION;
1452 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1453 if (!ep_servers) {
1454 smb_panic("out of memory in dcerpc_register");
1457 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1458 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1460 num_ep_servers++;
1462 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1463 ep_server->name));
1465 return NT_STATUS_OK;
1469 return the operations structure for a named backend of the specified type
1471 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1473 int i;
1475 for (i=0;i<num_ep_servers;i++) {
1476 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1477 return ep_servers[i].ep_server;
1481 return NULL;
1484 void dcerpc_server_init(struct loadparm_context *lp_ctx)
1486 static bool initialized;
1487 extern NTSTATUS dcerpc_server_wkssvc_init(void);
1488 extern NTSTATUS dcerpc_server_drsuapi_init(void);
1489 extern NTSTATUS dcerpc_server_winreg_init(void);
1490 extern NTSTATUS dcerpc_server_spoolss_init(void);
1491 extern NTSTATUS dcerpc_server_epmapper_init(void);
1492 extern NTSTATUS dcerpc_server_srvsvc_init(void);
1493 extern NTSTATUS dcerpc_server_netlogon_init(void);
1494 extern NTSTATUS dcerpc_server_rpcecho_init(void);
1495 extern NTSTATUS dcerpc_server_unixinfo_init(void);
1496 extern NTSTATUS dcerpc_server_samr_init(void);
1497 extern NTSTATUS dcerpc_server_remote_init(void);
1498 extern NTSTATUS dcerpc_server_lsa_init(void);
1499 extern NTSTATUS dcerpc_server_browser_init(void);
1500 init_module_fn static_init[] = { STATIC_dcerpc_server_MODULES };
1501 init_module_fn *shared_init;
1503 if (initialized) {
1504 return;
1506 initialized = true;
1508 shared_init = load_samba_modules(NULL, lp_ctx, "dcerpc_server");
1510 run_init_functions(static_init);
1511 run_init_functions(shared_init);
1513 talloc_free(shared_init);
1517 return the DCERPC module version, and the size of some critical types
1518 This can be used by endpoint server modules to either detect compilation errors, or provide
1519 multiple implementations for different smbd compilation options in one module
1521 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1523 static const struct dcesrv_critical_sizes critical_sizes = {
1524 DCERPC_MODULE_VERSION,
1525 sizeof(struct dcesrv_context),
1526 sizeof(struct dcesrv_endpoint),
1527 sizeof(struct dcesrv_endpoint_server),
1528 sizeof(struct dcesrv_interface),
1529 sizeof(struct dcesrv_if_list),
1530 sizeof(struct dcesrv_connection),
1531 sizeof(struct dcesrv_call_state),
1532 sizeof(struct dcesrv_auth),
1533 sizeof(struct dcesrv_handle)
1536 return &critical_sizes;
1540 initialise the dcerpc server context for ncacn_np based services
1542 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1543 struct dcesrv_context **_dce_ctx)
1545 NTSTATUS status;
1546 struct dcesrv_context *dce_ctx;
1548 dcerpc_server_init(lp_ctx);
1550 status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1551 NT_STATUS_NOT_OK_RETURN(status);
1553 *_dce_ctx = dce_ctx;
1554 return NT_STATUS_OK;