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/>.
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_ACCOC_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) {
53 if (!ep1->endpoint || !ep2->endpoint) {
54 return ep1->endpoint == ep2->endpoint;
57 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
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)) {
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,
84 struct dcesrv_connection_context *c;
85 for (c=conn->contexts;c;c=c->next) {
86 if (c->context_id == context_id) return c;
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);
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);
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) {
156 register an interface on an endpoint
158 _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
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;
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));
176 /* check if this endpoint exists
178 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
179 ep = talloc(dce_ctx, struct dcesrv_endpoint);
181 return NT_STATUS_NO_MEMORY;
184 ep->ep_description = talloc_reference(ep, binding);
187 /* add mgmt interface */
188 ifl = talloc(dce_ctx, struct dcesrv_if_list);
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);
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
219 /* if there's currently no security descriptor given on the endpoint
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);
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 */
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));
254 static 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;
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;
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)) {
286 session_key->length = MIN(session_key->length, 16);
293 destroy a link to an endpoint
295 static int dcesrv_endpoint_destructor(struct dcesrv_connection *p)
297 while (p->contexts) {
298 struct dcesrv_connection_context *c = p->contexts;
300 DLIST_REMOVE(p->contexts, c);
303 c->iface->unbind(c, c->iface);
312 connect to a dcerpc endpoint
314 _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
316 const struct dcesrv_endpoint *ep,
317 struct auth_session_info *session_info,
318 struct event_context *event_ctx,
319 struct messaging_context *msg_ctx,
320 struct server_id server_id,
321 uint32_t state_flags,
322 struct dcesrv_connection **_p)
324 struct dcesrv_connection *p;
327 return NT_STATUS_ACCESS_DENIED;
330 p = talloc(mem_ctx, struct dcesrv_connection);
331 NT_STATUS_HAVE_NO_MEMORY(p);
333 if (!talloc_reference(p, session_info)) {
335 return NT_STATUS_NO_MEMORY;
338 p->dce_ctx = dce_ctx;
342 p->packet_log_dir = lp_lockdir(dce_ctx->lp_ctx);
343 p->incoming_fragmented_call_list = NULL;
344 p->pending_call_list = NULL;
345 p->cli_max_recv_frag = 0;
346 p->partial_input = data_blob(NULL, 0);
347 p->auth_state.auth_info = NULL;
348 p->auth_state.gensec_security = NULL;
349 p->auth_state.session_info = session_info;
350 p->auth_state.session_key = dcesrv_generic_session_key;
351 p->event_ctx = event_ctx;
352 p->msg_ctx = msg_ctx;
353 p->server_id = server_id;
354 p->processing = false;
355 p->state_flags = state_flags;
356 ZERO_STRUCT(p->transport);
358 talloc_set_destructor(p, dcesrv_endpoint_destructor);
365 search and connect to a dcerpc endpoint
367 _PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
369 const struct dcerpc_binding *ep_description,
370 struct auth_session_info *session_info,
371 struct event_context *event_ctx,
372 struct messaging_context *msg_ctx,
373 struct server_id server_id,
374 uint32_t state_flags,
375 struct dcesrv_connection **dce_conn_p)
378 const struct dcesrv_endpoint *ep;
380 /* make sure this endpoint exists */
381 ep = find_endpoint(dce_ctx, ep_description);
383 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
386 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
387 event_ctx, msg_ctx, server_id,
388 state_flags, dce_conn_p);
389 NT_STATUS_NOT_OK_RETURN(status);
391 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
393 /* TODO: check security descriptor of the endpoint here
394 * if it's a smb named pipe
395 * if it's failed free dce_conn_p
402 static void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
405 pkt->rpc_vers_minor = 0;
409 pkt->drep[0] = DCERPC_DREP_LE;
417 move a call from an existing linked list to the specified list. This
418 prevents bugs where we forget to remove the call from a previous
421 static void dcesrv_call_set_list(struct dcesrv_call_state *call,
422 enum dcesrv_call_list list)
424 switch (call->list) {
425 case DCESRV_LIST_NONE:
427 case DCESRV_LIST_CALL_LIST:
428 DLIST_REMOVE(call->conn->call_list, call);
430 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
431 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
433 case DCESRV_LIST_PENDING_CALL_LIST:
434 DLIST_REMOVE(call->conn->pending_call_list, call);
439 case DCESRV_LIST_NONE:
441 case DCESRV_LIST_CALL_LIST:
442 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
444 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
445 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
447 case DCESRV_LIST_PENDING_CALL_LIST:
448 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
454 return a dcerpc fault
456 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
458 struct ncacn_packet pkt;
459 struct data_blob_list_item *rep;
463 /* setup a bind_ack */
464 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
466 pkt.call_id = call->pkt.call_id;
467 pkt.ptype = DCERPC_PKT_FAULT;
468 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
469 pkt.u.fault.alloc_hint = 0;
470 pkt.u.fault.context_id = 0;
471 pkt.u.fault.cancel_count = 0;
472 pkt.u.fault.status = fault_code;
475 pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
477 rep = talloc(call, struct data_blob_list_item);
479 return NT_STATUS_NO_MEMORY;
482 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
483 if (!NT_STATUS_IS_OK(status)) {
487 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
489 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
490 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
497 return a dcerpc bind_nak
499 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
501 struct ncacn_packet pkt;
502 struct data_blob_list_item *rep;
505 /* setup a bind_nak */
506 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
508 pkt.call_id = call->pkt.call_id;
509 pkt.ptype = DCERPC_PKT_BIND_NAK;
510 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
511 pkt.u.bind_nak.reject_reason = reason;
512 if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
513 pkt.u.bind_nak.versions.v.num_versions = 0;
516 rep = talloc(call, struct data_blob_list_item);
518 return NT_STATUS_NO_MEMORY;
521 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
522 if (!NT_STATUS_IS_OK(status)) {
526 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
528 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
529 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
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;
545 uint32_t result=0, reason=0;
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
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 call->pkt.u.bind.assoc_group_id != SAMBA_ACCOC_GROUP) {
562 return dcesrv_bind_nak(call, 0);
565 if (call->pkt.u.bind.num_contexts < 1 ||
566 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
567 return dcesrv_bind_nak(call, 0);
570 context_id = call->pkt.u.bind.ctx_list[0].context_id;
572 /* you can't bind twice on one context */
573 if (dcesrv_find_context(call->conn, context_id) != NULL) {
574 return dcesrv_bind_nak(call, 0);
577 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
578 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
580 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
581 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
582 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
583 ndr_transfer_syntax.if_version != transfer_syntax_version) {
584 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
585 /* we only do NDR encoded dcerpc */
586 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
587 talloc_free(uuid_str);
588 return dcesrv_bind_nak(call, 0);
591 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
593 char *uuid_str = GUID_string(call, &uuid);
594 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
595 talloc_free(uuid_str);
597 /* we don't know about that interface */
598 result = DCERPC_BIND_PROVIDER_REJECT;
599 reason = DCERPC_BIND_REASON_ASYNTAX;
603 /* add this context to the list of available context_ids */
604 struct dcesrv_connection_context *context = talloc(call->conn,
605 struct dcesrv_connection_context);
606 if (context == NULL) {
607 return dcesrv_bind_nak(call, 0);
609 context->conn = call->conn;
610 context->iface = iface;
611 context->context_id = context_id;
612 context->private = NULL;
613 context->handles = NULL;
614 DLIST_ADD(call->conn->contexts, context);
615 call->context = context;
618 if (call->conn->cli_max_recv_frag == 0) {
619 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
622 if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
623 lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
624 call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
625 extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
628 /* handle any authentication that is being requested */
629 if (!dcesrv_auth_bind(call)) {
630 return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
633 /* setup a bind_ack */
634 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
636 pkt.call_id = call->pkt.call_id;
637 pkt.ptype = DCERPC_PKT_BIND_ACK;
638 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
639 pkt.u.bind_ack.max_xmit_frag = 0x2000;
640 pkt.u.bind_ack.max_recv_frag = 0x2000;
641 /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
642 pkt.u.bind_ack.assoc_group_id = SAMBA_ACCOC_GROUP;
644 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
645 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
647 pkt.u.bind_ack.secondary_address = "";
649 pkt.u.bind_ack.num_results = 1;
650 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
651 if (!pkt.u.bind_ack.ctx_list) {
652 return NT_STATUS_NO_MEMORY;
654 pkt.u.bind_ack.ctx_list[0].result = result;
655 pkt.u.bind_ack.ctx_list[0].reason = reason;
656 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
657 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
659 status = dcesrv_auth_bind_ack(call, &pkt);
660 if (!NT_STATUS_IS_OK(status)) {
661 return dcesrv_bind_nak(call, 0);
665 status = iface->bind(call, iface);
666 if (!NT_STATUS_IS_OK(status)) {
667 char *uuid_str = GUID_string(call, &uuid);
668 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
669 uuid_str, if_version, nt_errstr(status)));
670 talloc_free(uuid_str);
671 return dcesrv_bind_nak(call, 0);
675 rep = talloc(call, struct data_blob_list_item);
677 return NT_STATUS_NO_MEMORY;
680 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
681 if (!NT_STATUS_IS_OK(status)) {
685 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
687 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
688 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
695 handle a auth3 request
697 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
699 /* handle the auth3 in the auth code */
700 if (!dcesrv_auth_auth3(call)) {
701 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
706 /* we don't send a reply to a auth3 request, except by a
713 handle a bind request
715 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
717 uint32_t if_version, transfer_syntax_version;
718 struct dcesrv_connection_context *context;
719 const struct dcesrv_interface *iface;
720 struct GUID uuid, *transfer_syntax_uuid;
723 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
724 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
726 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
727 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
728 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
729 ndr_transfer_syntax.if_version != transfer_syntax_version) {
730 /* we only do NDR encoded dcerpc */
731 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
734 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
736 char *uuid_str = GUID_string(call, &uuid);
737 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
738 talloc_free(uuid_str);
739 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
742 /* add this context to the list of available context_ids */
743 context = talloc(call->conn, struct dcesrv_connection_context);
744 if (context == NULL) {
745 return NT_STATUS_NO_MEMORY;
747 context->conn = call->conn;
748 context->iface = iface;
749 context->context_id = context_id;
750 context->private = NULL;
751 context->handles = NULL;
752 DLIST_ADD(call->conn->contexts, context);
753 call->context = context;
756 status = iface->bind(call, iface);
757 if (!NT_STATUS_IS_OK(status)) {
767 handle a alter context request
769 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
771 struct ncacn_packet pkt;
772 struct data_blob_list_item *rep;
774 uint32_t result=0, reason=0;
777 /* handle any authentication that is being requested */
778 if (!dcesrv_auth_alter(call)) {
779 /* TODO: work out the right reject code */
780 result = DCERPC_BIND_PROVIDER_REJECT;
781 reason = DCERPC_BIND_REASON_ASYNTAX;
784 context_id = call->pkt.u.alter.ctx_list[0].context_id;
786 /* see if they are asking for a new interface */
788 dcesrv_find_context(call->conn, context_id) == NULL) {
789 status = dcesrv_alter_new_context(call, context_id);
790 if (!NT_STATUS_IS_OK(status)) {
791 result = DCERPC_BIND_PROVIDER_REJECT;
792 reason = DCERPC_BIND_REASON_ASYNTAX;
796 /* setup a alter_resp */
797 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
799 pkt.call_id = call->pkt.call_id;
800 pkt.ptype = DCERPC_PKT_ALTER_RESP;
801 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
802 pkt.u.alter_resp.max_xmit_frag = 0x2000;
803 pkt.u.alter_resp.max_recv_frag = 0x2000;
804 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
805 pkt.u.alter_resp.num_results = 1;
806 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
807 if (!pkt.u.alter_resp.ctx_list) {
808 return NT_STATUS_NO_MEMORY;
810 pkt.u.alter_resp.ctx_list[0].result = result;
811 pkt.u.alter_resp.ctx_list[0].reason = reason;
812 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
813 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
814 pkt.u.alter_resp.secondary_address = "";
816 status = dcesrv_auth_alter_ack(call, &pkt);
817 if (!NT_STATUS_IS_OK(status)) {
818 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
819 || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
820 || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
821 || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
822 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
824 return dcesrv_fault(call, 0);
827 rep = talloc(call, struct data_blob_list_item);
829 return NT_STATUS_NO_MEMORY;
832 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
833 if (!NT_STATUS_IS_OK(status)) {
837 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
839 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
840 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
846 handle a dcerpc request packet
848 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
850 struct ndr_pull *pull;
852 struct dcesrv_connection_context *context;
854 /* if authenticated, and the mech we use can't do async replies, don't use them... */
855 if (call->conn->auth_state.gensec_security &&
856 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
857 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
860 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
861 if (context == NULL) {
862 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
865 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
866 lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
867 NT_STATUS_HAVE_NO_MEMORY(pull);
869 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
871 call->context = context;
872 call->ndr_pull = pull;
874 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
875 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
878 /* unravel the NDR for the packet */
879 status = context->iface->ndr_pull(call, call, pull, &call->r);
880 if (!NT_STATUS_IS_OK(status)) {
881 return dcesrv_fault(call, call->fault_code);
884 if (pull->offset != pull->data_size) {
885 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
886 pull->data_size - pull->offset));
887 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
890 /* call the dispatch function */
891 status = context->iface->dispatch(call, call, call->r);
892 if (!NT_STATUS_IS_OK(status)) {
893 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
894 context->iface->name,
895 call->pkt.u.request.opnum,
896 dcerpc_errstr(pull, call->fault_code)));
897 return dcesrv_fault(call, call->fault_code);
900 /* add the call to the pending list */
901 dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
903 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
907 return dcesrv_reply(call);
910 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
912 struct ndr_push *push;
915 uint32_t total_length, chunk_size;
916 struct dcesrv_connection_context *context = call->context;
919 /* call the reply function */
920 status = context->iface->reply(call, call, call->r);
921 if (!NT_STATUS_IS_OK(status)) {
922 return dcesrv_fault(call, call->fault_code);
925 /* form the reply NDR */
926 push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
927 NT_STATUS_HAVE_NO_MEMORY(push);
929 /* carry over the pointer count to the reply in case we are
930 using full pointer. See NDR specification for full
932 push->ptr_count = call->ndr_pull->ptr_count;
934 if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
935 push->flags |= LIBNDR_FLAG_BIGENDIAN;
938 status = context->iface->ndr_push(call, call, push, call->r);
939 if (!NT_STATUS_IS_OK(status)) {
940 return dcesrv_fault(call, call->fault_code);
943 stub = ndr_push_blob(push);
945 total_length = stub.length;
947 /* we can write a full max_recv_frag size, minus the dcerpc
948 request header size */
949 chunk_size = call->conn->cli_max_recv_frag;
950 chunk_size -= DCERPC_REQUEST_LENGTH;
951 if (call->conn->auth_state.auth_info &&
952 call->conn->auth_state.gensec_security) {
953 sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
954 call->conn->cli_max_recv_frag);
956 chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
957 chunk_size -= sig_size;
960 chunk_size -= (chunk_size % 16);
964 struct data_blob_list_item *rep;
965 struct ncacn_packet pkt;
967 rep = talloc(call, struct data_blob_list_item);
968 NT_STATUS_HAVE_NO_MEMORY(rep);
970 length = MIN(chunk_size, stub.length);
972 /* form the dcerpc response packet */
973 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
975 pkt.call_id = call->pkt.call_id;
976 pkt.ptype = DCERPC_PKT_RESPONSE;
978 if (stub.length == total_length) {
979 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
981 if (length == stub.length) {
982 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
984 pkt.u.response.alloc_hint = stub.length;
985 pkt.u.response.context_id = call->pkt.u.request.context_id;
986 pkt.u.response.cancel_count = 0;
987 pkt.u.response.stub_and_verifier.data = stub.data;
988 pkt.u.response.stub_and_verifier.length = length;
990 if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
991 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
994 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
996 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
999 stub.length -= length;
1000 } while (stub.length != 0);
1002 /* move the call from the pending to the finished calls list */
1003 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
1005 if (call->conn->call_list && call->conn->call_list->replies) {
1006 if (call->conn->transport.report_output_data) {
1007 call->conn->transport.report_output_data(call->conn);
1011 return NT_STATUS_OK;
1014 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1016 if (!conn->transport.get_my_addr) {
1020 return conn->transport.get_my_addr(conn, mem_ctx);
1023 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1025 if (!conn->transport.get_peer_addr) {
1029 return conn->transport.get_peer_addr(conn, mem_ctx);
1033 work out if we have a full packet yet
1035 static bool dce_full_packet(const DATA_BLOB *data)
1037 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
1040 if (dcerpc_get_frag_length(data) > data->length) {
1047 we might have consumed only part of our input - advance past that part
1049 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1053 if (dce_conn->partial_input.length == offset) {
1054 data_blob_free(&dce_conn->partial_input);
1058 blob = dce_conn->partial_input;
1059 dce_conn->partial_input = data_blob(blob.data + offset,
1060 blob.length - offset);
1061 data_blob_free(&blob);
1065 remove the call from the right list when freed
1067 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1069 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1074 process some input to a dcerpc endpoint server.
1076 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1078 struct ndr_pull *ndr;
1079 enum ndr_err_code ndr_err;
1081 struct dcesrv_call_state *call;
1084 call = talloc_zero(dce_conn, struct dcesrv_call_state);
1086 talloc_free(dce_conn->partial_input.data);
1087 return NT_STATUS_NO_MEMORY;
1089 call->conn = dce_conn;
1090 call->event_ctx = dce_conn->event_ctx;
1091 call->msg_ctx = dce_conn->msg_ctx;
1092 call->state_flags = call->conn->state_flags;
1093 call->time = timeval_current();
1094 call->list = DCESRV_LIST_NONE;
1096 talloc_set_destructor(call, dcesrv_call_dequeue);
1098 blob = dce_conn->partial_input;
1099 blob.length = dcerpc_get_frag_length(&blob);
1101 ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
1103 talloc_free(dce_conn->partial_input.data);
1105 return NT_STATUS_NO_MEMORY;
1108 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1109 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1112 if (CVAL(blob.data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
1113 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
1116 ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1117 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1118 talloc_free(dce_conn->partial_input.data);
1120 return ndr_map_error2ntstatus(ndr_err);
1123 /* we have to check the signing here, before combining the
1125 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1126 !dcesrv_auth_request(call, &blob)) {
1127 dce_partial_advance(dce_conn, blob.length);
1128 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
1131 dce_partial_advance(dce_conn, blob.length);
1133 /* see if this is a continued packet */
1134 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1135 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1136 struct dcesrv_call_state *call2 = call;
1137 uint32_t alloc_size;
1139 /* we only allow fragmented requests, no other packet types */
1140 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1141 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1144 /* this is a continuation of an existing call - find the call then
1145 tack it on the end */
1146 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1148 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1151 if (call->pkt.ptype != call2->pkt.ptype) {
1152 /* trying to play silly buggers are we? */
1153 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1156 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1157 call2->pkt.u.request.stub_and_verifier.length;
1158 if (call->pkt.u.request.alloc_hint > alloc_size) {
1159 alloc_size = call->pkt.u.request.alloc_hint;
1162 call->pkt.u.request.stub_and_verifier.data =
1163 talloc_realloc(call,
1164 call->pkt.u.request.stub_and_verifier.data,
1165 uint8_t, alloc_size);
1166 if (!call->pkt.u.request.stub_and_verifier.data) {
1167 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1169 memcpy(call->pkt.u.request.stub_and_verifier.data +
1170 call->pkt.u.request.stub_and_verifier.length,
1171 call2->pkt.u.request.stub_and_verifier.data,
1172 call2->pkt.u.request.stub_and_verifier.length);
1173 call->pkt.u.request.stub_and_verifier.length +=
1174 call2->pkt.u.request.stub_and_verifier.length;
1176 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1181 /* this may not be the last pdu in the chain - if its isn't then
1182 just put it on the incoming_fragmented_call_list and wait for the rest */
1183 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1184 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1185 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1186 return NT_STATUS_OK;
1189 /* This removes any fragments we may have had stashed away */
1190 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1192 switch (call->pkt.ptype) {
1193 case DCERPC_PKT_BIND:
1194 status = dcesrv_bind(call);
1196 case DCERPC_PKT_AUTH3:
1197 status = dcesrv_auth3(call);
1199 case DCERPC_PKT_ALTER:
1200 status = dcesrv_alter(call);
1202 case DCERPC_PKT_REQUEST:
1203 status = dcesrv_request(call);
1206 status = NT_STATUS_INVALID_PARAMETER;
1210 /* if we are going to be sending a reply then add
1211 it to the list of pending calls. We add it to the end to keep the call
1212 list in the order we will answer */
1213 if (!NT_STATUS_IS_OK(status)) {
1222 provide some input to a dcerpc endpoint server. This passes data
1223 from a dcerpc client into the server
1225 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1229 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1230 dce_conn->partial_input.data,
1232 dce_conn->partial_input.length + data->length);
1233 if (!dce_conn->partial_input.data) {
1234 return NT_STATUS_NO_MEMORY;
1236 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1237 data->data, data->length);
1238 dce_conn->partial_input.length += data->length;
1240 while (dce_full_packet(&dce_conn->partial_input)) {
1241 status = dcesrv_input_process(dce_conn);
1242 if (!NT_STATUS_IS_OK(status)) {
1247 return NT_STATUS_OK;
1251 retrieve some output from a dcerpc server
1252 The caller supplies a function that will be called to do the
1255 The first argument to write_fn() will be 'private', the second will
1256 be a pointer to a buffer containing the data to be sent and the 3rd
1257 will be a pointer to a size_t variable that will be set to the
1258 number of bytes that are consumed from the output.
1260 from the current fragment
1262 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1264 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1267 struct dcesrv_call_state *call;
1268 struct data_blob_list_item *rep;
1271 call = dce_conn->call_list;
1272 if (!call || !call->replies) {
1273 if (dce_conn->pending_call_list) {
1274 /* TODO: we need to say act async here
1275 * as we know we have pending requests
1276 * which will be finished at a time
1278 return NT_STATUS_FOOBAR;
1280 return NT_STATUS_FOOBAR;
1282 rep = call->replies;
1284 status = write_fn(private_data, &rep->blob, &nwritten);
1285 NT_STATUS_IS_ERR_RETURN(status);
1287 rep->blob.length -= nwritten;
1288 rep->blob.data += nwritten;
1290 if (rep->blob.length == 0) {
1291 /* we're done with this section of the call */
1292 DLIST_REMOVE(call->replies, rep);
1295 if (call->replies == NULL) {
1296 /* we're done with the whole call */
1297 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1304 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
1305 struct loadparm_context *lp_ctx,
1306 const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1309 struct dcesrv_context *dce_ctx;
1312 if (!endpoint_servers) {
1313 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1314 return NT_STATUS_INTERNAL_ERROR;
1317 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1318 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1319 dce_ctx->endpoint_list = NULL;
1320 dce_ctx->lp_ctx = lp_ctx;
1322 for (i=0;endpoint_servers[i];i++) {
1323 const struct dcesrv_endpoint_server *ep_server;
1325 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1327 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1328 return NT_STATUS_INTERNAL_ERROR;
1331 status = ep_server->init_server(dce_ctx, ep_server);
1332 if (!NT_STATUS_IS_OK(status)) {
1333 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1334 nt_errstr(status)));
1339 *_dce_ctx = dce_ctx;
1340 return NT_STATUS_OK;
1343 /* the list of currently registered DCERPC endpoint servers.
1345 static struct ep_server {
1346 struct dcesrv_endpoint_server *ep_server;
1347 } *ep_servers = NULL;
1348 static int num_ep_servers;
1351 register a DCERPC endpoint server.
1353 The 'name' can be later used by other backends to find the operations
1354 structure for this backend.
1356 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1358 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1360 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1362 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1363 /* its already registered! */
1364 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1366 return NT_STATUS_OBJECT_NAME_COLLISION;
1369 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1371 smb_panic("out of memory in dcerpc_register");
1374 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1375 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1379 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1382 return NT_STATUS_OK;
1386 return the operations structure for a named backend of the specified type
1388 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1392 for (i=0;i<num_ep_servers;i++) {
1393 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1394 return ep_servers[i].ep_server;
1402 return the DCERPC module version, and the size of some critical types
1403 This can be used by endpoint server modules to either detect compilation errors, or provide
1404 multiple implementations for different smbd compilation options in one module
1406 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1408 static const struct dcesrv_critical_sizes critical_sizes = {
1409 DCERPC_MODULE_VERSION,
1410 sizeof(struct dcesrv_context),
1411 sizeof(struct dcesrv_endpoint),
1412 sizeof(struct dcesrv_endpoint_server),
1413 sizeof(struct dcesrv_interface),
1414 sizeof(struct dcesrv_if_list),
1415 sizeof(struct dcesrv_connection),
1416 sizeof(struct dcesrv_call_state),
1417 sizeof(struct dcesrv_auth),
1418 sizeof(struct dcesrv_handle)
1421 return &critical_sizes;
1425 initialise the dcerpc server context for ncacn_np based services
1427 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1428 struct dcesrv_context **_dce_ctx)
1431 struct dcesrv_context *dce_ctx;
1433 status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1434 NT_STATUS_NOT_OK_RETURN(status);
1436 *_dce_ctx = dce_ctx;
1437 return NT_STATUS_OK;