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->incoming_fragmented_call_list = NULL;
343 p->pending_call_list = NULL;
344 p->cli_max_recv_frag = 0;
345 p->partial_input = data_blob(NULL, 0);
346 p->auth_state.auth_info = NULL;
347 p->auth_state.gensec_security = NULL;
348 p->auth_state.session_info = session_info;
349 p->auth_state.session_key = dcesrv_generic_session_key;
350 p->event_ctx = event_ctx;
351 p->msg_ctx = msg_ctx;
352 p->server_id = server_id;
353 p->processing = false;
354 p->state_flags = state_flags;
355 ZERO_STRUCT(p->transport);
357 talloc_set_destructor(p, dcesrv_endpoint_destructor);
364 search and connect to a dcerpc endpoint
366 _PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
368 const struct dcerpc_binding *ep_description,
369 struct auth_session_info *session_info,
370 struct event_context *event_ctx,
371 struct messaging_context *msg_ctx,
372 struct server_id server_id,
373 uint32_t state_flags,
374 struct dcesrv_connection **dce_conn_p)
377 const struct dcesrv_endpoint *ep;
379 /* make sure this endpoint exists */
380 ep = find_endpoint(dce_ctx, ep_description);
382 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
385 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
386 event_ctx, msg_ctx, server_id,
387 state_flags, dce_conn_p);
388 NT_STATUS_NOT_OK_RETURN(status);
390 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
392 /* TODO: check security descriptor of the endpoint here
393 * if it's a smb named pipe
394 * if it's failed free dce_conn_p
401 static void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
404 pkt->rpc_vers_minor = 0;
408 pkt->drep[0] = DCERPC_DREP_LE;
416 move a call from an existing linked list to the specified list. This
417 prevents bugs where we forget to remove the call from a previous
420 static void dcesrv_call_set_list(struct dcesrv_call_state *call,
421 enum dcesrv_call_list list)
423 switch (call->list) {
424 case DCESRV_LIST_NONE:
426 case DCESRV_LIST_CALL_LIST:
427 DLIST_REMOVE(call->conn->call_list, call);
429 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
430 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
432 case DCESRV_LIST_PENDING_CALL_LIST:
433 DLIST_REMOVE(call->conn->pending_call_list, call);
438 case DCESRV_LIST_NONE:
440 case DCESRV_LIST_CALL_LIST:
441 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
443 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
444 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
446 case DCESRV_LIST_PENDING_CALL_LIST:
447 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
453 return a dcerpc fault
455 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
457 struct ncacn_packet pkt;
458 struct data_blob_list_item *rep;
462 /* setup a bind_ack */
463 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
465 pkt.call_id = call->pkt.call_id;
466 pkt.ptype = DCERPC_PKT_FAULT;
467 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
468 pkt.u.fault.alloc_hint = 0;
469 pkt.u.fault.context_id = 0;
470 pkt.u.fault.cancel_count = 0;
471 pkt.u.fault.status = fault_code;
474 pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
476 rep = talloc(call, struct data_blob_list_item);
478 return NT_STATUS_NO_MEMORY;
481 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
482 if (!NT_STATUS_IS_OK(status)) {
486 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
488 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
489 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
496 return a dcerpc bind_nak
498 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
500 struct ncacn_packet pkt;
501 struct data_blob_list_item *rep;
504 /* setup a bind_nak */
505 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
507 pkt.call_id = call->pkt.call_id;
508 pkt.ptype = DCERPC_PKT_BIND_NAK;
509 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
510 pkt.u.bind_nak.reject_reason = reason;
511 if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
512 pkt.u.bind_nak.versions.v.num_versions = 0;
515 rep = talloc(call, struct data_blob_list_item);
517 return NT_STATUS_NO_MEMORY;
520 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
521 if (!NT_STATUS_IS_OK(status)) {
525 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
527 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
528 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
535 handle a bind request
537 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
539 uint32_t if_version, transfer_syntax_version;
540 struct GUID uuid, *transfer_syntax_uuid;
541 struct ncacn_packet pkt;
542 struct data_blob_list_item *rep;
544 uint32_t result=0, reason=0;
546 const struct dcesrv_interface *iface;
549 * Association groups allow policy handles to be shared across
550 * multiple client connections. We don't implement this yet.
552 * So we just allow 0 if the client wants to create a new
555 * And we allow the 0x12345678 value, we give away as
556 * assoc_group_id back to the clients
558 if (call->pkt.u.bind.assoc_group_id != 0 &&
559 call->pkt.u.bind.assoc_group_id != SAMBA_ACCOC_GROUP) {
560 return dcesrv_bind_nak(call, 0);
563 if (call->pkt.u.bind.num_contexts < 1 ||
564 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
565 return dcesrv_bind_nak(call, 0);
568 context_id = call->pkt.u.bind.ctx_list[0].context_id;
570 /* you can't bind twice on one context */
571 if (dcesrv_find_context(call->conn, context_id) != NULL) {
572 return dcesrv_bind_nak(call, 0);
575 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
576 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
578 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
579 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
580 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
581 ndr_transfer_syntax.if_version != transfer_syntax_version) {
582 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
583 /* we only do NDR encoded dcerpc */
584 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
585 talloc_free(uuid_str);
586 return dcesrv_bind_nak(call, 0);
589 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
591 char *uuid_str = GUID_string(call, &uuid);
592 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
593 talloc_free(uuid_str);
595 /* we don't know about that interface */
596 result = DCERPC_BIND_PROVIDER_REJECT;
597 reason = DCERPC_BIND_REASON_ASYNTAX;
601 /* add this context to the list of available context_ids */
602 struct dcesrv_connection_context *context = talloc(call->conn,
603 struct dcesrv_connection_context);
604 if (context == NULL) {
605 return dcesrv_bind_nak(call, 0);
607 context->conn = call->conn;
608 context->iface = iface;
609 context->context_id = context_id;
610 context->private = NULL;
611 context->handles = NULL;
612 DLIST_ADD(call->conn->contexts, context);
613 call->context = context;
616 if (call->conn->cli_max_recv_frag == 0) {
617 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
620 /* handle any authentication that is being requested */
621 if (!dcesrv_auth_bind(call)) {
622 return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
625 /* setup a bind_ack */
626 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
628 pkt.call_id = call->pkt.call_id;
629 pkt.ptype = DCERPC_PKT_BIND_ACK;
630 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
631 pkt.u.bind_ack.max_xmit_frag = 0x2000;
632 pkt.u.bind_ack.max_recv_frag = 0x2000;
633 /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
634 pkt.u.bind_ack.assoc_group_id = SAMBA_ACCOC_GROUP;
636 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
637 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
639 pkt.u.bind_ack.secondary_address = "";
641 pkt.u.bind_ack.num_results = 1;
642 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
643 if (!pkt.u.bind_ack.ctx_list) {
644 return NT_STATUS_NO_MEMORY;
646 pkt.u.bind_ack.ctx_list[0].result = result;
647 pkt.u.bind_ack.ctx_list[0].reason = reason;
648 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
649 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
651 status = dcesrv_auth_bind_ack(call, &pkt);
652 if (!NT_STATUS_IS_OK(status)) {
653 return dcesrv_bind_nak(call, 0);
657 status = iface->bind(call, iface);
658 if (!NT_STATUS_IS_OK(status)) {
659 char *uuid_str = GUID_string(call, &uuid);
660 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
661 uuid_str, if_version, nt_errstr(status)));
662 talloc_free(uuid_str);
663 return dcesrv_bind_nak(call, 0);
667 rep = talloc(call, struct data_blob_list_item);
669 return NT_STATUS_NO_MEMORY;
672 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
673 if (!NT_STATUS_IS_OK(status)) {
677 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
679 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
680 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
687 handle a auth3 request
689 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
691 /* handle the auth3 in the auth code */
692 if (!dcesrv_auth_auth3(call)) {
693 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
698 /* we don't send a reply to a auth3 request, except by a
705 handle a bind request
707 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
709 uint32_t if_version, transfer_syntax_version;
710 struct dcesrv_connection_context *context;
711 const struct dcesrv_interface *iface;
712 struct GUID uuid, *transfer_syntax_uuid;
715 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
716 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
718 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
719 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
720 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
721 ndr_transfer_syntax.if_version != transfer_syntax_version) {
722 /* we only do NDR encoded dcerpc */
723 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
726 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
728 char *uuid_str = GUID_string(call, &uuid);
729 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
730 talloc_free(uuid_str);
731 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
734 /* add this context to the list of available context_ids */
735 context = talloc(call->conn, struct dcesrv_connection_context);
736 if (context == NULL) {
737 return NT_STATUS_NO_MEMORY;
739 context->conn = call->conn;
740 context->iface = iface;
741 context->context_id = context_id;
742 context->private = NULL;
743 context->handles = NULL;
744 DLIST_ADD(call->conn->contexts, context);
745 call->context = context;
748 status = iface->bind(call, iface);
749 if (!NT_STATUS_IS_OK(status)) {
759 handle a alter context request
761 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
763 struct ncacn_packet pkt;
764 struct data_blob_list_item *rep;
766 uint32_t result=0, reason=0;
769 /* handle any authentication that is being requested */
770 if (!dcesrv_auth_alter(call)) {
771 /* TODO: work out the right reject code */
772 result = DCERPC_BIND_PROVIDER_REJECT;
773 reason = DCERPC_BIND_REASON_ASYNTAX;
776 context_id = call->pkt.u.alter.ctx_list[0].context_id;
778 /* see if they are asking for a new interface */
780 dcesrv_find_context(call->conn, context_id) == NULL) {
781 status = dcesrv_alter_new_context(call, context_id);
782 if (!NT_STATUS_IS_OK(status)) {
783 result = DCERPC_BIND_PROVIDER_REJECT;
784 reason = DCERPC_BIND_REASON_ASYNTAX;
788 /* setup a alter_resp */
789 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
791 pkt.call_id = call->pkt.call_id;
792 pkt.ptype = DCERPC_PKT_ALTER_RESP;
793 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
794 pkt.u.alter_resp.max_xmit_frag = 0x2000;
795 pkt.u.alter_resp.max_recv_frag = 0x2000;
796 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
797 pkt.u.alter_resp.num_results = 1;
798 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
799 if (!pkt.u.alter_resp.ctx_list) {
800 return NT_STATUS_NO_MEMORY;
802 pkt.u.alter_resp.ctx_list[0].result = result;
803 pkt.u.alter_resp.ctx_list[0].reason = reason;
804 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
805 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
806 pkt.u.alter_resp.secondary_address = "";
808 status = dcesrv_auth_alter_ack(call, &pkt);
809 if (!NT_STATUS_IS_OK(status)) {
810 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
811 || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
812 || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
813 || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
814 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
816 return dcesrv_fault(call, 0);
819 rep = talloc(call, struct data_blob_list_item);
821 return NT_STATUS_NO_MEMORY;
824 status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
825 if (!NT_STATUS_IS_OK(status)) {
829 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
831 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
832 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
838 handle a dcerpc request packet
840 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
842 struct ndr_pull *pull;
844 struct dcesrv_connection_context *context;
846 /* if authenticated, and the mech we use can't do async replies, don't use them... */
847 if (call->conn->auth_state.gensec_security &&
848 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
849 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
852 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
853 if (context == NULL) {
854 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
857 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
858 lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
859 NT_STATUS_HAVE_NO_MEMORY(pull);
861 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
863 call->context = context;
864 call->ndr_pull = pull;
866 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
867 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
870 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
871 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
874 /* unravel the NDR for the packet */
875 status = context->iface->ndr_pull(call, call, pull, &call->r);
876 if (!NT_STATUS_IS_OK(status)) {
877 return dcesrv_fault(call, call->fault_code);
880 if (pull->offset != pull->data_size) {
881 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
882 pull->data_size - pull->offset));
883 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
886 /* call the dispatch function */
887 status = context->iface->dispatch(call, call, call->r);
888 if (!NT_STATUS_IS_OK(status)) {
889 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
890 context->iface->name,
891 call->pkt.u.request.opnum,
892 dcerpc_errstr(pull, call->fault_code)));
893 return dcesrv_fault(call, call->fault_code);
896 /* add the call to the pending list */
897 dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
899 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
903 return dcesrv_reply(call);
906 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
908 struct ndr_push *push;
911 uint32_t total_length, chunk_size;
912 struct dcesrv_connection_context *context = call->context;
914 /* call the reply function */
915 status = context->iface->reply(call, call, call->r);
916 if (!NT_STATUS_IS_OK(status)) {
917 return dcesrv_fault(call, call->fault_code);
920 /* form the reply NDR */
921 push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
922 NT_STATUS_HAVE_NO_MEMORY(push);
924 /* carry over the pointer count to the reply in case we are
925 using full pointer. See NDR specification for full
927 push->ptr_count = call->ndr_pull->ptr_count;
929 if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
930 push->flags |= LIBNDR_FLAG_BIGENDIAN;
933 status = context->iface->ndr_push(call, call, push, call->r);
934 if (!NT_STATUS_IS_OK(status)) {
935 return dcesrv_fault(call, call->fault_code);
938 stub = ndr_push_blob(push);
940 total_length = stub.length;
942 /* we can write a full max_recv_frag size, minus the dcerpc
943 request header size */
944 chunk_size = call->conn->cli_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
948 struct data_blob_list_item *rep;
949 struct ncacn_packet pkt;
950 const uint32_t overhead = (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
952 rep = talloc(call, struct data_blob_list_item);
953 NT_STATUS_HAVE_NO_MEMORY(rep);
955 length = MIN(chunk_size, stub.length);
957 /* form the dcerpc response packet */
958 dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
960 pkt.call_id = call->pkt.call_id;
961 pkt.ptype = DCERPC_PKT_RESPONSE;
963 if (stub.length == total_length) {
964 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
966 if (length == stub.length) {
967 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
969 pkt.u.response.alloc_hint = stub.length;
970 pkt.u.response.context_id = call->pkt.u.request.context_id;
971 pkt.u.response.cancel_count = 0;
972 pkt.u.response.stub_and_verifier.data = stub.data;
973 pkt.u.response.stub_and_verifier.length = length;
975 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
976 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
979 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
981 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
984 stub.length -= length;
985 } while (stub.length != 0);
987 /* move the call from the pending to the finished calls list */
988 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
990 if (call->conn->call_list && call->conn->call_list->replies) {
991 if (call->conn->transport.report_output_data) {
992 call->conn->transport.report_output_data(call->conn);
999 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1001 if (!conn->transport.get_my_addr) {
1005 return conn->transport.get_my_addr(conn, mem_ctx);
1008 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
1010 if (!conn->transport.get_peer_addr) {
1014 return conn->transport.get_peer_addr(conn, mem_ctx);
1018 work out if we have a full packet yet
1020 static bool dce_full_packet(const DATA_BLOB *data)
1022 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
1025 if (dcerpc_get_frag_length(data) > data->length) {
1032 we might have consumed only part of our input - advance past that part
1034 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1038 if (dce_conn->partial_input.length == offset) {
1039 data_blob_free(&dce_conn->partial_input);
1043 blob = dce_conn->partial_input;
1044 dce_conn->partial_input = data_blob(blob.data + offset,
1045 blob.length - offset);
1046 data_blob_free(&blob);
1050 remove the call from the right list when freed
1052 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1054 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1059 process some input to a dcerpc endpoint server.
1061 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1063 struct ndr_pull *ndr;
1064 enum ndr_err_code ndr_err;
1066 struct dcesrv_call_state *call;
1069 call = talloc_zero(dce_conn, struct dcesrv_call_state);
1071 talloc_free(dce_conn->partial_input.data);
1072 return NT_STATUS_NO_MEMORY;
1074 call->conn = dce_conn;
1075 call->event_ctx = dce_conn->event_ctx;
1076 call->msg_ctx = dce_conn->msg_ctx;
1077 call->state_flags = call->conn->state_flags;
1078 call->time = timeval_current();
1079 call->list = DCESRV_LIST_NONE;
1081 talloc_set_destructor(call, dcesrv_call_dequeue);
1083 blob = dce_conn->partial_input;
1084 blob.length = dcerpc_get_frag_length(&blob);
1086 ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
1088 talloc_free(dce_conn->partial_input.data);
1090 return NT_STATUS_NO_MEMORY;
1093 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1094 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1097 ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1098 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1099 talloc_free(dce_conn->partial_input.data);
1101 return ndr_map_error2ntstatus(ndr_err);
1104 /* we have to check the signing here, before combining the
1106 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1107 !dcesrv_auth_request(call, &blob)) {
1108 dce_partial_advance(dce_conn, blob.length);
1109 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
1112 dce_partial_advance(dce_conn, blob.length);
1114 /* see if this is a continued packet */
1115 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1116 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1117 struct dcesrv_call_state *call2 = call;
1118 uint32_t alloc_size;
1120 /* we only allow fragmented requests, no other packet types */
1121 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1122 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1125 /* this is a continuation of an existing call - find the call then
1126 tack it on the end */
1127 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1129 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1132 if (call->pkt.ptype != call2->pkt.ptype) {
1133 /* trying to play silly buggers are we? */
1134 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1137 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1138 call2->pkt.u.request.stub_and_verifier.length;
1139 if (call->pkt.u.request.alloc_hint > alloc_size) {
1140 alloc_size = call->pkt.u.request.alloc_hint;
1143 call->pkt.u.request.stub_and_verifier.data =
1144 talloc_realloc(call,
1145 call->pkt.u.request.stub_and_verifier.data,
1146 uint8_t, alloc_size);
1147 if (!call->pkt.u.request.stub_and_verifier.data) {
1148 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1150 memcpy(call->pkt.u.request.stub_and_verifier.data +
1151 call->pkt.u.request.stub_and_verifier.length,
1152 call2->pkt.u.request.stub_and_verifier.data,
1153 call2->pkt.u.request.stub_and_verifier.length);
1154 call->pkt.u.request.stub_and_verifier.length +=
1155 call2->pkt.u.request.stub_and_verifier.length;
1157 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1162 /* this may not be the last pdu in the chain - if its isn't then
1163 just put it on the incoming_fragmented_call_list and wait for the rest */
1164 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1165 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1166 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1167 return NT_STATUS_OK;
1170 /* This removes any fragments we may have had stashed away */
1171 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1173 switch (call->pkt.ptype) {
1174 case DCERPC_PKT_BIND:
1175 status = dcesrv_bind(call);
1177 case DCERPC_PKT_AUTH3:
1178 status = dcesrv_auth3(call);
1180 case DCERPC_PKT_ALTER:
1181 status = dcesrv_alter(call);
1183 case DCERPC_PKT_REQUEST:
1184 status = dcesrv_request(call);
1187 status = NT_STATUS_INVALID_PARAMETER;
1191 /* if we are going to be sending a reply then add
1192 it to the list of pending calls. We add it to the end to keep the call
1193 list in the order we will answer */
1194 if (!NT_STATUS_IS_OK(status)) {
1203 provide some input to a dcerpc endpoint server. This passes data
1204 from a dcerpc client into the server
1206 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1210 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1211 dce_conn->partial_input.data,
1213 dce_conn->partial_input.length + data->length);
1214 if (!dce_conn->partial_input.data) {
1215 return NT_STATUS_NO_MEMORY;
1217 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1218 data->data, data->length);
1219 dce_conn->partial_input.length += data->length;
1221 while (dce_full_packet(&dce_conn->partial_input)) {
1222 status = dcesrv_input_process(dce_conn);
1223 if (!NT_STATUS_IS_OK(status)) {
1228 return NT_STATUS_OK;
1232 retrieve some output from a dcerpc server
1233 The caller supplies a function that will be called to do the
1236 The first argument to write_fn() will be 'private', the second will
1237 be a pointer to a buffer containing the data to be sent and the 3rd
1238 will be a pointer to a size_t variable that will be set to the
1239 number of bytes that are consumed from the output.
1241 from the current fragment
1243 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1245 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1248 struct dcesrv_call_state *call;
1249 struct data_blob_list_item *rep;
1252 call = dce_conn->call_list;
1253 if (!call || !call->replies) {
1254 if (dce_conn->pending_call_list) {
1255 /* TODO: we need to say act async here
1256 * as we know we have pending requests
1257 * which will be finished at a time
1259 return NT_STATUS_FOOBAR;
1261 return NT_STATUS_FOOBAR;
1263 rep = call->replies;
1265 status = write_fn(private_data, &rep->blob, &nwritten);
1266 NT_STATUS_IS_ERR_RETURN(status);
1268 rep->blob.length -= nwritten;
1269 rep->blob.data += nwritten;
1271 if (rep->blob.length == 0) {
1272 /* we're done with this section of the call */
1273 DLIST_REMOVE(call->replies, rep);
1276 if (call->replies == NULL) {
1277 /* we're done with the whole call */
1278 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1285 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
1286 struct loadparm_context *lp_ctx,
1287 const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1290 struct dcesrv_context *dce_ctx;
1293 if (!endpoint_servers) {
1294 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1295 return NT_STATUS_INTERNAL_ERROR;
1298 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1299 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1300 dce_ctx->endpoint_list = NULL;
1301 dce_ctx->lp_ctx = lp_ctx;
1303 for (i=0;endpoint_servers[i];i++) {
1304 const struct dcesrv_endpoint_server *ep_server;
1306 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1308 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1309 return NT_STATUS_INTERNAL_ERROR;
1312 status = ep_server->init_server(dce_ctx, ep_server);
1313 if (!NT_STATUS_IS_OK(status)) {
1314 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1315 nt_errstr(status)));
1320 *_dce_ctx = dce_ctx;
1321 return NT_STATUS_OK;
1324 /* the list of currently registered DCERPC endpoint servers.
1326 static struct ep_server {
1327 struct dcesrv_endpoint_server *ep_server;
1328 } *ep_servers = NULL;
1329 static int num_ep_servers;
1332 register a DCERPC endpoint server.
1334 The 'name' can be later used by other backends to find the operations
1335 structure for this backend.
1337 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1339 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1341 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1343 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1344 /* its already registered! */
1345 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1347 return NT_STATUS_OBJECT_NAME_COLLISION;
1350 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1352 smb_panic("out of memory in dcerpc_register");
1355 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1356 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1360 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1363 return NT_STATUS_OK;
1367 return the operations structure for a named backend of the specified type
1369 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1373 for (i=0;i<num_ep_servers;i++) {
1374 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1375 return ep_servers[i].ep_server;
1383 return the DCERPC module version, and the size of some critical types
1384 This can be used by endpoint server modules to either detect compilation errors, or provide
1385 multiple implementations for different smbd compilation options in one module
1387 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1389 static const struct dcesrv_critical_sizes critical_sizes = {
1390 DCERPC_MODULE_VERSION,
1391 sizeof(struct dcesrv_context),
1392 sizeof(struct dcesrv_endpoint),
1393 sizeof(struct dcesrv_endpoint_server),
1394 sizeof(struct dcesrv_interface),
1395 sizeof(struct dcesrv_if_list),
1396 sizeof(struct dcesrv_connection),
1397 sizeof(struct dcesrv_call_state),
1398 sizeof(struct dcesrv_auth),
1399 sizeof(struct dcesrv_handle)
1402 return &critical_sizes;
1406 initialise the dcerpc server context for ncacn_np based services
1408 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
1409 struct dcesrv_context **_dce_ctx)
1412 struct dcesrv_context *dce_ctx;
1414 status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
1415 NT_STATUS_NOT_OK_RETURN(status);
1417 *_dce_ctx = dce_ctx;
1418 return NT_STATUS_OK;