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 3 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, see <http://www.gnu.org/licenses/>.
24 #include "librpc/gen_ndr/ndr_dcerpc.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "lib/util/dlinklist.h"
28 #include "rpc_server/dcerpc_server.h"
29 #include "lib/events/events.h"
30 #include "smbd/service_task.h"
31 #include "smbd/service_stream.h"
32 #include "smbd/service.h"
33 #include "system/filesys.h"
34 #include "libcli/security/security.h"
36 #include "param/param.h"
38 extern const struct dcesrv_interface dcesrv_mgmt_interface;
41 see if two endpoints match
43 static bool endpoints_match(const struct dcerpc_binding *ep1,
44 const struct dcerpc_binding *ep2)
46 if (ep1->transport != ep2->transport) {
50 if (!ep1->endpoint || !ep2->endpoint) {
51 return ep1->endpoint == ep2->endpoint;
54 if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
61 find an endpoint in the dcesrv_context
63 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
64 const struct dcerpc_binding *ep_description)
66 struct dcesrv_endpoint *ep;
67 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
68 if (endpoints_match(ep->ep_description, ep_description)) {
76 find a registered context_id from a bind or alter_context
78 static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
81 struct dcesrv_connection_context *c;
82 for (c=conn->contexts;c;c=c->next) {
83 if (c->context_id == context_id) return c;
89 see if a uuid and if_version match to an interface
91 static BOOL interface_match(const struct dcesrv_interface *if1,
92 const struct dcesrv_interface *if2)
94 return (if1->syntax_id.if_version == if2->syntax_id.if_version &&
95 GUID_equal(&if1->syntax_id.uuid, &if2->syntax_id.uuid));
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 struct GUID *uuid, uint32_t if_version)
119 return (iface->syntax_id.if_version == if_version &&
120 GUID_equal(&iface->syntax_id.uuid, uuid));
124 find the interface operations on an endpoint by uuid
126 static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
127 const struct GUID *uuid, uint32_t if_version)
129 struct dcesrv_if_list *ifl;
130 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
131 if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
132 return &(ifl->iface);
139 find the earlier parts of a fragmented call awaiting reassembily
141 static struct dcesrv_call_state *dcesrv_find_fragmented_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
143 struct dcesrv_call_state *c;
144 for (c=dce_conn->incoming_fragmented_call_list;c;c=c->next) {
145 if (c->pkt.call_id == call_id) {
153 register an interface on an endpoint
155 _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
157 const struct dcesrv_interface *iface,
158 const struct security_descriptor *sd)
160 struct dcesrv_endpoint *ep;
161 struct dcesrv_if_list *ifl;
162 struct dcerpc_binding *binding;
166 status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
168 if (NT_STATUS_IS_ERR(status)) {
169 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
173 /* check if this endpoint exists
175 if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
176 ep = talloc(dce_ctx, struct dcesrv_endpoint);
178 return NT_STATUS_NO_MEMORY;
181 ep->ep_description = talloc_reference(ep, binding);
184 /* add mgmt interface */
185 ifl = talloc(dce_ctx, struct dcesrv_if_list);
187 return NT_STATUS_NO_MEMORY;
190 memcpy(&(ifl->iface), &dcesrv_mgmt_interface,
191 sizeof(struct dcesrv_interface));
193 DLIST_ADD(ep->interface_list, ifl);
196 /* see if the interface is already registered on te endpoint */
197 if (find_interface(ep, iface)!=NULL) {
198 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
199 iface->name, ep_name));
200 return NT_STATUS_OBJECT_NAME_COLLISION;
203 /* talloc a new interface list element */
204 ifl = talloc(dce_ctx, struct dcesrv_if_list);
206 return NT_STATUS_NO_MEMORY;
209 /* copy the given interface struct to the one on the endpoints interface list */
210 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
212 /* if we have a security descriptor given,
213 * we should see if we can set it up on the endpoint
216 /* if there's currently no security descriptor given on the endpoint
219 if (ep->sd == NULL) {
220 ep->sd = security_descriptor_copy(dce_ctx, sd);
223 /* if now there's no security descriptor given on the endpoint
224 * something goes wrong, either we failed to copy the security descriptor
225 * or there was already one on the endpoint
227 if (ep->sd != NULL) {
228 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
229 " on endpoint '%s'\n",
230 iface->name, ep_name));
231 if (add_ep) free(ep);
233 return NT_STATUS_OBJECT_NAME_COLLISION;
237 /* finally add the interface on the endpoint */
238 DLIST_ADD(ep->interface_list, ifl);
240 /* if it's a new endpoint add it to the dcesrv_context */
242 DLIST_ADD(dce_ctx->endpoint_list, ep);
245 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
246 iface->name, ep_name));
251 static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
252 DATA_BLOB *session_key)
254 if (p->auth_state.session_info->session_key.length) {
255 *session_key = p->auth_state.session_info->session_key;
258 return NT_STATUS_NO_USER_SESSION_KEY;
261 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
262 DATA_BLOB *session_key)
264 /* this took quite a few CPU cycles to find ... */
265 session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
266 session_key->length = 16;
271 fetch the user session key - may be default (above) or the SMB session key
273 _PUBLIC_ NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
274 DATA_BLOB *session_key)
276 return p->auth_state.session_key(p, session_key);
281 destroy a link to an endpoint
283 static int dcesrv_endpoint_destructor(struct dcesrv_connection *p)
285 while (p->contexts) {
286 struct dcesrv_connection_context *c = p->contexts;
288 DLIST_REMOVE(p->contexts, c);
291 c->iface->unbind(c, c->iface);
300 connect to a dcerpc endpoint
302 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
304 const struct dcesrv_endpoint *ep,
305 struct auth_session_info *session_info,
306 struct event_context *event_ctx,
307 struct messaging_context *msg_ctx,
308 struct server_id server_id,
309 uint32_t state_flags,
310 struct dcesrv_connection **_p)
312 struct dcesrv_connection *p;
315 return NT_STATUS_ACCESS_DENIED;
318 p = talloc(mem_ctx, struct dcesrv_connection);
319 NT_STATUS_HAVE_NO_MEMORY(p);
321 if (!talloc_reference(p, session_info)) {
323 return NT_STATUS_NO_MEMORY;
326 p->dce_ctx = dce_ctx;
330 p->incoming_fragmented_call_list = NULL;
331 p->pending_call_list = NULL;
332 p->cli_max_recv_frag = 0;
333 p->partial_input = data_blob(NULL, 0);
334 p->auth_state.auth_info = NULL;
335 p->auth_state.gensec_security = NULL;
336 p->auth_state.session_info = session_info;
337 p->auth_state.session_key = dcesrv_generic_session_key;
338 p->event_ctx = event_ctx;
339 p->msg_ctx = msg_ctx;
340 p->server_id = server_id;
341 p->processing = False;
342 p->state_flags = state_flags;
343 ZERO_STRUCT(p->transport);
345 talloc_set_destructor(p, dcesrv_endpoint_destructor);
352 search and connect to a dcerpc endpoint
354 _PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
356 const struct dcerpc_binding *ep_description,
357 struct auth_session_info *session_info,
358 struct event_context *event_ctx,
359 struct messaging_context *msg_ctx,
360 struct server_id server_id,
361 uint32_t state_flags,
362 struct dcesrv_connection **dce_conn_p)
365 const struct dcesrv_endpoint *ep;
367 /* make sure this endpoint exists */
368 ep = find_endpoint(dce_ctx, ep_description);
370 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
373 status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
374 event_ctx, msg_ctx, server_id,
375 state_flags, dce_conn_p);
376 NT_STATUS_NOT_OK_RETURN(status);
378 (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
380 /* TODO: check security descriptor of the endpoint here
381 * if it's a smb named pipe
382 * if it's failed free dce_conn_p
389 static void dcesrv_init_hdr(struct ncacn_packet *pkt)
392 pkt->rpc_vers_minor = 0;
393 if (lp_rpc_big_endian(global_loadparm)) {
396 pkt->drep[0] = DCERPC_DREP_LE;
404 move a call from an existing linked list to the specified list. This
405 prevents bugs where we forget to remove the call from a previous
408 static void dcesrv_call_set_list(struct dcesrv_call_state *call,
409 enum dcesrv_call_list list)
411 switch (call->list) {
412 case DCESRV_LIST_NONE:
414 case DCESRV_LIST_CALL_LIST:
415 DLIST_REMOVE(call->conn->call_list, call);
417 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
418 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
420 case DCESRV_LIST_PENDING_CALL_LIST:
421 DLIST_REMOVE(call->conn->pending_call_list, call);
426 case DCESRV_LIST_NONE:
428 case DCESRV_LIST_CALL_LIST:
429 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
431 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
432 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
434 case DCESRV_LIST_PENDING_CALL_LIST:
435 DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
441 return a dcerpc fault
443 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
445 struct ncacn_packet pkt;
446 struct data_blob_list_item *rep;
449 /* setup a bind_ack */
450 dcesrv_init_hdr(&pkt);
452 pkt.call_id = call->pkt.call_id;
453 pkt.ptype = DCERPC_PKT_FAULT;
454 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
455 pkt.u.fault.alloc_hint = 0;
456 pkt.u.fault.context_id = 0;
457 pkt.u.fault.cancel_count = 0;
458 pkt.u.fault.status = fault_code;
460 rep = talloc(call, struct data_blob_list_item);
462 return NT_STATUS_NO_MEMORY;
465 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
466 if (!NT_STATUS_IS_OK(status)) {
470 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
472 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
473 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
480 return a dcerpc bind_nak
482 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
484 struct ncacn_packet pkt;
485 struct data_blob_list_item *rep;
488 /* setup a bind_nak */
489 dcesrv_init_hdr(&pkt);
491 pkt.call_id = call->pkt.call_id;
492 pkt.ptype = DCERPC_PKT_BIND_NAK;
493 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
494 pkt.u.bind_nak.reject_reason = reason;
495 if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
496 pkt.u.bind_nak.versions.v.num_versions = 0;
499 rep = talloc(call, struct data_blob_list_item);
501 return NT_STATUS_NO_MEMORY;
504 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
505 if (!NT_STATUS_IS_OK(status)) {
509 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
511 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
512 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
519 handle a bind request
521 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
523 uint32_t if_version, transfer_syntax_version;
524 struct GUID uuid, *transfer_syntax_uuid;
525 struct ncacn_packet pkt;
526 struct data_blob_list_item *rep;
528 uint32_t result=0, reason=0;
530 const struct dcesrv_interface *iface;
532 if (call->pkt.u.bind.assoc_group_id != 0) {
533 return dcesrv_bind_nak(call, 0);
536 if (call->pkt.u.bind.num_contexts < 1 ||
537 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
538 return dcesrv_bind_nak(call, 0);
541 context_id = call->pkt.u.bind.ctx_list[0].context_id;
543 /* you can't bind twice on one context */
544 if (dcesrv_find_context(call->conn, context_id) != NULL) {
545 return dcesrv_bind_nak(call, 0);
548 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
549 uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
551 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
552 transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
553 if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
554 ndr_transfer_syntax.if_version != transfer_syntax_version) {
555 char *uuid_str = GUID_string(call, transfer_syntax_uuid);
556 /* we only do NDR encoded dcerpc */
557 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
558 talloc_free(uuid_str);
559 return dcesrv_bind_nak(call, 0);
562 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
564 char *uuid_str = GUID_string(call, &uuid);
565 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
566 talloc_free(uuid_str);
568 /* we don't know about that interface */
569 result = DCERPC_BIND_PROVIDER_REJECT;
570 reason = DCERPC_BIND_REASON_ASYNTAX;
574 /* add this context to the list of available context_ids */
575 struct dcesrv_connection_context *context = talloc(call->conn,
576 struct dcesrv_connection_context);
577 if (context == NULL) {
578 return dcesrv_bind_nak(call, 0);
580 context->conn = call->conn;
581 context->iface = iface;
582 context->context_id = context_id;
583 context->private = NULL;
584 context->handles = NULL;
585 DLIST_ADD(call->conn->contexts, context);
586 call->context = context;
589 if (call->conn->cli_max_recv_frag == 0) {
590 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
593 /* handle any authentication that is being requested */
594 if (!dcesrv_auth_bind(call)) {
595 return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
598 /* setup a bind_ack */
599 dcesrv_init_hdr(&pkt);
601 pkt.call_id = call->pkt.call_id;
602 pkt.ptype = DCERPC_PKT_BIND_ACK;
603 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
604 pkt.u.bind_ack.max_xmit_frag = 0x2000;
605 pkt.u.bind_ack.max_recv_frag = 0x2000;
606 /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
607 pkt.u.bind_ack.assoc_group_id = 0x12345678;
609 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
610 pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
612 pkt.u.bind_ack.secondary_address = "";
614 pkt.u.bind_ack.num_results = 1;
615 pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
616 if (!pkt.u.bind_ack.ctx_list) {
617 return NT_STATUS_NO_MEMORY;
619 pkt.u.bind_ack.ctx_list[0].result = result;
620 pkt.u.bind_ack.ctx_list[0].reason = reason;
621 pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
622 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
624 status = dcesrv_auth_bind_ack(call, &pkt);
625 if (!NT_STATUS_IS_OK(status)) {
626 return dcesrv_bind_nak(call, 0);
630 status = iface->bind(call, iface);
631 if (!NT_STATUS_IS_OK(status)) {
632 char *uuid_str = GUID_string(call, &uuid);
633 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
634 uuid_str, if_version, nt_errstr(status)));
635 talloc_free(uuid_str);
636 return dcesrv_bind_nak(call, 0);
640 rep = talloc(call, struct data_blob_list_item);
642 return NT_STATUS_NO_MEMORY;
645 status = ncacn_push_auth(&rep->blob, call, &pkt,
646 call->conn->auth_state.auth_info);
647 if (!NT_STATUS_IS_OK(status)) {
651 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
653 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
654 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
661 handle a auth3 request
663 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
665 /* handle the auth3 in the auth code */
666 if (!dcesrv_auth_auth3(call)) {
667 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
672 /* we don't send a reply to a auth3 request, except by a
679 handle a bind request
681 static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
683 uint32_t if_version, transfer_syntax_version;
684 struct dcesrv_connection_context *context;
685 const struct dcesrv_interface *iface;
686 struct GUID uuid, *transfer_syntax_uuid;
688 if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
689 uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
691 transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
692 transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
693 if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
694 ndr_transfer_syntax.if_version != transfer_syntax_version) {
695 /* we only do NDR encoded dcerpc */
696 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
699 iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
701 char *uuid_str = GUID_string(call, &uuid);
702 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
703 talloc_free(uuid_str);
704 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
707 /* add this context to the list of available context_ids */
708 context = talloc(call->conn, struct dcesrv_connection_context);
709 if (context == NULL) {
710 return NT_STATUS_NO_MEMORY;
712 context->conn = call->conn;
713 context->iface = iface;
714 context->context_id = context_id;
715 context->private = NULL;
716 context->handles = NULL;
717 DLIST_ADD(call->conn->contexts, context);
718 call->context = context;
725 handle a alter context request
727 static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
729 struct ncacn_packet pkt;
730 struct data_blob_list_item *rep;
732 uint32_t result=0, reason=0;
735 /* handle any authentication that is being requested */
736 if (!dcesrv_auth_alter(call)) {
737 /* TODO: work out the right reject code */
738 result = DCERPC_BIND_PROVIDER_REJECT;
739 reason = DCERPC_BIND_REASON_ASYNTAX;
742 context_id = call->pkt.u.alter.ctx_list[0].context_id;
744 /* see if they are asking for a new interface */
746 dcesrv_find_context(call->conn, context_id) == NULL) {
747 status = dcesrv_alter_new_context(call, context_id);
748 if (!NT_STATUS_IS_OK(status)) {
749 result = DCERPC_BIND_PROVIDER_REJECT;
750 reason = DCERPC_BIND_REASON_ASYNTAX;
754 /* setup a alter_resp */
755 dcesrv_init_hdr(&pkt);
757 pkt.call_id = call->pkt.call_id;
758 pkt.ptype = DCERPC_PKT_ALTER_RESP;
759 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
760 pkt.u.alter_resp.max_xmit_frag = 0x2000;
761 pkt.u.alter_resp.max_recv_frag = 0x2000;
762 pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
763 pkt.u.alter_resp.num_results = 1;
764 pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
765 if (!pkt.u.alter_resp.ctx_list) {
766 return NT_STATUS_NO_MEMORY;
768 pkt.u.alter_resp.ctx_list[0].result = result;
769 pkt.u.alter_resp.ctx_list[0].reason = reason;
770 pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
771 pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
772 pkt.u.alter_resp.secondary_address = "";
774 status = dcesrv_auth_alter_ack(call, &pkt);
775 if (!NT_STATUS_IS_OK(status)) {
776 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
777 || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
778 || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
779 || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
780 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
782 return dcesrv_fault(call, 0);
785 rep = talloc(call, struct data_blob_list_item);
787 return NT_STATUS_NO_MEMORY;
790 status = ncacn_push_auth(&rep->blob, call, &pkt,
791 call->conn->auth_state.auth_info);
792 if (!NT_STATUS_IS_OK(status)) {
796 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
798 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
799 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
805 handle a dcerpc request packet
807 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
809 struct ndr_pull *pull;
811 struct dcesrv_connection_context *context;
813 /* if authenticated, and the mech we use can't do async replies, don't use them... */
814 if (call->conn->auth_state.gensec_security &&
815 !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
816 call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
819 context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
820 if (context == NULL) {
821 return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
824 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
825 NT_STATUS_HAVE_NO_MEMORY(pull);
827 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
829 call->context = context;
830 call->ndr_pull = pull;
832 if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
833 pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
836 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
837 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
840 /* unravel the NDR for the packet */
841 status = context->iface->ndr_pull(call, call, pull, &call->r);
842 if (!NT_STATUS_IS_OK(status)) {
843 return dcesrv_fault(call, call->fault_code);
846 if (pull->offset != pull->data_size) {
847 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
848 pull->data_size - pull->offset));
849 dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
852 /* call the dispatch function */
853 status = context->iface->dispatch(call, call, call->r);
854 if (!NT_STATUS_IS_OK(status)) {
855 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
856 context->iface->name,
857 call->pkt.u.request.opnum,
858 dcerpc_errstr(pull, call->fault_code)));
859 return dcesrv_fault(call, call->fault_code);
862 /* add the call to the pending list */
863 dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
865 if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
869 return dcesrv_reply(call);
872 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
874 struct ndr_push *push;
877 uint32_t total_length;
878 struct dcesrv_connection_context *context = call->context;
880 /* call the reply function */
881 status = context->iface->reply(call, call, call->r);
882 if (!NT_STATUS_IS_OK(status)) {
883 return dcesrv_fault(call, call->fault_code);
886 /* form the reply NDR */
887 push = ndr_push_init_ctx(call);
888 NT_STATUS_HAVE_NO_MEMORY(push);
890 /* carry over the pointer count to the reply in case we are
891 using full pointer. See NDR specification for full
893 push->ptr_count = call->ndr_pull->ptr_count;
895 if (lp_rpc_big_endian(global_loadparm)) {
896 push->flags |= LIBNDR_FLAG_BIGENDIAN;
899 status = context->iface->ndr_push(call, call, push, call->r);
900 if (!NT_STATUS_IS_OK(status)) {
901 return dcesrv_fault(call, call->fault_code);
904 stub = ndr_push_blob(push);
906 total_length = stub.length;
910 struct data_blob_list_item *rep;
911 struct ncacn_packet pkt;
913 rep = talloc(call, struct data_blob_list_item);
914 NT_STATUS_HAVE_NO_MEMORY(rep);
916 length = stub.length;
917 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
918 /* the 32 is to cope with signing data */
919 length = call->conn->cli_max_recv_frag -
920 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
923 /* form the dcerpc response packet */
924 dcesrv_init_hdr(&pkt);
926 pkt.call_id = call->pkt.call_id;
927 pkt.ptype = DCERPC_PKT_RESPONSE;
929 if (stub.length == total_length) {
930 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
932 if (length == stub.length) {
933 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
935 pkt.u.response.alloc_hint = stub.length;
936 pkt.u.response.context_id = call->pkt.u.request.context_id;
937 pkt.u.response.cancel_count = 0;
938 pkt.u.response.stub_and_verifier.data = stub.data;
939 pkt.u.response.stub_and_verifier.length = length;
941 if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
942 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
945 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
947 DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
950 stub.length -= length;
951 } while (stub.length != 0);
953 /* move the call from the pending to the finished calls list */
954 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
956 if (call->conn->call_list && call->conn->call_list->replies) {
957 if (call->conn->transport.report_output_data) {
958 call->conn->transport.report_output_data(call->conn);
965 _PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
967 if (!conn->transport.get_my_addr) {
971 return conn->transport.get_my_addr(conn, mem_ctx);
974 _PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
976 if (!conn->transport.get_peer_addr) {
980 return conn->transport.get_peer_addr(conn, mem_ctx);
984 work out if we have a full packet yet
986 static BOOL dce_full_packet(const DATA_BLOB *data)
988 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
991 if (dcerpc_get_frag_length(data) > data->length) {
998 we might have consumed only part of our input - advance past that part
1000 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
1004 if (dce_conn->partial_input.length == offset) {
1005 data_blob_free(&dce_conn->partial_input);
1009 blob = dce_conn->partial_input;
1010 dce_conn->partial_input = data_blob(blob.data + offset,
1011 blob.length - offset);
1012 data_blob_free(&blob);
1016 remove the call from the right list when freed
1018 static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
1020 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1025 process some input to a dcerpc endpoint server.
1027 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
1029 struct ndr_pull *ndr;
1031 struct dcesrv_call_state *call;
1034 call = talloc_zero(dce_conn, struct dcesrv_call_state);
1036 talloc_free(dce_conn->partial_input.data);
1037 return NT_STATUS_NO_MEMORY;
1039 call->conn = dce_conn;
1040 call->event_ctx = dce_conn->event_ctx;
1041 call->msg_ctx = dce_conn->msg_ctx;
1042 call->state_flags = call->conn->state_flags;
1043 call->time = timeval_current();
1044 call->list = DCESRV_LIST_NONE;
1046 talloc_set_destructor(call, dcesrv_call_dequeue);
1048 blob = dce_conn->partial_input;
1049 blob.length = dcerpc_get_frag_length(&blob);
1051 ndr = ndr_pull_init_blob(&blob, call);
1053 talloc_free(dce_conn->partial_input.data);
1055 return NT_STATUS_NO_MEMORY;
1058 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
1059 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
1062 status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
1063 if (!NT_STATUS_IS_OK(status)) {
1064 talloc_free(dce_conn->partial_input.data);
1069 /* we have to check the signing here, before combining the
1071 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1072 !dcesrv_auth_request(call, &blob)) {
1073 dce_partial_advance(dce_conn, blob.length);
1074 return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
1077 dce_partial_advance(dce_conn, blob.length);
1079 /* see if this is a continued packet */
1080 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1081 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
1082 struct dcesrv_call_state *call2 = call;
1083 uint32_t alloc_size;
1085 /* we only allow fragmented requests, no other packet types */
1086 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
1087 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1090 /* this is a continuation of an existing call - find the call then
1091 tack it on the end */
1092 call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
1094 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1097 if (call->pkt.ptype != call2->pkt.ptype) {
1098 /* trying to play silly buggers are we? */
1099 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1102 alloc_size = call->pkt.u.request.stub_and_verifier.length +
1103 call2->pkt.u.request.stub_and_verifier.length;
1104 if (call->pkt.u.request.alloc_hint > alloc_size) {
1105 alloc_size = call->pkt.u.request.alloc_hint;
1108 call->pkt.u.request.stub_and_verifier.data =
1109 talloc_realloc(call,
1110 call->pkt.u.request.stub_and_verifier.data,
1111 uint8_t, alloc_size);
1112 if (!call->pkt.u.request.stub_and_verifier.data) {
1113 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
1115 memcpy(call->pkt.u.request.stub_and_verifier.data +
1116 call->pkt.u.request.stub_and_verifier.length,
1117 call2->pkt.u.request.stub_and_verifier.data,
1118 call2->pkt.u.request.stub_and_verifier.length);
1119 call->pkt.u.request.stub_and_verifier.length +=
1120 call2->pkt.u.request.stub_and_verifier.length;
1122 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
1127 /* this may not be the last pdu in the chain - if its isn't then
1128 just put it on the incoming_fragmented_call_list and wait for the rest */
1129 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
1130 !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
1131 dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
1132 return NT_STATUS_OK;
1135 /* This removes any fragments we may have had stashed away */
1136 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1138 switch (call->pkt.ptype) {
1139 case DCERPC_PKT_BIND:
1140 status = dcesrv_bind(call);
1142 case DCERPC_PKT_AUTH3:
1143 status = dcesrv_auth3(call);
1145 case DCERPC_PKT_ALTER:
1146 status = dcesrv_alter(call);
1148 case DCERPC_PKT_REQUEST:
1149 status = dcesrv_request(call);
1152 status = NT_STATUS_INVALID_PARAMETER;
1156 /* if we are going to be sending a reply then add
1157 it to the list of pending calls. We add it to the end to keep the call
1158 list in the order we will answer */
1159 if (!NT_STATUS_IS_OK(status)) {
1168 provide some input to a dcerpc endpoint server. This passes data
1169 from a dcerpc client into the server
1171 _PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
1175 dce_conn->partial_input.data = talloc_realloc(dce_conn,
1176 dce_conn->partial_input.data,
1178 dce_conn->partial_input.length + data->length);
1179 if (!dce_conn->partial_input.data) {
1180 return NT_STATUS_NO_MEMORY;
1182 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
1183 data->data, data->length);
1184 dce_conn->partial_input.length += data->length;
1186 while (dce_full_packet(&dce_conn->partial_input)) {
1187 status = dcesrv_input_process(dce_conn);
1188 if (!NT_STATUS_IS_OK(status)) {
1193 return NT_STATUS_OK;
1197 retrieve some output from a dcerpc server
1198 The caller supplies a function that will be called to do the
1201 The first argument to write_fn() will be 'private', the second will
1202 be a pointer to a buffer containing the data to be sent and the 3rd
1203 will be a pointer to a size_t variable that will be set to the
1204 number of bytes that are consumed from the output.
1206 from the current fragment
1208 _PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
1210 NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
1213 struct dcesrv_call_state *call;
1214 struct data_blob_list_item *rep;
1217 call = dce_conn->call_list;
1218 if (!call || !call->replies) {
1219 if (dce_conn->pending_call_list) {
1220 /* TODO: we need to say act async here
1221 * as we know we have pending requests
1222 * which will be finished at a time
1224 return NT_STATUS_FOOBAR;
1226 return NT_STATUS_FOOBAR;
1228 rep = call->replies;
1230 status = write_fn(private_data, &rep->blob, &nwritten);
1231 NT_STATUS_IS_ERR_RETURN(status);
1233 rep->blob.length -= nwritten;
1234 rep->blob.data += nwritten;
1236 if (rep->blob.length == 0) {
1237 /* we're done with this section of the call */
1238 DLIST_REMOVE(call->replies, rep);
1241 if (call->replies == NULL) {
1242 /* we're done with the whole call */
1243 dcesrv_call_set_list(call, DCESRV_LIST_NONE);
1250 _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
1253 struct dcesrv_context *dce_ctx;
1256 if (!endpoint_servers) {
1257 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1258 return NT_STATUS_INTERNAL_ERROR;
1261 dce_ctx = talloc(mem_ctx, struct dcesrv_context);
1262 NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
1263 dce_ctx->endpoint_list = NULL;
1265 for (i=0;endpoint_servers[i];i++) {
1266 const struct dcesrv_endpoint_server *ep_server;
1268 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
1270 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
1271 return NT_STATUS_INTERNAL_ERROR;
1274 status = ep_server->init_server(dce_ctx, ep_server);
1275 if (!NT_STATUS_IS_OK(status)) {
1276 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
1277 nt_errstr(status)));
1282 *_dce_ctx = dce_ctx;
1283 return NT_STATUS_OK;
1286 /* the list of currently registered DCERPC endpoint servers.
1288 static struct ep_server {
1289 struct dcesrv_endpoint_server *ep_server;
1290 } *ep_servers = NULL;
1291 static int num_ep_servers;
1294 register a DCERPC endpoint server.
1296 The 'name' can be later used by other backends to find the operations
1297 structure for this backend.
1299 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1301 _PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
1303 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1305 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1306 /* its already registered! */
1307 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1309 return NT_STATUS_OBJECT_NAME_COLLISION;
1312 ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
1314 smb_panic("out of memory in dcerpc_register");
1317 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1318 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1322 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1325 return NT_STATUS_OK;
1329 return the operations structure for a named backend of the specified type
1331 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1335 for (i=0;i<num_ep_servers;i++) {
1336 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1337 return ep_servers[i].ep_server;
1345 return the DCERPC module version, and the size of some critical types
1346 This can be used by endpoint server modules to either detect compilation errors, or provide
1347 multiple implementations for different smbd compilation options in one module
1349 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1351 static const struct dcesrv_critical_sizes critical_sizes = {
1352 DCERPC_MODULE_VERSION,
1353 sizeof(struct dcesrv_context),
1354 sizeof(struct dcesrv_endpoint),
1355 sizeof(struct dcesrv_endpoint_server),
1356 sizeof(struct dcesrv_interface),
1357 sizeof(struct dcesrv_if_list),
1358 sizeof(struct dcesrv_connection),
1359 sizeof(struct dcesrv_call_state),
1360 sizeof(struct dcesrv_auth),
1361 sizeof(struct dcesrv_handle)
1364 return &critical_sizes;
1368 initialise the dcerpc server context for ncacn_np based services
1370 _PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
1373 struct dcesrv_context *dce_ctx;
1375 status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(global_loadparm), &dce_ctx);
1376 NT_STATUS_NOT_OK_RETURN(status);
1378 *_dce_ctx = dce_ctx;
1379 return NT_STATUS_OK;