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"
35 see if two endpoints match
37 static BOOL endpoints_match(const struct dcerpc_binding *ep1,
38 const struct dcerpc_binding *ep2)
40 if (ep1->transport != ep2->transport) {
44 if (!ep1->endpoint || !ep2->endpoint) {
45 return ep1->endpoint == ep2->endpoint;
48 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
55 find an endpoint in the dcesrv_context
57 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
58 const struct dcerpc_binding *ep_description)
60 struct dcesrv_endpoint *ep;
61 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
62 if (endpoints_match(ep->ep_description, ep_description)) {
70 find a registered context_id from a bind or alter_context
72 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
75 struct dcesrv_connection_context *c;
76 for (c=conn->contexts;c;c=c->next) {
77 if (c->context_id == context_id) return c;
83 see if a uuid and if_version match to an interface
85 static BOOL interface_match(const struct dcesrv_interface *if1,
86 const struct dcesrv_interface *if2)
88 if (if1->if_version != if2->if_version) {
92 if (strcmp(if1->uuid, if2->uuid)==0) {
100 find the interface operations on an endpoint
102 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
103 const struct dcesrv_interface *iface)
105 struct dcesrv_if_list *ifl;
106 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
107 if (interface_match(&(ifl->iface), iface)) {
108 return &(ifl->iface);
115 see if a uuid and if_version match to an interface
117 static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
118 const char *uuid, uint32_t if_version)
120 if (iface->if_version != if_version) {
124 if (strcmp(iface->uuid, uuid)==0) {
132 find the interface operations on an endpoint by uuid
134 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
135 const char *uuid, uint32_t if_version)
137 struct dcesrv_if_list *ifl;
138 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
139 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
140 return &(ifl->iface);
147 find a call that is pending in our call list
149 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
151 struct dcesrv_call_state *c;
152 for (c=dce_conn->call_list;c;c=c->next) {
153 if (c->pkt.call_id == call_id) {
161 register an interface on an endpoint
163 NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
165 const struct dcesrv_interface *iface,
166 const struct security_descriptor *sd)
168 struct dcesrv_endpoint *ep;
169 struct dcesrv_if_list *ifl;
170 struct dcerpc_binding *binding;
174 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
176 if (NT_STATUS_IS_ERR(status)) {
177 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
181 /* check if this endpoint exists
183 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
184 ep = talloc(dce_ctx, struct dcesrv_endpoint);
186 return NT_STATUS_NO_MEMORY;
189 ep->ep_description = talloc_reference(ep, binding);
193 /* see if the interface is already registered on te endpoint */
194 if (find_interface(ep, iface)!=NULL) {
195 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
196 iface->name, ep_name));
197 return NT_STATUS_OBJECT_NAME_COLLISION;
200 /* talloc a new interface list element */
201 ifl = talloc(dce_ctx, struct dcesrv_if_list);
203 return NT_STATUS_NO_MEMORY;
206 /* copy the given interface struct to the one on the endpoints interface list */
207 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
209 /* if we have a security descriptor given,
210 * we should see if we can set it up on the endpoint
213 /* if there's currently no security descriptor given on the endpoint
216 if (ep->sd == NULL) {
217 ep->sd = security_descriptor_copy(dce_ctx, sd);
220 /* if now there's no security descriptor given on the endpoint
221 * something goes wrong, either we failed to copy the security descriptor
222 * or there was already one on the endpoint
224 if (ep->sd != NULL) {
225 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
226 " on endpoint '%s'\n",
227 iface->name, ep_name));
228 if (add_ep) free(ep);
230 return NT_STATUS_OBJECT_NAME_COLLISION;
234 /* finally add the interface on the endpoint */
235 DLIST_ADD(ep->interface_list, ifl);
237 /* if it's a new endpoint add it to the dcesrv_context */
239 DLIST_ADD(dce_ctx->endpoint_list, ep);
242 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
243 iface->name, ep_name));
248 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
249 DATA_BLOB *session_key)
251 if (p->auth_state.session_info->session_key.length) {
252 *session_key = p->auth_state.session_info->session_key;
255 return NT_STATUS_NO_USER_SESSION_KEY;
258 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
259 DATA_BLOB *session_key)
261 /* this took quite a few CPU cycles to find ... */
262 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
263 session_key->length = 16;
268 fetch the user session key - may be default (above) or the SMB session key
270 NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
271 DATA_BLOB *session_key)
273 return p->auth_state.session_key(p, session_key);
278 destroy a link to an endpoint
280 static int dcesrv_endpoint_destructor(void *ptr)
282 struct dcesrv_connection *p = ptr;
284 while (p->contexts) {
285 struct dcesrv_connection_context *c = p->contexts;
287 DLIST_REMOVE(p->contexts, c);
290 c->iface->unbind(c, c->iface);
299 connect to a dcerpc endpoint
301 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
303 const struct dcesrv_endpoint *ep,
304 struct stream_connection *srv_conn,
305 struct dcesrv_connection **_p)
307 struct dcesrv_connection *p;
309 p = talloc(mem_ctx, struct dcesrv_connection);
310 NT_STATUS_HAVE_NO_MEMORY(p);
312 p->dce_ctx = dce_ctx;
316 p->pending_call_list = NULL;
317 p->cli_max_recv_frag = 0;
318 p->partial_input = data_blob(NULL, 0);
319 p->auth_state.auth_info = NULL;
320 p->auth_state.gensec_security = NULL;
321 p->auth_state.session_info = NULL;
322 p->auth_state.session_key = dcesrv_generic_session_key;
323 p->srv_conn = srv_conn;
324 p->processing = False;
326 talloc_set_destructor(p, dcesrv_endpoint_destructor);
333 search and connect to a dcerpc endpoint
335 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
337 const struct dcerpc_binding *ep_description,
338 struct auth_session_info *session_info,
339 struct stream_connection *srv_conn,
340 struct dcesrv_connection **dce_conn_p)
343 const struct dcesrv_endpoint *ep;
345 /* make sure this endpoint exists */
346 ep = find_endpoint(dce_ctx, ep_description);
348 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
351 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, dce_conn_p);
352 if (!NT_STATUS_IS_OK(status)) {
356 (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
357 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
359 /* TODO: check security descriptor of the endpoint here
360 * if it's a smb named pipe
361 * if it's failed free dce_conn_p
368 static void dcesrv_init_hdr(struct ncacn_packet *pkt)
371 pkt->rpc_vers_minor = 0;
372 if (lp_rpc_big_endian()) {
375 pkt->drep[0] = DCERPC_DREP_LE;
383 return a dcerpc fault
385 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
387 struct ncacn_packet pkt;
388 struct data_blob_list_item *rep;
391 /* setup a bind_ack */
392 dcesrv_init_hdr(&pkt);
394 pkt.call_id = call->pkt.call_id;
395 pkt.ptype = DCERPC_PKT_FAULT;
396 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
397 pkt.u.fault.alloc_hint = 0;
398 pkt.u.fault.context_id = 0;
399 pkt.u.fault.cancel_count = 0;
400 pkt.u.fault.status = fault_code;
402 rep = talloc(call, struct data_blob_list_item);
404 return NT_STATUS_NO_MEMORY;
407 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
408 if (!NT_STATUS_IS_OK(status)) {
412 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
414 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
415 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
422 return a dcerpc bind_nak
424 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
426 struct ncacn_packet pkt;
427 struct data_blob_list_item *rep;
430 /* setup a bind_nak */
431 dcesrv_init_hdr(&pkt);
433 pkt.call_id = call->pkt.call_id;
434 pkt.ptype = DCERPC_PKT_BIND_NAK;
435 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
436 pkt.u.bind_nak.reject_reason = reason;
437 pkt.u.bind_nak.num_versions = 0;
439 rep = talloc(call, struct data_blob_list_item);
441 return NT_STATUS_NO_MEMORY;
444 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
445 if (!NT_STATUS_IS_OK(status)) {
449 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
451 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
452 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
459 handle a bind request
461 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
463 const char *uuid, *transfer_syntax;
464 uint32_t if_version, transfer_syntax_version;
465 struct ncacn_packet pkt;
466 struct data_blob_list_item *rep;
468 uint32_t result=0, reason=0;
470 const struct dcesrv_interface *iface;
472 if (call->pkt.u.bind.num_contexts < 1 ||
473 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
474 return dcesrv_bind_nak(call, 0);
477 context_id = call->pkt.u.bind.ctx_list[0].context_id;
479 /* you can't bind twice on one context */
480 if (dcesrv_find_context(call->conn, context_id) != NULL) {
481 return dcesrv_bind_nak(call, 0);
484 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
485 uuid = GUID_string(call, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
487 return dcesrv_bind_nak(call, 0);
490 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
491 transfer_syntax = GUID_string(call,
492 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
493 if (!transfer_syntax ||
494 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
495 NDR_GUID_VERSION != transfer_syntax_version) {
496 /* we only do NDR encoded dcerpc */
497 DEBUG(0,("Non NDR transfer syntax requested - %s\n", transfer_syntax));
498 return dcesrv_bind_nak(call, 0);
501 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
503 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
504 /* we don't know about that interface */
505 result = DCERPC_BIND_PROVIDER_REJECT;
506 reason = DCERPC_BIND_REASON_ASYNTAX;
510 /* add this context to the list of available context_ids */
511 struct dcesrv_connection_context *context = talloc(call->conn,
512 struct dcesrv_connection_context);
513 if (context == NULL) {
514 return dcesrv_bind_nak(call, 0);
516 context->conn = call->conn;
517 context->iface = iface;
518 context->context_id = context_id;
519 context->private = NULL;
520 context->handles = NULL;
521 DLIST_ADD(call->conn->contexts, context);
522 call->context = context;
525 if (call->conn->cli_max_recv_frag == 0) {
526 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
529 /* handle any authentication that is being requested */
530 if (!dcesrv_auth_bind(call)) {
531 /* TODO: work out the right reject code */
532 return dcesrv_bind_nak(call, 0);
535 /* setup a bind_ack */
536 dcesrv_init_hdr(&pkt);
538 pkt.call_id = call->pkt.call_id;
539 pkt.ptype = DCERPC_PKT_BIND_ACK;
540 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
541 pkt.u.bind_ack.max_xmit_frag = 0x2000;
542 pkt.u.bind_ack.max_recv_frag = 0x2000;
543 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
545 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
546 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
548 pkt.u.bind_ack.secondary_address = "";
550 pkt.u.bind_ack.num_results = 1;
551 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
552 if (!pkt.u.bind_ack.ctx_list) {
553 return NT_STATUS_NO_MEMORY;
555 pkt.u.bind_ack.ctx_list[0].result = result;
556 pkt.u.bind_ack.ctx_list[0].reason = reason;
557 GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
558 pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
559 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
561 if (!dcesrv_auth_bind_ack(call, &pkt)) {
562 return dcesrv_bind_nak(call, 0);
566 status = iface->bind(call, iface);
567 if (!NT_STATUS_IS_OK(status)) {
568 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
569 uuid, if_version, nt_errstr(status)));
570 return dcesrv_bind_nak(call, 0);
574 rep = talloc(call, struct data_blob_list_item);
576 return NT_STATUS_NO_MEMORY;
579 status = ncacn_push_auth(&rep->blob, call, &pkt,
580 call->conn->auth_state.auth_info);
581 if (!NT_STATUS_IS_OK(status)) {
585 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
587 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
588 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
595 handle a auth3 request
597 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
599 /* handle the auth3 in the auth code */
600 if (!dcesrv_auth_auth3(call)) {
601 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
606 /* we don't send a reply to a auth3 request, except by a
613 handle a bind request
615 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
617 uint32_t if_version, transfer_syntax_version;
618 const char *uuid, *transfer_syntax;
619 struct dcesrv_connection_context *context;
620 const struct dcesrv_interface *iface;
622 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
623 uuid = GUID_string(call, &call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid);
625 return NT_STATUS_NO_MEMORY;
628 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
629 transfer_syntax = GUID_string(call,
630 &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid);
631 if (!transfer_syntax ||
632 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
633 NDR_GUID_VERSION != transfer_syntax_version) {
634 /* we only do NDR encoded dcerpc */
635 return NT_STATUS_NO_MEMORY;
638 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
640 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
641 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
644 /* add this context to the list of available context_ids */
645 context = talloc(call->conn, struct dcesrv_connection_context);
646 if (context == NULL) {
647 return NT_STATUS_NO_MEMORY;
649 context->conn = call->conn;
650 context->iface = iface;
651 context->context_id = context_id;
652 context->private = NULL;
653 context->handles = NULL;
654 DLIST_ADD(call->conn->contexts, context);
655 call->context = context;
662 handle a bind request
664 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
666 struct ncacn_packet pkt;
667 struct data_blob_list_item *rep;
669 uint32_t result=0, reason=0;
672 /* handle any authentication that is being requested */
673 if (!dcesrv_auth_alter(call)) {
674 /* TODO: work out the right reject code */
675 result = DCERPC_BIND_PROVIDER_REJECT;
676 reason = DCERPC_BIND_REASON_ASYNTAX;
679 context_id = call->pkt.u.alter.ctx_list[0].context_id;
681 /* see if they are asking for a new interface */
683 dcesrv_find_context(call->conn, context_id) == NULL) {
684 status = dcesrv_alter_new_context(call, context_id);
685 if (!NT_STATUS_IS_OK(status)) {
686 result = DCERPC_BIND_PROVIDER_REJECT;
687 reason = DCERPC_BIND_REASON_ASYNTAX;
691 /* setup a alter_resp */
692 dcesrv_init_hdr(&pkt);
694 pkt.call_id = call->pkt.call_id;
695 pkt.ptype = DCERPC_PKT_ALTER_RESP;
696 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
697 pkt.u.alter_resp.max_xmit_frag = 0x2000;
698 pkt.u.alter_resp.max_recv_frag = 0x2000;
699 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
700 pkt.u.alter_resp.num_results = 1;
701 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
702 if (!pkt.u.alter_resp.ctx_list) {
703 return NT_STATUS_NO_MEMORY;
705 pkt.u.alter_resp.ctx_list[0].result = result;
706 pkt.u.alter_resp.ctx_list[0].reason = reason;
707 GUID_from_string(NDR_GUID, &pkt.u.alter_resp.ctx_list[0].syntax.uuid);
708 pkt.u.alter_resp.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
709 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
710 pkt.u.alter_resp.secondary_address = "";
712 if (!dcesrv_auth_alter_ack(call, &pkt)) {
713 return dcesrv_bind_nak(call, 0);
716 rep = talloc(call, struct data_blob_list_item);
718 return NT_STATUS_NO_MEMORY;
721 status = ncacn_push_auth(&rep->blob, call, &pkt,
722 call->conn->auth_state.auth_info);
723 if (!NT_STATUS_IS_OK(status)) {
727 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
729 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
730 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
736 handle a dcerpc request packet
738 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
740 struct ndr_pull *pull;
742 struct dcesrv_connection_context *context;
744 call->fault_code = 0;
745 call->state_flags = call->conn->dce_ctx->state_flags;
746 call->time = timeval_current();
748 /* if authenticated, and the mech we use can't do async replies, don't use them... */
749 if (call->conn->auth_state.gensec_security &&
750 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
751 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
754 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
755 if (context == NULL) {
756 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
759 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
760 NT_STATUS_HAVE_NO_MEMORY(pull);
762 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
764 call->context = context;
765 call->event_ctx = context->conn->srv_conn->event.ctx;
766 call->ndr_pull = pull;
768 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
769 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
772 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
773 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
776 /* unravel the NDR for the packet */
777 status = context->iface->ndr_pull(call, call, pull, &call->r);
778 if (!NT_STATUS_IS_OK(status)) {
779 return dcesrv_fault(call, call->fault_code);
782 if (pull->offset != pull->data_size) {
783 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
784 pull->data_size - pull->offset));
785 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
788 /* call the dispatch function */
789 status = context->iface->dispatch(call, call, call->r);
790 if (!NT_STATUS_IS_OK(status)) {
791 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
792 context->iface->name,
793 call->pkt.u.request.opnum,
794 dcerpc_errstr(pull, call->fault_code)));
795 return dcesrv_fault(call, call->fault_code);
798 /* add the call to the pending list */
799 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
801 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
805 return dcesrv_reply(call);
808 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
810 struct ndr_push *push;
813 uint32_t total_length;
814 struct dcesrv_connection_context *context = call->context;
816 /* call the reply function */
817 status = context->iface->reply(call, call, call->r);
818 if (!NT_STATUS_IS_OK(status)) {
819 return dcesrv_fault(call, call->fault_code);
822 /* form the reply NDR */
823 push = ndr_push_init_ctx(call);
824 NT_STATUS_HAVE_NO_MEMORY(push);
826 /* carry over the pointer count to the reply in case we are
827 using full pointer. See NDR specification for full
829 push->ptr_count = call->ndr_pull->ptr_count;
831 if (lp_rpc_big_endian()) {
832 push->flags |= LIBNDR_FLAG_BIGENDIAN;
835 status = context->iface->ndr_push(call, call, push, call->r);
836 if (!NT_STATUS_IS_OK(status)) {
837 return dcesrv_fault(call, call->fault_code);
840 stub = ndr_push_blob(push);
842 total_length = stub.length;
846 struct data_blob_list_item *rep;
847 struct ncacn_packet pkt;
849 rep = talloc(call, struct data_blob_list_item);
850 NT_STATUS_HAVE_NO_MEMORY(rep);
852 length = stub.length;
853 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
854 /* the 32 is to cope with signing data */
855 length = call->conn->cli_max_recv_frag -
856 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
859 /* form the dcerpc response packet */
860 dcesrv_init_hdr(&pkt);
862 pkt.call_id = call->pkt.call_id;
863 pkt.ptype = DCERPC_PKT_RESPONSE;
865 if (stub.length == total_length) {
866 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
868 if (length == stub.length) {
869 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
871 pkt.u.response.alloc_hint = stub.length;
872 pkt.u.response.context_id = call->pkt.u.request.context_id;
873 pkt.u.response.cancel_count = 0;
874 pkt.u.response.stub_and_verifier.data = stub.data;
875 pkt.u.response.stub_and_verifier.length = length;
877 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
878 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
881 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
883 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
886 stub.length -= length;
887 } while (stub.length != 0);
889 /* move the call from the pending to the finished calls list */
890 DLIST_REMOVE(call->conn->pending_call_list, call);
891 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
893 if (call->conn->call_list && call->conn->call_list->replies) {
894 if (call->conn->srv_conn &&
895 call->conn->srv_conn->event.fde) {
896 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
905 work out if we have a full packet yet
907 static BOOL dce_full_packet(const DATA_BLOB *data)
909 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
912 if (dcerpc_get_frag_length(data) > data->length) {
919 we might have consumed only part of our input - advance past that part
921 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
925 if (dce_conn->partial_input.length == offset) {
926 data_blob_free(&dce_conn->partial_input);
930 blob = dce_conn->partial_input;
931 dce_conn->partial_input = data_blob(blob.data + offset,
932 blob.length - offset);
933 data_blob_free(&blob);
937 process some input to a dcerpc endpoint server.
939 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
941 struct ndr_pull *ndr;
943 struct dcesrv_call_state *call;
946 call = talloc(dce_conn, struct dcesrv_call_state);
948 talloc_free(dce_conn->partial_input.data);
949 return NT_STATUS_NO_MEMORY;
951 call->conn = dce_conn;
952 call->replies = NULL;
953 call->context = NULL;
954 call->event_ctx = dce_conn->srv_conn->event.ctx;
956 blob = dce_conn->partial_input;
957 blob.length = dcerpc_get_frag_length(&blob);
959 ndr = ndr_pull_init_blob(&blob, call);
961 talloc_free(dce_conn->partial_input.data);
963 return NT_STATUS_NO_MEMORY;
966 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
967 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
970 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
971 if (!NT_STATUS_IS_OK(status)) {
972 talloc_free(dce_conn->partial_input.data);
977 /* we have to check the signing here, before combining the
979 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
980 !dcesrv_auth_request(call, &blob)) {
981 dce_partial_advance(dce_conn, blob.length);
982 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
985 dce_partial_advance(dce_conn, blob.length);
987 /* see if this is a continued packet */
988 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
989 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
990 struct dcesrv_call_state *call2 = call;
993 /* we only allow fragmented requests, no other packet types */
994 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
995 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
998 /* this is a continuation of an existing call - find the call then
999 tack it on the end */
1000 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
1002 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1005 if (call->pkt.ptype != call2->pkt.ptype) {
1006 /* trying to play silly buggers are we? */
1007 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1010 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1011 call2->pkt.u.request.stub_and_verifier.length;
1012 if (call->pkt.u.request.alloc_hint > alloc_size) {
1013 alloc_size = call->pkt.u.request.alloc_hint;
1016 call->pkt.u.request.stub_and_verifier.data =
1017 talloc_realloc(call,
1018 call->pkt.u.request.stub_and_verifier.data,
1019 uint8_t, alloc_size);
1020 if (!call->pkt.u.request.stub_and_verifier.data) {
1021 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1023 memcpy(call->pkt.u.request.stub_and_verifier.data +
1024 call->pkt.u.request.stub_and_verifier.length,
1025 call2->pkt.u.request.stub_and_verifier.data,
1026 call2->pkt.u.request.stub_and_verifier.length);
1027 call->pkt.u.request.stub_and_verifier.length +=
1028 call2->pkt.u.request.stub_and_verifier.length;
1030 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1035 /* this may not be the last pdu in the chain - if its isn't then
1036 just put it on the call_list and wait for the rest */
1037 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1038 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1039 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1040 return NT_STATUS_OK;
1043 switch (call->pkt.ptype) {
1044 case DCERPC_PKT_BIND:
1045 status = dcesrv_bind(call);
1047 case DCERPC_PKT_AUTH3:
1048 status = dcesrv_auth3(call);
1050 case DCERPC_PKT_ALTER:
1051 status = dcesrv_alter(call);
1053 case DCERPC_PKT_REQUEST:
1054 status = dcesrv_request(call);
1057 status = NT_STATUS_INVALID_PARAMETER;
1061 /* if we are going to be sending a reply then add
1062 it to the list of pending calls. We add it to the end to keep the call
1063 list in the order we will answer */
1064 if (!NT_STATUS_IS_OK(status)) {
1073 provide some input to a dcerpc endpoint server. This passes data
1074 from a dcerpc client into the server
1076 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1080 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1081 dce_conn->partial_input.data,
1083 dce_conn->partial_input.length + data->length);
1084 if (!dce_conn->partial_input.data) {
1085 return NT_STATUS_NO_MEMORY;
1087 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1088 data->data, data->length);
1089 dce_conn->partial_input.length += data->length;
1091 while (dce_full_packet(&dce_conn->partial_input)) {
1092 status = dcesrv_input_process(dce_conn);
1093 if (!NT_STATUS_IS_OK(status)) {
1098 return NT_STATUS_OK;
1102 retrieve some output from a dcerpc server
1103 The caller supplies a function that will be called to do the
1106 The first argument to write_fn() will be 'private', the second will
1107 be a pointer to a buffer containing the data to be sent and the 3rd
1108 will be a pointer to a size_t variable that will be set to the
1109 number of bytes that are consumed from the output.
1111 from the current fragment
1113 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1115 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1118 struct dcesrv_call_state *call;
1119 struct data_blob_list_item *rep;
1122 call = dce_conn->call_list;
1123 if (!call || !call->replies) {
1124 if (dce_conn->pending_call_list) {
1125 /* TODO: we need to say act async here
1126 * as we know we have pending requests
1127 * which will be finished at a time
1129 return NT_STATUS_FOOBAR;
1131 return NT_STATUS_FOOBAR;
1133 rep = call->replies;
1135 status = write_fn(private_data, &rep->blob, &nwritten);
1136 NT_STATUS_IS_ERR_RETURN(status);
1138 rep->blob.length -= nwritten;
1139 rep->blob.data += nwritten;
1141 if (rep->blob.length == 0) {
1142 /* we're done with this section of the call */
1143 DLIST_REMOVE(call->replies, rep);
1146 if (call->replies == NULL) {
1147 /* we're done with the whole call */
1148 DLIST_REMOVE(dce_conn->call_list, call);
1155 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
1158 struct dcesrv_context *dce_ctx;
1161 if (!endpoint_servers) {
1162 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1163 return NT_STATUS_INTERNAL_ERROR;
1166 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1167 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1168 dce_ctx->endpoint_list = NULL;
1169 dce_ctx->state_flags = state_flags;
1171 for (i=0;endpoint_servers[i];i++) {
1172 const struct dcesrv_endpoint_server *ep_server;
1174 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1176 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1177 return NT_STATUS_INTERNAL_ERROR;
1180 status = ep_server->init_server(dce_ctx, ep_server);
1181 if (!NT_STATUS_IS_OK(status)) {
1182 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1183 nt_errstr(status)));
1188 *_dce_ctx = dce_ctx;
1189 return NT_STATUS_OK;
1193 initialise the dcerpc server context for ncacn_np based services
1195 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1198 struct dcesrv_context *dce_ctx;
1200 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx);
1201 NT_STATUS_NOT_OK_RETURN(status);
1203 *_dce_ctx = dce_ctx;
1204 return NT_STATUS_OK;
1207 /* the list of currently registered DCERPC endpoint servers.
1209 static struct ep_server {
1210 struct dcesrv_endpoint_server *ep_server;
1211 } *ep_servers = NULL;
1212 static int num_ep_servers;
1215 register a DCERPC endpoint server.
1217 The 'name' can be later used by other backends to find the operations
1218 structure for this backend.
1220 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1222 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1224 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1226 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1227 /* its already registered! */
1228 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1230 return NT_STATUS_OBJECT_NAME_COLLISION;
1233 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1235 smb_panic("out of memory in dcerpc_register");
1238 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1239 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1243 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1246 return NT_STATUS_OK;
1250 return the operations structure for a named backend of the specified type
1252 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1256 for (i=0;i<num_ep_servers;i++) {
1257 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1258 return ep_servers[i].ep_server;
1266 return the DCERPC module version, and the size of some critical types
1267 This can be used by endpoint server modules to either detect compilation errors, or provide
1268 multiple implementations for different smbd compilation options in one module
1270 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1272 static const struct dcesrv_critical_sizes critical_sizes = {
1273 DCERPC_MODULE_VERSION,
1274 sizeof(struct dcesrv_context),
1275 sizeof(struct dcesrv_endpoint),
1276 sizeof(struct dcesrv_endpoint_server),
1277 sizeof(struct dcesrv_interface),
1278 sizeof(struct dcesrv_if_list),
1279 sizeof(struct dcesrv_connection),
1280 sizeof(struct dcesrv_call_state),
1281 sizeof(struct dcesrv_auth),
1282 sizeof(struct dcesrv_handle)
1285 return &critical_sizes;
1289 initialise the dcerpc server context for socket based services
1291 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1294 struct dcesrv_context *dce_ctx;
1296 status = dcesrv_init_context(event_context,
1297 lp_dcerpc_endpoint_servers(),
1298 DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
1300 NT_STATUS_NOT_OK_RETURN(status);
1302 return dcesrv_sock_init(dce_ctx, event_context, model_ops);
1306 NTSTATUS server_service_rpc_init(void)
1308 return register_server_service("rpc", dcesrv_init);