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_oxidresolver.h"
27 #include "auth/auth.h"
28 #include "dlinklist.h"
29 #include "rpc_server/dcerpc_server.h"
30 #include "lib/events/events.h"
31 #include "smbd/service_stream.h"
34 see if two endpoints match
36 static BOOL endpoints_match(const struct dcerpc_binding *ep1,
37 const struct dcerpc_binding *ep2)
39 if (ep1->transport != ep2->transport) {
43 if (!ep1->endpoint || !ep2->endpoint) {
44 return ep1->endpoint == ep2->endpoint;
47 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
54 find an endpoint in the dcesrv_context
56 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
57 const struct dcerpc_binding *ep_description)
59 struct dcesrv_endpoint *ep;
60 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
61 if (endpoints_match(ep->ep_description, ep_description)) {
69 find a registered context_id from a bind or alter_context
71 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
74 struct dcesrv_connection_context *c;
75 for (c=conn->contexts;c;c=c->next) {
76 if (c->context_id == context_id) return c;
82 see if a uuid and if_version match to an interface
84 static BOOL interface_match(const struct dcesrv_interface *if1,
85 const struct dcesrv_interface *if2)
87 if (if1->if_version != if2->if_version) {
91 if (strcmp(if1->uuid, if2->uuid)==0) {
99 find the interface operations on an endpoint
101 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
102 const struct dcesrv_interface *iface)
104 struct dcesrv_if_list *ifl;
105 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
106 if (interface_match(&(ifl->iface), iface)) {
107 return &(ifl->iface);
114 see if a uuid and if_version match to an interface
116 static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
117 const char *uuid, uint32_t if_version)
119 if (iface->if_version != if_version) {
123 if (strcmp(iface->uuid, uuid)==0) {
131 find the interface operations on an endpoint by uuid
133 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
134 const char *uuid, uint32_t if_version)
136 struct dcesrv_if_list *ifl;
137 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
138 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
139 return &(ifl->iface);
146 find a call that is pending in our call list
148 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
150 struct dcesrv_call_state *c;
151 for (c=dce_conn->call_list;c;c=c->next) {
152 if (c->pkt.call_id == call_id) {
160 register an interface on an endpoint
162 NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
164 const struct dcesrv_interface *iface,
165 const struct security_descriptor *sd)
167 struct dcesrv_endpoint *ep;
168 struct dcesrv_if_list *ifl;
169 struct dcerpc_binding *binding;
173 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
175 if (NT_STATUS_IS_ERR(status)) {
176 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
180 /* check if this endpoint exists
182 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
183 ep = talloc(dce_ctx, struct dcesrv_endpoint);
185 return NT_STATUS_NO_MEMORY;
188 ep->ep_description = talloc_reference(ep, binding);
192 /* see if the interface is already registered on te endpoint */
193 if (find_interface(ep, iface)!=NULL) {
194 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
195 iface->name, ep_name));
196 return NT_STATUS_OBJECT_NAME_COLLISION;
199 /* talloc a new interface list element */
200 ifl = talloc(dce_ctx, struct dcesrv_if_list);
202 return NT_STATUS_NO_MEMORY;
205 /* copy the given interface struct to the one on the endpoints interface list */
206 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
208 /* if we have a security descriptor given,
209 * we should see if we can set it up on the endpoint
212 /* if there's currently no security descriptor given on the endpoint
215 if (ep->sd == NULL) {
216 ep->sd = security_descriptor_copy(dce_ctx, sd);
219 /* if now there's no security descriptor given on the endpoint
220 * something goes wrong, either we failed to copy the security descriptor
221 * or there was already one on the endpoint
223 if (ep->sd != NULL) {
224 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
225 " on endpoint '%s'\n",
226 iface->name, ep_name));
227 if (add_ep) free(ep);
229 return NT_STATUS_OBJECT_NAME_COLLISION;
233 /* finally add the interface on the endpoint */
234 DLIST_ADD(ep->interface_list, ifl);
236 /* if it's a new endpoint add it to the dcesrv_context */
238 DLIST_ADD(dce_ctx->endpoint_list, ep);
241 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
242 iface->name, ep_name));
247 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
248 DATA_BLOB *session_key)
250 if (p->auth_state.session_info->session_key.length) {
251 *session_key = p->auth_state.session_info->session_key;
254 return NT_STATUS_NO_USER_SESSION_KEY;
257 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
258 DATA_BLOB *session_key)
260 /* this took quite a few CPU cycles to find ... */
261 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
262 session_key->length = 16;
267 fetch the user session key - may be default (above) or the SMB session key
269 NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
270 DATA_BLOB *session_key)
272 return p->auth_state.session_key(p, session_key);
277 destroy a link to an endpoint
279 static int dcesrv_endpoint_destructor(void *ptr)
281 struct dcesrv_connection *p = ptr;
283 while (p->contexts) {
284 struct dcesrv_connection_context *c = p->contexts;
286 DLIST_REMOVE(p->contexts, c);
289 c->iface->unbind(c, c->iface);
298 connect to a dcerpc endpoint
300 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
302 const struct dcesrv_endpoint *ep,
303 struct stream_connection *srv_conn,
304 struct dcesrv_connection **_p)
306 struct dcesrv_connection *p;
308 p = talloc(mem_ctx, struct dcesrv_connection);
309 NT_STATUS_HAVE_NO_MEMORY(p);
311 p->dce_ctx = dce_ctx;
315 p->pending_call_list = NULL;
316 p->cli_max_recv_frag = 0;
317 p->partial_input = data_blob(NULL, 0);
318 p->auth_state.auth_info = NULL;
319 p->auth_state.gensec_security = NULL;
320 p->auth_state.session_info = NULL;
321 p->auth_state.session_key = dcesrv_generic_session_key;
322 p->srv_conn = srv_conn;
324 talloc_set_destructor(p, dcesrv_endpoint_destructor);
331 search and connect to a dcerpc endpoint
333 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
335 const struct dcerpc_binding *ep_description,
336 struct auth_session_info *session_info,
337 struct stream_connection *srv_conn,
338 struct dcesrv_connection **dce_conn_p)
341 const struct dcesrv_endpoint *ep;
343 /* make sure this endpoint exists */
344 ep = find_endpoint(dce_ctx, ep_description);
346 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
349 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, dce_conn_p);
350 if (!NT_STATUS_IS_OK(status)) {
354 (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
355 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
357 /* TODO: check security descriptor of the endpoint here
358 * if it's a smb named pipe
359 * if it's failed free dce_conn_p
366 static void dcesrv_init_hdr(struct dcerpc_packet *pkt)
369 pkt->rpc_vers_minor = 0;
370 if (lp_rpc_big_endian()) {
373 pkt->drep[0] = DCERPC_DREP_LE;
381 return a dcerpc fault
383 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
385 struct dcerpc_packet pkt;
386 struct dcesrv_call_reply *rep;
389 /* setup a bind_ack */
390 dcesrv_init_hdr(&pkt);
392 pkt.call_id = call->pkt.call_id;
393 pkt.ptype = DCERPC_PKT_FAULT;
394 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
395 pkt.u.fault.alloc_hint = 0;
396 pkt.u.fault.context_id = 0;
397 pkt.u.fault.cancel_count = 0;
398 pkt.u.fault.status = fault_code;
400 rep = talloc(call, struct dcesrv_call_reply);
402 return NT_STATUS_NO_MEMORY;
405 status = dcerpc_push_auth(&rep->data, call, &pkt, NULL);
406 if (!NT_STATUS_IS_OK(status)) {
410 dcerpc_set_frag_length(&rep->data, rep->data.length);
412 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
413 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
420 return a dcerpc bind_nak
422 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
424 struct dcerpc_packet pkt;
425 struct dcesrv_call_reply *rep;
428 /* setup a bind_nak */
429 dcesrv_init_hdr(&pkt);
431 pkt.call_id = call->pkt.call_id;
432 pkt.ptype = DCERPC_PKT_BIND_NAK;
433 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
434 pkt.u.bind_nak.reject_reason = reason;
435 pkt.u.bind_nak.num_versions = 0;
437 rep = talloc(call, struct dcesrv_call_reply);
439 return NT_STATUS_NO_MEMORY;
442 status = dcerpc_push_auth(&rep->data, call, &pkt, NULL);
443 if (!NT_STATUS_IS_OK(status)) {
447 dcerpc_set_frag_length(&rep->data, rep->data.length);
449 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
450 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
457 handle a bind request
459 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
461 const char *uuid, *transfer_syntax;
462 uint32_t if_version, transfer_syntax_version;
463 struct dcerpc_packet pkt;
464 struct dcesrv_call_reply *rep;
466 uint32_t result=0, reason=0;
468 const struct dcesrv_interface *iface;
470 if (call->pkt.u.bind.num_contexts != 1 ||
471 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
472 return dcesrv_bind_nak(call, 0);
475 context_id = call->pkt.u.bind.ctx_list[0].context_id;
477 /* you can't bind twice on one context */
478 if (dcesrv_find_context(call->conn, context_id) != NULL) {
479 return dcesrv_bind_nak(call, 0);
482 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
483 uuid = GUID_string(call, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
485 return dcesrv_bind_nak(call, 0);
488 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
489 transfer_syntax = GUID_string(call,
490 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
491 if (!transfer_syntax ||
492 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
493 NDR_GUID_VERSION != transfer_syntax_version) {
494 /* we only do NDR encoded dcerpc */
495 return dcesrv_bind_nak(call, 0);
498 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
500 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
501 /* we don't know about that interface */
502 result = DCERPC_BIND_PROVIDER_REJECT;
503 reason = DCERPC_BIND_REASON_ASYNTAX;
507 /* add this context to the list of available context_ids */
508 struct dcesrv_connection_context *context = talloc(call->conn,
509 struct dcesrv_connection_context);
510 if (context == NULL) {
511 return dcesrv_bind_nak(call, 0);
513 context->conn = call->conn;
514 context->iface = iface;
515 context->context_id = context_id;
516 context->private = NULL;
517 context->handles = NULL;
518 DLIST_ADD(call->conn->contexts, context);
519 call->context = context;
522 if (call->conn->cli_max_recv_frag == 0) {
523 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
526 /* handle any authentication that is being requested */
527 if (!dcesrv_auth_bind(call)) {
528 /* TODO: work out the right reject code */
529 return dcesrv_bind_nak(call, 0);
532 /* setup a bind_ack */
533 dcesrv_init_hdr(&pkt);
535 pkt.call_id = call->pkt.call_id;
536 pkt.ptype = DCERPC_PKT_BIND_ACK;
537 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
538 pkt.u.bind_ack.max_xmit_frag = 0x2000;
539 pkt.u.bind_ack.max_recv_frag = 0x2000;
540 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
542 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
543 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
545 pkt.u.bind_ack.secondary_address = "";
547 pkt.u.bind_ack.num_results = 1;
548 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
549 if (!pkt.u.bind_ack.ctx_list) {
550 return NT_STATUS_NO_MEMORY;
552 pkt.u.bind_ack.ctx_list[0].result = result;
553 pkt.u.bind_ack.ctx_list[0].reason = reason;
554 GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
555 pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
556 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
558 if (!dcesrv_auth_bind_ack(call, &pkt)) {
559 return dcesrv_bind_nak(call, 0);
563 status = iface->bind(call, iface);
564 if (!NT_STATUS_IS_OK(status)) {
565 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
566 uuid, if_version, nt_errstr(status)));
567 return dcesrv_bind_nak(call, 0);
571 rep = talloc(call, struct dcesrv_call_reply);
573 return NT_STATUS_NO_MEMORY;
576 status = dcerpc_push_auth(&rep->data, call, &pkt,
577 call->conn->auth_state.auth_info);
578 if (!NT_STATUS_IS_OK(status)) {
582 dcerpc_set_frag_length(&rep->data, rep->data.length);
584 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
585 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
592 handle a auth3 request
594 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
596 /* handle the auth3 in the auth code */
597 if (!dcesrv_auth_auth3(call)) {
598 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
603 /* we don't send a reply to a auth3 request, except by a
610 handle a bind request
612 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
614 uint32_t if_version, transfer_syntax_version;
615 const char *uuid, *transfer_syntax;
616 struct dcesrv_connection_context *context;
617 const struct dcesrv_interface *iface;
619 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
620 uuid = GUID_string(call, &call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid);
622 return NT_STATUS_NO_MEMORY;
625 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
626 transfer_syntax = GUID_string(call,
627 &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid);
628 if (!transfer_syntax ||
629 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
630 NDR_GUID_VERSION != transfer_syntax_version) {
631 /* we only do NDR encoded dcerpc */
632 return NT_STATUS_NO_MEMORY;
635 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
637 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
638 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
641 /* add this context to the list of available context_ids */
642 context = talloc(call->conn, struct dcesrv_connection_context);
643 if (context == NULL) {
644 return NT_STATUS_NO_MEMORY;
646 context->conn = call->conn;
647 context->iface = iface;
648 context->context_id = context_id;
649 context->private = NULL;
650 context->handles = NULL;
651 DLIST_ADD(call->conn->contexts, context);
652 call->context = context;
659 handle a bind request
661 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
663 struct dcerpc_packet pkt;
664 struct dcesrv_call_reply *rep;
666 uint32_t result=0, reason=0;
669 /* handle any authentication that is being requested */
670 if (!dcesrv_auth_alter(call)) {
671 /* TODO: work out the right reject code */
672 result = DCERPC_BIND_PROVIDER_REJECT;
673 reason = DCERPC_BIND_REASON_ASYNTAX;
676 context_id = call->pkt.u.alter.ctx_list[0].context_id;
678 /* see if they are asking for a new interface */
680 dcesrv_find_context(call->conn, context_id) == NULL) {
681 status = dcesrv_alter_new_context(call, context_id);
682 if (!NT_STATUS_IS_OK(status)) {
683 result = DCERPC_BIND_PROVIDER_REJECT;
684 reason = DCERPC_BIND_REASON_ASYNTAX;
688 /* setup a alter_resp */
689 dcesrv_init_hdr(&pkt);
691 pkt.call_id = call->pkt.call_id;
692 pkt.ptype = DCERPC_PKT_ALTER_RESP;
693 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
694 pkt.u.alter_resp.max_xmit_frag = 0x2000;
695 pkt.u.alter_resp.max_recv_frag = 0x2000;
696 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
697 pkt.u.alter_resp.num_results = 1;
698 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
699 if (!pkt.u.alter_resp.ctx_list) {
700 return NT_STATUS_NO_MEMORY;
702 pkt.u.alter_resp.ctx_list[0].result = result;
703 pkt.u.alter_resp.ctx_list[0].reason = reason;
704 GUID_from_string(NDR_GUID, &pkt.u.alter_resp.ctx_list[0].syntax.uuid);
705 pkt.u.alter_resp.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
706 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
707 pkt.u.alter_resp.secondary_address = "";
709 if (!dcesrv_auth_alter_ack(call, &pkt)) {
710 return dcesrv_bind_nak(call, 0);
713 rep = talloc(call, struct dcesrv_call_reply);
715 return NT_STATUS_NO_MEMORY;
718 status = dcerpc_push_auth(&rep->data, call, &pkt,
719 call->conn->auth_state.auth_info);
720 if (!NT_STATUS_IS_OK(status)) {
724 dcerpc_set_frag_length(&rep->data, rep->data.length);
726 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
727 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
733 handle a dcerpc request packet
735 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
737 struct ndr_pull *pull;
739 struct dcesrv_connection_context *context;
741 call->fault_code = 0;
742 call->state_flags = call->conn->dce_ctx->state_flags;
743 call->time = timeval_current();
745 /* if authenticated, and the mech we use can't do async replies, don't use them... */
746 if (call->conn->auth_state.gensec_security &&
747 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
748 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
751 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
752 if (context == NULL) {
753 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
756 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
757 NT_STATUS_HAVE_NO_MEMORY(pull);
759 call->context = context;
760 call->event_ctx = context->conn->srv_conn->event.ctx;
761 call->ndr_pull = pull;
763 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
764 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
767 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
768 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
771 /* unravel the NDR for the packet */
772 status = context->iface->ndr_pull(call, call, pull, &call->r);
773 if (!NT_STATUS_IS_OK(status)) {
774 return dcesrv_fault(call, call->fault_code);
777 if (pull->offset != pull->data_size) {
778 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
779 pull->data_size - pull->offset));
780 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
783 /* call the dispatch function */
784 status = context->iface->dispatch(call, call, call->r);
785 if (!NT_STATUS_IS_OK(status)) {
786 return dcesrv_fault(call, call->fault_code);
789 /* add the call to the pending list */
790 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
792 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
796 return dcesrv_reply(call);
799 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
801 struct ndr_push *push;
804 uint32_t total_length;
805 struct dcesrv_connection_context *context = call->context;
807 /* call the reply function */
808 status = context->iface->reply(call, call, call->r);
809 if (!NT_STATUS_IS_OK(status)) {
810 return dcesrv_fault(call, call->fault_code);
813 /* form the reply NDR */
814 push = ndr_push_init_ctx(call);
815 NT_STATUS_HAVE_NO_MEMORY(push);
817 /* carry over the pointer count to the reply in case we are
818 using full pointer. See NDR specification for full
820 push->ptr_count = call->ndr_pull->ptr_count;
822 if (lp_rpc_big_endian()) {
823 push->flags |= LIBNDR_FLAG_BIGENDIAN;
826 status = context->iface->ndr_push(call, call, push, call->r);
827 if (!NT_STATUS_IS_OK(status)) {
828 return dcesrv_fault(call, call->fault_code);
831 stub = ndr_push_blob(push);
833 total_length = stub.length;
837 struct dcesrv_call_reply *rep;
838 struct dcerpc_packet pkt;
840 rep = talloc(call, struct dcesrv_call_reply);
841 NT_STATUS_HAVE_NO_MEMORY(rep);
843 length = stub.length;
844 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
845 /* the 32 is to cope with signing data */
846 length = call->conn->cli_max_recv_frag -
847 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
850 /* form the dcerpc response packet */
851 dcesrv_init_hdr(&pkt);
853 pkt.call_id = call->pkt.call_id;
854 pkt.ptype = DCERPC_PKT_RESPONSE;
856 if (stub.length == total_length) {
857 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
859 if (length == stub.length) {
860 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
862 pkt.u.response.alloc_hint = stub.length;
863 pkt.u.response.context_id = call->pkt.u.request.context_id;
864 pkt.u.response.cancel_count = 0;
865 pkt.u.response.stub_and_verifier.data = stub.data;
866 pkt.u.response.stub_and_verifier.length = length;
868 if (!dcesrv_auth_response(call, &rep->data, &pkt)) {
869 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
872 dcerpc_set_frag_length(&rep->data, rep->data.length);
874 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
877 stub.length -= length;
878 } while (stub.length != 0);
880 /* move the call from the pending to the finished calls list */
881 DLIST_REMOVE(call->conn->pending_call_list, call);
882 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
884 if (call->conn->call_list && call->conn->call_list->replies) {
885 if (call->conn->srv_conn &&
886 call->conn->srv_conn->event.fde) {
887 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
896 work out if we have a full packet yet
898 static BOOL dce_full_packet(const DATA_BLOB *data)
900 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
903 if (dcerpc_get_frag_length(data) > data->length) {
910 we might have consumed only part of our input - advance past that part
912 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
916 if (dce_conn->partial_input.length == offset) {
917 data_blob_free(&dce_conn->partial_input);
921 blob = dce_conn->partial_input;
922 dce_conn->partial_input = data_blob(blob.data + offset,
923 blob.length - offset);
924 data_blob_free(&blob);
928 process some input to a dcerpc endpoint server.
930 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
932 struct ndr_pull *ndr;
934 struct dcesrv_call_state *call;
937 call = talloc(dce_conn, struct dcesrv_call_state);
939 talloc_free(dce_conn->partial_input.data);
940 return NT_STATUS_NO_MEMORY;
942 call->conn = dce_conn;
943 call->replies = NULL;
944 call->context = NULL;
946 blob = dce_conn->partial_input;
947 blob.length = dcerpc_get_frag_length(&blob);
949 ndr = ndr_pull_init_blob(&blob, call);
951 talloc_free(dce_conn->partial_input.data);
953 return NT_STATUS_NO_MEMORY;
956 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
957 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
960 status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
961 if (!NT_STATUS_IS_OK(status)) {
962 talloc_free(dce_conn->partial_input.data);
967 /* we have to check the signing here, before combining the
969 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
970 !dcesrv_auth_request(call, &blob)) {
971 dce_partial_advance(dce_conn, blob.length);
972 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
975 dce_partial_advance(dce_conn, blob.length);
977 /* see if this is a continued packet */
978 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
979 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
980 struct dcesrv_call_state *call2 = call;
983 /* we only allow fragmented requests, no other packet types */
984 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
985 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
988 /* this is a continuation of an existing call - find the call then
989 tack it on the end */
990 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
992 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
995 if (call->pkt.ptype != call2->pkt.ptype) {
996 /* trying to play silly buggers are we? */
997 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1000 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1001 call2->pkt.u.request.stub_and_verifier.length;
1002 if (call->pkt.u.request.alloc_hint > alloc_size) {
1003 alloc_size = call->pkt.u.request.alloc_hint;
1006 call->pkt.u.request.stub_and_verifier.data =
1007 talloc_realloc(call,
1008 call->pkt.u.request.stub_and_verifier.data,
1009 uint8_t, alloc_size);
1010 if (!call->pkt.u.request.stub_and_verifier.data) {
1011 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1013 memcpy(call->pkt.u.request.stub_and_verifier.data +
1014 call->pkt.u.request.stub_and_verifier.length,
1015 call2->pkt.u.request.stub_and_verifier.data,
1016 call2->pkt.u.request.stub_and_verifier.length);
1017 call->pkt.u.request.stub_and_verifier.length +=
1018 call2->pkt.u.request.stub_and_verifier.length;
1020 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1025 /* this may not be the last pdu in the chain - if its isn't then
1026 just put it on the call_list and wait for the rest */
1027 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1028 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1029 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1030 return NT_STATUS_OK;
1033 switch (call->pkt.ptype) {
1034 case DCERPC_PKT_BIND:
1035 status = dcesrv_bind(call);
1037 case DCERPC_PKT_AUTH3:
1038 status = dcesrv_auth3(call);
1040 case DCERPC_PKT_ALTER:
1041 status = dcesrv_alter(call);
1043 case DCERPC_PKT_REQUEST:
1044 status = dcesrv_request(call);
1047 status = NT_STATUS_INVALID_PARAMETER;
1051 /* if we are going to be sending a reply then add
1052 it to the list of pending calls. We add it to the end to keep the call
1053 list in the order we will answer */
1054 if (!NT_STATUS_IS_OK(status)) {
1063 provide some input to a dcerpc endpoint server. This passes data
1064 from a dcerpc client into the server
1066 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1070 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1071 dce_conn->partial_input.data,
1073 dce_conn->partial_input.length + data->length);
1074 if (!dce_conn->partial_input.data) {
1075 return NT_STATUS_NO_MEMORY;
1077 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1078 data->data, data->length);
1079 dce_conn->partial_input.length += data->length;
1081 while (dce_full_packet(&dce_conn->partial_input)) {
1082 status = dcesrv_input_process(dce_conn);
1083 if (!NT_STATUS_IS_OK(status)) {
1088 return NT_STATUS_OK;
1092 retrieve some output from a dcerpc server
1093 The caller supplies a function that will be called to do the
1096 The first argument to write_fn() will be 'private', the second will
1097 be a pointer to a buffer containing the data to be sent and the 3rd
1098 will be the number of bytes to be sent.
1100 write_fn() should return the number of bytes successfully written.
1102 this will return STATUS_BUFFER_OVERFLOW if there is more to be read
1103 from the current fragment
1105 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1107 ssize_t (*write_fn)(void *, DATA_BLOB *))
1109 struct dcesrv_call_state *call;
1110 struct dcesrv_call_reply *rep;
1113 call = dce_conn->call_list;
1114 if (!call || !call->replies) {
1115 if (dce_conn->pending_call_list) {
1116 /* TODO: we need to say act async here
1117 * as we know we have pending requests
1118 * which will be finished at a time
1120 return NT_STATUS_FOOBAR;
1122 return NT_STATUS_FOOBAR;
1124 rep = call->replies;
1126 nwritten = write_fn(private, &rep->data);
1127 if (nwritten == -1) {
1128 /* TODO: hmm, how do we cope with this? destroy the
1129 connection perhaps? */
1130 return NT_STATUS_UNSUCCESSFUL;
1133 rep->data.length -= nwritten;
1134 rep->data.data += nwritten;
1136 if (rep->data.length == 0) {
1137 /* we're done with this section of the call */
1138 DLIST_REMOVE(call->replies, rep);
1140 return STATUS_BUFFER_OVERFLOW;
1143 if (call->replies == NULL) {
1144 /* we're done with the whole call */
1145 DLIST_REMOVE(dce_conn->call_list, call);
1149 return NT_STATUS_OK;
1154 write_fn() for dcesrv_output_blob()
1156 static ssize_t dcesrv_output_blob_write_fn(void *private, DATA_BLOB *out)
1158 DATA_BLOB *blob = private;
1159 if (out->length < blob->length) {
1160 blob->length = out->length;
1162 memcpy(blob->data, out->data, blob->length);
1163 return blob->length;
1167 a simple wrapper for dcesrv_output() for when we want to output
1170 NTSTATUS dcesrv_output_blob(struct dcesrv_connection *dce_conn,
1173 return dcesrv_output(dce_conn, blob, dcesrv_output_blob_write_fn);
1176 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
1179 struct dcesrv_context *dce_ctx;
1182 if (!endpoint_servers) {
1183 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1184 return NT_STATUS_INTERNAL_ERROR;
1187 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1188 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1189 dce_ctx->endpoint_list = NULL;
1190 dce_ctx->state_flags = state_flags;
1192 for (i=0;endpoint_servers[i];i++) {
1193 const struct dcesrv_endpoint_server *ep_server;
1195 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1197 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1198 return NT_STATUS_INTERNAL_ERROR;
1201 status = ep_server->init_server(dce_ctx, ep_server);
1202 if (!NT_STATUS_IS_OK(status)) {
1203 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1204 nt_errstr(status)));
1209 *_dce_ctx = dce_ctx;
1210 return NT_STATUS_OK;
1214 initialise the dcerpc server context for ncacn_np based services
1216 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1219 struct dcesrv_context *dce_ctx;
1221 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx);
1222 NT_STATUS_NOT_OK_RETURN(status);
1224 *_dce_ctx = dce_ctx;
1225 return NT_STATUS_OK;
1228 /* the list of currently registered DCERPC endpoint servers.
1230 static struct ep_server {
1231 struct dcesrv_endpoint_server *ep_server;
1232 } *ep_servers = NULL;
1233 static int num_ep_servers;
1236 register a DCERPC endpoint server.
1238 The 'name' can be later used by other backends to find the operations
1239 structure for this backend.
1241 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1243 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1245 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1247 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1248 /* its already registered! */
1249 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1251 return NT_STATUS_OBJECT_NAME_COLLISION;
1254 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1256 smb_panic("out of memory in dcerpc_register");
1259 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1260 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1264 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1267 return NT_STATUS_OK;
1271 return the operations structure for a named backend of the specified type
1273 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1277 for (i=0;i<num_ep_servers;i++) {
1278 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1279 return ep_servers[i].ep_server;
1287 return the DCERPC module version, and the size of some critical types
1288 This can be used by endpoint server modules to either detect compilation errors, or provide
1289 multiple implementations for different smbd compilation options in one module
1291 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1293 static const struct dcesrv_critical_sizes critical_sizes = {
1294 DCERPC_MODULE_VERSION,
1295 sizeof(struct dcesrv_context),
1296 sizeof(struct dcesrv_endpoint),
1297 sizeof(struct dcesrv_endpoint_server),
1298 sizeof(struct dcesrv_interface),
1299 sizeof(struct dcesrv_if_list),
1300 sizeof(struct dcesrv_connection),
1301 sizeof(struct dcesrv_call_state),
1302 sizeof(struct dcesrv_auth),
1303 sizeof(struct dcesrv_handle)
1306 return &critical_sizes;
1310 initialise the dcerpc server context for socket based services
1312 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1315 struct dcesrv_context *dce_ctx;
1317 status = dcesrv_init_context(event_context,
1318 lp_dcerpc_endpoint_servers(),
1319 DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
1321 NT_STATUS_NOT_OK_RETURN(status);
1323 return dcesrv_sock_init(dce_ctx, event_context, model_ops);
1327 NTSTATUS server_service_rpc_init(void)
1329 return register_server_service("rpc", dcesrv_init);