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 DEBUG(0,("Non NDR transfer syntax requested - %s\n", transfer_syntax));
497 return dcesrv_bind_nak(call, 0);
500 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
502 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
503 /* we don't know about that interface */
504 result = DCERPC_BIND_PROVIDER_REJECT;
505 reason = DCERPC_BIND_REASON_ASYNTAX;
509 /* add this context to the list of available context_ids */
510 struct dcesrv_connection_context *context = talloc(call->conn,
511 struct dcesrv_connection_context);
512 if (context == NULL) {
513 return dcesrv_bind_nak(call, 0);
515 context->conn = call->conn;
516 context->iface = iface;
517 context->context_id = context_id;
518 context->private = NULL;
519 context->handles = NULL;
520 DLIST_ADD(call->conn->contexts, context);
521 call->context = context;
524 if (call->conn->cli_max_recv_frag == 0) {
525 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
528 /* handle any authentication that is being requested */
529 if (!dcesrv_auth_bind(call)) {
530 /* TODO: work out the right reject code */
531 return dcesrv_bind_nak(call, 0);
534 /* setup a bind_ack */
535 dcesrv_init_hdr(&pkt);
537 pkt.call_id = call->pkt.call_id;
538 pkt.ptype = DCERPC_PKT_BIND_ACK;
539 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
540 pkt.u.bind_ack.max_xmit_frag = 0x2000;
541 pkt.u.bind_ack.max_recv_frag = 0x2000;
542 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
544 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
545 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
547 pkt.u.bind_ack.secondary_address = "";
549 pkt.u.bind_ack.num_results = 1;
550 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
551 if (!pkt.u.bind_ack.ctx_list) {
552 return NT_STATUS_NO_MEMORY;
554 pkt.u.bind_ack.ctx_list[0].result = result;
555 pkt.u.bind_ack.ctx_list[0].reason = reason;
556 GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
557 pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
558 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
560 if (!dcesrv_auth_bind_ack(call, &pkt)) {
561 return dcesrv_bind_nak(call, 0);
565 status = iface->bind(call, iface);
566 if (!NT_STATUS_IS_OK(status)) {
567 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
568 uuid, if_version, nt_errstr(status)));
569 return dcesrv_bind_nak(call, 0);
573 rep = talloc(call, struct dcesrv_call_reply);
575 return NT_STATUS_NO_MEMORY;
578 status = ncacn_push_auth(&rep->data, call, &pkt,
579 call->conn->auth_state.auth_info);
580 if (!NT_STATUS_IS_OK(status)) {
584 dcerpc_set_frag_length(&rep->data, rep->data.length);
586 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
587 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
594 handle a auth3 request
596 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
598 /* handle the auth3 in the auth code */
599 if (!dcesrv_auth_auth3(call)) {
600 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
605 /* we don't send a reply to a auth3 request, except by a
612 handle a bind request
614 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
616 uint32_t if_version, transfer_syntax_version;
617 const char *uuid, *transfer_syntax;
618 struct dcesrv_connection_context *context;
619 const struct dcesrv_interface *iface;
621 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
622 uuid = GUID_string(call, &call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid);
624 return NT_STATUS_NO_MEMORY;
627 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
628 transfer_syntax = GUID_string(call,
629 &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid);
630 if (!transfer_syntax ||
631 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
632 NDR_GUID_VERSION != transfer_syntax_version) {
633 /* we only do NDR encoded dcerpc */
634 return NT_STATUS_NO_MEMORY;
637 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
639 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
640 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
643 /* add this context to the list of available context_ids */
644 context = talloc(call->conn, struct dcesrv_connection_context);
645 if (context == NULL) {
646 return NT_STATUS_NO_MEMORY;
648 context->conn = call->conn;
649 context->iface = iface;
650 context->context_id = context_id;
651 context->private = NULL;
652 context->handles = NULL;
653 DLIST_ADD(call->conn->contexts, context);
654 call->context = context;
661 handle a bind request
663 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
665 struct ncacn_packet pkt;
666 struct dcesrv_call_reply *rep;
668 uint32_t result=0, reason=0;
671 /* handle any authentication that is being requested */
672 if (!dcesrv_auth_alter(call)) {
673 /* TODO: work out the right reject code */
674 result = DCERPC_BIND_PROVIDER_REJECT;
675 reason = DCERPC_BIND_REASON_ASYNTAX;
678 context_id = call->pkt.u.alter.ctx_list[0].context_id;
680 /* see if they are asking for a new interface */
682 dcesrv_find_context(call->conn, context_id) == NULL) {
683 status = dcesrv_alter_new_context(call, context_id);
684 if (!NT_STATUS_IS_OK(status)) {
685 result = DCERPC_BIND_PROVIDER_REJECT;
686 reason = DCERPC_BIND_REASON_ASYNTAX;
690 /* setup a alter_resp */
691 dcesrv_init_hdr(&pkt);
693 pkt.call_id = call->pkt.call_id;
694 pkt.ptype = DCERPC_PKT_ALTER_RESP;
695 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
696 pkt.u.alter_resp.max_xmit_frag = 0x2000;
697 pkt.u.alter_resp.max_recv_frag = 0x2000;
698 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
699 pkt.u.alter_resp.num_results = 1;
700 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
701 if (!pkt.u.alter_resp.ctx_list) {
702 return NT_STATUS_NO_MEMORY;
704 pkt.u.alter_resp.ctx_list[0].result = result;
705 pkt.u.alter_resp.ctx_list[0].reason = reason;
706 GUID_from_string(NDR_GUID, &pkt.u.alter_resp.ctx_list[0].syntax.uuid);
707 pkt.u.alter_resp.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
708 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
709 pkt.u.alter_resp.secondary_address = "";
711 if (!dcesrv_auth_alter_ack(call, &pkt)) {
712 return dcesrv_bind_nak(call, 0);
715 rep = talloc(call, struct dcesrv_call_reply);
717 return NT_STATUS_NO_MEMORY;
720 status = ncacn_push_auth(&rep->data, call, &pkt,
721 call->conn->auth_state.auth_info);
722 if (!NT_STATUS_IS_OK(status)) {
726 dcerpc_set_frag_length(&rep->data, rep->data.length);
728 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
729 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
735 handle a dcerpc request packet
737 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
739 struct ndr_pull *pull;
741 struct dcesrv_connection_context *context;
743 call->fault_code = 0;
744 call->state_flags = call->conn->dce_ctx->state_flags;
745 call->time = timeval_current();
747 /* if authenticated, and the mech we use can't do async replies, don't use them... */
748 if (call->conn->auth_state.gensec_security &&
749 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
750 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
753 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
754 if (context == NULL) {
755 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
758 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
759 NT_STATUS_HAVE_NO_MEMORY(pull);
761 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
763 call->context = context;
764 call->event_ctx = context->conn->srv_conn->event.ctx;
765 call->ndr_pull = pull;
767 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
768 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
771 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
772 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
775 /* unravel the NDR for the packet */
776 status = context->iface->ndr_pull(call, call, pull, &call->r);
777 if (!NT_STATUS_IS_OK(status)) {
778 return dcesrv_fault(call, call->fault_code);
781 if (pull->offset != pull->data_size) {
782 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
783 pull->data_size - pull->offset));
784 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
787 /* call the dispatch function */
788 status = context->iface->dispatch(call, call, call->r);
789 if (!NT_STATUS_IS_OK(status)) {
790 return dcesrv_fault(call, call->fault_code);
793 /* add the call to the pending list */
794 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
796 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
800 return dcesrv_reply(call);
803 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
805 struct ndr_push *push;
808 uint32_t total_length;
809 struct dcesrv_connection_context *context = call->context;
811 /* call the reply function */
812 status = context->iface->reply(call, call, call->r);
813 if (!NT_STATUS_IS_OK(status)) {
814 return dcesrv_fault(call, call->fault_code);
817 /* form the reply NDR */
818 push = ndr_push_init_ctx(call);
819 NT_STATUS_HAVE_NO_MEMORY(push);
821 /* carry over the pointer count to the reply in case we are
822 using full pointer. See NDR specification for full
824 push->ptr_count = call->ndr_pull->ptr_count;
826 if (lp_rpc_big_endian()) {
827 push->flags |= LIBNDR_FLAG_BIGENDIAN;
830 status = context->iface->ndr_push(call, call, push, call->r);
831 if (!NT_STATUS_IS_OK(status)) {
832 return dcesrv_fault(call, call->fault_code);
835 stub = ndr_push_blob(push);
837 total_length = stub.length;
841 struct dcesrv_call_reply *rep;
842 struct ncacn_packet pkt;
844 rep = talloc(call, struct dcesrv_call_reply);
845 NT_STATUS_HAVE_NO_MEMORY(rep);
847 length = stub.length;
848 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
849 /* the 32 is to cope with signing data */
850 length = call->conn->cli_max_recv_frag -
851 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
854 /* form the dcerpc response packet */
855 dcesrv_init_hdr(&pkt);
857 pkt.call_id = call->pkt.call_id;
858 pkt.ptype = DCERPC_PKT_RESPONSE;
860 if (stub.length == total_length) {
861 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
863 if (length == stub.length) {
864 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
866 pkt.u.response.alloc_hint = stub.length;
867 pkt.u.response.context_id = call->pkt.u.request.context_id;
868 pkt.u.response.cancel_count = 0;
869 pkt.u.response.stub_and_verifier.data = stub.data;
870 pkt.u.response.stub_and_verifier.length = length;
872 if (!dcesrv_auth_response(call, &rep->data, &pkt)) {
873 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
876 dcerpc_set_frag_length(&rep->data, rep->data.length);
878 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
881 stub.length -= length;
882 } while (stub.length != 0);
884 /* move the call from the pending to the finished calls list */
885 DLIST_REMOVE(call->conn->pending_call_list, call);
886 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
888 if (call->conn->call_list && call->conn->call_list->replies) {
889 if (call->conn->srv_conn &&
890 call->conn->srv_conn->event.fde) {
891 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
900 work out if we have a full packet yet
902 static BOOL dce_full_packet(const DATA_BLOB *data)
904 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
907 if (dcerpc_get_frag_length(data) > data->length) {
914 we might have consumed only part of our input - advance past that part
916 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
920 if (dce_conn->partial_input.length == offset) {
921 data_blob_free(&dce_conn->partial_input);
925 blob = dce_conn->partial_input;
926 dce_conn->partial_input = data_blob(blob.data + offset,
927 blob.length - offset);
928 data_blob_free(&blob);
932 process some input to a dcerpc endpoint server.
934 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
936 struct ndr_pull *ndr;
938 struct dcesrv_call_state *call;
941 call = talloc(dce_conn, struct dcesrv_call_state);
943 talloc_free(dce_conn->partial_input.data);
944 return NT_STATUS_NO_MEMORY;
946 call->conn = dce_conn;
947 call->replies = NULL;
948 call->context = NULL;
949 call->event_ctx = dce_conn->srv_conn->event.ctx;
951 blob = dce_conn->partial_input;
952 blob.length = dcerpc_get_frag_length(&blob);
954 ndr = ndr_pull_init_blob(&blob, call);
956 talloc_free(dce_conn->partial_input.data);
958 return NT_STATUS_NO_MEMORY;
961 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
962 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
965 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
966 if (!NT_STATUS_IS_OK(status)) {
967 talloc_free(dce_conn->partial_input.data);
972 /* we have to check the signing here, before combining the
974 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
975 !dcesrv_auth_request(call, &blob)) {
976 dce_partial_advance(dce_conn, blob.length);
977 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
980 dce_partial_advance(dce_conn, blob.length);
982 /* see if this is a continued packet */
983 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
984 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
985 struct dcesrv_call_state *call2 = call;
988 /* we only allow fragmented requests, no other packet types */
989 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
990 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
993 /* this is a continuation of an existing call - find the call then
994 tack it on the end */
995 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
997 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1000 if (call->pkt.ptype != call2->pkt.ptype) {
1001 /* trying to play silly buggers are we? */
1002 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1005 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1006 call2->pkt.u.request.stub_and_verifier.length;
1007 if (call->pkt.u.request.alloc_hint > alloc_size) {
1008 alloc_size = call->pkt.u.request.alloc_hint;
1011 call->pkt.u.request.stub_and_verifier.data =
1012 talloc_realloc(call,
1013 call->pkt.u.request.stub_and_verifier.data,
1014 uint8_t, alloc_size);
1015 if (!call->pkt.u.request.stub_and_verifier.data) {
1016 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1018 memcpy(call->pkt.u.request.stub_and_verifier.data +
1019 call->pkt.u.request.stub_and_verifier.length,
1020 call2->pkt.u.request.stub_and_verifier.data,
1021 call2->pkt.u.request.stub_and_verifier.length);
1022 call->pkt.u.request.stub_and_verifier.length +=
1023 call2->pkt.u.request.stub_and_verifier.length;
1025 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1030 /* this may not be the last pdu in the chain - if its isn't then
1031 just put it on the call_list and wait for the rest */
1032 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1033 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1034 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1035 return NT_STATUS_OK;
1038 switch (call->pkt.ptype) {
1039 case DCERPC_PKT_BIND:
1040 status = dcesrv_bind(call);
1042 case DCERPC_PKT_AUTH3:
1043 status = dcesrv_auth3(call);
1045 case DCERPC_PKT_ALTER:
1046 status = dcesrv_alter(call);
1048 case DCERPC_PKT_REQUEST:
1049 status = dcesrv_request(call);
1052 status = NT_STATUS_INVALID_PARAMETER;
1056 /* if we are going to be sending a reply then add
1057 it to the list of pending calls. We add it to the end to keep the call
1058 list in the order we will answer */
1059 if (!NT_STATUS_IS_OK(status)) {
1068 provide some input to a dcerpc endpoint server. This passes data
1069 from a dcerpc client into the server
1071 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1075 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1076 dce_conn->partial_input.data,
1078 dce_conn->partial_input.length + data->length);
1079 if (!dce_conn->partial_input.data) {
1080 return NT_STATUS_NO_MEMORY;
1082 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1083 data->data, data->length);
1084 dce_conn->partial_input.length += data->length;
1086 while (dce_full_packet(&dce_conn->partial_input)) {
1087 status = dcesrv_input_process(dce_conn);
1088 if (!NT_STATUS_IS_OK(status)) {
1093 return NT_STATUS_OK;
1097 retrieve some output from a dcerpc server
1098 The caller supplies a function that will be called to do the
1101 The first argument to write_fn() will be 'private', the second will
1102 be a pointer to a buffer containing the data to be sent and the 3rd
1103 will be the number of bytes to be sent.
1105 write_fn() should return the number of bytes successfully written.
1107 this will return STATUS_BUFFER_OVERFLOW if there is more to be written
1108 from the current fragment
1110 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1112 ssize_t (*write_fn)(void *, DATA_BLOB *))
1114 struct dcesrv_call_state *call;
1115 struct dcesrv_call_reply *rep;
1118 call = dce_conn->call_list;
1119 if (!call || !call->replies) {
1120 if (dce_conn->pending_call_list) {
1121 /* TODO: we need to say act async here
1122 * as we know we have pending requests
1123 * which will be finished at a time
1125 return NT_STATUS_FOOBAR;
1127 return NT_STATUS_FOOBAR;
1129 rep = call->replies;
1131 nwritten = write_fn(private, &rep->data);
1132 if (nwritten == -1) {
1133 /* TODO: hmm, how do we cope with this? destroy the
1134 connection perhaps? */
1135 return NT_STATUS_UNSUCCESSFUL;
1138 rep->data.length -= nwritten;
1139 rep->data.data += nwritten;
1141 if (rep->data.length == 0) {
1142 /* we're done with this section of the call */
1143 DLIST_REMOVE(call->replies, rep);
1145 return STATUS_BUFFER_OVERFLOW;
1148 if (call->replies == NULL) {
1149 /* we're done with the whole call */
1150 DLIST_REMOVE(dce_conn->call_list, call);
1154 return NT_STATUS_OK;
1159 write_fn() for dcesrv_output_blob()
1161 static ssize_t dcesrv_output_blob_write_fn(void *private, DATA_BLOB *out)
1163 DATA_BLOB *blob = private;
1164 if (out->length < blob->length) {
1165 blob->length = out->length;
1167 memcpy(blob->data, out->data, blob->length);
1168 return blob->length;
1172 a simple wrapper for dcesrv_output() for when we want to output
1175 NTSTATUS dcesrv_output_blob(struct dcesrv_connection *dce_conn,
1178 return dcesrv_output(dce_conn, blob, dcesrv_output_blob_write_fn);
1181 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
1184 struct dcesrv_context *dce_ctx;
1187 if (!endpoint_servers) {
1188 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1189 return NT_STATUS_INTERNAL_ERROR;
1192 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1193 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1194 dce_ctx->endpoint_list = NULL;
1195 dce_ctx->state_flags = state_flags;
1197 for (i=0;endpoint_servers[i];i++) {
1198 const struct dcesrv_endpoint_server *ep_server;
1200 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1202 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1203 return NT_STATUS_INTERNAL_ERROR;
1206 status = ep_server->init_server(dce_ctx, ep_server);
1207 if (!NT_STATUS_IS_OK(status)) {
1208 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1209 nt_errstr(status)));
1214 *_dce_ctx = dce_ctx;
1215 return NT_STATUS_OK;
1219 initialise the dcerpc server context for ncacn_np based services
1221 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1224 struct dcesrv_context *dce_ctx;
1226 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx);
1227 NT_STATUS_NOT_OK_RETURN(status);
1229 *_dce_ctx = dce_ctx;
1230 return NT_STATUS_OK;
1233 /* the list of currently registered DCERPC endpoint servers.
1235 static struct ep_server {
1236 struct dcesrv_endpoint_server *ep_server;
1237 } *ep_servers = NULL;
1238 static int num_ep_servers;
1241 register a DCERPC endpoint server.
1243 The 'name' can be later used by other backends to find the operations
1244 structure for this backend.
1246 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1248 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1250 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1252 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1253 /* its already registered! */
1254 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1256 return NT_STATUS_OBJECT_NAME_COLLISION;
1259 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1261 smb_panic("out of memory in dcerpc_register");
1264 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1265 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1269 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1272 return NT_STATUS_OK;
1276 return the operations structure for a named backend of the specified type
1278 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1282 for (i=0;i<num_ep_servers;i++) {
1283 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1284 return ep_servers[i].ep_server;
1292 return the DCERPC module version, and the size of some critical types
1293 This can be used by endpoint server modules to either detect compilation errors, or provide
1294 multiple implementations for different smbd compilation options in one module
1296 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1298 static const struct dcesrv_critical_sizes critical_sizes = {
1299 DCERPC_MODULE_VERSION,
1300 sizeof(struct dcesrv_context),
1301 sizeof(struct dcesrv_endpoint),
1302 sizeof(struct dcesrv_endpoint_server),
1303 sizeof(struct dcesrv_interface),
1304 sizeof(struct dcesrv_if_list),
1305 sizeof(struct dcesrv_connection),
1306 sizeof(struct dcesrv_call_state),
1307 sizeof(struct dcesrv_auth),
1308 sizeof(struct dcesrv_handle)
1311 return &critical_sizes;
1315 initialise the dcerpc server context for socket based services
1317 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1320 struct dcesrv_context *dce_ctx;
1322 status = dcesrv_init_context(event_context,
1323 lp_dcerpc_endpoint_servers(),
1324 DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
1326 NT_STATUS_NOT_OK_RETURN(status);
1328 return dcesrv_sock_init(dce_ctx, event_context, model_ops);
1332 NTSTATUS server_service_rpc_init(void)
1334 return register_server_service("rpc", dcesrv_init);