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_dcerpc.h"
26 #include "auth/auth.h"
27 #include "dlinklist.h"
28 #include "rpc_server/dcerpc_server.h"
29 #include "lib/events/events.h"
30 #include "smbd/service_stream.h"
31 #include "system/filesys.h"
32 #include "smb_build.h"
35 see if two endpoints match
37 static BOOL endpoints_match(const struct dcerpc_binding *ep1,
38 const struct dcerpc_binding *ep2)
40 if (ep1->transport != ep2->transport) {
44 if (!ep1->endpoint || !ep2->endpoint) {
45 return ep1->endpoint == ep2->endpoint;
48 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
55 find an endpoint in the dcesrv_context
57 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
58 const struct dcerpc_binding *ep_description)
60 struct dcesrv_endpoint *ep;
61 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
62 if (endpoints_match(ep->ep_description, ep_description)) {
70 find a registered context_id from a bind or alter_context
72 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
75 struct dcesrv_connection_context *c;
76 for (c=conn->contexts;c;c=c->next) {
77 if (c->context_id == context_id) return c;
83 see if a uuid and if_version match to an interface
85 static BOOL interface_match(const struct dcesrv_interface *if1,
86 const struct dcesrv_interface *if2)
88 return (if1->if_version == if2->if_version &&
89 GUID_equal(&if1->uuid, &if2->uuid));
93 find the interface operations on an endpoint
95 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
96 const struct dcesrv_interface *iface)
98 struct dcesrv_if_list *ifl;
99 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
100 if (interface_match(&(ifl->iface), iface)) {
101 return &(ifl->iface);
108 see if a uuid and if_version match to an interface
110 static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
111 const struct GUID *uuid, uint32_t if_version)
113 return (iface->if_version == if_version && GUID_equal(&iface->uuid, uuid));
117 find the interface operations on an endpoint by uuid
119 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
120 const struct GUID *uuid, uint32_t if_version)
122 struct dcesrv_if_list *ifl;
123 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
124 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
125 return &(ifl->iface);
132 find a call that is pending in our call list
134 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
136 struct dcesrv_call_state *c;
137 for (c=dce_conn->call_list;c;c=c->next) {
138 if (c->pkt.call_id == call_id) {
146 register an interface on an endpoint
148 NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
150 const struct dcesrv_interface *iface,
151 const struct security_descriptor *sd)
153 struct dcesrv_endpoint *ep;
154 struct dcesrv_if_list *ifl;
155 struct dcerpc_binding *binding;
159 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
161 if (NT_STATUS_IS_ERR(status)) {
162 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
166 /* check if this endpoint exists
168 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
169 ep = talloc(dce_ctx, struct dcesrv_endpoint);
171 return NT_STATUS_NO_MEMORY;
174 ep->ep_description = talloc_reference(ep, binding);
178 /* see if the interface is already registered on te endpoint */
179 if (find_interface(ep, iface)!=NULL) {
180 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
181 iface->name, ep_name));
182 return NT_STATUS_OBJECT_NAME_COLLISION;
185 /* talloc a new interface list element */
186 ifl = talloc(dce_ctx, struct dcesrv_if_list);
188 return NT_STATUS_NO_MEMORY;
191 /* copy the given interface struct to the one on the endpoints interface list */
192 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
194 /* if we have a security descriptor given,
195 * we should see if we can set it up on the endpoint
198 /* if there's currently no security descriptor given on the endpoint
201 if (ep->sd == NULL) {
202 ep->sd = security_descriptor_copy(dce_ctx, sd);
205 /* if now there's no security descriptor given on the endpoint
206 * something goes wrong, either we failed to copy the security descriptor
207 * or there was already one on the endpoint
209 if (ep->sd != NULL) {
210 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
211 " on endpoint '%s'\n",
212 iface->name, ep_name));
213 if (add_ep) free(ep);
215 return NT_STATUS_OBJECT_NAME_COLLISION;
219 /* finally add the interface on the endpoint */
220 DLIST_ADD(ep->interface_list, ifl);
222 /* if it's a new endpoint add it to the dcesrv_context */
224 DLIST_ADD(dce_ctx->endpoint_list, ep);
227 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
228 iface->name, ep_name));
233 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
234 DATA_BLOB *session_key)
236 if (p->auth_state.session_info->session_key.length) {
237 *session_key = p->auth_state.session_info->session_key;
240 return NT_STATUS_NO_USER_SESSION_KEY;
243 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
244 DATA_BLOB *session_key)
246 /* this took quite a few CPU cycles to find ... */
247 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
248 session_key->length = 16;
253 fetch the user session key - may be default (above) or the SMB session key
255 NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
256 DATA_BLOB *session_key)
258 return p->auth_state.session_key(p, session_key);
263 destroy a link to an endpoint
265 static int dcesrv_endpoint_destructor(void *ptr)
267 struct dcesrv_connection *p = ptr;
269 while (p->contexts) {
270 struct dcesrv_connection_context *c = p->contexts;
272 DLIST_REMOVE(p->contexts, c);
275 c->iface->unbind(c, c->iface);
284 connect to a dcerpc endpoint
286 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
288 const struct dcesrv_endpoint *ep,
289 struct stream_connection *srv_conn,
290 struct dcesrv_connection **_p)
292 struct dcesrv_connection *p;
294 p = talloc(mem_ctx, struct dcesrv_connection);
295 NT_STATUS_HAVE_NO_MEMORY(p);
297 p->dce_ctx = dce_ctx;
301 p->pending_call_list = NULL;
302 p->cli_max_recv_frag = 0;
303 p->partial_input = data_blob(NULL, 0);
304 p->auth_state.auth_info = NULL;
305 p->auth_state.gensec_security = NULL;
306 p->auth_state.session_info = NULL;
307 p->auth_state.session_key = dcesrv_generic_session_key;
308 p->srv_conn = srv_conn;
309 p->processing = False;
311 talloc_set_destructor(p, dcesrv_endpoint_destructor);
318 search and connect to a dcerpc endpoint
320 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
322 const struct dcerpc_binding *ep_description,
323 struct auth_session_info *session_info,
324 struct stream_connection *srv_conn,
325 struct dcesrv_connection **dce_conn_p)
328 const struct dcesrv_endpoint *ep;
330 /* make sure this endpoint exists */
331 ep = find_endpoint(dce_ctx, ep_description);
333 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
336 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, dce_conn_p);
337 if (!NT_STATUS_IS_OK(status)) {
341 (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
342 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
344 /* TODO: check security descriptor of the endpoint here
345 * if it's a smb named pipe
346 * if it's failed free dce_conn_p
353 static void dcesrv_init_hdr(struct ncacn_packet *pkt)
356 pkt->rpc_vers_minor = 0;
357 if (lp_rpc_big_endian()) {
360 pkt->drep[0] = DCERPC_DREP_LE;
368 return a dcerpc fault
370 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
372 struct ncacn_packet pkt;
373 struct data_blob_list_item *rep;
376 /* setup a bind_ack */
377 dcesrv_init_hdr(&pkt);
379 pkt.call_id = call->pkt.call_id;
380 pkt.ptype = DCERPC_PKT_FAULT;
381 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
382 pkt.u.fault.alloc_hint = 0;
383 pkt.u.fault.context_id = 0;
384 pkt.u.fault.cancel_count = 0;
385 pkt.u.fault.status = fault_code;
387 rep = talloc(call, struct data_blob_list_item);
389 return NT_STATUS_NO_MEMORY;
392 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
393 if (!NT_STATUS_IS_OK(status)) {
397 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
399 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
400 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
407 return a dcerpc bind_nak
409 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
411 struct ncacn_packet pkt;
412 struct data_blob_list_item *rep;
415 /* setup a bind_nak */
416 dcesrv_init_hdr(&pkt);
418 pkt.call_id = call->pkt.call_id;
419 pkt.ptype = DCERPC_PKT_BIND_NAK;
420 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
421 pkt.u.bind_nak.reject_reason = reason;
422 pkt.u.bind_nak.num_versions = 0;
424 rep = talloc(call, struct data_blob_list_item);
426 return NT_STATUS_NO_MEMORY;
429 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
430 if (!NT_STATUS_IS_OK(status)) {
434 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
436 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
437 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
444 handle a bind request
446 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
448 uint32_t if_version, transfer_syntax_version;
449 struct GUID uuid, *transfer_syntax_uuid;
450 struct ncacn_packet pkt;
451 struct data_blob_list_item *rep;
453 uint32_t result=0, reason=0;
455 const struct dcesrv_interface *iface;
457 if (call->pkt.u.bind.num_contexts < 1 ||
458 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
459 return dcesrv_bind_nak(call, 0);
462 context_id = call->pkt.u.bind.ctx_list[0].context_id;
464 /* you can't bind twice on one context */
465 if (dcesrv_find_context(call->conn, context_id) != NULL) {
466 return dcesrv_bind_nak(call, 0);
469 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
470 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
472 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
473 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
474 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
475 ndr_transfer_syntax.if_version != transfer_syntax_version) {
476 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
477 /* we only do NDR encoded dcerpc */
478 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
479 talloc_free(uuid_str);
480 return dcesrv_bind_nak(call, 0);
483 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
485 char *uuid_str = GUID_string(call, &uuid);
486 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
487 talloc_free(uuid_str);
489 /* we don't know about that interface */
490 result = DCERPC_BIND_PROVIDER_REJECT;
491 reason = DCERPC_BIND_REASON_ASYNTAX;
495 /* add this context to the list of available context_ids */
496 struct dcesrv_connection_context *context = talloc(call->conn,
497 struct dcesrv_connection_context);
498 if (context == NULL) {
499 return dcesrv_bind_nak(call, 0);
501 context->conn = call->conn;
502 context->iface = iface;
503 context->context_id = context_id;
504 context->private = NULL;
505 context->handles = NULL;
506 DLIST_ADD(call->conn->contexts, context);
507 call->context = context;
510 if (call->conn->cli_max_recv_frag == 0) {
511 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
514 /* handle any authentication that is being requested */
515 if (!dcesrv_auth_bind(call)) {
516 /* TODO: work out the right reject code */
517 return dcesrv_bind_nak(call, 0);
520 /* setup a bind_ack */
521 dcesrv_init_hdr(&pkt);
523 pkt.call_id = call->pkt.call_id;
524 pkt.ptype = DCERPC_PKT_BIND_ACK;
525 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
526 pkt.u.bind_ack.max_xmit_frag = 0x2000;
527 pkt.u.bind_ack.max_recv_frag = 0x2000;
528 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
530 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
531 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
533 pkt.u.bind_ack.secondary_address = "";
535 pkt.u.bind_ack.num_results = 1;
536 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
537 if (!pkt.u.bind_ack.ctx_list) {
538 return NT_STATUS_NO_MEMORY;
540 pkt.u.bind_ack.ctx_list[0].result = result;
541 pkt.u.bind_ack.ctx_list[0].reason = reason;
542 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
543 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
545 if (!dcesrv_auth_bind_ack(call, &pkt)) {
546 return dcesrv_bind_nak(call, 0);
550 status = iface->bind(call, iface);
551 if (!NT_STATUS_IS_OK(status)) {
552 char *uuid_str = GUID_string(call, &uuid);
553 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
554 uuid_str, if_version, nt_errstr(status)));
555 talloc_free(uuid_str);
556 return dcesrv_bind_nak(call, 0);
560 rep = talloc(call, struct data_blob_list_item);
562 return NT_STATUS_NO_MEMORY;
565 status = ncacn_push_auth(&rep->blob, call, &pkt,
566 call->conn->auth_state.auth_info);
567 if (!NT_STATUS_IS_OK(status)) {
571 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
573 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
574 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
581 handle a auth3 request
583 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
585 /* handle the auth3 in the auth code */
586 if (!dcesrv_auth_auth3(call)) {
587 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
592 /* we don't send a reply to a auth3 request, except by a
599 handle a bind request
601 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
603 uint32_t if_version, transfer_syntax_version;
604 struct dcesrv_connection_context *context;
605 const struct dcesrv_interface *iface;
606 struct GUID uuid, *transfer_syntax_uuid;
608 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
609 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
611 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
612 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
613 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
614 ndr_transfer_syntax.if_version != transfer_syntax_version) {
615 /* we only do NDR encoded dcerpc */
616 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
619 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
621 char *uuid_str = GUID_string(call, &uuid);
622 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
623 talloc_free(uuid_str);
624 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
627 /* add this context to the list of available context_ids */
628 context = talloc(call->conn, struct dcesrv_connection_context);
629 if (context == NULL) {
630 return NT_STATUS_NO_MEMORY;
632 context->conn = call->conn;
633 context->iface = iface;
634 context->context_id = context_id;
635 context->private = NULL;
636 context->handles = NULL;
637 DLIST_ADD(call->conn->contexts, context);
638 call->context = context;
645 handle a alter context request
647 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
649 struct ncacn_packet pkt;
650 struct data_blob_list_item *rep;
652 uint32_t result=0, reason=0;
655 /* handle any authentication that is being requested */
656 if (!dcesrv_auth_alter(call)) {
657 /* TODO: work out the right reject code */
658 result = DCERPC_BIND_PROVIDER_REJECT;
659 reason = DCERPC_BIND_REASON_ASYNTAX;
662 context_id = call->pkt.u.alter.ctx_list[0].context_id;
664 /* see if they are asking for a new interface */
666 dcesrv_find_context(call->conn, context_id) == NULL) {
667 status = dcesrv_alter_new_context(call, context_id);
668 if (!NT_STATUS_IS_OK(status)) {
669 result = DCERPC_BIND_PROVIDER_REJECT;
670 reason = DCERPC_BIND_REASON_ASYNTAX;
674 /* setup a alter_resp */
675 dcesrv_init_hdr(&pkt);
677 pkt.call_id = call->pkt.call_id;
678 pkt.ptype = DCERPC_PKT_ALTER_RESP;
679 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
680 pkt.u.alter_resp.max_xmit_frag = 0x2000;
681 pkt.u.alter_resp.max_recv_frag = 0x2000;
682 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
683 pkt.u.alter_resp.num_results = 1;
684 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
685 if (!pkt.u.alter_resp.ctx_list) {
686 return NT_STATUS_NO_MEMORY;
688 pkt.u.alter_resp.ctx_list[0].result = result;
689 pkt.u.alter_resp.ctx_list[0].reason = reason;
690 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
691 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
692 pkt.u.alter_resp.secondary_address = "";
694 if (!dcesrv_auth_alter_ack(call, &pkt)) {
695 return dcesrv_bind_nak(call, 0);
698 rep = talloc(call, struct data_blob_list_item);
700 return NT_STATUS_NO_MEMORY;
703 status = ncacn_push_auth(&rep->blob, call, &pkt,
704 call->conn->auth_state.auth_info);
705 if (!NT_STATUS_IS_OK(status)) {
709 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
711 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
712 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
718 handle a dcerpc request packet
720 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
722 struct ndr_pull *pull;
724 struct dcesrv_connection_context *context;
726 call->fault_code = 0;
727 call->state_flags = call->conn->dce_ctx->state_flags;
728 call->time = timeval_current();
730 /* if authenticated, and the mech we use can't do async replies, don't use them... */
731 if (call->conn->auth_state.gensec_security &&
732 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
733 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
736 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
737 if (context == NULL) {
738 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
741 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
742 NT_STATUS_HAVE_NO_MEMORY(pull);
744 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
746 call->context = context;
747 call->event_ctx = context->conn->srv_conn->event.ctx;
748 call->ndr_pull = pull;
750 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
751 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
754 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
755 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
758 /* unravel the NDR for the packet */
759 status = context->iface->ndr_pull(call, call, pull, &call->r);
760 if (!NT_STATUS_IS_OK(status)) {
761 return dcesrv_fault(call, call->fault_code);
764 if (pull->offset != pull->data_size) {
765 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
766 pull->data_size - pull->offset));
767 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
770 /* call the dispatch function */
771 status = context->iface->dispatch(call, call, call->r);
772 if (!NT_STATUS_IS_OK(status)) {
773 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
774 context->iface->name,
775 call->pkt.u.request.opnum,
776 dcerpc_errstr(pull, call->fault_code)));
777 return dcesrv_fault(call, call->fault_code);
780 /* add the call to the pending list */
781 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
783 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
787 return dcesrv_reply(call);
790 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
792 struct ndr_push *push;
795 uint32_t total_length;
796 struct dcesrv_connection_context *context = call->context;
798 /* call the reply function */
799 status = context->iface->reply(call, call, call->r);
800 if (!NT_STATUS_IS_OK(status)) {
801 return dcesrv_fault(call, call->fault_code);
804 /* form the reply NDR */
805 push = ndr_push_init_ctx(call);
806 NT_STATUS_HAVE_NO_MEMORY(push);
808 /* carry over the pointer count to the reply in case we are
809 using full pointer. See NDR specification for full
811 push->ptr_count = call->ndr_pull->ptr_count;
813 if (lp_rpc_big_endian()) {
814 push->flags |= LIBNDR_FLAG_BIGENDIAN;
817 status = context->iface->ndr_push(call, call, push, call->r);
818 if (!NT_STATUS_IS_OK(status)) {
819 return dcesrv_fault(call, call->fault_code);
822 stub = ndr_push_blob(push);
824 total_length = stub.length;
828 struct data_blob_list_item *rep;
829 struct ncacn_packet pkt;
831 rep = talloc(call, struct data_blob_list_item);
832 NT_STATUS_HAVE_NO_MEMORY(rep);
834 length = stub.length;
835 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
836 /* the 32 is to cope with signing data */
837 length = call->conn->cli_max_recv_frag -
838 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
841 /* form the dcerpc response packet */
842 dcesrv_init_hdr(&pkt);
844 pkt.call_id = call->pkt.call_id;
845 pkt.ptype = DCERPC_PKT_RESPONSE;
847 if (stub.length == total_length) {
848 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
850 if (length == stub.length) {
851 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
853 pkt.u.response.alloc_hint = stub.length;
854 pkt.u.response.context_id = call->pkt.u.request.context_id;
855 pkt.u.response.cancel_count = 0;
856 pkt.u.response.stub_and_verifier.data = stub.data;
857 pkt.u.response.stub_and_verifier.length = length;
859 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
860 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
863 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
865 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
868 stub.length -= length;
869 } while (stub.length != 0);
871 /* move the call from the pending to the finished calls list */
872 DLIST_REMOVE(call->conn->pending_call_list, call);
873 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
875 if (call->conn->call_list && call->conn->call_list->replies) {
876 if (call->conn->srv_conn &&
877 call->conn->srv_conn->event.fde) {
878 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
887 work out if we have a full packet yet
889 static BOOL dce_full_packet(const DATA_BLOB *data)
891 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
894 if (dcerpc_get_frag_length(data) > data->length) {
901 we might have consumed only part of our input - advance past that part
903 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
907 if (dce_conn->partial_input.length == offset) {
908 data_blob_free(&dce_conn->partial_input);
912 blob = dce_conn->partial_input;
913 dce_conn->partial_input = data_blob(blob.data + offset,
914 blob.length - offset);
915 data_blob_free(&blob);
919 process some input to a dcerpc endpoint server.
921 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
923 struct ndr_pull *ndr;
925 struct dcesrv_call_state *call;
928 call = talloc(dce_conn, struct dcesrv_call_state);
930 talloc_free(dce_conn->partial_input.data);
931 return NT_STATUS_NO_MEMORY;
933 call->conn = dce_conn;
934 call->replies = NULL;
935 call->context = NULL;
936 call->event_ctx = dce_conn->srv_conn->event.ctx;
938 blob = dce_conn->partial_input;
939 blob.length = dcerpc_get_frag_length(&blob);
941 ndr = ndr_pull_init_blob(&blob, call);
943 talloc_free(dce_conn->partial_input.data);
945 return NT_STATUS_NO_MEMORY;
948 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
949 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
952 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
953 if (!NT_STATUS_IS_OK(status)) {
954 talloc_free(dce_conn->partial_input.data);
959 /* we have to check the signing here, before combining the
961 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
962 !dcesrv_auth_request(call, &blob)) {
963 dce_partial_advance(dce_conn, blob.length);
964 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
967 dce_partial_advance(dce_conn, blob.length);
969 /* see if this is a continued packet */
970 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
971 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
972 struct dcesrv_call_state *call2 = call;
975 /* we only allow fragmented requests, no other packet types */
976 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
977 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
980 /* this is a continuation of an existing call - find the call then
981 tack it on the end */
982 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
984 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
987 if (call->pkt.ptype != call2->pkt.ptype) {
988 /* trying to play silly buggers are we? */
989 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
992 alloc_size = call->pkt.u.request.stub_and_verifier.length +
993 call2->pkt.u.request.stub_and_verifier.length;
994 if (call->pkt.u.request.alloc_hint > alloc_size) {
995 alloc_size = call->pkt.u.request.alloc_hint;
998 call->pkt.u.request.stub_and_verifier.data =
1000 call->pkt.u.request.stub_and_verifier.data,
1001 uint8_t, alloc_size);
1002 if (!call->pkt.u.request.stub_and_verifier.data) {
1003 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1005 memcpy(call->pkt.u.request.stub_and_verifier.data +
1006 call->pkt.u.request.stub_and_verifier.length,
1007 call2->pkt.u.request.stub_and_verifier.data,
1008 call2->pkt.u.request.stub_and_verifier.length);
1009 call->pkt.u.request.stub_and_verifier.length +=
1010 call2->pkt.u.request.stub_and_verifier.length;
1012 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1017 /* this may not be the last pdu in the chain - if its isn't then
1018 just put it on the call_list and wait for the rest */
1019 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1020 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1021 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1022 return NT_STATUS_OK;
1025 switch (call->pkt.ptype) {
1026 case DCERPC_PKT_BIND:
1027 status = dcesrv_bind(call);
1029 case DCERPC_PKT_AUTH3:
1030 status = dcesrv_auth3(call);
1032 case DCERPC_PKT_ALTER:
1033 status = dcesrv_alter(call);
1035 case DCERPC_PKT_REQUEST:
1036 status = dcesrv_request(call);
1039 status = NT_STATUS_INVALID_PARAMETER;
1043 /* if we are going to be sending a reply then add
1044 it to the list of pending calls. We add it to the end to keep the call
1045 list in the order we will answer */
1046 if (!NT_STATUS_IS_OK(status)) {
1055 provide some input to a dcerpc endpoint server. This passes data
1056 from a dcerpc client into the server
1058 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1062 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1063 dce_conn->partial_input.data,
1065 dce_conn->partial_input.length + data->length);
1066 if (!dce_conn->partial_input.data) {
1067 return NT_STATUS_NO_MEMORY;
1069 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1070 data->data, data->length);
1071 dce_conn->partial_input.length += data->length;
1073 while (dce_full_packet(&dce_conn->partial_input)) {
1074 status = dcesrv_input_process(dce_conn);
1075 if (!NT_STATUS_IS_OK(status)) {
1080 return NT_STATUS_OK;
1084 retrieve some output from a dcerpc server
1085 The caller supplies a function that will be called to do the
1088 The first argument to write_fn() will be 'private', the second will
1089 be a pointer to a buffer containing the data to be sent and the 3rd
1090 will be a pointer to a size_t variable that will be set to the
1091 number of bytes that are consumed from the output.
1093 from the current fragment
1095 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1097 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1100 struct dcesrv_call_state *call;
1101 struct data_blob_list_item *rep;
1104 call = dce_conn->call_list;
1105 if (!call || !call->replies) {
1106 if (dce_conn->pending_call_list) {
1107 /* TODO: we need to say act async here
1108 * as we know we have pending requests
1109 * which will be finished at a time
1111 return NT_STATUS_FOOBAR;
1113 return NT_STATUS_FOOBAR;
1115 rep = call->replies;
1117 status = write_fn(private_data, &rep->blob, &nwritten);
1118 NT_STATUS_IS_ERR_RETURN(status);
1120 rep->blob.length -= nwritten;
1121 rep->blob.data += nwritten;
1123 if (rep->blob.length == 0) {
1124 /* we're done with this section of the call */
1125 DLIST_REMOVE(call->replies, rep);
1128 if (call->replies == NULL) {
1129 /* we're done with the whole call */
1130 DLIST_REMOVE(dce_conn->call_list, call);
1137 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
1140 struct dcesrv_context *dce_ctx;
1143 if (!endpoint_servers) {
1144 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1145 return NT_STATUS_INTERNAL_ERROR;
1148 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1149 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1150 dce_ctx->endpoint_list = NULL;
1151 dce_ctx->state_flags = state_flags;
1153 for (i=0;endpoint_servers[i];i++) {
1154 const struct dcesrv_endpoint_server *ep_server;
1156 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1158 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1159 return NT_STATUS_INTERNAL_ERROR;
1162 status = ep_server->init_server(dce_ctx, ep_server);
1163 if (!NT_STATUS_IS_OK(status)) {
1164 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1165 nt_errstr(status)));
1170 *_dce_ctx = dce_ctx;
1171 return NT_STATUS_OK;
1174 /* the list of currently registered DCERPC endpoint servers.
1176 static struct ep_server {
1177 struct dcesrv_endpoint_server *ep_server;
1178 } *ep_servers = NULL;
1179 static int num_ep_servers;
1182 register a DCERPC endpoint server.
1184 The 'name' can be later used by other backends to find the operations
1185 structure for this backend.
1187 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1189 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1191 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1193 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1194 /* its already registered! */
1195 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1197 return NT_STATUS_OBJECT_NAME_COLLISION;
1200 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1202 smb_panic("out of memory in dcerpc_register");
1205 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1206 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1210 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1213 return NT_STATUS_OK;
1217 return the operations structure for a named backend of the specified type
1219 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1223 for (i=0;i<num_ep_servers;i++) {
1224 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1225 return ep_servers[i].ep_server;
1233 return the DCERPC module version, and the size of some critical types
1234 This can be used by endpoint server modules to either detect compilation errors, or provide
1235 multiple implementations for different smbd compilation options in one module
1237 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1239 static const struct dcesrv_critical_sizes critical_sizes = {
1240 DCERPC_MODULE_VERSION,
1241 sizeof(struct dcesrv_context),
1242 sizeof(struct dcesrv_endpoint),
1243 sizeof(struct dcesrv_endpoint_server),
1244 sizeof(struct dcesrv_interface),
1245 sizeof(struct dcesrv_if_list),
1246 sizeof(struct dcesrv_connection),
1247 sizeof(struct dcesrv_call_state),
1248 sizeof(struct dcesrv_auth),
1249 sizeof(struct dcesrv_handle)
1252 return &critical_sizes;
1256 initialise the dcerpc server context
1258 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1261 struct dcesrv_context *dce_ctx;
1262 struct dcesrv_endpoint *e;
1264 status = dcesrv_init_context(event_context,
1265 lp_dcerpc_endpoint_servers(),
1266 DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
1268 NT_STATUS_NOT_OK_RETURN(status);
1270 /* Make sure the directory for NCALRPC exists */
1271 if (!directory_exist(lp_ncalrpc_dir())) {
1272 mkdir(lp_ncalrpc_dir(), 0755);
1275 for (e=dce_ctx->endpoint_list;e;e=e->next) {
1276 switch (e->ep_description->transport) {
1277 case NCACN_UNIX_STREAM:
1278 status = dcesrv_add_ep_unix(dce_ctx, e, event_context, model_ops);
1279 NT_STATUS_NOT_OK_RETURN(status);
1283 status = dcesrv_add_ep_ncalrpc(dce_ctx, e, event_context, model_ops);
1284 NT_STATUS_NOT_OK_RETURN(status);
1288 status = dcesrv_add_ep_tcp(dce_ctx, e, event_context, model_ops);
1289 NT_STATUS_NOT_OK_RETURN(status);
1293 status = dcesrv_add_ep_np(dce_ctx, e, event_context, model_ops);
1294 NT_STATUS_NOT_OK_RETURN(status);
1298 return NT_STATUS_NOT_SUPPORTED;
1302 return NT_STATUS_OK;
1305 NTSTATUS server_service_rpc_init(void)
1307 init_module_fn static_init[] = STATIC_DCERPC_SERVER_MODULES;
1308 init_module_fn *shared_init = load_samba_modules(NULL, "rpc_server");
1310 run_init_functions(static_init);
1311 run_init_functions(shared_init);
1313 talloc_free(shared_init);
1315 return register_server_service("rpc", dcesrv_init);