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 "smbd/proto.h"
32 #include "system/filesys.h"
33 #include "libcli/security/proto.h"
37 see if two endpoints match
39 static BOOL endpoints_match(const struct dcerpc_binding *ep1,
40 const struct dcerpc_binding *ep2)
42 if (ep1->transport != ep2->transport) {
46 if (!ep1->endpoint || !ep2->endpoint) {
47 return ep1->endpoint == ep2->endpoint;
50 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
57 find an endpoint in the dcesrv_context
59 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
60 const struct dcerpc_binding *ep_description)
62 struct dcesrv_endpoint *ep;
63 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
64 if (endpoints_match(ep->ep_description, ep_description)) {
72 find a registered context_id from a bind or alter_context
74 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
77 struct dcesrv_connection_context *c;
78 for (c=conn->contexts;c;c=c->next) {
79 if (c->context_id == context_id) return c;
85 see if a uuid and if_version match to an interface
87 static BOOL interface_match(const struct dcesrv_interface *if1,
88 const struct dcesrv_interface *if2)
90 return (if1->if_version == if2->if_version &&
91 GUID_equal(&if1->uuid, &if2->uuid));
95 find the interface operations on an endpoint
97 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
98 const struct dcesrv_interface *iface)
100 struct dcesrv_if_list *ifl;
101 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
102 if (interface_match(&(ifl->iface), iface)) {
103 return &(ifl->iface);
110 see if a uuid and if_version match to an interface
112 static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
113 const struct GUID *uuid, uint32_t if_version)
115 return (iface->if_version == if_version && GUID_equal(&iface->uuid, uuid));
119 find the interface operations on an endpoint by uuid
121 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
122 const struct GUID *uuid, uint32_t if_version)
124 struct dcesrv_if_list *ifl;
125 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
126 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
127 return &(ifl->iface);
134 find a call that is pending in our call list
136 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
138 struct dcesrv_call_state *c;
139 for (c=dce_conn->call_list;c;c=c->next) {
140 if (c->pkt.call_id == call_id) {
148 register an interface on an endpoint
150 NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
152 const struct dcesrv_interface *iface,
153 const struct security_descriptor *sd)
155 struct dcesrv_endpoint *ep;
156 struct dcesrv_if_list *ifl;
157 struct dcerpc_binding *binding;
161 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
163 if (NT_STATUS_IS_ERR(status)) {
164 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
168 /* check if this endpoint exists
170 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
171 ep = talloc(dce_ctx, struct dcesrv_endpoint);
173 return NT_STATUS_NO_MEMORY;
176 ep->ep_description = talloc_reference(ep, binding);
180 /* see if the interface is already registered on te endpoint */
181 if (find_interface(ep, iface)!=NULL) {
182 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
183 iface->name, ep_name));
184 return NT_STATUS_OBJECT_NAME_COLLISION;
187 /* talloc a new interface list element */
188 ifl = talloc(dce_ctx, struct dcesrv_if_list);
190 return NT_STATUS_NO_MEMORY;
193 /* copy the given interface struct to the one on the endpoints interface list */
194 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
196 /* if we have a security descriptor given,
197 * we should see if we can set it up on the endpoint
200 /* if there's currently no security descriptor given on the endpoint
203 if (ep->sd == NULL) {
204 ep->sd = security_descriptor_copy(dce_ctx, sd);
207 /* if now there's no security descriptor given on the endpoint
208 * something goes wrong, either we failed to copy the security descriptor
209 * or there was already one on the endpoint
211 if (ep->sd != NULL) {
212 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
213 " on endpoint '%s'\n",
214 iface->name, ep_name));
215 if (add_ep) free(ep);
217 return NT_STATUS_OBJECT_NAME_COLLISION;
221 /* finally add the interface on the endpoint */
222 DLIST_ADD(ep->interface_list, ifl);
224 /* if it's a new endpoint add it to the dcesrv_context */
226 DLIST_ADD(dce_ctx->endpoint_list, ep);
229 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
230 iface->name, ep_name));
235 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
236 DATA_BLOB *session_key)
238 if (p->auth_state.session_info->session_key.length) {
239 *session_key = p->auth_state.session_info->session_key;
242 return NT_STATUS_NO_USER_SESSION_KEY;
245 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
246 DATA_BLOB *session_key)
248 /* this took quite a few CPU cycles to find ... */
249 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
250 session_key->length = 16;
255 fetch the user session key - may be default (above) or the SMB session key
257 NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
258 DATA_BLOB *session_key)
260 return p->auth_state.session_key(p, session_key);
265 destroy a link to an endpoint
267 static int dcesrv_endpoint_destructor(void *ptr)
269 struct dcesrv_connection *p = ptr;
271 while (p->contexts) {
272 struct dcesrv_connection_context *c = p->contexts;
274 DLIST_REMOVE(p->contexts, c);
277 c->iface->unbind(c, c->iface);
286 connect to a dcerpc endpoint
288 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
290 const struct dcesrv_endpoint *ep,
291 struct stream_connection *srv_conn,
292 uint32_t state_flags,
293 struct dcesrv_connection **_p)
295 struct dcesrv_connection *p;
297 p = talloc(mem_ctx, struct dcesrv_connection);
298 NT_STATUS_HAVE_NO_MEMORY(p);
300 p->dce_ctx = dce_ctx;
304 p->pending_call_list = NULL;
305 p->cli_max_recv_frag = 0;
306 p->partial_input = data_blob(NULL, 0);
307 p->auth_state.auth_info = NULL;
308 p->auth_state.gensec_security = NULL;
309 p->auth_state.session_info = NULL;
310 p->auth_state.session_key = dcesrv_generic_session_key;
311 p->srv_conn = srv_conn;
312 p->processing = False;
313 p->state_flags = state_flags;
315 talloc_set_destructor(p, dcesrv_endpoint_destructor);
322 search and connect to a dcerpc endpoint
324 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
326 const struct dcerpc_binding *ep_description,
327 struct auth_session_info *session_info,
328 struct stream_connection *srv_conn,
329 uint32_t state_flags,
330 struct dcesrv_connection **dce_conn_p)
333 const struct dcesrv_endpoint *ep;
335 /* make sure this endpoint exists */
336 ep = find_endpoint(dce_ctx, ep_description);
338 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
341 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, state_flags, dce_conn_p);
342 if (!NT_STATUS_IS_OK(status)) {
346 (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
347 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
349 /* TODO: check security descriptor of the endpoint here
350 * if it's a smb named pipe
351 * if it's failed free dce_conn_p
358 static void dcesrv_init_hdr(struct ncacn_packet *pkt)
361 pkt->rpc_vers_minor = 0;
362 if (lp_rpc_big_endian()) {
365 pkt->drep[0] = DCERPC_DREP_LE;
373 return a dcerpc fault
375 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
377 struct ncacn_packet pkt;
378 struct data_blob_list_item *rep;
381 /* setup a bind_ack */
382 dcesrv_init_hdr(&pkt);
384 pkt.call_id = call->pkt.call_id;
385 pkt.ptype = DCERPC_PKT_FAULT;
386 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
387 pkt.u.fault.alloc_hint = 0;
388 pkt.u.fault.context_id = 0;
389 pkt.u.fault.cancel_count = 0;
390 pkt.u.fault.status = fault_code;
392 rep = talloc(call, struct data_blob_list_item);
394 return NT_STATUS_NO_MEMORY;
397 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
398 if (!NT_STATUS_IS_OK(status)) {
402 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
404 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
405 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
412 return a dcerpc bind_nak
414 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
416 struct ncacn_packet pkt;
417 struct data_blob_list_item *rep;
420 /* setup a bind_nak */
421 dcesrv_init_hdr(&pkt);
423 pkt.call_id = call->pkt.call_id;
424 pkt.ptype = DCERPC_PKT_BIND_NAK;
425 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
426 pkt.u.bind_nak.reject_reason = reason;
427 pkt.u.bind_nak.num_versions = 0;
429 rep = talloc(call, struct data_blob_list_item);
431 return NT_STATUS_NO_MEMORY;
434 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
435 if (!NT_STATUS_IS_OK(status)) {
439 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
441 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
442 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
449 handle a bind request
451 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
453 uint32_t if_version, transfer_syntax_version;
454 struct GUID uuid, *transfer_syntax_uuid;
455 struct ncacn_packet pkt;
456 struct data_blob_list_item *rep;
458 uint32_t result=0, reason=0;
460 const struct dcesrv_interface *iface;
462 if (call->pkt.u.bind.num_contexts < 1 ||
463 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
464 return dcesrv_bind_nak(call, 0);
467 context_id = call->pkt.u.bind.ctx_list[0].context_id;
469 /* you can't bind twice on one context */
470 if (dcesrv_find_context(call->conn, context_id) != NULL) {
471 return dcesrv_bind_nak(call, 0);
474 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
475 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
477 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
478 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
479 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
480 ndr_transfer_syntax.if_version != transfer_syntax_version) {
481 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
482 /* we only do NDR encoded dcerpc */
483 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
484 talloc_free(uuid_str);
485 return dcesrv_bind_nak(call, 0);
488 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
490 char *uuid_str = GUID_string(call, &uuid);
491 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
492 talloc_free(uuid_str);
494 /* we don't know about that interface */
495 result = DCERPC_BIND_PROVIDER_REJECT;
496 reason = DCERPC_BIND_REASON_ASYNTAX;
500 /* add this context to the list of available context_ids */
501 struct dcesrv_connection_context *context = talloc(call->conn,
502 struct dcesrv_connection_context);
503 if (context == NULL) {
504 return dcesrv_bind_nak(call, 0);
506 context->conn = call->conn;
507 context->iface = iface;
508 context->context_id = context_id;
509 context->private = NULL;
510 context->handles = NULL;
511 DLIST_ADD(call->conn->contexts, context);
512 call->context = context;
515 if (call->conn->cli_max_recv_frag == 0) {
516 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
519 /* handle any authentication that is being requested */
520 if (!dcesrv_auth_bind(call)) {
521 /* TODO: work out the right reject code */
522 return dcesrv_bind_nak(call, 0);
525 /* setup a bind_ack */
526 dcesrv_init_hdr(&pkt);
528 pkt.call_id = call->pkt.call_id;
529 pkt.ptype = DCERPC_PKT_BIND_ACK;
530 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
531 pkt.u.bind_ack.max_xmit_frag = 0x2000;
532 pkt.u.bind_ack.max_recv_frag = 0x2000;
533 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
535 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
536 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
538 pkt.u.bind_ack.secondary_address = "";
540 pkt.u.bind_ack.num_results = 1;
541 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
542 if (!pkt.u.bind_ack.ctx_list) {
543 return NT_STATUS_NO_MEMORY;
545 pkt.u.bind_ack.ctx_list[0].result = result;
546 pkt.u.bind_ack.ctx_list[0].reason = reason;
547 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
548 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
550 if (!dcesrv_auth_bind_ack(call, &pkt)) {
551 return dcesrv_bind_nak(call, 0);
555 status = iface->bind(call, iface);
556 if (!NT_STATUS_IS_OK(status)) {
557 char *uuid_str = GUID_string(call, &uuid);
558 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
559 uuid_str, if_version, nt_errstr(status)));
560 talloc_free(uuid_str);
561 return dcesrv_bind_nak(call, 0);
565 rep = talloc(call, struct data_blob_list_item);
567 return NT_STATUS_NO_MEMORY;
570 status = ncacn_push_auth(&rep->blob, call, &pkt,
571 call->conn->auth_state.auth_info);
572 if (!NT_STATUS_IS_OK(status)) {
576 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
578 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
579 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
586 handle a auth3 request
588 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
590 /* handle the auth3 in the auth code */
591 if (!dcesrv_auth_auth3(call)) {
592 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
597 /* we don't send a reply to a auth3 request, except by a
604 handle a bind request
606 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
608 uint32_t if_version, transfer_syntax_version;
609 struct dcesrv_connection_context *context;
610 const struct dcesrv_interface *iface;
611 struct GUID uuid, *transfer_syntax_uuid;
613 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
614 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
616 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
617 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
618 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
619 ndr_transfer_syntax.if_version != transfer_syntax_version) {
620 /* we only do NDR encoded dcerpc */
621 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
624 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
626 char *uuid_str = GUID_string(call, &uuid);
627 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
628 talloc_free(uuid_str);
629 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
632 /* add this context to the list of available context_ids */
633 context = talloc(call->conn, struct dcesrv_connection_context);
634 if (context == NULL) {
635 return NT_STATUS_NO_MEMORY;
637 context->conn = call->conn;
638 context->iface = iface;
639 context->context_id = context_id;
640 context->private = NULL;
641 context->handles = NULL;
642 DLIST_ADD(call->conn->contexts, context);
643 call->context = context;
650 handle a alter context request
652 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
654 struct ncacn_packet pkt;
655 struct data_blob_list_item *rep;
657 uint32_t result=0, reason=0;
660 /* handle any authentication that is being requested */
661 if (!dcesrv_auth_alter(call)) {
662 /* TODO: work out the right reject code */
663 result = DCERPC_BIND_PROVIDER_REJECT;
664 reason = DCERPC_BIND_REASON_ASYNTAX;
667 context_id = call->pkt.u.alter.ctx_list[0].context_id;
669 /* see if they are asking for a new interface */
671 dcesrv_find_context(call->conn, context_id) == NULL) {
672 status = dcesrv_alter_new_context(call, context_id);
673 if (!NT_STATUS_IS_OK(status)) {
674 result = DCERPC_BIND_PROVIDER_REJECT;
675 reason = DCERPC_BIND_REASON_ASYNTAX;
679 /* setup a alter_resp */
680 dcesrv_init_hdr(&pkt);
682 pkt.call_id = call->pkt.call_id;
683 pkt.ptype = DCERPC_PKT_ALTER_RESP;
684 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
685 pkt.u.alter_resp.max_xmit_frag = 0x2000;
686 pkt.u.alter_resp.max_recv_frag = 0x2000;
687 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
688 pkt.u.alter_resp.num_results = 1;
689 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
690 if (!pkt.u.alter_resp.ctx_list) {
691 return NT_STATUS_NO_MEMORY;
693 pkt.u.alter_resp.ctx_list[0].result = result;
694 pkt.u.alter_resp.ctx_list[0].reason = reason;
695 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
696 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
697 pkt.u.alter_resp.secondary_address = "";
699 if (!dcesrv_auth_alter_ack(call, &pkt)) {
700 return dcesrv_bind_nak(call, 0);
703 rep = talloc(call, struct data_blob_list_item);
705 return NT_STATUS_NO_MEMORY;
708 status = ncacn_push_auth(&rep->blob, call, &pkt,
709 call->conn->auth_state.auth_info);
710 if (!NT_STATUS_IS_OK(status)) {
714 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
716 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
717 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
723 handle a dcerpc request packet
725 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
727 struct ndr_pull *pull;
729 struct dcesrv_connection_context *context;
731 call->fault_code = 0;
732 call->state_flags = call->conn->state_flags;
733 call->time = timeval_current();
735 /* if authenticated, and the mech we use can't do async replies, don't use them... */
736 if (call->conn->auth_state.gensec_security &&
737 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
738 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
741 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
742 if (context == NULL) {
743 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
746 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
747 NT_STATUS_HAVE_NO_MEMORY(pull);
749 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
751 call->context = context;
752 call->event_ctx = context->conn->srv_conn->event.ctx;
753 call->ndr_pull = pull;
755 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
756 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
759 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
760 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
763 /* unravel the NDR for the packet */
764 status = context->iface->ndr_pull(call, call, pull, &call->r);
765 if (!NT_STATUS_IS_OK(status)) {
766 return dcesrv_fault(call, call->fault_code);
769 if (pull->offset != pull->data_size) {
770 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
771 pull->data_size - pull->offset));
772 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
775 /* call the dispatch function */
776 status = context->iface->dispatch(call, call, call->r);
777 if (!NT_STATUS_IS_OK(status)) {
778 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
779 context->iface->name,
780 call->pkt.u.request.opnum,
781 dcerpc_errstr(pull, call->fault_code)));
782 return dcesrv_fault(call, call->fault_code);
785 /* add the call to the pending list */
786 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
788 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
792 return dcesrv_reply(call);
795 NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
797 struct ndr_push *push;
800 uint32_t total_length;
801 struct dcesrv_connection_context *context = call->context;
803 /* call the reply function */
804 status = context->iface->reply(call, call, call->r);
805 if (!NT_STATUS_IS_OK(status)) {
806 return dcesrv_fault(call, call->fault_code);
809 /* form the reply NDR */
810 push = ndr_push_init_ctx(call);
811 NT_STATUS_HAVE_NO_MEMORY(push);
813 /* carry over the pointer count to the reply in case we are
814 using full pointer. See NDR specification for full
816 push->ptr_count = call->ndr_pull->ptr_count;
818 if (lp_rpc_big_endian()) {
819 push->flags |= LIBNDR_FLAG_BIGENDIAN;
822 status = context->iface->ndr_push(call, call, push, call->r);
823 if (!NT_STATUS_IS_OK(status)) {
824 return dcesrv_fault(call, call->fault_code);
827 stub = ndr_push_blob(push);
829 total_length = stub.length;
833 struct data_blob_list_item *rep;
834 struct ncacn_packet pkt;
836 rep = talloc(call, struct data_blob_list_item);
837 NT_STATUS_HAVE_NO_MEMORY(rep);
839 length = stub.length;
840 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
841 /* the 32 is to cope with signing data */
842 length = call->conn->cli_max_recv_frag -
843 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
846 /* form the dcerpc response packet */
847 dcesrv_init_hdr(&pkt);
849 pkt.call_id = call->pkt.call_id;
850 pkt.ptype = DCERPC_PKT_RESPONSE;
852 if (stub.length == total_length) {
853 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
855 if (length == stub.length) {
856 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
858 pkt.u.response.alloc_hint = stub.length;
859 pkt.u.response.context_id = call->pkt.u.request.context_id;
860 pkt.u.response.cancel_count = 0;
861 pkt.u.response.stub_and_verifier.data = stub.data;
862 pkt.u.response.stub_and_verifier.length = length;
864 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
865 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
868 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
870 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
873 stub.length -= length;
874 } while (stub.length != 0);
876 /* move the call from the pending to the finished calls list */
877 DLIST_REMOVE(call->conn->pending_call_list, call);
878 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
880 if (call->conn->call_list && call->conn->call_list->replies) {
881 if (call->conn->srv_conn &&
882 call->conn->srv_conn->event.fde) {
883 EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
892 work out if we have a full packet yet
894 static BOOL dce_full_packet(const DATA_BLOB *data)
896 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
899 if (dcerpc_get_frag_length(data) > data->length) {
906 we might have consumed only part of our input - advance past that part
908 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
912 if (dce_conn->partial_input.length == offset) {
913 data_blob_free(&dce_conn->partial_input);
917 blob = dce_conn->partial_input;
918 dce_conn->partial_input = data_blob(blob.data + offset,
919 blob.length - offset);
920 data_blob_free(&blob);
924 process some input to a dcerpc endpoint server.
926 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
928 struct ndr_pull *ndr;
930 struct dcesrv_call_state *call;
933 call = talloc(dce_conn, struct dcesrv_call_state);
935 talloc_free(dce_conn->partial_input.data);
936 return NT_STATUS_NO_MEMORY;
938 call->conn = dce_conn;
939 call->replies = NULL;
940 call->context = NULL;
941 call->event_ctx = dce_conn->srv_conn->event.ctx;
943 blob = dce_conn->partial_input;
944 blob.length = dcerpc_get_frag_length(&blob);
946 ndr = ndr_pull_init_blob(&blob, call);
948 talloc_free(dce_conn->partial_input.data);
950 return NT_STATUS_NO_MEMORY;
953 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
954 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
957 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
958 if (!NT_STATUS_IS_OK(status)) {
959 talloc_free(dce_conn->partial_input.data);
964 /* we have to check the signing here, before combining the
966 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
967 !dcesrv_auth_request(call, &blob)) {
968 dce_partial_advance(dce_conn, blob.length);
969 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
972 dce_partial_advance(dce_conn, blob.length);
974 /* see if this is a continued packet */
975 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
976 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
977 struct dcesrv_call_state *call2 = call;
980 /* we only allow fragmented requests, no other packet types */
981 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
982 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
985 /* this is a continuation of an existing call - find the call then
986 tack it on the end */
987 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
989 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
992 if (call->pkt.ptype != call2->pkt.ptype) {
993 /* trying to play silly buggers are we? */
994 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
997 alloc_size = call->pkt.u.request.stub_and_verifier.length +
998 call2->pkt.u.request.stub_and_verifier.length;
999 if (call->pkt.u.request.alloc_hint > alloc_size) {
1000 alloc_size = call->pkt.u.request.alloc_hint;
1003 call->pkt.u.request.stub_and_verifier.data =
1004 talloc_realloc(call,
1005 call->pkt.u.request.stub_and_verifier.data,
1006 uint8_t, alloc_size);
1007 if (!call->pkt.u.request.stub_and_verifier.data) {
1008 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1010 memcpy(call->pkt.u.request.stub_and_verifier.data +
1011 call->pkt.u.request.stub_and_verifier.length,
1012 call2->pkt.u.request.stub_and_verifier.data,
1013 call2->pkt.u.request.stub_and_verifier.length);
1014 call->pkt.u.request.stub_and_verifier.length +=
1015 call2->pkt.u.request.stub_and_verifier.length;
1017 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1022 /* this may not be the last pdu in the chain - if its isn't then
1023 just put it on the call_list and wait for the rest */
1024 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1025 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1026 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
1027 return NT_STATUS_OK;
1030 switch (call->pkt.ptype) {
1031 case DCERPC_PKT_BIND:
1032 status = dcesrv_bind(call);
1034 case DCERPC_PKT_AUTH3:
1035 status = dcesrv_auth3(call);
1037 case DCERPC_PKT_ALTER:
1038 status = dcesrv_alter(call);
1040 case DCERPC_PKT_REQUEST:
1041 status = dcesrv_request(call);
1044 status = NT_STATUS_INVALID_PARAMETER;
1048 /* if we are going to be sending a reply then add
1049 it to the list of pending calls. We add it to the end to keep the call
1050 list in the order we will answer */
1051 if (!NT_STATUS_IS_OK(status)) {
1060 provide some input to a dcerpc endpoint server. This passes data
1061 from a dcerpc client into the server
1063 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1067 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1068 dce_conn->partial_input.data,
1070 dce_conn->partial_input.length + data->length);
1071 if (!dce_conn->partial_input.data) {
1072 return NT_STATUS_NO_MEMORY;
1074 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1075 data->data, data->length);
1076 dce_conn->partial_input.length += data->length;
1078 while (dce_full_packet(&dce_conn->partial_input)) {
1079 status = dcesrv_input_process(dce_conn);
1080 if (!NT_STATUS_IS_OK(status)) {
1085 return NT_STATUS_OK;
1089 retrieve some output from a dcerpc server
1090 The caller supplies a function that will be called to do the
1093 The first argument to write_fn() will be 'private', the second will
1094 be a pointer to a buffer containing the data to be sent and the 3rd
1095 will be a pointer to a size_t variable that will be set to the
1096 number of bytes that are consumed from the output.
1098 from the current fragment
1100 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1102 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1105 struct dcesrv_call_state *call;
1106 struct data_blob_list_item *rep;
1109 call = dce_conn->call_list;
1110 if (!call || !call->replies) {
1111 if (dce_conn->pending_call_list) {
1112 /* TODO: we need to say act async here
1113 * as we know we have pending requests
1114 * which will be finished at a time
1116 return NT_STATUS_FOOBAR;
1118 return NT_STATUS_FOOBAR;
1120 rep = call->replies;
1122 status = write_fn(private_data, &rep->blob, &nwritten);
1123 NT_STATUS_IS_ERR_RETURN(status);
1125 rep->blob.length -= nwritten;
1126 rep->blob.data += nwritten;
1128 if (rep->blob.length == 0) {
1129 /* we're done with this section of the call */
1130 DLIST_REMOVE(call->replies, rep);
1133 if (call->replies == NULL) {
1134 /* we're done with the whole call */
1135 DLIST_REMOVE(dce_conn->call_list, call);
1142 static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1145 struct dcesrv_context *dce_ctx;
1148 if (!endpoint_servers) {
1149 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1150 return NT_STATUS_INTERNAL_ERROR;
1153 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1154 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1155 dce_ctx->endpoint_list = NULL;
1157 for (i=0;endpoint_servers[i];i++) {
1158 const struct dcesrv_endpoint_server *ep_server;
1160 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1162 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1163 return NT_STATUS_INTERNAL_ERROR;
1166 status = ep_server->init_server(dce_ctx, ep_server);
1167 if (!NT_STATUS_IS_OK(status)) {
1168 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1169 nt_errstr(status)));
1174 *_dce_ctx = dce_ctx;
1175 return NT_STATUS_OK;
1179 initialise the dcerpc server context for ncacn_np based services
1181 NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1184 struct dcesrv_context *dce_ctx;
1186 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), &dce_ctx);
1187 NT_STATUS_NOT_OK_RETURN(status);
1189 *_dce_ctx = dce_ctx;
1190 return NT_STATUS_OK;
1193 /* the list of currently registered DCERPC endpoint servers.
1195 static struct ep_server {
1196 struct dcesrv_endpoint_server *ep_server;
1197 } *ep_servers = NULL;
1198 static int num_ep_servers;
1201 register a DCERPC endpoint server.
1203 The 'name' can be later used by other backends to find the operations
1204 structure for this backend.
1206 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1208 NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1210 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1212 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1213 /* its already registered! */
1214 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1216 return NT_STATUS_OBJECT_NAME_COLLISION;
1219 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1221 smb_panic("out of memory in dcerpc_register");
1224 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1225 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1229 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1232 return NT_STATUS_OK;
1236 return the operations structure for a named backend of the specified type
1238 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1242 for (i=0;i<num_ep_servers;i++) {
1243 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1244 return ep_servers[i].ep_server;
1252 return the DCERPC module version, and the size of some critical types
1253 This can be used by endpoint server modules to either detect compilation errors, or provide
1254 multiple implementations for different smbd compilation options in one module
1256 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1258 static const struct dcesrv_critical_sizes critical_sizes = {
1259 DCERPC_MODULE_VERSION,
1260 sizeof(struct dcesrv_context),
1261 sizeof(struct dcesrv_endpoint),
1262 sizeof(struct dcesrv_endpoint_server),
1263 sizeof(struct dcesrv_interface),
1264 sizeof(struct dcesrv_if_list),
1265 sizeof(struct dcesrv_connection),
1266 sizeof(struct dcesrv_call_state),
1267 sizeof(struct dcesrv_auth),
1268 sizeof(struct dcesrv_handle)
1271 return &critical_sizes;
1275 initialise the dcerpc server context
1277 static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
1280 struct dcesrv_context *dce_ctx;
1281 struct dcesrv_endpoint *e;
1283 status = dcesrv_init_context(event_context,
1284 lp_dcerpc_endpoint_servers(),
1286 NT_STATUS_NOT_OK_RETURN(status);
1288 /* Make sure the directory for NCALRPC exists */
1289 if (!directory_exist(lp_ncalrpc_dir())) {
1290 mkdir(lp_ncalrpc_dir(), 0755);
1293 for (e=dce_ctx->endpoint_list;e;e=e->next) {
1294 switch (e->ep_description->transport) {
1295 case NCACN_UNIX_STREAM:
1296 status = dcesrv_add_ep_unix(dce_ctx, e, event_context, model_ops);
1297 NT_STATUS_NOT_OK_RETURN(status);
1301 status = dcesrv_add_ep_ncalrpc(dce_ctx, e, event_context, model_ops);
1302 NT_STATUS_NOT_OK_RETURN(status);
1306 status = dcesrv_add_ep_tcp(dce_ctx, e, event_context, model_ops);
1307 NT_STATUS_NOT_OK_RETURN(status);
1311 /* FIXME: status = dcesrv_add_ep_np(dce_ctx, e, event_context, model_ops);
1312 NT_STATUS_NOT_OK_RETURN(status); */
1316 return NT_STATUS_NOT_SUPPORTED;
1320 return NT_STATUS_OK;
1323 NTSTATUS server_service_rpc_init(void)
1325 init_module_fn static_init[] = STATIC_DCERPC_SERVER_MODULES;
1326 init_module_fn *shared_init = load_samba_modules(NULL, "rpc_server");
1328 run_init_functions(static_init);
1329 run_init_functions(shared_init);
1331 talloc_free(shared_init);
1333 return register_server_service("rpc", dcesrv_init);