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);
402 NT_STATUS_HAVE_NO_MEMORY(rep);
404 rep->data = talloc(call, DATA_BLOB);
405 NT_STATUS_HAVE_NO_MEMORY(rep->data);
407 status = ncacn_push_auth(rep->data, call, &pkt, NULL);
408 NT_STATUS_NOT_OK_RETURN(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 ncacn_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);
438 NT_STATUS_HAVE_NO_MEMORY(rep);
440 rep->data = talloc(call, DATA_BLOB);
441 NT_STATUS_HAVE_NO_MEMORY(rep->data);
443 status = ncacn_push_auth(rep->data, call, &pkt, NULL);
444 NT_STATUS_NOT_OK_RETURN(status);
446 dcerpc_set_frag_length(rep->data, rep->data->length);
448 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
449 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
456 handle a bind request
458 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
460 const char *uuid, *transfer_syntax;
461 uint32_t if_version, transfer_syntax_version;
462 struct ncacn_packet pkt;
463 struct dcesrv_call_reply *rep;
465 uint32_t result=0, reason=0;
467 const struct dcesrv_interface *iface;
469 if (call->pkt.u.bind.num_contexts < 1 ||
470 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
471 return dcesrv_bind_nak(call, 0);
474 context_id = call->pkt.u.bind.ctx_list[0].context_id;
476 /* you can't bind twice on one context */
477 if (dcesrv_find_context(call->conn, context_id) != NULL) {
478 return dcesrv_bind_nak(call, 0);
481 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
482 uuid = GUID_string(call, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
484 return dcesrv_bind_nak(call, 0);
487 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
488 transfer_syntax = GUID_string(call,
489 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
490 if (!transfer_syntax ||
491 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
492 NDR_GUID_VERSION != transfer_syntax_version) {
493 /* we only do NDR encoded dcerpc */
494 DEBUG(0,("Non NDR transfer syntax requested - %s\n", transfer_syntax));
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);
572 NT_STATUS_HAVE_NO_MEMORY(rep);
574 rep->data = talloc(call, DATA_BLOB);
575 NT_STATUS_HAVE_NO_MEMORY(rep->data);
577 status = ncacn_push_auth(rep->data, call, &pkt,
578 call->conn->auth_state.auth_info);
579 NT_STATUS_NOT_OK_RETURN(status);
581 dcerpc_set_frag_length(rep->data, rep->data->length);
583 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
584 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
591 handle a auth3 request
593 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
595 /* handle the auth3 in the auth code */
596 if (!dcesrv_auth_auth3(call)) {
597 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
602 /* we don't send a reply to a auth3 request, except by a
609 handle a bind request
611 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
613 uint32_t if_version, transfer_syntax_version;
614 const char *uuid, *transfer_syntax;
615 struct dcesrv_connection_context *context;
616 const struct dcesrv_interface *iface;
618 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
619 uuid = GUID_string(call, &call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid);
621 return NT_STATUS_NO_MEMORY;
624 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
625 transfer_syntax = GUID_string(call,
626 &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid);
627 if (!transfer_syntax ||
628 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
629 NDR_GUID_VERSION != transfer_syntax_version) {
630 /* we only do NDR encoded dcerpc */
631 return NT_STATUS_NO_MEMORY;
634 iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
636 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
637 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
640 /* add this context to the list of available context_ids */
641 context = talloc(call->conn, struct dcesrv_connection_context);
642 if (context == NULL) {
643 return NT_STATUS_NO_MEMORY;
645 context->conn = call->conn;
646 context->iface = iface;
647 context->context_id = context_id;
648 context->private = NULL;
649 context->handles = NULL;
650 DLIST_ADD(call->conn->contexts, context);
651 call->context = context;
658 handle a bind request
660 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
662 struct ncacn_packet pkt;
663 struct dcesrv_call_reply *rep;
665 uint32_t result=0, reason=0;
668 /* handle any authentication that is being requested */
669 if (!dcesrv_auth_alter(call)) {
670 /* TODO: work out the right reject code */
671 result = DCERPC_BIND_PROVIDER_REJECT;
672 reason = DCERPC_BIND_REASON_ASYNTAX;
675 context_id = call->pkt.u.alter.ctx_list[0].context_id;
677 /* see if they are asking for a new interface */
679 dcesrv_find_context(call->conn, context_id) == NULL) {
680 status = dcesrv_alter_new_context(call, context_id);
681 if (!NT_STATUS_IS_OK(status)) {
682 result = DCERPC_BIND_PROVIDER_REJECT;
683 reason = DCERPC_BIND_REASON_ASYNTAX;
687 /* setup a alter_resp */
688 dcesrv_init_hdr(&pkt);
690 pkt.call_id = call->pkt.call_id;
691 pkt.ptype = DCERPC_PKT_ALTER_RESP;
692 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
693 pkt.u.alter_resp.max_xmit_frag = 0x2000;
694 pkt.u.alter_resp.max_recv_frag = 0x2000;
695 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
696 pkt.u.alter_resp.num_results = 1;
697 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
698 if (!pkt.u.alter_resp.ctx_list) {
699 return NT_STATUS_NO_MEMORY;
701 pkt.u.alter_resp.ctx_list[0].result = result;
702 pkt.u.alter_resp.ctx_list[0].reason = reason;
703 GUID_from_string(NDR_GUID, &pkt.u.alter_resp.ctx_list[0].syntax.uuid);
704 pkt.u.alter_resp.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
705 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
706 pkt.u.alter_resp.secondary_address = "";
708 if (!dcesrv_auth_alter_ack(call, &pkt)) {
709 return dcesrv_bind_nak(call, 0);
712 rep = talloc(call, struct dcesrv_call_reply);
713 NT_STATUS_HAVE_NO_MEMORY(rep);
715 rep->data = talloc(call, DATA_BLOB);
716 NT_STATUS_HAVE_NO_MEMORY(rep->data);
718 status = ncacn_push_auth(rep->data, call, &pkt,
719 call->conn->auth_state.auth_info);
720 NT_STATUS_IS_OK_RETURN(status);
722 dcerpc_set_frag_length(rep->data, rep->data->length);
724 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
725 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
731 handle a dcerpc request packet
733 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
735 struct ndr_pull *pull;
737 struct dcesrv_connection_context *context;
739 call->fault_code = 0;
740 call->state_flags = call->conn->dce_ctx->state_flags;
741 call->time = timeval_current();
743 /* if authenticated, and the mech we use can't do async replies, don't use them... */
744 if (call->conn->auth_state.gensec_security &&
745 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
746 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
749 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
750 if (context == NULL) {
751 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
754 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
755 NT_STATUS_HAVE_NO_MEMORY(pull);
757 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
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 ncacn_packet pkt;
840 rep = talloc(call, struct dcesrv_call_reply);
841 NT_STATUS_HAVE_NO_MEMORY(rep);
843 rep->data = talloc(call, DATA_BLOB);
844 NT_STATUS_HAVE_NO_MEMORY(rep->data);
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 a pointer to a size_t variable that will be set to the
1103 number of bytes that are consumed from the output.
1105 from the current fragment
1107 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1109 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1112 struct dcesrv_call_state *call;
1113 struct dcesrv_call_reply *rep;
1116 call = dce_conn->call_list;
1117 if (!call || !call->replies) {
1118 if (dce_conn->pending_call_list) {
1119 /* TODO: we need to say act async here
1120 * as we know we have pending requests
1121 * which will be finished at a time
1123 return NT_STATUS_FOOBAR;
1125 return NT_STATUS_FOOBAR;
1127 rep = call->replies;
1129 status = write_fn(private_data, rep->data, &nwritten);
1130 NT_STATUS_IS_ERR_RETURN(status);
1132 rep->data->length -= nwritten;
1133 rep->data->data += nwritten;
1135 if (rep->data->length == 0) {
1136 /* we're done with this section of the call */
1137 DLIST_REMOVE(call->replies, rep);
1140 if (call->replies == NULL) {
1141 /* we're done with the whole call */
1142 DLIST_REMOVE(dce_conn->call_list, call);
1149 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
1152 struct dcesrv_context *dce_ctx;
1155 if (!endpoint_servers) {
1156 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1157 return NT_STATUS_INTERNAL_ERROR;
1160 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1161 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1162 dce_ctx->endpoint_list = NULL;
1163 dce_ctx->state_flags = state_flags;
1165 for (i=0;endpoint_servers[i];i++) {
1166 const struct dcesrv_endpoint_server *ep_server;
1168 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1170 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1171 return NT_STATUS_INTERNAL_ERROR;
1174 status = ep_server->init_server(dce_ctx, ep_server);
1175 if (!NT_STATUS_IS_OK(status)) {
1176 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1177 nt_errstr(status)));
1182 *_dce_ctx = dce_ctx;
1183 return NT_STATUS_OK;
1187 initialise the dcerpc server context for ncacn_np based services
1189 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1192 struct dcesrv_context *dce_ctx;
1194 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx);
1195 NT_STATUS_NOT_OK_RETURN(status);
1197 *_dce_ctx = dce_ctx;
1198 return NT_STATUS_OK;
1201 /* the list of currently registered DCERPC endpoint servers.
1203 static struct ep_server {
1204 struct dcesrv_endpoint_server *ep_server;
1205 } *ep_servers = NULL;
1206 static int num_ep_servers;
1209 register a DCERPC endpoint server.
1211 The 'name' can be later used by other backends to find the operations
1212 structure for this backend.
1214 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1216 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1218 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1220 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1221 /* its already registered! */
1222 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1224 return NT_STATUS_OBJECT_NAME_COLLISION;
1227 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1229 smb_panic("out of memory in dcerpc_register");
1232 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1233 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1237 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1240 return NT_STATUS_OK;
1244 return the operations structure for a named backend of the specified type
1246 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1250 for (i=0;i<num_ep_servers;i++) {
1251 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1252 return ep_servers[i].ep_server;
1260 return the DCERPC module version, and the size of some critical types
1261 This can be used by endpoint server modules to either detect compilation errors, or provide
1262 multiple implementations for different smbd compilation options in one module
1264 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1266 static const struct dcesrv_critical_sizes critical_sizes = {
1267 DCERPC_MODULE_VERSION,
1268 sizeof(struct dcesrv_context),
1269 sizeof(struct dcesrv_endpoint),
1270 sizeof(struct dcesrv_endpoint_server),
1271 sizeof(struct dcesrv_interface),
1272 sizeof(struct dcesrv_if_list),
1273 sizeof(struct dcesrv_connection),
1274 sizeof(struct dcesrv_call_state),
1275 sizeof(struct dcesrv_auth),
1276 sizeof(struct dcesrv_handle)
1279 return &critical_sizes;
1283 initialise the dcerpc server context for socket based services
1285 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1288 struct dcesrv_context *dce_ctx;
1290 status = dcesrv_init_context(event_context,
1291 lp_dcerpc_endpoint_servers(),
1292 DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
1294 NT_STATUS_NOT_OK_RETURN(status);
1296 return dcesrv_sock_init(dce_ctx, event_context, model_ops);
1300 NTSTATUS server_service_rpc_init(void)
1302 return register_server_service("rpc", dcesrv_init);