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_dcerpc.h"
26 #include "auth/auth.h"
27 #include "dlinklist.h"
28 #include "rpc_server/dcerpc_server.h"
29 #include "lib/events/events.h"
30 #include "smbd/service_stream.h"
31 #include "system/filesys.h"
32 #include "libcli/security/proto.h"
36 see if two endpoints match
38 static BOOL endpoints_match(const struct dcerpc_binding *ep1,
39 const struct dcerpc_binding *ep2)
41 if (ep1->transport != ep2->transport) {
45 if (!ep1->endpoint || !ep2->endpoint) {
46 return ep1->endpoint == ep2->endpoint;
49 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
56 find an endpoint in the dcesrv_context
58 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
59 const struct dcerpc_binding *ep_description)
61 struct dcesrv_endpoint *ep;
62 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
63 if (endpoints_match(ep->ep_description, ep_description)) {
71 find a registered context_id from a bind or alter_context
73 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
76 struct dcesrv_connection_context *c;
77 for (c=conn->contexts;c;c=c->next) {
78 if (c->context_id == context_id) return c;
84 see if a uuid and if_version match to an interface
86 static BOOL interface_match(const struct dcesrv_interface *if1,
87 const struct dcesrv_interface *if2)
89 return (if1->if_version == if2->if_version &&
90 GUID_equal(&if1->uuid, &if2->uuid));
94 find the interface operations on an endpoint
96 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
97 const struct dcesrv_interface *iface)
99 struct dcesrv_if_list *ifl;
100 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
101 if (interface_match(&(ifl->iface), iface)) {
102 return &(ifl->iface);
109 see if a uuid and if_version match to an interface
111 static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
112 const struct GUID *uuid, uint32_t if_version)
114 return (iface->if_version == if_version && GUID_equal(&iface->uuid, uuid));
118 find the interface operations on an endpoint by uuid
120 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
121 const struct GUID *uuid, uint32_t if_version)
123 struct dcesrv_if_list *ifl;
124 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
125 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
126 return &(ifl->iface);
133 find a call that is pending in our call list
135 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
137 struct dcesrv_call_state *c;
138 for (c=dce_conn->call_list;c;c=c->next) {
139 if (c->pkt.call_id == call_id) {
147 register an interface on an endpoint
149 NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
151 const struct dcesrv_interface *iface,
152 const struct security_descriptor *sd)
154 struct dcesrv_endpoint *ep;
155 struct dcesrv_if_list *ifl;
156 struct dcerpc_binding *binding;
160 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
162 if (NT_STATUS_IS_ERR(status)) {
163 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
167 /* check if this endpoint exists
169 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
170 ep = talloc(dce_ctx, struct dcesrv_endpoint);
172 return NT_STATUS_NO_MEMORY;
175 ep->ep_description = talloc_reference(ep, binding);
179 /* see if the interface is already registered on te endpoint */
180 if (find_interface(ep, iface)!=NULL) {
181 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
182 iface->name, ep_name));
183 return NT_STATUS_OBJECT_NAME_COLLISION;
186 /* talloc a new interface list element */
187 ifl = talloc(dce_ctx, struct dcesrv_if_list);
189 return NT_STATUS_NO_MEMORY;
192 /* copy the given interface struct to the one on the endpoints interface list */
193 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
195 /* if we have a security descriptor given,
196 * we should see if we can set it up on the endpoint
199 /* if there's currently no security descriptor given on the endpoint
202 if (ep->sd == NULL) {
203 ep->sd = security_descriptor_copy(dce_ctx, sd);
206 /* if now there's no security descriptor given on the endpoint
207 * something goes wrong, either we failed to copy the security descriptor
208 * or there was already one on the endpoint
210 if (ep->sd != NULL) {
211 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
212 " on endpoint '%s'\n",
213 iface->name, ep_name));
214 if (add_ep) free(ep);
216 return NT_STATUS_OBJECT_NAME_COLLISION;
220 /* finally add the interface on the endpoint */
221 DLIST_ADD(ep->interface_list, ifl);
223 /* if it's a new endpoint add it to the dcesrv_context */
225 DLIST_ADD(dce_ctx->endpoint_list, ep);
228 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
229 iface->name, ep_name));
234 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
235 DATA_BLOB *session_key)
237 if (p->auth_state.session_info->session_key.length) {
238 *session_key = p->auth_state.session_info->session_key;
241 return NT_STATUS_NO_USER_SESSION_KEY;
244 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
245 DATA_BLOB *session_key)
247 /* this took quite a few CPU cycles to find ... */
248 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
249 session_key->length = 16;
254 fetch the user session key - may be default (above) or the SMB session key
256 NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
257 DATA_BLOB *session_key)
259 return p->auth_state.session_key(p, session_key);
264 destroy a link to an endpoint
266 static int dcesrv_endpoint_destructor(void *ptr)
268 struct dcesrv_connection *p = ptr;
270 while (p->contexts) {
271 struct dcesrv_connection_context *c = p->contexts;
273 DLIST_REMOVE(p->contexts, c);
276 c->iface->unbind(c, c->iface);
285 connect to a dcerpc endpoint
287 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
289 const struct dcesrv_endpoint *ep,
290 struct stream_connection *srv_conn,
291 uint32_t state_flags,
292 struct dcesrv_connection **_p)
294 struct dcesrv_connection *p;
296 p = talloc(mem_ctx, struct dcesrv_connection);
297 NT_STATUS_HAVE_NO_MEMORY(p);
299 p->dce_ctx = dce_ctx;
303 p->pending_call_list = NULL;
304 p->cli_max_recv_frag = 0;
305 p->partial_input = data_blob(NULL, 0);
306 p->auth_state.auth_info = NULL;
307 p->auth_state.gensec_security = NULL;
308 p->auth_state.session_info = NULL;
309 p->auth_state.session_key = dcesrv_generic_session_key;
310 p->srv_conn = srv_conn;
311 p->processing = False;
312 p->state_flags = state_flags;
314 talloc_set_destructor(p, dcesrv_endpoint_destructor);
321 search and connect to a dcerpc endpoint
323 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
325 const struct dcerpc_binding *ep_description,
326 struct auth_session_info *session_info,
327 struct stream_connection *srv_conn,
328 uint32_t state_flags,
329 struct dcesrv_connection **dce_conn_p)
332 const struct dcesrv_endpoint *ep;
334 /* make sure this endpoint exists */
335 ep = find_endpoint(dce_ctx, ep_description);
337 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
340 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, state_flags, dce_conn_p);
341 if (!NT_STATUS_IS_OK(status)) {
345 (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
346 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
348 /* TODO: check security descriptor of the endpoint here
349 * if it's a smb named pipe
350 * if it's failed free dce_conn_p
357 static void dcesrv_init_hdr(struct ncacn_packet *pkt)
360 pkt->rpc_vers_minor = 0;
361 if (lp_rpc_big_endian()) {
364 pkt->drep[0] = DCERPC_DREP_LE;
372 return a dcerpc fault
374 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
376 struct ncacn_packet pkt;
377 struct data_blob_list_item *rep;
380 /* setup a bind_ack */
381 dcesrv_init_hdr(&pkt);
383 pkt.call_id = call->pkt.call_id;
384 pkt.ptype = DCERPC_PKT_FAULT;
385 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
386 pkt.u.fault.alloc_hint = 0;
387 pkt.u.fault.context_id = 0;
388 pkt.u.fault.cancel_count = 0;
389 pkt.u.fault.status = fault_code;
391 rep = talloc(call, struct data_blob_list_item);
393 return NT_STATUS_NO_MEMORY;
396 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
397 if (!NT_STATUS_IS_OK(status)) {
401 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
403 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
404 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
411 return a dcerpc bind_nak
413 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
415 struct ncacn_packet pkt;
416 struct data_blob_list_item *rep;
419 /* setup a bind_nak */
420 dcesrv_init_hdr(&pkt);
422 pkt.call_id = call->pkt.call_id;
423 pkt.ptype = DCERPC_PKT_BIND_NAK;
424 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
425 pkt.u.bind_nak.reject_reason = reason;
426 pkt.u.bind_nak.num_versions = 0;
428 rep = talloc(call, struct data_blob_list_item);
430 return NT_STATUS_NO_MEMORY;
433 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
434 if (!NT_STATUS_IS_OK(status)) {
438 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
440 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
441 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
448 handle a bind request
450 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
452 uint32_t if_version, transfer_syntax_version;
453 struct GUID uuid, *transfer_syntax_uuid;
454 struct ncacn_packet pkt;
455 struct data_blob_list_item *rep;
457 uint32_t result=0, reason=0;
459 const struct dcesrv_interface *iface;
461 if (call->pkt.u.bind.num_contexts < 1 ||
462 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
463 return dcesrv_bind_nak(call, 0);
466 context_id = call->pkt.u.bind.ctx_list[0].context_id;
468 /* you can't bind twice on one context */
469 if (dcesrv_find_context(call->conn, context_id) != NULL) {
470 return dcesrv_bind_nak(call, 0);
473 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
474 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
476 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
477 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
478 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
479 ndr_transfer_syntax.if_version != transfer_syntax_version) {
480 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
481 /* we only do NDR encoded dcerpc */
482 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
483 talloc_free(uuid_str);
484 return dcesrv_bind_nak(call, 0);
487 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
489 char *uuid_str = GUID_string(call, &uuid);
490 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
491 talloc_free(uuid_str);
493 /* we don't know about that interface */
494 result = DCERPC_BIND_PROVIDER_REJECT;
495 reason = DCERPC_BIND_REASON_ASYNTAX;
499 /* add this context to the list of available context_ids */
500 struct dcesrv_connection_context *context = talloc(call->conn,
501 struct dcesrv_connection_context);
502 if (context == NULL) {
503 return dcesrv_bind_nak(call, 0);
505 context->conn = call->conn;
506 context->iface = iface;
507 context->context_id = context_id;
508 context->private = NULL;
509 context->handles = NULL;
510 DLIST_ADD(call->conn->contexts, context);
511 call->context = context;
514 if (call->conn->cli_max_recv_frag == 0) {
515 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
518 /* handle any authentication that is being requested */
519 if (!dcesrv_auth_bind(call)) {
520 /* TODO: work out the right reject code */
521 return dcesrv_bind_nak(call, 0);
524 /* setup a bind_ack */
525 dcesrv_init_hdr(&pkt);
527 pkt.call_id = call->pkt.call_id;
528 pkt.ptype = DCERPC_PKT_BIND_ACK;
529 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
530 pkt.u.bind_ack.max_xmit_frag = 0x2000;
531 pkt.u.bind_ack.max_recv_frag = 0x2000;
532 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
534 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
535 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
537 pkt.u.bind_ack.secondary_address = "";
539 pkt.u.bind_ack.num_results = 1;
540 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
541 if (!pkt.u.bind_ack.ctx_list) {
542 return NT_STATUS_NO_MEMORY;
544 pkt.u.bind_ack.ctx_list[0].result = result;
545 pkt.u.bind_ack.ctx_list[0].reason = reason;
546 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
547 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
549 if (!dcesrv_auth_bind_ack(call, &pkt)) {
550 return dcesrv_bind_nak(call, 0);
554 status = iface->bind(call, iface);
555 if (!NT_STATUS_IS_OK(status)) {
556 char *uuid_str = GUID_string(call, &uuid);
557 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
558 uuid_str, if_version, nt_errstr(status)));
559 talloc_free(uuid_str);
560 return dcesrv_bind_nak(call, 0);
564 rep = talloc(call, struct data_blob_list_item);
566 return NT_STATUS_NO_MEMORY;
569 status = ncacn_push_auth(&rep->blob, call, &pkt,
570 call->conn->auth_state.auth_info);
571 if (!NT_STATUS_IS_OK(status)) {
575 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
577 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
578 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
585 handle a auth3 request
587 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
589 /* handle the auth3 in the auth code */
590 if (!dcesrv_auth_auth3(call)) {
591 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
596 /* we don't send a reply to a auth3 request, except by a
603 handle a bind request
605 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
607 uint32_t if_version, transfer_syntax_version;
608 struct dcesrv_connection_context *context;
609 const struct dcesrv_interface *iface;
610 struct GUID uuid, *transfer_syntax_uuid;
612 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
613 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
615 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
616 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
617 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
618 ndr_transfer_syntax.if_version != transfer_syntax_version) {
619 /* we only do NDR encoded dcerpc */
620 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
623 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
625 char *uuid_str = GUID_string(call, &uuid);
626 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
627 talloc_free(uuid_str);
628 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
631 /* add this context to the list of available context_ids */
632 context = talloc(call->conn, struct dcesrv_connection_context);
633 if (context == NULL) {
634 return NT_STATUS_NO_MEMORY;
636 context->conn = call->conn;
637 context->iface = iface;
638 context->context_id = context_id;
639 context->private = NULL;
640 context->handles = NULL;
641 DLIST_ADD(call->conn->contexts, context);
642 call->context = context;
649 handle a alter context request
651 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
653 struct ncacn_packet pkt;
654 struct data_blob_list_item *rep;
656 uint32_t result=0, reason=0;
659 /* handle any authentication that is being requested */
660 if (!dcesrv_auth_alter(call)) {
661 /* TODO: work out the right reject code */
662 result = DCERPC_BIND_PROVIDER_REJECT;
663 reason = DCERPC_BIND_REASON_ASYNTAX;
666 context_id = call->pkt.u.alter.ctx_list[0].context_id;
668 /* see if they are asking for a new interface */
670 dcesrv_find_context(call->conn, context_id) == NULL) {
671 status = dcesrv_alter_new_context(call, context_id);
672 if (!NT_STATUS_IS_OK(status)) {
673 result = DCERPC_BIND_PROVIDER_REJECT;
674 reason = DCERPC_BIND_REASON_ASYNTAX;
678 /* setup a alter_resp */
679 dcesrv_init_hdr(&pkt);
681 pkt.call_id = call->pkt.call_id;
682 pkt.ptype = DCERPC_PKT_ALTER_RESP;
683 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
684 pkt.u.alter_resp.max_xmit_frag = 0x2000;
685 pkt.u.alter_resp.max_recv_frag = 0x2000;
686 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
687 pkt.u.alter_resp.num_results = 1;
688 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
689 if (!pkt.u.alter_resp.ctx_list) {
690 return NT_STATUS_NO_MEMORY;
692 pkt.u.alter_resp.ctx_list[0].result = result;
693 pkt.u.alter_resp.ctx_list[0].reason = reason;
694 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
695 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
696 pkt.u.alter_resp.secondary_address = "";
698 if (!dcesrv_auth_alter_ack(call, &pkt)) {
699 return dcesrv_bind_nak(call, 0);
702 rep = talloc(call, struct data_blob_list_item);
704 return NT_STATUS_NO_MEMORY;
707 status = ncacn_push_auth(&rep->blob, call, &pkt,
708 call->conn->auth_state.auth_info);
709 if (!NT_STATUS_IS_OK(status)) {
713 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
715 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
716 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
722 handle a dcerpc request packet
724 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
726 struct ndr_pull *pull;
728 struct dcesrv_connection_context *context;
730 call->fault_code = 0;
731 call->state_flags = call->conn->state_flags;
732 call->time = timeval_current();
734 /* if authenticated, and the mech we use can't do async replies, don't use them... */
735 if (call->conn->auth_state.gensec_security &&
736 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
737 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
740 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
741 if (context == NULL) {
742 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
745 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
746 NT_STATUS_HAVE_NO_MEMORY(pull);
748 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
750 call->context = context;
751 call->event_ctx = context->conn->srv_conn->event.ctx;
752 call->ndr_pull = pull;
754 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
755 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
758 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
759 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
762 /* unravel the NDR for the packet */
763 status = context->iface->ndr_pull(call, call, pull, &call->r);
764 if (!NT_STATUS_IS_OK(status)) {
765 return dcesrv_fault(call, call->fault_code);
768 if (pull->offset != pull->data_size) {
769 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
770 pull->data_size - pull->offset));
771 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
774 /* call the dispatch function */
775 status = context->iface->dispatch(call, call, call->r);
776 if (!NT_STATUS_IS_OK(status)) {
777 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
778 context->iface->name,
779 call->pkt.u.request.opnum,
780 dcerpc_errstr(pull, call->fault_code)));
781 return dcesrv_fault(call, call->fault_code);
784 /* add the call to the pending list */
785 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
787 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
791 return dcesrv_reply(call);
794 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
796 struct ndr_push *push;
799 uint32_t total_length;
800 struct dcesrv_connection_context *context = call->context;
802 /* call the reply function */
803 status = context->iface->reply(call, call, call->r);
804 if (!NT_STATUS_IS_OK(status)) {
805 return dcesrv_fault(call, call->fault_code);
808 /* form the reply NDR */
809 push = ndr_push_init_ctx(call);
810 NT_STATUS_HAVE_NO_MEMORY(push);
812 /* carry over the pointer count to the reply in case we are
813 using full pointer. See NDR specification for full
815 push->ptr_count = call->ndr_pull->ptr_count;
817 if (lp_rpc_big_endian()) {
818 push->flags |= LIBNDR_FLAG_BIGENDIAN;
821 status = context->iface->ndr_push(call, call, push, call->r);
822 if (!NT_STATUS_IS_OK(status)) {
823 return dcesrv_fault(call, call->fault_code);
826 stub = ndr_push_blob(push);
828 total_length = stub.length;
832 struct data_blob_list_item *rep;
833 struct ncacn_packet pkt;
835 rep = talloc(call, struct data_blob_list_item);
836 NT_STATUS_HAVE_NO_MEMORY(rep);
838 length = stub.length;
839 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
840 /* the 32 is to cope with signing data */
841 length = call->conn->cli_max_recv_frag -
842 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
845 /* form the dcerpc response packet */
846 dcesrv_init_hdr(&pkt);
848 pkt.call_id = call->pkt.call_id;
849 pkt.ptype = DCERPC_PKT_RESPONSE;
851 if (stub.length == total_length) {
852 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
854 if (length == stub.length) {
855 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
857 pkt.u.response.alloc_hint = stub.length;
858 pkt.u.response.context_id = call->pkt.u.request.context_id;
859 pkt.u.response.cancel_count = 0;
860 pkt.u.response.stub_and_verifier.data = stub.data;
861 pkt.u.response.stub_and_verifier.length = length;
863 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
864 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
867 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
869 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
872 stub.length -= length;
873 } while (stub.length != 0);
875 /* move the call from the pending to the finished calls list */
876 DLIST_REMOVE(call->conn->pending_call_list, call);
877 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
879 if (call->conn->call_list && call->conn->call_list->replies) {
880 if (call->conn->srv_conn &&
881 call->conn->srv_conn->event.fde) {
882 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
891 work out if we have a full packet yet
893 static BOOL dce_full_packet(const DATA_BLOB *data)
895 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
898 if (dcerpc_get_frag_length(data) > data->length) {
905 we might have consumed only part of our input - advance past that part
907 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
911 if (dce_conn->partial_input.length == offset) {
912 data_blob_free(&dce_conn->partial_input);
916 blob = dce_conn->partial_input;
917 dce_conn->partial_input = data_blob(blob.data + offset,
918 blob.length - offset);
919 data_blob_free(&blob);
923 process some input to a dcerpc endpoint server.
925 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
927 struct ndr_pull *ndr;
929 struct dcesrv_call_state *call;
932 call = talloc(dce_conn, struct dcesrv_call_state);
934 talloc_free(dce_conn->partial_input.data);
935 return NT_STATUS_NO_MEMORY;
937 call->conn = dce_conn;
938 call->replies = NULL;
939 call->context = NULL;
940 call->event_ctx = dce_conn->srv_conn->event.ctx;
942 blob = dce_conn->partial_input;
943 blob.length = dcerpc_get_frag_length(&blob);
945 ndr = ndr_pull_init_blob(&blob, call);
947 talloc_free(dce_conn->partial_input.data);
949 return NT_STATUS_NO_MEMORY;
952 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
953 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
956 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
957 if (!NT_STATUS_IS_OK(status)) {
958 talloc_free(dce_conn->partial_input.data);
963 /* we have to check the signing here, before combining the
965 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
966 !dcesrv_auth_request(call, &blob)) {
967 dce_partial_advance(dce_conn, blob.length);
968 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
971 dce_partial_advance(dce_conn, blob.length);
973 /* see if this is a continued packet */
974 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
975 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
976 struct dcesrv_call_state *call2 = call;
979 /* we only allow fragmented requests, no other packet types */
980 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
981 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
984 /* this is a continuation of an existing call - find the call then
985 tack it on the end */
986 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
988 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
991 if (call->pkt.ptype != call2->pkt.ptype) {
992 /* trying to play silly buggers are we? */
993 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
996 alloc_size = call->pkt.u.request.stub_and_verifier.length +
997 call2->pkt.u.request.stub_and_verifier.length;
998 if (call->pkt.u.request.alloc_hint > alloc_size) {
999 alloc_size = call->pkt.u.request.alloc_hint;
1002 call->pkt.u.request.stub_and_verifier.data =
1003 talloc_realloc(call,
1004 call->pkt.u.request.stub_and_verifier.data,
1005 uint8_t, alloc_size);
1006 if (!call->pkt.u.request.stub_and_verifier.data) {
1007 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1009 memcpy(call->pkt.u.request.stub_and_verifier.data +
1010 call->pkt.u.request.stub_and_verifier.length,
1011 call2->pkt.u.request.stub_and_verifier.data,
1012 call2->pkt.u.request.stub_and_verifier.length);
1013 call->pkt.u.request.stub_and_verifier.length +=
1014 call2->pkt.u.request.stub_and_verifier.length;
1016 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1021 /* this may not be the last pdu in the chain - if its isn't then
1022 just put it on the call_list and wait for the rest */
1023 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1024 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1025 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1026 return NT_STATUS_OK;
1029 switch (call->pkt.ptype) {
1030 case DCERPC_PKT_BIND:
1031 status = dcesrv_bind(call);
1033 case DCERPC_PKT_AUTH3:
1034 status = dcesrv_auth3(call);
1036 case DCERPC_PKT_ALTER:
1037 status = dcesrv_alter(call);
1039 case DCERPC_PKT_REQUEST:
1040 status = dcesrv_request(call);
1043 status = NT_STATUS_INVALID_PARAMETER;
1047 /* if we are going to be sending a reply then add
1048 it to the list of pending calls. We add it to the end to keep the call
1049 list in the order we will answer */
1050 if (!NT_STATUS_IS_OK(status)) {
1059 provide some input to a dcerpc endpoint server. This passes data
1060 from a dcerpc client into the server
1062 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1066 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1067 dce_conn->partial_input.data,
1069 dce_conn->partial_input.length + data->length);
1070 if (!dce_conn->partial_input.data) {
1071 return NT_STATUS_NO_MEMORY;
1073 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1074 data->data, data->length);
1075 dce_conn->partial_input.length += data->length;
1077 while (dce_full_packet(&dce_conn->partial_input)) {
1078 status = dcesrv_input_process(dce_conn);
1079 if (!NT_STATUS_IS_OK(status)) {
1084 return NT_STATUS_OK;
1088 retrieve some output from a dcerpc server
1089 The caller supplies a function that will be called to do the
1092 The first argument to write_fn() will be 'private', the second will
1093 be a pointer to a buffer containing the data to be sent and the 3rd
1094 will be a pointer to a size_t variable that will be set to the
1095 number of bytes that are consumed from the output.
1097 from the current fragment
1099 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1101 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1104 struct dcesrv_call_state *call;
1105 struct data_blob_list_item *rep;
1108 call = dce_conn->call_list;
1109 if (!call || !call->replies) {
1110 if (dce_conn->pending_call_list) {
1111 /* TODO: we need to say act async here
1112 * as we know we have pending requests
1113 * which will be finished at a time
1115 return NT_STATUS_FOOBAR;
1117 return NT_STATUS_FOOBAR;
1119 rep = call->replies;
1121 status = write_fn(private_data, &rep->blob, &nwritten);
1122 NT_STATUS_IS_ERR_RETURN(status);
1124 rep->blob.length -= nwritten;
1125 rep->blob.data += nwritten;
1127 if (rep->blob.length == 0) {
1128 /* we're done with this section of the call */
1129 DLIST_REMOVE(call->replies, rep);
1132 if (call->replies == NULL) {
1133 /* we're done with the whole call */
1134 DLIST_REMOVE(dce_conn->call_list, call);
1141 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1144 struct dcesrv_context *dce_ctx;
1147 if (!endpoint_servers) {
1148 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1149 return NT_STATUS_INTERNAL_ERROR;
1152 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1153 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1154 dce_ctx->endpoint_list = NULL;
1156 for (i=0;endpoint_servers[i];i++) {
1157 const struct dcesrv_endpoint_server *ep_server;
1159 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1161 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1162 return NT_STATUS_INTERNAL_ERROR;
1165 status = ep_server->init_server(dce_ctx, ep_server);
1166 if (!NT_STATUS_IS_OK(status)) {
1167 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1168 nt_errstr(status)));
1173 *_dce_ctx = dce_ctx;
1174 return NT_STATUS_OK;
1178 initialise the dcerpc server context for ncacn_np based services
1180 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1183 struct dcesrv_context *dce_ctx;
1185 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), &dce_ctx);
1186 NT_STATUS_NOT_OK_RETURN(status);
1188 *_dce_ctx = dce_ctx;
1189 return NT_STATUS_OK;
1192 /* the list of currently registered DCERPC endpoint servers.
1194 static struct ep_server {
1195 struct dcesrv_endpoint_server *ep_server;
1196 } *ep_servers = NULL;
1197 static int num_ep_servers;
1200 register a DCERPC endpoint server.
1202 The 'name' can be later used by other backends to find the operations
1203 structure for this backend.
1205 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1207 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1209 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1211 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1212 /* its already registered! */
1213 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1215 return NT_STATUS_OBJECT_NAME_COLLISION;
1218 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1220 smb_panic("out of memory in dcerpc_register");
1223 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1224 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1228 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1231 return NT_STATUS_OK;
1235 return the operations structure for a named backend of the specified type
1237 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1241 for (i=0;i<num_ep_servers;i++) {
1242 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1243 return ep_servers[i].ep_server;
1251 return the DCERPC module version, and the size of some critical types
1252 This can be used by endpoint server modules to either detect compilation errors, or provide
1253 multiple implementations for different smbd compilation options in one module
1255 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1257 static const struct dcesrv_critical_sizes critical_sizes = {
1258 DCERPC_MODULE_VERSION,
1259 sizeof(struct dcesrv_context),
1260 sizeof(struct dcesrv_endpoint),
1261 sizeof(struct dcesrv_endpoint_server),
1262 sizeof(struct dcesrv_interface),
1263 sizeof(struct dcesrv_if_list),
1264 sizeof(struct dcesrv_connection),
1265 sizeof(struct dcesrv_call_state),
1266 sizeof(struct dcesrv_auth),
1267 sizeof(struct dcesrv_handle)
1270 return &critical_sizes;
1274 initialise the dcerpc server context
1276 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1279 struct dcesrv_context *dce_ctx;
1280 struct dcesrv_endpoint *e;
1282 status = dcesrv_init_context(event_context,
1283 lp_dcerpc_endpoint_servers(),
1285 NT_STATUS_NOT_OK_RETURN(status);
1287 /* Make sure the directory for NCALRPC exists */
1288 if (!directory_exist(lp_ncalrpc_dir())) {
1289 mkdir(lp_ncalrpc_dir(), 0755);
1292 for (e=dce_ctx->endpoint_list;e;e=e->next) {
1293 switch (e->ep_description->transport) {
1294 case NCACN_UNIX_STREAM:
1295 status = dcesrv_add_ep_unix(dce_ctx, e, event_context, model_ops);
1296 NT_STATUS_NOT_OK_RETURN(status);
1300 status = dcesrv_add_ep_ncalrpc(dce_ctx, e, event_context, model_ops);
1301 NT_STATUS_NOT_OK_RETURN(status);
1305 status = dcesrv_add_ep_tcp(dce_ctx, e, event_context, model_ops);
1306 NT_STATUS_NOT_OK_RETURN(status);
1310 /* FIXME: status = dcesrv_add_ep_np(dce_ctx, e, event_context, model_ops);
1311 NT_STATUS_NOT_OK_RETURN(status); */
1315 return NT_STATUS_NOT_SUPPORTED;
1319 return NT_STATUS_OK;
1322 NTSTATUS server_service_rpc_init(void)
1324 init_module_fn static_init[] = STATIC_DCERPC_SERVER_MODULES;
1325 init_module_fn *shared_init = load_samba_modules(NULL, "rpc_server");
1327 run_init_functions(static_init);
1328 run_init_functions(shared_init);
1330 talloc_free(shared_init);
1332 return register_server_service("rpc", dcesrv_init);