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;
323 p->processing = False;
325 talloc_set_destructor(p, dcesrv_endpoint_destructor);
332 search and connect to a dcerpc endpoint
334 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
336 const struct dcerpc_binding *ep_description,
337 struct auth_session_info *session_info,
338 struct stream_connection *srv_conn,
339 struct dcesrv_connection **dce_conn_p)
342 const struct dcesrv_endpoint *ep;
344 /* make sure this endpoint exists */
345 ep = find_endpoint(dce_ctx, ep_description);
347 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
350 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, dce_conn_p);
351 if (!NT_STATUS_IS_OK(status)) {
355 (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
356 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
358 /* TODO: check security descriptor of the endpoint here
359 * if it's a smb named pipe
360 * if it's failed free dce_conn_p
367 static void dcesrv_init_hdr(struct ncacn_packet *pkt)
370 pkt->rpc_vers_minor = 0;
371 if (lp_rpc_big_endian()) {
374 pkt->drep[0] = DCERPC_DREP_LE;
382 return a dcerpc fault
384 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
386 struct ncacn_packet pkt;
387 struct dcesrv_call_reply *rep;
390 /* setup a bind_ack */
391 dcesrv_init_hdr(&pkt);
393 pkt.call_id = call->pkt.call_id;
394 pkt.ptype = DCERPC_PKT_FAULT;
395 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
396 pkt.u.fault.alloc_hint = 0;
397 pkt.u.fault.context_id = 0;
398 pkt.u.fault.cancel_count = 0;
399 pkt.u.fault.status = fault_code;
401 rep = talloc(call, struct dcesrv_call_reply);
403 return NT_STATUS_NO_MEMORY;
406 status = ncacn_push_auth(&rep->data, call, &pkt, NULL);
407 if (!NT_STATUS_IS_OK(status)) {
411 dcerpc_set_frag_length(&rep->data, rep->data.length);
413 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
414 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
421 return a dcerpc bind_nak
423 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
425 struct ncacn_packet pkt;
426 struct dcesrv_call_reply *rep;
429 /* setup a bind_nak */
430 dcesrv_init_hdr(&pkt);
432 pkt.call_id = call->pkt.call_id;
433 pkt.ptype = DCERPC_PKT_BIND_NAK;
434 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
435 pkt.u.bind_nak.reject_reason = reason;
436 pkt.u.bind_nak.num_versions = 0;
438 rep = talloc(call, struct dcesrv_call_reply);
440 return NT_STATUS_NO_MEMORY;
443 status = ncacn_push_auth(&rep->data, call, &pkt, NULL);
444 if (!NT_STATUS_IS_OK(status)) {
448 dcerpc_set_frag_length(&rep->data, rep->data.length);
450 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
451 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
458 handle a bind request
460 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
462 const char *uuid, *transfer_syntax;
463 uint32_t if_version, transfer_syntax_version;
464 struct ncacn_packet pkt;
465 struct dcesrv_call_reply *rep;
467 uint32_t result=0, reason=0;
469 const struct dcesrv_interface *iface;
471 if (call->pkt.u.bind.num_contexts != 1 ||
472 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
473 return dcesrv_bind_nak(call, 0);
476 context_id = call->pkt.u.bind.ctx_list[0].context_id;
478 /* you can't bind twice on one context */
479 if (dcesrv_find_context(call->conn, context_id) != NULL) {
480 return dcesrv_bind_nak(call, 0);
483 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
484 uuid = GUID_string(call, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
486 return dcesrv_bind_nak(call, 0);
489 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
490 transfer_syntax = GUID_string(call,
491 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
492 if (!transfer_syntax ||
493 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
494 NDR_GUID_VERSION != transfer_syntax_version) {
495 /* we only do NDR encoded dcerpc */
496 return dcesrv_bind_nak(call, 0);
499 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
501 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
502 /* we don't know about that interface */
503 result = DCERPC_BIND_PROVIDER_REJECT;
504 reason = DCERPC_BIND_REASON_ASYNTAX;
508 /* add this context to the list of available context_ids */
509 struct dcesrv_connection_context *context = talloc(call->conn,
510 struct dcesrv_connection_context);
511 if (context == NULL) {
512 return dcesrv_bind_nak(call, 0);
514 context->conn = call->conn;
515 context->iface = iface;
516 context->context_id = context_id;
517 context->private = NULL;
518 context->handles = NULL;
519 DLIST_ADD(call->conn->contexts, context);
520 call->context = context;
523 if (call->conn->cli_max_recv_frag == 0) {
524 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
527 /* handle any authentication that is being requested */
528 if (!dcesrv_auth_bind(call)) {
529 /* TODO: work out the right reject code */
530 return dcesrv_bind_nak(call, 0);
533 /* setup a bind_ack */
534 dcesrv_init_hdr(&pkt);
536 pkt.call_id = call->pkt.call_id;
537 pkt.ptype = DCERPC_PKT_BIND_ACK;
538 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
539 pkt.u.bind_ack.max_xmit_frag = 0x2000;
540 pkt.u.bind_ack.max_recv_frag = 0x2000;
541 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
543 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
544 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
546 pkt.u.bind_ack.secondary_address = "";
548 pkt.u.bind_ack.num_results = 1;
549 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
550 if (!pkt.u.bind_ack.ctx_list) {
551 return NT_STATUS_NO_MEMORY;
553 pkt.u.bind_ack.ctx_list[0].result = result;
554 pkt.u.bind_ack.ctx_list[0].reason = reason;
555 GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
556 pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
557 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
559 if (!dcesrv_auth_bind_ack(call, &pkt)) {
560 return dcesrv_bind_nak(call, 0);
564 status = iface->bind(call, iface);
565 if (!NT_STATUS_IS_OK(status)) {
566 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
567 uuid, if_version, nt_errstr(status)));
568 return dcesrv_bind_nak(call, 0);
572 rep = talloc(call, struct dcesrv_call_reply);
574 return NT_STATUS_NO_MEMORY;
577 status = ncacn_push_auth(&rep->data, call, &pkt,
578 call->conn->auth_state.auth_info);
579 if (!NT_STATUS_IS_OK(status)) {
583 dcerpc_set_frag_length(&rep->data, rep->data.length);
585 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
586 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
593 handle a auth3 request
595 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
597 /* handle the auth3 in the auth code */
598 if (!dcesrv_auth_auth3(call)) {
599 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
604 /* we don't send a reply to a auth3 request, except by a
611 handle a bind request
613 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
615 uint32_t if_version, transfer_syntax_version;
616 const char *uuid, *transfer_syntax;
617 struct dcesrv_connection_context *context;
618 const struct dcesrv_interface *iface;
620 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
621 uuid = GUID_string(call, &call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid);
623 return NT_STATUS_NO_MEMORY;
626 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
627 transfer_syntax = GUID_string(call,
628 &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid);
629 if (!transfer_syntax ||
630 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
631 NDR_GUID_VERSION != transfer_syntax_version) {
632 /* we only do NDR encoded dcerpc */
633 return NT_STATUS_NO_MEMORY;
636 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
638 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
639 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
642 /* add this context to the list of available context_ids */
643 context = talloc(call->conn, struct dcesrv_connection_context);
644 if (context == NULL) {
645 return NT_STATUS_NO_MEMORY;
647 context->conn = call->conn;
648 context->iface = iface;
649 context->context_id = context_id;
650 context->private = NULL;
651 context->handles = NULL;
652 DLIST_ADD(call->conn->contexts, context);
653 call->context = context;
660 handle a bind request
662 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
664 struct ncacn_packet pkt;
665 struct dcesrv_call_reply *rep;
667 uint32_t result=0, reason=0;
670 /* handle any authentication that is being requested */
671 if (!dcesrv_auth_alter(call)) {
672 /* TODO: work out the right reject code */
673 result = DCERPC_BIND_PROVIDER_REJECT;
674 reason = DCERPC_BIND_REASON_ASYNTAX;
677 context_id = call->pkt.u.alter.ctx_list[0].context_id;
679 /* see if they are asking for a new interface */
681 dcesrv_find_context(call->conn, context_id) == NULL) {
682 status = dcesrv_alter_new_context(call, context_id);
683 if (!NT_STATUS_IS_OK(status)) {
684 result = DCERPC_BIND_PROVIDER_REJECT;
685 reason = DCERPC_BIND_REASON_ASYNTAX;
689 /* setup a alter_resp */
690 dcesrv_init_hdr(&pkt);
692 pkt.call_id = call->pkt.call_id;
693 pkt.ptype = DCERPC_PKT_ALTER_RESP;
694 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
695 pkt.u.alter_resp.max_xmit_frag = 0x2000;
696 pkt.u.alter_resp.max_recv_frag = 0x2000;
697 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
698 pkt.u.alter_resp.num_results = 1;
699 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
700 if (!pkt.u.alter_resp.ctx_list) {
701 return NT_STATUS_NO_MEMORY;
703 pkt.u.alter_resp.ctx_list[0].result = result;
704 pkt.u.alter_resp.ctx_list[0].reason = reason;
705 GUID_from_string(NDR_GUID, &pkt.u.alter_resp.ctx_list[0].syntax.uuid);
706 pkt.u.alter_resp.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
707 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
708 pkt.u.alter_resp.secondary_address = "";
710 if (!dcesrv_auth_alter_ack(call, &pkt)) {
711 return dcesrv_bind_nak(call, 0);
714 rep = talloc(call, struct dcesrv_call_reply);
716 return NT_STATUS_NO_MEMORY;
719 status = ncacn_push_auth(&rep->data, call, &pkt,
720 call->conn->auth_state.auth_info);
721 if (!NT_STATUS_IS_OK(status)) {
725 dcerpc_set_frag_length(&rep->data, rep->data.length);
727 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
728 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
734 handle a dcerpc request packet
736 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
738 struct ndr_pull *pull;
740 struct dcesrv_connection_context *context;
742 call->fault_code = 0;
743 call->state_flags = call->conn->dce_ctx->state_flags;
744 call->time = timeval_current();
746 /* if authenticated, and the mech we use can't do async replies, don't use them... */
747 if (call->conn->auth_state.gensec_security &&
748 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
749 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
752 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
753 if (context == NULL) {
754 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
757 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
758 NT_STATUS_HAVE_NO_MEMORY(pull);
760 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
762 call->context = context;
763 call->event_ctx = context->conn->srv_conn->event.ctx;
764 call->ndr_pull = pull;
766 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
767 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
770 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
771 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
774 /* unravel the NDR for the packet */
775 status = context->iface->ndr_pull(call, call, pull, &call->r);
776 if (!NT_STATUS_IS_OK(status)) {
777 return dcesrv_fault(call, call->fault_code);
780 if (pull->offset != pull->data_size) {
781 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
782 pull->data_size - pull->offset));
783 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
786 /* call the dispatch function */
787 status = context->iface->dispatch(call, call, call->r);
788 if (!NT_STATUS_IS_OK(status)) {
789 return dcesrv_fault(call, call->fault_code);
792 /* add the call to the pending list */
793 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
795 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
799 return dcesrv_reply(call);
802 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
804 struct ndr_push *push;
807 uint32_t total_length;
808 struct dcesrv_connection_context *context = call->context;
810 /* call the reply function */
811 status = context->iface->reply(call, call, call->r);
812 if (!NT_STATUS_IS_OK(status)) {
813 return dcesrv_fault(call, call->fault_code);
816 /* form the reply NDR */
817 push = ndr_push_init_ctx(call);
818 NT_STATUS_HAVE_NO_MEMORY(push);
820 /* carry over the pointer count to the reply in case we are
821 using full pointer. See NDR specification for full
823 push->ptr_count = call->ndr_pull->ptr_count;
825 if (lp_rpc_big_endian()) {
826 push->flags |= LIBNDR_FLAG_BIGENDIAN;
829 status = context->iface->ndr_push(call, call, push, call->r);
830 if (!NT_STATUS_IS_OK(status)) {
831 return dcesrv_fault(call, call->fault_code);
834 stub = ndr_push_blob(push);
836 total_length = stub.length;
840 struct dcesrv_call_reply *rep;
841 struct ncacn_packet pkt;
843 rep = talloc(call, struct dcesrv_call_reply);
844 NT_STATUS_HAVE_NO_MEMORY(rep);
846 length = stub.length;
847 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
848 /* the 32 is to cope with signing data */
849 length = call->conn->cli_max_recv_frag -
850 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
853 /* form the dcerpc response packet */
854 dcesrv_init_hdr(&pkt);
856 pkt.call_id = call->pkt.call_id;
857 pkt.ptype = DCERPC_PKT_RESPONSE;
859 if (stub.length == total_length) {
860 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
862 if (length == stub.length) {
863 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
865 pkt.u.response.alloc_hint = stub.length;
866 pkt.u.response.context_id = call->pkt.u.request.context_id;
867 pkt.u.response.cancel_count = 0;
868 pkt.u.response.stub_and_verifier.data = stub.data;
869 pkt.u.response.stub_and_verifier.length = length;
871 if (!dcesrv_auth_response(call, &rep->data, &pkt)) {
872 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
875 dcerpc_set_frag_length(&rep->data, rep->data.length);
877 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
880 stub.length -= length;
881 } while (stub.length != 0);
883 /* move the call from the pending to the finished calls list */
884 DLIST_REMOVE(call->conn->pending_call_list, call);
885 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
887 if (call->conn->call_list && call->conn->call_list->replies) {
888 if (call->conn->srv_conn &&
889 call->conn->srv_conn->event.fde) {
890 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
899 work out if we have a full packet yet
901 static BOOL dce_full_packet(const DATA_BLOB *data)
903 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
906 if (dcerpc_get_frag_length(data) > data->length) {
913 we might have consumed only part of our input - advance past that part
915 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
919 if (dce_conn->partial_input.length == offset) {
920 data_blob_free(&dce_conn->partial_input);
924 blob = dce_conn->partial_input;
925 dce_conn->partial_input = data_blob(blob.data + offset,
926 blob.length - offset);
927 data_blob_free(&blob);
931 process some input to a dcerpc endpoint server.
933 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
935 struct ndr_pull *ndr;
937 struct dcesrv_call_state *call;
940 call = talloc(dce_conn, struct dcesrv_call_state);
942 talloc_free(dce_conn->partial_input.data);
943 return NT_STATUS_NO_MEMORY;
945 call->conn = dce_conn;
946 call->replies = NULL;
947 call->context = NULL;
948 call->event_ctx = dce_conn->srv_conn->event.ctx;
950 blob = dce_conn->partial_input;
951 blob.length = dcerpc_get_frag_length(&blob);
953 ndr = ndr_pull_init_blob(&blob, call);
955 talloc_free(dce_conn->partial_input.data);
957 return NT_STATUS_NO_MEMORY;
960 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
961 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
964 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
965 if (!NT_STATUS_IS_OK(status)) {
966 talloc_free(dce_conn->partial_input.data);
971 /* we have to check the signing here, before combining the
973 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
974 !dcesrv_auth_request(call, &blob)) {
975 dce_partial_advance(dce_conn, blob.length);
976 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
979 dce_partial_advance(dce_conn, blob.length);
981 /* see if this is a continued packet */
982 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
983 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
984 struct dcesrv_call_state *call2 = call;
987 /* we only allow fragmented requests, no other packet types */
988 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
989 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
992 /* this is a continuation of an existing call - find the call then
993 tack it on the end */
994 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
996 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
999 if (call->pkt.ptype != call2->pkt.ptype) {
1000 /* trying to play silly buggers are we? */
1001 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1004 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1005 call2->pkt.u.request.stub_and_verifier.length;
1006 if (call->pkt.u.request.alloc_hint > alloc_size) {
1007 alloc_size = call->pkt.u.request.alloc_hint;
1010 call->pkt.u.request.stub_and_verifier.data =
1011 talloc_realloc(call,
1012 call->pkt.u.request.stub_and_verifier.data,
1013 uint8_t, alloc_size);
1014 if (!call->pkt.u.request.stub_and_verifier.data) {
1015 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1017 memcpy(call->pkt.u.request.stub_and_verifier.data +
1018 call->pkt.u.request.stub_and_verifier.length,
1019 call2->pkt.u.request.stub_and_verifier.data,
1020 call2->pkt.u.request.stub_and_verifier.length);
1021 call->pkt.u.request.stub_and_verifier.length +=
1022 call2->pkt.u.request.stub_and_verifier.length;
1024 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1029 /* this may not be the last pdu in the chain - if its isn't then
1030 just put it on the call_list and wait for the rest */
1031 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1032 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1033 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1034 return NT_STATUS_OK;
1037 switch (call->pkt.ptype) {
1038 case DCERPC_PKT_BIND:
1039 status = dcesrv_bind(call);
1041 case DCERPC_PKT_AUTH3:
1042 status = dcesrv_auth3(call);
1044 case DCERPC_PKT_ALTER:
1045 status = dcesrv_alter(call);
1047 case DCERPC_PKT_REQUEST:
1048 status = dcesrv_request(call);
1051 status = NT_STATUS_INVALID_PARAMETER;
1055 /* if we are going to be sending a reply then add
1056 it to the list of pending calls. We add it to the end to keep the call
1057 list in the order we will answer */
1058 if (!NT_STATUS_IS_OK(status)) {
1067 provide some input to a dcerpc endpoint server. This passes data
1068 from a dcerpc client into the server
1070 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1074 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1075 dce_conn->partial_input.data,
1077 dce_conn->partial_input.length + data->length);
1078 if (!dce_conn->partial_input.data) {
1079 return NT_STATUS_NO_MEMORY;
1081 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1082 data->data, data->length);
1083 dce_conn->partial_input.length += data->length;
1085 while (dce_full_packet(&dce_conn->partial_input)) {
1086 status = dcesrv_input_process(dce_conn);
1087 if (!NT_STATUS_IS_OK(status)) {
1092 return NT_STATUS_OK;
1096 retrieve some output from a dcerpc server
1097 The caller supplies a function that will be called to do the
1100 The first argument to write_fn() will be 'private', the second will
1101 be a pointer to a buffer containing the data to be sent and the 3rd
1102 will be the number of bytes to be sent.
1104 write_fn() should return the number of bytes successfully written.
1106 this will return STATUS_BUFFER_OVERFLOW if there is more to be read
1107 from the current fragment
1109 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1111 ssize_t (*write_fn)(void *, DATA_BLOB *))
1113 struct dcesrv_call_state *call;
1114 struct dcesrv_call_reply *rep;
1117 call = dce_conn->call_list;
1118 if (!call || !call->replies) {
1119 if (dce_conn->pending_call_list) {
1120 /* TODO: we need to say act async here
1121 * as we know we have pending requests
1122 * which will be finished at a time
1124 return NT_STATUS_FOOBAR;
1126 return NT_STATUS_FOOBAR;
1128 rep = call->replies;
1130 nwritten = write_fn(private, &rep->data);
1131 if (nwritten == -1) {
1132 /* TODO: hmm, how do we cope with this? destroy the
1133 connection perhaps? */
1134 return NT_STATUS_UNSUCCESSFUL;
1137 rep->data.length -= nwritten;
1138 rep->data.data += nwritten;
1140 if (rep->data.length == 0) {
1141 /* we're done with this section of the call */
1142 DLIST_REMOVE(call->replies, rep);
1144 return STATUS_BUFFER_OVERFLOW;
1147 if (call->replies == NULL) {
1148 /* we're done with the whole call */
1149 DLIST_REMOVE(dce_conn->call_list, call);
1153 return NT_STATUS_OK;
1158 write_fn() for dcesrv_output_blob()
1160 static ssize_t dcesrv_output_blob_write_fn(void *private, DATA_BLOB *out)
1162 DATA_BLOB *blob = private;
1163 if (out->length < blob->length) {
1164 blob->length = out->length;
1166 memcpy(blob->data, out->data, blob->length);
1167 return blob->length;
1171 a simple wrapper for dcesrv_output() for when we want to output
1174 NTSTATUS dcesrv_output_blob(struct dcesrv_connection *dce_conn,
1177 return dcesrv_output(dce_conn, blob, dcesrv_output_blob_write_fn);
1180 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
1183 struct dcesrv_context *dce_ctx;
1186 if (!endpoint_servers) {
1187 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1188 return NT_STATUS_INTERNAL_ERROR;
1191 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1192 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1193 dce_ctx->endpoint_list = NULL;
1194 dce_ctx->state_flags = state_flags;
1196 for (i=0;endpoint_servers[i];i++) {
1197 const struct dcesrv_endpoint_server *ep_server;
1199 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1201 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1202 return NT_STATUS_INTERNAL_ERROR;
1205 status = ep_server->init_server(dce_ctx, ep_server);
1206 if (!NT_STATUS_IS_OK(status)) {
1207 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1208 nt_errstr(status)));
1213 *_dce_ctx = dce_ctx;
1214 return NT_STATUS_OK;
1218 initialise the dcerpc server context for ncacn_np based services
1220 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1223 struct dcesrv_context *dce_ctx;
1225 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx);
1226 NT_STATUS_NOT_OK_RETURN(status);
1228 *_dce_ctx = dce_ctx;
1229 return NT_STATUS_OK;
1232 /* the list of currently registered DCERPC endpoint servers.
1234 static struct ep_server {
1235 struct dcesrv_endpoint_server *ep_server;
1236 } *ep_servers = NULL;
1237 static int num_ep_servers;
1240 register a DCERPC endpoint server.
1242 The 'name' can be later used by other backends to find the operations
1243 structure for this backend.
1245 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1247 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1249 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1251 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1252 /* its already registered! */
1253 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1255 return NT_STATUS_OBJECT_NAME_COLLISION;
1258 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1260 smb_panic("out of memory in dcerpc_register");
1263 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1264 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1268 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1271 return NT_STATUS_OK;
1275 return the operations structure for a named backend of the specified type
1277 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1281 for (i=0;i<num_ep_servers;i++) {
1282 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1283 return ep_servers[i].ep_server;
1291 return the DCERPC module version, and the size of some critical types
1292 This can be used by endpoint server modules to either detect compilation errors, or provide
1293 multiple implementations for different smbd compilation options in one module
1295 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1297 static const struct dcesrv_critical_sizes critical_sizes = {
1298 DCERPC_MODULE_VERSION,
1299 sizeof(struct dcesrv_context),
1300 sizeof(struct dcesrv_endpoint),
1301 sizeof(struct dcesrv_endpoint_server),
1302 sizeof(struct dcesrv_interface),
1303 sizeof(struct dcesrv_if_list),
1304 sizeof(struct dcesrv_connection),
1305 sizeof(struct dcesrv_call_state),
1306 sizeof(struct dcesrv_auth),
1307 sizeof(struct dcesrv_handle)
1310 return &critical_sizes;
1314 initialise the dcerpc server context for socket based services
1316 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1319 struct dcesrv_context *dce_ctx;
1321 status = dcesrv_init_context(event_context,
1322 lp_dcerpc_endpoint_servers(),
1323 DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
1325 NT_STATUS_NOT_OK_RETURN(status);
1327 return dcesrv_sock_init(dce_ctx, event_context, model_ops);
1331 NTSTATUS server_service_rpc_init(void)
1333 return register_server_service("rpc", dcesrv_init);