2 Unix SMB/CIFS implementation.
4 server side dcerpc core code
6 Copyright (C) Andrew Tridgell 2003
7 Copyright (C) Stefan (metze) Metzmacher 2004
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.
27 see if two endpoints match
29 static BOOL endpoints_match(const struct dcesrv_ep_description *ep1,
30 const struct dcesrv_ep_description *ep2)
32 if (ep1->type != ep2->type) {
38 if (strcmp(ep1->info.smb_pipe,ep2->info.smb_pipe)==0) {
43 if (ep1->info.tcp_port == ep2->info.tcp_port) {
53 find an endpoint in the dcesrv_context
55 static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
56 const struct dcesrv_ep_description *ep_description)
58 struct dcesrv_endpoint *ep;
59 for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
60 if (endpoints_match(&ep->ep_description, ep_description)) {
68 see if a uuid and if_version match to an interface
70 static BOOL interface_match(const struct dcesrv_interface *if1,
71 const struct dcesrv_interface *if2)
73 if (if1->ndr->if_version != if2->ndr->if_version) {
77 if (strcmp(if1->ndr->uuid, if2->ndr->uuid)==0) {
85 find the interface operations on an endpoint
87 static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
88 const struct dcesrv_interface *iface)
90 struct dcesrv_if_list *ifl;
91 for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
92 if (interface_match(&(ifl->iface), iface)) {
100 see if a uuid and if_version match to an interface
102 static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
103 const char *uuid, uint32 if_version)
105 if (iface->ndr->if_version != if_version) {
109 if (strcmp(iface->ndr->uuid, uuid)==0) {
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 char *uuid, uint32 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 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_ep_description ep_description;
154 struct dcesrv_endpoint *ep;
155 struct dcesrv_if_list *ifl;
159 tcp = (strncasecmp(ep_name, "TCP-", 4) == 0);
162 ep_description.type = ENDPOINT_TCP;
163 ep_description.info.tcp_port = atoi(ep_name+4);
165 ep_description.type = ENDPOINT_SMB;
166 ep_description.info.smb_pipe = ep_name;
169 /* check if this endpoint exists
171 if ((ep=find_endpoint(dce_ctx, &ep_description))==NULL) {
172 ep = talloc(dce_ctx->mem_ctx, sizeof(*ep));
174 return NT_STATUS_NO_MEMORY;
178 ep->ep_description.type = ENDPOINT_TCP;
179 ep->ep_description.info.tcp_port = atoi(ep_name+4);
181 ep->ep_description.type = ENDPOINT_SMB;
182 ep->ep_description.info.smb_pipe = smb_xstrdup(ep_name);
187 /* see if the interface is already registered on te endpoint */
188 if (find_interface(ep, iface)!=NULL) {
189 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
190 iface->ndr->name, ep_name));
191 return NT_STATUS_OBJECT_NAME_COLLISION;
194 /* talloc a new interface list element */
195 ifl = talloc(dce_ctx->mem_ctx, sizeof(*ifl));
197 return NT_STATUS_NO_MEMORY;
200 /* copy the given interface struct to the one on the endpoints interface list */
201 memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
203 /* if we have a security descriptor given,
204 * we should see if we can set it up on the endpoint
207 /* if there's currently no security descriptor given on the endpoint
210 if (ep->sd == NULL) {
211 ep->sd = copy_security_descriptor(dce_ctx->mem_ctx, sd);
214 /* if now there's no security descriptor given on the endpoint
215 * something goes wrong, either we failed to copy the security descriptor
216 * or there was already one on the endpoint
218 if (ep->sd != NULL) {
219 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
220 " on endpoint '%s'\n",
221 iface->ndr->name, ep_name));
222 if (add_ep) free(ep);
224 return NT_STATUS_OBJECT_NAME_COLLISION;
228 /* finally add the interface on the endpoint */
229 DLIST_ADD(ep->interface_list, ifl);
231 /* if it's a new endpoint add it to the dcesrv_context */
233 DLIST_ADD(dce_ctx->endpoint_list, ep);
236 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
237 iface->ndr->name, ep_name));
243 connect to a dcerpc endpoint
245 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
246 const struct dcesrv_endpoint *ep,
247 struct dcesrv_connection **p)
251 mem_ctx = talloc_init("dcesrv_endpoint_connect");
253 return NT_STATUS_NO_MEMORY;
256 *p = talloc_p(mem_ctx, struct dcesrv_connection);
258 talloc_destroy(mem_ctx);
259 return NT_STATUS_NO_MEMORY;
262 (*p)->dce_ctx = dce_ctx;
263 (*p)->mem_ctx = mem_ctx;
266 (*p)->private = NULL;
267 (*p)->call_list = NULL;
268 (*p)->cli_max_recv_frag = 0;
269 (*p)->handles = NULL;
270 (*p)->partial_input = data_blob(NULL, 0);
271 (*p)->auth_state.ntlmssp_state = NULL;
272 (*p)->auth_state.auth_info = NULL;
273 (*p)->session_key = data_blob(NULL, 0);
279 set the transport level session key
281 void dcesrv_set_session_key(struct dcesrv_connection *p, DATA_BLOB key)
283 p->session_key = data_blob_talloc(p->mem_ctx, key.data, key.length);
287 search and connect to a dcerpc endpoint
289 NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
290 const struct dcesrv_ep_description *ep_description,
291 struct dcesrv_connection **dce_conn_p)
294 const struct dcesrv_endpoint *ep;
296 /* make sure this endpoint exists */
297 ep = find_endpoint(dce_ctx, ep_description);
299 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
302 status = dcesrv_endpoint_connect(dce_ctx, ep, dce_conn_p);
303 if (!NT_STATUS_IS_OK(status)) {
307 /* TODO: check security descriptor of the endpoint here
308 * if it's a smb named pipe
309 * if it's failed free dce_conn_p
317 disconnect a link to an endpoint
319 void dcesrv_endpoint_disconnect(struct dcesrv_connection *p)
322 p->iface->unbind(p, p->iface);
325 /* destroy any handles */
327 dcesrv_handle_destroy(p, p->handles);
330 talloc_destroy(p->mem_ctx);
333 static void dcesrv_init_hdr(struct dcerpc_packet *pkt)
336 pkt->rpc_vers_minor = 0;
337 if (lp_rpc_big_endian()) {
340 pkt->drep[0] = DCERPC_DREP_LE;
348 return a dcerpc fault
350 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
352 struct dcerpc_packet pkt;
353 struct dcesrv_call_reply *rep;
356 /* setup a bind_ack */
357 dcesrv_init_hdr(&pkt);
359 pkt.call_id = call->pkt.call_id;
360 pkt.ptype = DCERPC_PKT_FAULT;
361 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
362 pkt.u.fault.alloc_hint = 0;
363 pkt.u.fault.context_id = 0;
364 pkt.u.fault.cancel_count = 0;
365 pkt.u.fault.status = fault_code;
367 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
369 return NT_STATUS_NO_MEMORY;
372 status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL);
373 if (!NT_STATUS_IS_OK(status)) {
377 dcerpc_set_frag_length(&rep->data, rep->data.length);
379 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
380 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
387 return a dcerpc bind_nak
389 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
391 struct dcerpc_packet pkt;
392 struct dcesrv_call_reply *rep;
395 /* setup a bind_nak */
396 dcesrv_init_hdr(&pkt);
398 pkt.call_id = call->pkt.call_id;
399 pkt.ptype = DCERPC_PKT_BIND_NAK;
400 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
401 pkt.u.bind_nak.reject_reason = reason;
402 pkt.u.bind_nak.num_versions = 0;
404 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
406 return NT_STATUS_NO_MEMORY;
409 status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL);
410 if (!NT_STATUS_IS_OK(status)) {
414 dcerpc_set_frag_length(&rep->data, rep->data.length);
416 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
417 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
424 handle a bind request
426 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
428 const char *uuid, *transfer_syntax;
429 uint32 if_version, transfer_syntax_version;
430 struct dcerpc_packet pkt;
431 struct dcesrv_call_reply *rep;
433 uint32 result=0, reason=0;
435 if (call->pkt.u.bind.num_contexts != 1 ||
436 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
437 return dcesrv_bind_nak(call, 0);
440 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
441 uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
443 return dcesrv_bind_nak(call, 0);
446 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
447 transfer_syntax = GUID_string(call->mem_ctx,
448 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
449 if (!transfer_syntax ||
450 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
451 NDR_GUID_VERSION != transfer_syntax_version) {
452 /* we only do NDR encoded dcerpc */
453 return dcesrv_bind_nak(call, 0);
456 call->conn->iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
457 if (!call->conn->iface) {
458 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
459 /* we don't know about that interface */
460 result = DCERPC_BIND_PROVIDER_REJECT;
461 reason = DCERPC_BIND_REASON_ASYNTAX;
464 if (call->conn->cli_max_recv_frag == 0) {
465 call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
468 /* handle any authentication that is being requested */
469 if (!dcesrv_auth_bind(call)) {
470 /* TODO: work out the right reject code */
471 return dcesrv_bind_nak(call, 0);
474 /* setup a bind_ack */
475 dcesrv_init_hdr(&pkt);
477 pkt.call_id = call->pkt.call_id;
478 pkt.ptype = DCERPC_PKT_BIND_ACK;
479 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
480 pkt.u.bind_ack.max_xmit_frag = 0x2000;
481 pkt.u.bind_ack.max_recv_frag = 0x2000;
482 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
483 if (call->conn->iface && call->conn->iface->ndr) {
484 pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s",
485 call->conn->iface->ndr->name);
487 pkt.u.bind_ack.secondary_address = "";
489 pkt.u.bind_ack.num_results = 1;
490 pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx);
491 if (!pkt.u.bind_ack.ctx_list) {
492 return NT_STATUS_NO_MEMORY;
494 pkt.u.bind_ack.ctx_list[0].result = result;
495 pkt.u.bind_ack.ctx_list[0].reason = reason;
496 GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
497 pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
498 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
500 if (!dcesrv_auth_bind_ack(call, &pkt)) {
501 return dcesrv_bind_nak(call, 0);
504 if (call->conn->iface) {
505 status = call->conn->iface->bind(call, call->conn->iface);
506 if (!NT_STATUS_IS_OK(status)) {
507 DEBUG(2,("Request for dcerpc interface %s/%d rejected\n", uuid, if_version));
512 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
514 return NT_STATUS_NO_MEMORY;
517 status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt,
518 call->conn->auth_state.auth_info);
519 if (!NT_STATUS_IS_OK(status)) {
523 dcerpc_set_frag_length(&rep->data, rep->data.length);
525 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
526 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
533 handle a auth3 request
535 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
537 /* handle the auth3 in the auth code */
538 if (!dcesrv_auth_auth3(call)) {
539 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
542 talloc_destroy(call->mem_ctx);
544 /* we don't send a reply to a auth3 request, except by a
551 handle a dcerpc request packet
553 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
555 struct ndr_pull *pull;
556 struct ndr_push *push;
563 opnum = call->pkt.u.request.opnum;
565 if (opnum >= call->conn->iface->ndr->num_calls) {
566 return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
569 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
571 return NT_STATUS_NO_MEMORY;
574 r = talloc(call->mem_ctx, call->conn->iface->ndr->calls[opnum].struct_size);
576 return NT_STATUS_NO_MEMORY;
579 if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
580 pull->flags |= LIBNDR_FLAG_BIGENDIAN;
583 /* unravel the NDR for the packet */
584 status = call->conn->iface->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
585 if (!NT_STATUS_IS_OK(status)) {
586 return dcesrv_fault(call, DCERPC_FAULT_NDR);
589 call->fault_code = 0;
591 /* call the dispatch function */
592 status = call->conn->iface->dispatch(call, call->mem_ctx, r);
593 if (!NT_STATUS_IS_OK(status)) {
594 return dcesrv_fault(call, call->fault_code);
597 /* form the reply NDR */
598 push = ndr_push_init_ctx(call->mem_ctx);
600 return NT_STATUS_NO_MEMORY;
603 if (lp_rpc_big_endian()) {
604 push->flags |= LIBNDR_FLAG_BIGENDIAN;
607 status = call->conn->iface->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
608 if (!NT_STATUS_IS_OK(status)) {
609 return dcesrv_fault(call, DCERPC_FAULT_NDR);
612 stub = ndr_push_blob(push);
614 total_length = stub.length;
618 struct dcesrv_call_reply *rep;
619 struct dcerpc_packet pkt;
621 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
623 return NT_STATUS_NO_MEMORY;
626 length = stub.length;
627 if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
628 /* the 32 is to cope with signing data */
629 length = call->conn->cli_max_recv_frag -
630 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
633 /* form the dcerpc response packet */
634 dcesrv_init_hdr(&pkt);
636 pkt.call_id = call->pkt.call_id;
637 pkt.ptype = DCERPC_PKT_RESPONSE;
639 if (stub.length == total_length) {
640 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
642 if (length == stub.length) {
643 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
645 pkt.u.response.alloc_hint = stub.length;
646 pkt.u.response.context_id = call->pkt.u.request.context_id;
647 pkt.u.response.cancel_count = 0;
648 pkt.u.response.stub_and_verifier.data = stub.data;
649 pkt.u.response.stub_and_verifier.length = length;
651 if (!dcesrv_auth_response(call, &rep->data, &pkt)) {
652 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
655 dcerpc_set_frag_length(&rep->data, rep->data.length);
657 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
660 stub.length -= length;
661 } while (stub.length != 0);
663 DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
670 work out if we have a full packet yet
672 static BOOL dce_full_packet(const DATA_BLOB *data)
674 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
677 if (dcerpc_get_frag_length(data) > data->length) {
684 we might have consumed only part of our input - advance past that part
686 static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32 offset)
690 if (dce_conn->partial_input.length == offset) {
691 data_blob_free(&dce_conn->partial_input);
695 blob = dce_conn->partial_input;
696 dce_conn->partial_input = data_blob(blob.data + offset,
697 blob.length - offset);
698 data_blob_free(&blob);
702 process some input to a dcerpc endpoint server.
704 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
706 struct ndr_pull *ndr;
709 struct dcesrv_call_state *call;
712 mem_ctx = talloc_init("dcesrv_input");
714 return NT_STATUS_NO_MEMORY;
716 call = talloc_p(mem_ctx, struct dcesrv_call_state);
718 talloc_free(dce_conn->mem_ctx, dce_conn->partial_input.data);
719 talloc_destroy(mem_ctx);
720 return NT_STATUS_NO_MEMORY;
722 call->mem_ctx = mem_ctx;
723 call->conn = dce_conn;
724 call->replies = NULL;
726 blob = dce_conn->partial_input;
727 blob.length = dcerpc_get_frag_length(&blob);
729 ndr = ndr_pull_init_blob(&blob, mem_ctx);
731 talloc_free(dce_conn->mem_ctx, dce_conn->partial_input.data);
732 talloc_destroy(mem_ctx);
733 return NT_STATUS_NO_MEMORY;
736 if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
737 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
740 status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
741 if (!NT_STATUS_IS_OK(status)) {
742 talloc_free(dce_conn->mem_ctx, dce_conn->partial_input.data);
743 talloc_destroy(mem_ctx);
747 dce_partial_advance(dce_conn, blob.length);
749 /* we have to check the signing here, before combining the
751 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
752 !dcesrv_auth_request(call)) {
753 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
756 /* see if this is a continued packet */
757 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
758 struct dcesrv_call_state *call2 = call;
761 /* we only allow fragmented requests, no other packet types */
762 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
763 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
766 /* this is a continuation of an existing call - find the call then
767 tack it on the end */
768 call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
770 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
773 if (call->pkt.ptype != call2->pkt.ptype) {
774 /* trying to play silly buggers are we? */
775 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
778 alloc_size = call->pkt.u.request.stub_and_verifier.length +
779 call2->pkt.u.request.stub_and_verifier.length;
780 if (call->pkt.u.request.alloc_hint > alloc_size) {
781 alloc_size = call->pkt.u.request.alloc_hint;
784 call->pkt.u.request.stub_and_verifier.data =
785 talloc_realloc(call->mem_ctx,
786 call->pkt.u.request.stub_and_verifier.data, alloc_size);
787 if (!call->pkt.u.request.stub_and_verifier.data) {
788 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
790 memcpy(call->pkt.u.request.stub_and_verifier.data +
791 call->pkt.u.request.stub_and_verifier.length,
792 call2->pkt.u.request.stub_and_verifier.data,
793 call2->pkt.u.request.stub_and_verifier.length);
794 call->pkt.u.request.stub_and_verifier.length +=
795 call2->pkt.u.request.stub_and_verifier.length;
797 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
800 /* this may not be the last pdu in the chain - if its isn't then
801 just put it on the call_list and wait for the rest */
802 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
803 DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
807 switch (call->pkt.ptype) {
808 case DCERPC_PKT_BIND:
809 status = dcesrv_bind(call);
811 case DCERPC_PKT_AUTH3:
812 status = dcesrv_auth3(call);
814 case DCERPC_PKT_REQUEST:
815 status = dcesrv_request(call);
818 status = NT_STATUS_INVALID_PARAMETER;
822 /* if we are going to be sending a reply then add
823 it to the list of pending calls. We add it to the end to keep the call
824 list in the order we will answer */
825 if (!NT_STATUS_IS_OK(status)) {
826 talloc_destroy(mem_ctx);
834 provide some input to a dcerpc endpoint server. This passes data
835 from a dcerpc client into the server
837 NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
841 /* handle the very common case that the input contains a full packet and there
842 is no partial packet pending. In this case we can avoid a copy of the
844 if (dce_conn->partial_input.length == 0) {
845 dce_conn->partial_input = *data;
846 /* make sure that dce_partial_advance doesn't free this data */
847 dce_conn->partial_input.free = NULL;
848 while (dce_full_packet(&dce_conn->partial_input)) {
849 status = dcesrv_input_process(dce_conn);
850 if (!NT_STATUS_IS_OK(status)) {
854 if (dce_conn->partial_input.length) {
855 /* there was some data left over. We have to copy this
856 as the caller may free the data */
857 dce_conn->partial_input =
858 data_blob(dce_conn->partial_input.data,
859 dce_conn->partial_input.length);
860 if (!dce_conn->partial_input.data) {
861 return NT_STATUS_NO_MEMORY;
867 dce_conn->partial_input.data = Realloc(dce_conn->partial_input.data,
868 dce_conn->partial_input.length + data->length);
869 if (!dce_conn->partial_input.data) {
870 return NT_STATUS_NO_MEMORY;
872 memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
873 data->data, data->length);
874 dce_conn->partial_input.length += data->length;
876 while (dce_full_packet(&dce_conn->partial_input)) {
877 status = dcesrv_input_process(dce_conn);
878 if (!NT_STATUS_IS_OK(status)) {
887 retrieve some output from a dcerpc server
888 The caller supplies a function that will be called to do the
891 The first argument to write_fn() will be 'private', the second will
892 be a pointer to a buffer containing the data to be sent and the 3rd
893 will be the number of bytes to be sent.
895 write_fn() should return the number of bytes successfully written.
897 NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
899 ssize_t (*write_fn)(void *, const void *, size_t))
901 struct dcesrv_call_state *call;
902 struct dcesrv_call_reply *rep;
905 call = dce_conn->call_list;
906 if (!call || !call->replies) {
907 return NT_STATUS_FOOBAR;
911 nwritten = write_fn(private, rep->data.data, rep->data.length);
912 if (nwritten == -1) {
913 /* TODO: hmm, how do we cope with this? destroy the
914 connection perhaps? */
915 return NT_STATUS_UNSUCCESSFUL;
918 rep->data.length -= nwritten;
919 rep->data.data += nwritten;
921 if (rep->data.length == 0) {
922 /* we're done with this section of the call */
923 DLIST_REMOVE(call->replies, rep);
926 if (call->replies == NULL) {
927 /* we're done with the whole call */
928 DLIST_REMOVE(dce_conn->call_list, call);
929 talloc_destroy(call->mem_ctx);
937 write_fn() for dcesrv_output_blob()
939 static ssize_t dcesrv_output_blob_write_fn(void *private, const void *buf, size_t count)
941 DATA_BLOB *blob = private;
942 if (count < blob->length) {
943 blob->length = count;
945 memcpy(blob->data, buf, blob->length);
950 a simple wrapper for dcesrv_output() for when we want to output
953 NTSTATUS dcesrv_output_blob(struct dcesrv_connection *dce_conn,
956 return dcesrv_output(dce_conn, blob, dcesrv_output_blob_write_fn);
960 initialise the dcerpc server context
962 NTSTATUS dcesrv_init_context(struct dcesrv_context *dce_ctx)
965 const char **endpoint_servers = lp_dcerpc_endpoint_servers();
967 dce_ctx->mem_ctx = talloc_init("struct dcesrv_context");
968 if (!dce_ctx->mem_ctx) {
969 DEBUG(3,("dcesrv_init_context: talloc_init failed\n"));
970 return NT_STATUS_NO_MEMORY;
973 dce_ctx->endpoint_list = NULL;
975 if (!endpoint_servers) {
976 DEBUG(3,("dcesrv_init_context: no endpoint servers configured\n"));
980 for (i=0;endpoint_servers[i];i++) {
982 const struct dcesrv_endpoint_server *ep_server;
984 ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
986 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
987 return NT_STATUS_UNSUCCESSFUL;
990 ret = ep_server->init_server(dce_ctx, ep_server);
991 if (!NT_STATUS_IS_OK(ret)) {
992 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s'\n", endpoint_servers[i]));
1000 /* the list of currently registered DCERPC endpoint servers.
1003 struct dcesrv_endpoint_server *ep_server;
1004 } *ep_servers = NULL;
1005 static int num_ep_servers;
1008 register a DCERPC endpoint server.
1010 The 'name' can be later used by other backends to find the operations
1011 structure for this backend.
1013 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1015 static NTSTATUS decrpc_register_ep_server(void *_ep_server)
1017 const struct dcesrv_endpoint_server *ep_server = _ep_server;
1019 if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
1020 /* its already registered! */
1021 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1023 return NT_STATUS_OBJECT_NAME_COLLISION;
1026 ep_servers = Realloc(ep_servers, sizeof(ep_servers[0]) * (num_ep_servers+1));
1028 smb_panic("out of memory in decrpc_register");
1031 ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
1032 ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
1036 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1039 return NT_STATUS_OK;
1043 return the operations structure for a named backend of the specified type
1045 const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
1049 for (i=0;i<num_ep_servers;i++) {
1050 if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
1051 return ep_servers[i].ep_server;
1059 return the DCERPC module version, and the size of some critical types
1060 This can be used by endpoint server modules to either detect compilation errors, or provide
1061 multiple implementations for different smbd compilation options in one module
1063 const struct dcesrv_critical_sizes *dcerpc_module_version(void)
1065 static const struct dcesrv_critical_sizes critical_sizes = {
1066 DCERPC_MODULE_VERSION,
1067 sizeof(struct dcesrv_context),
1068 sizeof(struct dcesrv_endpoint),
1069 sizeof(struct dcesrv_endpoint_server),
1070 sizeof(struct dcesrv_ep_description),
1071 sizeof(struct dcesrv_interface),
1072 sizeof(struct dcesrv_if_list),
1073 sizeof(struct dcesrv_connection),
1074 sizeof(struct dcesrv_call_state),
1075 sizeof(struct dcesrv_auth),
1076 sizeof(struct dcesrv_handle)
1079 return &critical_sizes;
1083 initialise the DCERPC subsystem
1085 BOOL dcesrv_init(void)
1089 status = register_subsystem("dcerpc", decrpc_register_ep_server);
1090 if (!NT_STATUS_IS_OK(status)) {
1094 /* FIXME: Perhaps panic if a basic endpoint server, such as EPMAPER, fails to initialise? */
1097 DEBUG(3,("DCERPC subsystem version %d initialised\n", DCERPC_MODULE_VERSION));