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 2 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, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "librpc/gen_ndr/ndr_epmapper.h"
26 #include "librpc/gen_ndr/ndr_dcerpc.h"
27 #include "librpc/gen_ndr/ndr_oxidresolver.h"
28 #include "auth/auth.h"
29 #include "dlinklist.h"
30 #include "rpc_server/dcerpc_server.h"
31 #include "lib/events/events.h"
32 #include "smbd/service_stream.h"
33 #include "system/filesys.h"
34 #include "smb_build.h"
37 see if two endpoints match
39 static BOOL endpoints_match(const struct dcerpc_binding *ep1,
40 const struct dcerpc_binding *ep2)
42 if (ep1->transport != ep2->transport) {
46 if (!ep1->endpoint || !ep2->endpoint) {
47 return ep1->endpoint == ep2->endpoint;
50 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
57 find an endpoint in the dcesrv_context
59 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
60 const struct dcerpc_binding *ep_description)
62 struct dcesrv_endpoint *ep;
63 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
64 if (endpoints_match(ep->ep_description, ep_description)) {
72 find a registered context_id from a bind or alter_context
74 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
77 struct dcesrv_connection_context *c;
78 for (c=conn->contexts;c;c=c->next) {
79 if (c->context_id == context_id) return c;
85 see if a uuid and if_version match to an interface
87 static BOOL interface_match(const struct dcesrv_interface *if1,
88 const struct dcesrv_interface *if2)
90 if (if1->if_version != if2->if_version) {
94 if (strcmp(if1->uuid, if2->uuid)==0) {
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 char *uuid, uint32_t if_version)
122 if (iface->if_version != if_version) {
126 if (strcmp(iface->uuid, uuid)==0) {
134 find the interface operations on an endpoint by uuid
136 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
137 const char *uuid, uint32_t if_version)
139 struct dcesrv_if_list *ifl;
140 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
141 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
142 return &(ifl->iface);
149 find a call that is pending in our call list
151 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
153 struct dcesrv_call_state *c;
154 for (c=dce_conn->call_list;c;c=c->next) {
155 if (c->pkt.call_id == call_id) {
163 register an interface on an endpoint
165 NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
167 const struct dcesrv_interface *iface,
168 const struct security_descriptor *sd)
170 struct dcesrv_endpoint *ep;
171 struct dcesrv_if_list *ifl;
172 struct dcerpc_binding *binding;
176 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
178 if (NT_STATUS_IS_ERR(status)) {
179 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
183 /* check if this endpoint exists
185 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
186 ep = talloc(dce_ctx, struct dcesrv_endpoint);
188 return NT_STATUS_NO_MEMORY;
191 ep->ep_description = talloc_reference(ep, binding);
195 /* see if the interface is already registered on te endpoint */
196 if (find_interface(ep, iface)!=NULL) {
197 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
198 iface->name, ep_name));
199 return NT_STATUS_OBJECT_NAME_COLLISION;
202 /* talloc a new interface list element */
203 ifl = talloc(dce_ctx, struct dcesrv_if_list);
205 return NT_STATUS_NO_MEMORY;
208 /* copy the given interface struct to the one on the endpoints interface list */
209 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
211 /* if we have a security descriptor given,
212 * we should see if we can set it up on the endpoint
215 /* if there's currently no security descriptor given on the endpoint
218 if (ep->sd == NULL) {
219 ep->sd = security_descriptor_copy(dce_ctx, sd);
222 /* if now there's no security descriptor given on the endpoint
223 * something goes wrong, either we failed to copy the security descriptor
224 * or there was already one on the endpoint
226 if (ep->sd != NULL) {
227 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
228 " on endpoint '%s'\n",
229 iface->name, ep_name));
230 if (add_ep) free(ep);
232 return NT_STATUS_OBJECT_NAME_COLLISION;
236 /* finally add the interface on the endpoint */
237 DLIST_ADD(ep->interface_list, ifl);
239 /* if it's a new endpoint add it to the dcesrv_context */
241 DLIST_ADD(dce_ctx->endpoint_list, ep);
244 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
245 iface->name, ep_name));
250 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
251 DATA_BLOB *session_key)
253 if (p->auth_state.session_info->session_key.length) {
254 *session_key = p->auth_state.session_info->session_key;
257 return NT_STATUS_NO_USER_SESSION_KEY;
260 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
261 DATA_BLOB *session_key)
263 /* this took quite a few CPU cycles to find ... */
264 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
265 session_key->length = 16;
270 fetch the user session key - may be default (above) or the SMB session key
272 NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
273 DATA_BLOB *session_key)
275 return p->auth_state.session_key(p, session_key);
280 destroy a link to an endpoint
282 static int dcesrv_endpoint_destructor(void *ptr)
284 struct dcesrv_connection *p = ptr;
286 while (p->contexts) {
287 struct dcesrv_connection_context *c = p->contexts;
289 DLIST_REMOVE(p->contexts, c);
292 c->iface->unbind(c, c->iface);
301 connect to a dcerpc endpoint
303 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
305 const struct dcesrv_endpoint *ep,
306 struct stream_connection *srv_conn,
307 struct dcesrv_connection **_p)
309 struct dcesrv_connection *p;
311 p = talloc(mem_ctx, struct dcesrv_connection);
312 NT_STATUS_HAVE_NO_MEMORY(p);
314 p->dce_ctx = dce_ctx;
318 p->pending_call_list = NULL;
319 p->cli_max_recv_frag = 0;
320 p->partial_input = data_blob(NULL, 0);
321 p->auth_state.auth_info = NULL;
322 p->auth_state.gensec_security = NULL;
323 p->auth_state.session_info = NULL;
324 p->auth_state.session_key = dcesrv_generic_session_key;
325 p->srv_conn = srv_conn;
326 p->processing = False;
328 talloc_set_destructor(p, dcesrv_endpoint_destructor);
335 search and connect to a dcerpc endpoint
337 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
339 const struct dcerpc_binding *ep_description,
340 struct auth_session_info *session_info,
341 struct stream_connection *srv_conn,
342 struct dcesrv_connection **dce_conn_p)
345 const struct dcesrv_endpoint *ep;
347 /* make sure this endpoint exists */
348 ep = find_endpoint(dce_ctx, ep_description);
350 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
353 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, dce_conn_p);
354 if (!NT_STATUS_IS_OK(status)) {
358 (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
359 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
361 /* TODO: check security descriptor of the endpoint here
362 * if it's a smb named pipe
363 * if it's failed free dce_conn_p
370 static void dcesrv_init_hdr(struct ncacn_packet *pkt)
373 pkt->rpc_vers_minor = 0;
374 if (lp_rpc_big_endian()) {
377 pkt->drep[0] = DCERPC_DREP_LE;
385 return a dcerpc fault
387 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
389 struct ncacn_packet pkt;
390 struct data_blob_list_item *rep;
393 /* setup a bind_ack */
394 dcesrv_init_hdr(&pkt);
396 pkt.call_id = call->pkt.call_id;
397 pkt.ptype = DCERPC_PKT_FAULT;
398 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
399 pkt.u.fault.alloc_hint = 0;
400 pkt.u.fault.context_id = 0;
401 pkt.u.fault.cancel_count = 0;
402 pkt.u.fault.status = fault_code;
404 rep = talloc(call, struct data_blob_list_item);
406 return NT_STATUS_NO_MEMORY;
409 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
410 if (!NT_STATUS_IS_OK(status)) {
414 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
416 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
417 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
424 return a dcerpc bind_nak
426 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
428 struct ncacn_packet pkt;
429 struct data_blob_list_item *rep;
432 /* setup a bind_nak */
433 dcesrv_init_hdr(&pkt);
435 pkt.call_id = call->pkt.call_id;
436 pkt.ptype = DCERPC_PKT_BIND_NAK;
437 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
438 pkt.u.bind_nak.reject_reason = reason;
439 pkt.u.bind_nak.num_versions = 0;
441 rep = talloc(call, struct data_blob_list_item);
443 return NT_STATUS_NO_MEMORY;
446 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
447 if (!NT_STATUS_IS_OK(status)) {
451 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
453 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
454 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
461 handle a bind request
463 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
465 const char *uuid, *transfer_syntax;
466 uint32_t if_version, transfer_syntax_version;
467 struct ncacn_packet pkt;
468 struct data_blob_list_item *rep;
470 uint32_t result=0, reason=0;
472 const struct dcesrv_interface *iface;
474 if (call->pkt.u.bind.num_contexts < 1 ||
475 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
476 return dcesrv_bind_nak(call, 0);
479 context_id = call->pkt.u.bind.ctx_list[0].context_id;
481 /* you can't bind twice on one context */
482 if (dcesrv_find_context(call->conn, context_id) != NULL) {
483 return dcesrv_bind_nak(call, 0);
486 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
487 uuid = GUID_string(call, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
489 return dcesrv_bind_nak(call, 0);
492 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
493 transfer_syntax = GUID_string(call,
494 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
495 if (!transfer_syntax ||
496 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
497 NDR_GUID_VERSION != transfer_syntax_version) {
498 /* we only do NDR encoded dcerpc */
499 DEBUG(0,("Non NDR transfer syntax requested - %s\n", transfer_syntax));
500 return dcesrv_bind_nak(call, 0);
503 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
505 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
506 /* we don't know about that interface */
507 result = DCERPC_BIND_PROVIDER_REJECT;
508 reason = DCERPC_BIND_REASON_ASYNTAX;
512 /* add this context to the list of available context_ids */
513 struct dcesrv_connection_context *context = talloc(call->conn,
514 struct dcesrv_connection_context);
515 if (context == NULL) {
516 return dcesrv_bind_nak(call, 0);
518 context->conn = call->conn;
519 context->iface = iface;
520 context->context_id = context_id;
521 context->private = NULL;
522 context->handles = NULL;
523 DLIST_ADD(call->conn->contexts, context);
524 call->context = context;
527 if (call->conn->cli_max_recv_frag == 0) {
528 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
531 /* handle any authentication that is being requested */
532 if (!dcesrv_auth_bind(call)) {
533 /* TODO: work out the right reject code */
534 return dcesrv_bind_nak(call, 0);
537 /* setup a bind_ack */
538 dcesrv_init_hdr(&pkt);
540 pkt.call_id = call->pkt.call_id;
541 pkt.ptype = DCERPC_PKT_BIND_ACK;
542 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
543 pkt.u.bind_ack.max_xmit_frag = 0x2000;
544 pkt.u.bind_ack.max_recv_frag = 0x2000;
545 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
547 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
548 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
550 pkt.u.bind_ack.secondary_address = "";
552 pkt.u.bind_ack.num_results = 1;
553 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
554 if (!pkt.u.bind_ack.ctx_list) {
555 return NT_STATUS_NO_MEMORY;
557 pkt.u.bind_ack.ctx_list[0].result = result;
558 pkt.u.bind_ack.ctx_list[0].reason = reason;
559 GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
560 pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
561 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
563 if (!dcesrv_auth_bind_ack(call, &pkt)) {
564 return dcesrv_bind_nak(call, 0);
568 status = iface->bind(call, iface);
569 if (!NT_STATUS_IS_OK(status)) {
570 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
571 uuid, if_version, nt_errstr(status)));
572 return dcesrv_bind_nak(call, 0);
576 rep = talloc(call, struct data_blob_list_item);
578 return NT_STATUS_NO_MEMORY;
581 status = ncacn_push_auth(&rep->blob, call, &pkt,
582 call->conn->auth_state.auth_info);
583 if (!NT_STATUS_IS_OK(status)) {
587 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
589 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
590 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
597 handle a auth3 request
599 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
601 /* handle the auth3 in the auth code */
602 if (!dcesrv_auth_auth3(call)) {
603 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
608 /* we don't send a reply to a auth3 request, except by a
615 handle a bind request
617 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
619 uint32_t if_version, transfer_syntax_version;
620 const char *uuid, *transfer_syntax;
621 struct dcesrv_connection_context *context;
622 const struct dcesrv_interface *iface;
624 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
625 uuid = GUID_string(call, &call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid);
627 return NT_STATUS_NO_MEMORY;
630 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
631 transfer_syntax = GUID_string(call,
632 &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid);
633 if (!transfer_syntax ||
634 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
635 NDR_GUID_VERSION != transfer_syntax_version) {
636 /* we only do NDR encoded dcerpc */
637 return NT_STATUS_NO_MEMORY;
640 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
642 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
643 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
646 /* add this context to the list of available context_ids */
647 context = talloc(call->conn, struct dcesrv_connection_context);
648 if (context == NULL) {
649 return NT_STATUS_NO_MEMORY;
651 context->conn = call->conn;
652 context->iface = iface;
653 context->context_id = context_id;
654 context->private = NULL;
655 context->handles = NULL;
656 DLIST_ADD(call->conn->contexts, context);
657 call->context = context;
664 handle a bind request
666 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
668 struct ncacn_packet pkt;
669 struct data_blob_list_item *rep;
671 uint32_t result=0, reason=0;
674 /* handle any authentication that is being requested */
675 if (!dcesrv_auth_alter(call)) {
676 /* TODO: work out the right reject code */
677 result = DCERPC_BIND_PROVIDER_REJECT;
678 reason = DCERPC_BIND_REASON_ASYNTAX;
681 context_id = call->pkt.u.alter.ctx_list[0].context_id;
683 /* see if they are asking for a new interface */
685 dcesrv_find_context(call->conn, context_id) == NULL) {
686 status = dcesrv_alter_new_context(call, context_id);
687 if (!NT_STATUS_IS_OK(status)) {
688 result = DCERPC_BIND_PROVIDER_REJECT;
689 reason = DCERPC_BIND_REASON_ASYNTAX;
693 /* setup a alter_resp */
694 dcesrv_init_hdr(&pkt);
696 pkt.call_id = call->pkt.call_id;
697 pkt.ptype = DCERPC_PKT_ALTER_RESP;
698 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
699 pkt.u.alter_resp.max_xmit_frag = 0x2000;
700 pkt.u.alter_resp.max_recv_frag = 0x2000;
701 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
702 pkt.u.alter_resp.num_results = 1;
703 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
704 if (!pkt.u.alter_resp.ctx_list) {
705 return NT_STATUS_NO_MEMORY;
707 pkt.u.alter_resp.ctx_list[0].result = result;
708 pkt.u.alter_resp.ctx_list[0].reason = reason;
709 GUID_from_string(NDR_GUID, &pkt.u.alter_resp.ctx_list[0].syntax.uuid);
710 pkt.u.alter_resp.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
711 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
712 pkt.u.alter_resp.secondary_address = "";
714 if (!dcesrv_auth_alter_ack(call, &pkt)) {
715 return dcesrv_bind_nak(call, 0);
718 rep = talloc(call, struct data_blob_list_item);
720 return NT_STATUS_NO_MEMORY;
723 status = ncacn_push_auth(&rep->blob, call, &pkt,
724 call->conn->auth_state.auth_info);
725 if (!NT_STATUS_IS_OK(status)) {
729 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
731 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
732 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
738 handle a dcerpc request packet
740 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
742 struct ndr_pull *pull;
744 struct dcesrv_connection_context *context;
746 call->fault_code = 0;
747 call->state_flags = call->conn->dce_ctx->state_flags;
748 call->time = timeval_current();
750 /* if authenticated, and the mech we use can't do async replies, don't use them... */
751 if (call->conn->auth_state.gensec_security &&
752 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
753 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
756 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
757 if (context == NULL) {
758 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
761 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
762 NT_STATUS_HAVE_NO_MEMORY(pull);
764 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
766 call->context = context;
767 call->event_ctx = context->conn->srv_conn->event.ctx;
768 call->ndr_pull = pull;
770 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
771 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
774 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
775 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
778 /* unravel the NDR for the packet */
779 status = context->iface->ndr_pull(call, call, pull, &call->r);
780 if (!NT_STATUS_IS_OK(status)) {
781 return dcesrv_fault(call, call->fault_code);
784 if (pull->offset != pull->data_size) {
785 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
786 pull->data_size - pull->offset));
787 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
790 /* call the dispatch function */
791 status = context->iface->dispatch(call, call, call->r);
792 if (!NT_STATUS_IS_OK(status)) {
793 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
794 context->iface->name,
795 call->pkt.u.request.opnum,
796 dcerpc_errstr(pull, call->fault_code)));
797 return dcesrv_fault(call, call->fault_code);
800 /* add the call to the pending list */
801 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
803 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
807 return dcesrv_reply(call);
810 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
812 struct ndr_push *push;
815 uint32_t total_length;
816 struct dcesrv_connection_context *context = call->context;
818 /* call the reply function */
819 status = context->iface->reply(call, call, call->r);
820 if (!NT_STATUS_IS_OK(status)) {
821 return dcesrv_fault(call, call->fault_code);
824 /* form the reply NDR */
825 push = ndr_push_init_ctx(call);
826 NT_STATUS_HAVE_NO_MEMORY(push);
828 /* carry over the pointer count to the reply in case we are
829 using full pointer. See NDR specification for full
831 push->ptr_count = call->ndr_pull->ptr_count;
833 if (lp_rpc_big_endian()) {
834 push->flags |= LIBNDR_FLAG_BIGENDIAN;
837 status = context->iface->ndr_push(call, call, push, call->r);
838 if (!NT_STATUS_IS_OK(status)) {
839 return dcesrv_fault(call, call->fault_code);
842 stub = ndr_push_blob(push);
844 total_length = stub.length;
848 struct data_blob_list_item *rep;
849 struct ncacn_packet pkt;
851 rep = talloc(call, struct data_blob_list_item);
852 NT_STATUS_HAVE_NO_MEMORY(rep);
854 length = stub.length;
855 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
856 /* the 32 is to cope with signing data */
857 length = call->conn->cli_max_recv_frag -
858 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
861 /* form the dcerpc response packet */
862 dcesrv_init_hdr(&pkt);
864 pkt.call_id = call->pkt.call_id;
865 pkt.ptype = DCERPC_PKT_RESPONSE;
867 if (stub.length == total_length) {
868 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
870 if (length == stub.length) {
871 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
873 pkt.u.response.alloc_hint = stub.length;
874 pkt.u.response.context_id = call->pkt.u.request.context_id;
875 pkt.u.response.cancel_count = 0;
876 pkt.u.response.stub_and_verifier.data = stub.data;
877 pkt.u.response.stub_and_verifier.length = length;
879 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
880 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
883 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
885 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
888 stub.length -= length;
889 } while (stub.length != 0);
891 /* move the call from the pending to the finished calls list */
892 DLIST_REMOVE(call->conn->pending_call_list, call);
893 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
895 if (call->conn->call_list && call->conn->call_list->replies) {
896 if (call->conn->srv_conn &&
897 call->conn->srv_conn->event.fde) {
898 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
907 work out if we have a full packet yet
909 static BOOL dce_full_packet(const DATA_BLOB *data)
911 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
914 if (dcerpc_get_frag_length(data) > data->length) {
921 we might have consumed only part of our input - advance past that part
923 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
927 if (dce_conn->partial_input.length == offset) {
928 data_blob_free(&dce_conn->partial_input);
932 blob = dce_conn->partial_input;
933 dce_conn->partial_input = data_blob(blob.data + offset,
934 blob.length - offset);
935 data_blob_free(&blob);
939 process some input to a dcerpc endpoint server.
941 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
943 struct ndr_pull *ndr;
945 struct dcesrv_call_state *call;
948 call = talloc(dce_conn, struct dcesrv_call_state);
950 talloc_free(dce_conn->partial_input.data);
951 return NT_STATUS_NO_MEMORY;
953 call->conn = dce_conn;
954 call->replies = NULL;
955 call->context = NULL;
956 call->event_ctx = dce_conn->srv_conn->event.ctx;
958 blob = dce_conn->partial_input;
959 blob.length = dcerpc_get_frag_length(&blob);
961 ndr = ndr_pull_init_blob(&blob, call);
963 talloc_free(dce_conn->partial_input.data);
965 return NT_STATUS_NO_MEMORY;
968 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
969 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
972 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
973 if (!NT_STATUS_IS_OK(status)) {
974 talloc_free(dce_conn->partial_input.data);
979 /* we have to check the signing here, before combining the
981 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
982 !dcesrv_auth_request(call, &blob)) {
983 dce_partial_advance(dce_conn, blob.length);
984 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
987 dce_partial_advance(dce_conn, blob.length);
989 /* see if this is a continued packet */
990 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
991 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
992 struct dcesrv_call_state *call2 = call;
995 /* we only allow fragmented requests, no other packet types */
996 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
997 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1000 /* this is a continuation of an existing call - find the call then
1001 tack it on the end */
1002 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
1004 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1007 if (call->pkt.ptype != call2->pkt.ptype) {
1008 /* trying to play silly buggers are we? */
1009 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1012 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1013 call2->pkt.u.request.stub_and_verifier.length;
1014 if (call->pkt.u.request.alloc_hint > alloc_size) {
1015 alloc_size = call->pkt.u.request.alloc_hint;
1018 call->pkt.u.request.stub_and_verifier.data =
1019 talloc_realloc(call,
1020 call->pkt.u.request.stub_and_verifier.data,
1021 uint8_t, alloc_size);
1022 if (!call->pkt.u.request.stub_and_verifier.data) {
1023 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1025 memcpy(call->pkt.u.request.stub_and_verifier.data +
1026 call->pkt.u.request.stub_and_verifier.length,
1027 call2->pkt.u.request.stub_and_verifier.data,
1028 call2->pkt.u.request.stub_and_verifier.length);
1029 call->pkt.u.request.stub_and_verifier.length +=
1030 call2->pkt.u.request.stub_and_verifier.length;
1032 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1037 /* this may not be the last pdu in the chain - if its isn't then
1038 just put it on the call_list and wait for the rest */
1039 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1040 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1041 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1042 return NT_STATUS_OK;
1045 switch (call->pkt.ptype) {
1046 case DCERPC_PKT_BIND:
1047 status = dcesrv_bind(call);
1049 case DCERPC_PKT_AUTH3:
1050 status = dcesrv_auth3(call);
1052 case DCERPC_PKT_ALTER:
1053 status = dcesrv_alter(call);
1055 case DCERPC_PKT_REQUEST:
1056 status = dcesrv_request(call);
1059 status = NT_STATUS_INVALID_PARAMETER;
1063 /* if we are going to be sending a reply then add
1064 it to the list of pending calls. We add it to the end to keep the call
1065 list in the order we will answer */
1066 if (!NT_STATUS_IS_OK(status)) {
1075 provide some input to a dcerpc endpoint server. This passes data
1076 from a dcerpc client into the server
1078 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1082 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1083 dce_conn->partial_input.data,
1085 dce_conn->partial_input.length + data->length);
1086 if (!dce_conn->partial_input.data) {
1087 return NT_STATUS_NO_MEMORY;
1089 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1090 data->data, data->length);
1091 dce_conn->partial_input.length += data->length;
1093 while (dce_full_packet(&dce_conn->partial_input)) {
1094 status = dcesrv_input_process(dce_conn);
1095 if (!NT_STATUS_IS_OK(status)) {
1100 return NT_STATUS_OK;
1104 retrieve some output from a dcerpc server
1105 The caller supplies a function that will be called to do the
1108 The first argument to write_fn() will be 'private', the second will
1109 be a pointer to a buffer containing the data to be sent and the 3rd
1110 will be a pointer to a size_t variable that will be set to the
1111 number of bytes that are consumed from the output.
1113 from the current fragment
1115 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1117 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1120 struct dcesrv_call_state *call;
1121 struct data_blob_list_item *rep;
1124 call = dce_conn->call_list;
1125 if (!call || !call->replies) {
1126 if (dce_conn->pending_call_list) {
1127 /* TODO: we need to say act async here
1128 * as we know we have pending requests
1129 * which will be finished at a time
1131 return NT_STATUS_FOOBAR;
1133 return NT_STATUS_FOOBAR;
1135 rep = call->replies;
1137 status = write_fn(private_data, &rep->blob, &nwritten);
1138 NT_STATUS_IS_ERR_RETURN(status);
1140 rep->blob.length -= nwritten;
1141 rep->blob.data += nwritten;
1143 if (rep->blob.length == 0) {
1144 /* we're done with this section of the call */
1145 DLIST_REMOVE(call->replies, rep);
1148 if (call->replies == NULL) {
1149 /* we're done with the whole call */
1150 DLIST_REMOVE(dce_conn->call_list, call);
1157 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
1160 struct dcesrv_context *dce_ctx;
1163 if (!endpoint_servers) {
1164 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1165 return NT_STATUS_INTERNAL_ERROR;
1168 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1169 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1170 dce_ctx->endpoint_list = NULL;
1171 dce_ctx->state_flags = state_flags;
1173 for (i=0;endpoint_servers[i];i++) {
1174 const struct dcesrv_endpoint_server *ep_server;
1176 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1178 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1179 return NT_STATUS_INTERNAL_ERROR;
1182 status = ep_server->init_server(dce_ctx, ep_server);
1183 if (!NT_STATUS_IS_OK(status)) {
1184 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1185 nt_errstr(status)));
1190 *_dce_ctx = dce_ctx;
1191 return NT_STATUS_OK;
1195 initialise the dcerpc server context for ncacn_np based services
1197 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1200 struct dcesrv_context *dce_ctx;
1202 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx);
1203 NT_STATUS_NOT_OK_RETURN(status);
1205 *_dce_ctx = dce_ctx;
1206 return NT_STATUS_OK;
1209 /* the list of currently registered DCERPC endpoint servers.
1211 static struct ep_server {
1212 struct dcesrv_endpoint_server *ep_server;
1213 } *ep_servers = NULL;
1214 static int num_ep_servers;
1217 register a DCERPC endpoint server.
1219 The 'name' can be later used by other backends to find the operations
1220 structure for this backend.
1222 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1224 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1226 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1228 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1229 /* its already registered! */
1230 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1232 return NT_STATUS_OBJECT_NAME_COLLISION;
1235 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1237 smb_panic("out of memory in dcerpc_register");
1240 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1241 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1245 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1248 return NT_STATUS_OK;
1252 return the operations structure for a named backend of the specified type
1254 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1258 for (i=0;i<num_ep_servers;i++) {
1259 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1260 return ep_servers[i].ep_server;
1268 return the DCERPC module version, and the size of some critical types
1269 This can be used by endpoint server modules to either detect compilation errors, or provide
1270 multiple implementations for different smbd compilation options in one module
1272 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1274 static const struct dcesrv_critical_sizes critical_sizes = {
1275 DCERPC_MODULE_VERSION,
1276 sizeof(struct dcesrv_context),
1277 sizeof(struct dcesrv_endpoint),
1278 sizeof(struct dcesrv_endpoint_server),
1279 sizeof(struct dcesrv_interface),
1280 sizeof(struct dcesrv_if_list),
1281 sizeof(struct dcesrv_connection),
1282 sizeof(struct dcesrv_call_state),
1283 sizeof(struct dcesrv_auth),
1284 sizeof(struct dcesrv_handle)
1287 return &critical_sizes;
1291 initialise the dcerpc server context
1293 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1296 struct dcesrv_context *dce_ctx;
1297 struct dcesrv_endpoint *e;
1299 status = dcesrv_init_context(event_context,
1300 lp_dcerpc_endpoint_servers(),
1301 DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
1303 NT_STATUS_NOT_OK_RETURN(status);
1305 /* Make sure the directory for NCALRPC exists */
1306 if (!directory_exist(lp_ncalrpc_dir())) {
1307 mkdir(lp_ncalrpc_dir(), 0755);
1310 for (e=dce_ctx->endpoint_list;e;e=e->next) {
1311 switch (e->ep_description->transport) {
1312 case NCACN_UNIX_STREAM:
1313 status = dcesrv_add_ep_unix(dce_ctx, e, event_context, model_ops);
1314 NT_STATUS_NOT_OK_RETURN(status);
1318 status = dcesrv_add_ep_ncalrpc(dce_ctx, e, event_context, model_ops);
1319 NT_STATUS_NOT_OK_RETURN(status);
1323 status = dcesrv_add_ep_tcp(dce_ctx, e, event_context, model_ops);
1324 NT_STATUS_NOT_OK_RETURN(status);
1328 /* FIXME: status = dcesrv_add_ep_np(dce_ctx, e, event_context, model_ops);
1329 NT_STATUS_NOT_OK_RETURN(status); */
1333 return NT_STATUS_NOT_SUPPORTED;
1337 return NT_STATUS_OK;
1340 NTSTATUS server_service_rpc_init(void)
1342 init_module_fn static_init[] = STATIC_DCERPC_MODULES;
1343 init_module_fn *shared_init = load_samba_modules(NULL, "rpc_server");
1345 run_init_functions(static_init);
1346 run_init_functions(shared_init);
1348 talloc_free(shared_init);
1350 return register_server_service("rpc", dcesrv_init);