2 Unix SMB/CIFS implementation.
4 server side dcerpc core code
6 Copyright (C) Andrew Tridgell 2003
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 find the set of endpoint operations for an endpoint server
28 static const struct dcesrv_endpoint_ops *find_endpoint(struct dcesrv_context *dce,
29 const struct dcesrv_endpoint *endpoint)
31 struct dce_endpoint *ep;
32 for (ep=dce->endpoint_list; ep; ep=ep->next) {
33 if (ep->endpoint_ops->query_endpoint(endpoint)) {
34 return ep->endpoint_ops;
41 find a call that is pending in our call list
43 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_state *dce, uint16 call_id)
45 struct dcesrv_call_state *c;
46 for (c=dce->call_list;c;c=c->next) {
47 if (c->pkt.call_id == call_id) {
55 register an endpoint server
57 BOOL dcesrv_endpoint_register(struct dcesrv_context *dce,
58 const struct dcesrv_endpoint_ops *ops,
59 const struct dcerpc_interface_table *table)
65 for (i=0;i<table->endpoints->count;i++) {
66 struct dce_endpoint *ep;
69 tcp = (strncasecmp(table->endpoints->names[i], "TCP-", 4) == 0);
72 if (done_tcp) continue;
75 if (done_smb) continue;
79 ep = malloc(sizeof(*ep));
85 ep->endpoint.type = ENDPOINT_TCP;
86 ep->endpoint.info.tcp_port = atoi(table->endpoints->names[i]+4);
88 ep->endpoint.type = ENDPOINT_SMB;
89 ep->endpoint.info.smb_pipe = table->endpoints->names[i];
92 ep->endpoint_ops = ops;
93 DLIST_ADD(dce->endpoint_list, ep);
100 connect to a dcerpc endpoint
102 NTSTATUS dcesrv_endpoint_connect_ops(struct dcesrv_context *dce,
103 const struct dcesrv_endpoint *endpoint,
104 const struct dcesrv_endpoint_ops *ops,
105 struct dcesrv_state **p)
110 mem_ctx = talloc_init("dcesrv_endpoint_connect");
112 return NT_STATUS_NO_MEMORY;
115 *p = talloc_p(mem_ctx, struct dcesrv_state);
117 talloc_destroy(mem_ctx);
118 return NT_STATUS_NO_MEMORY;
122 (*p)->mem_ctx = mem_ctx;
123 (*p)->endpoint = *endpoint;
125 (*p)->private = NULL;
126 (*p)->call_list = NULL;
127 (*p)->cli_max_recv_frag = 0;
129 (*p)->dispatch = NULL;
130 (*p)->handles = NULL;
131 (*p)->next_handle = 0;
132 (*p)->partial_input = data_blob(NULL, 0);
134 /* make sure the endpoint server likes the connection */
135 status = ops->connect(*p);
136 if (!NT_STATUS_IS_OK(status)) {
137 talloc_destroy(mem_ctx);
145 connect to a dcerpc endpoint
147 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce,
148 const struct dcesrv_endpoint *endpoint,
149 struct dcesrv_state **p)
151 const struct dcesrv_endpoint_ops *ops;
153 /* make sure this endpoint exists */
154 ops = find_endpoint(dce, endpoint);
156 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
159 return dcesrv_endpoint_connect_ops(dce, endpoint, ops, p);
164 disconnect a link to an endpoint
166 void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
168 p->ops->disconnect(p);
170 /* destroy any handles */
172 TALLOC_CTX *m = p->handles->mem_ctx;
173 DLIST_REMOVE(p->handles, p->handles);
177 talloc_destroy(p->mem_ctx);
181 return a dcerpc fault
183 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
185 struct ndr_push *push;
186 struct dcerpc_packet pkt;
187 struct dcesrv_call_reply *rep;
190 /* setup a bind_ack */
192 pkt.rpc_vers_minor = 0;
193 pkt.drep[0] = 0x10; /* Little endian */
198 pkt.call_id = call->pkt.call_id;
199 pkt.ptype = DCERPC_PKT_FAULT;
200 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
201 pkt.u.fault.alloc_hint = 0;
202 pkt.u.fault.context_id = 0;
203 pkt.u.fault.cancel_count = 0;
204 pkt.u.fault.status = fault_code;
206 /* now form the NDR for the fault */
207 push = ndr_push_init_ctx(call->mem_ctx);
209 return NT_STATUS_NO_MEMORY;
212 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
213 if (!NT_STATUS_IS_OK(status)) {
217 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
219 return NT_STATUS_NO_MEMORY;
222 rep->data = ndr_push_blob(push);
223 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
225 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
232 return a dcerpc fault from a ntstatus code
234 static NTSTATUS dcesrv_fault_nt(struct dcesrv_call_state *call, NTSTATUS status)
236 uint32 fault_code = DCERPC_FAULT_OTHER;
238 /* TODO: we need to expand this table to include more mappings */
239 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
240 fault_code = DCERPC_FAULT_CONTEXT_MISMATCH;
243 return dcesrv_fault(call, fault_code);
248 return a dcerpc bind_nak
250 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
252 struct ndr_push *push;
253 struct dcerpc_packet pkt;
254 struct dcesrv_call_reply *rep;
257 /* setup a bind_ack */
259 pkt.rpc_vers_minor = 0;
260 pkt.drep[0] = 0x10; /* Little endian */
265 pkt.call_id = call->pkt.call_id;
266 pkt.ptype = DCERPC_PKT_BIND_NAK;
267 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
268 pkt.u.bind_nak.reject_reason = reason;
269 pkt.u.bind_nak.num_versions = 0;
271 /* now form the NDR for the bind_nak */
272 push = ndr_push_init_ctx(call->mem_ctx);
274 return NT_STATUS_NO_MEMORY;
277 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
278 if (!NT_STATUS_IS_OK(status)) {
282 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
284 return NT_STATUS_NO_MEMORY;
287 rep->data = ndr_push_blob(push);
288 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
290 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
297 handle a bind request
299 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
301 const char *uuid, *transfer_syntax;
302 uint32 if_version, transfer_syntax_version;
303 struct dcerpc_packet pkt;
304 struct ndr_push *push;
305 struct dcesrv_call_reply *rep;
307 uint32 result=0, reason=0;
309 if (call->pkt.u.bind.num_contexts != 1 ||
310 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
311 return dcesrv_bind_nak(call, 0);
314 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.major_version;
315 uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
317 return dcesrv_bind_nak(call, 0);
320 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].major_version;
321 transfer_syntax = GUID_string(call->mem_ctx,
322 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
323 if (!transfer_syntax ||
324 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
325 NDR_GUID_VERSION != transfer_syntax_version) {
326 /* we only do NDR encoded dcerpc */
327 return dcesrv_bind_nak(call, 0);
330 if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) {
331 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
332 /* we don't know about that interface */
333 result = DCERPC_BIND_PROVIDER_REJECT;
334 reason = DCERPC_BIND_REASON_ASYNTAX;
337 if (call->dce->cli_max_recv_frag == 0) {
338 call->dce->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
341 /* setup a bind_ack */
343 pkt.rpc_vers_minor = 0;
344 pkt.drep[0] = 0x10; /* Little endian */
349 pkt.call_id = call->pkt.call_id;
350 pkt.ptype = DCERPC_PKT_BIND_ACK;
351 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
352 pkt.u.bind_ack.max_xmit_frag = 0x2000;
353 pkt.u.bind_ack.max_recv_frag = 0x2000;
354 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
355 if (call->dce->ndr) {
356 pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s",
357 call->dce->ndr->name);
359 pkt.u.bind_ack.secondary_address = "";
361 pkt.u.bind_ack.num_results = 1;
362 pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx);
363 if (!pkt.u.bind_ack.ctx_list) {
364 return NT_STATUS_NO_MEMORY;
366 pkt.u.bind_ack.ctx_list[0].result = result;
367 pkt.u.bind_ack.ctx_list[0].reason = reason;
368 GUID_from_string(uuid, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
369 pkt.u.bind_ack.ctx_list[0].syntax.major_version = if_version;
370 pkt.u.bind_ack.ctx_list[0].syntax.minor_version = 0;
371 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
373 /* now form the NDR for the bind_ack */
374 push = ndr_push_init_ctx(call->mem_ctx);
376 return NT_STATUS_NO_MEMORY;
379 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
380 if (!NT_STATUS_IS_OK(status)) {
384 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
386 return NT_STATUS_NO_MEMORY;
389 rep->data = ndr_push_blob(push);
390 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
392 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
399 handle a dcerpc request packet
401 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
403 struct ndr_pull *pull;
404 struct ndr_push *push;
410 opnum = call->pkt.u.request.opnum;
412 if (opnum >= call->dce->ndr->num_calls) {
413 return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
416 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
418 return NT_STATUS_NO_MEMORY;
421 r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size);
423 return NT_STATUS_NO_MEMORY;
426 /* unravel the NDR for the packet */
427 status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
428 if (!NT_STATUS_IS_OK(status)) {
429 return dcesrv_fault(call, DCERPC_FAULT_NDR);
432 /* call the dispatch function */
433 status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
434 if (!NT_STATUS_IS_OK(status)) {
435 return dcesrv_fault_nt(call, status);
438 /* form the reply NDR */
439 push = ndr_push_init_ctx(call->mem_ctx);
441 return NT_STATUS_NO_MEMORY;
444 status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
445 if (!NT_STATUS_IS_OK(status)) {
446 return dcesrv_fault(call, DCERPC_FAULT_NDR);
449 stub = ndr_push_blob(push);
453 struct dcesrv_call_reply *rep;
454 struct dcerpc_packet pkt;
456 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
458 return NT_STATUS_NO_MEMORY;
461 length = stub.length;
462 if (length + DCERPC_RESPONSE_LENGTH > call->dce->cli_max_recv_frag) {
463 length = call->dce->cli_max_recv_frag - DCERPC_RESPONSE_LENGTH;
466 /* form the dcerpc response packet */
468 pkt.rpc_vers_minor = 0;
469 pkt.drep[0] = 0x10; /* Little endian */
474 pkt.call_id = call->pkt.call_id;
475 pkt.ptype = DCERPC_PKT_RESPONSE;
477 if (!call->replies) {
478 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
480 if (length == stub.length) {
481 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
483 pkt.u.response.alloc_hint = stub.length;
484 pkt.u.response.context_id = call->pkt.u.request.context_id;
485 pkt.u.response.cancel_count = 0;
486 pkt.u.response.stub_and_verifier.data = stub.data;
487 pkt.u.response.stub_and_verifier.length = length;
489 push = ndr_push_init_ctx(call->mem_ctx);
491 return NT_STATUS_NO_MEMORY;
494 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
495 if (!NT_STATUS_IS_OK(status)) {
499 rep->data = ndr_push_blob(push);
500 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
502 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
505 stub.length -= length;
506 } while (stub.length != 0);
513 work out if we have a full packet yet
515 static BOOL dce_full_packet(const DATA_BLOB *data)
517 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
520 if (SVAL(data->data, DCERPC_FRAG_LEN_OFFSET) > data->length) {
527 we might have consumed only part of our input - advance past that part
529 static void dce_partial_advance(struct dcesrv_state *dce, uint32 offset)
533 if (dce->partial_input.length == offset) {
534 talloc_free(dce->mem_ctx, dce->partial_input.data);
535 dce->partial_input = data_blob(NULL, 0);
539 blob = dce->partial_input;
540 dce->partial_input = data_blob_talloc(dce->mem_ctx,
542 blob.length - offset);
543 talloc_free(dce->mem_ctx, blob.data);
547 provide some input to a dcerpc endpoint server. This passes data
548 from a dcerpc client into the server
550 NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
552 struct ndr_pull *ndr;
555 struct dcesrv_call_state *call;
557 dce->partial_input.data = talloc_realloc(dce->mem_ctx,
558 dce->partial_input.data,
559 dce->partial_input.length + data->length);
560 if (!dce->partial_input.data) {
561 return NT_STATUS_NO_MEMORY;
563 memcpy(dce->partial_input.data + dce->partial_input.length,
564 data->data, data->length);
565 dce->partial_input.length += data->length;
567 if (!dce_full_packet(&dce->partial_input)) {
571 mem_ctx = talloc_init("dcesrv_input");
573 return NT_STATUS_NO_MEMORY;
575 call = talloc_p(mem_ctx, struct dcesrv_call_state);
577 talloc_free(dce->mem_ctx, dce->partial_input.data);
578 talloc_destroy(mem_ctx);
579 return NT_STATUS_NO_MEMORY;
581 call->mem_ctx = mem_ctx;
583 call->replies = NULL;
585 ndr = ndr_pull_init_blob(&dce->partial_input, mem_ctx);
587 talloc_free(dce->mem_ctx, dce->partial_input.data);
588 talloc_destroy(mem_ctx);
589 return NT_STATUS_NO_MEMORY;
592 status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
593 if (!NT_STATUS_IS_OK(status)) {
594 talloc_free(dce->mem_ctx, dce->partial_input.data);
595 talloc_destroy(mem_ctx);
599 dce_partial_advance(dce, ndr->offset);
601 /* see if this is a continued packet */
602 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
603 struct dcesrv_call_state *call2 = call;
606 /* we only allow fragmented requests, no other packet types */
607 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
608 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
611 /* this is a continuation of an existing call - find the call then
612 tack it on the end */
613 call = dcesrv_find_call(dce, call2->pkt.call_id);
615 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
618 if (call->pkt.ptype != call2->pkt.ptype) {
619 /* trying to play silly buggers are we? */
620 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
623 alloc_size = call->pkt.u.request.stub_and_verifier.length +
624 call2->pkt.u.request.stub_and_verifier.length;
625 if (call->pkt.u.request.alloc_hint > alloc_size) {
626 alloc_size = call->pkt.u.request.alloc_hint;
629 call->pkt.u.request.stub_and_verifier.data =
630 talloc_realloc(call->mem_ctx,
631 call->pkt.u.request.stub_and_verifier.data, alloc_size);
632 if (!call->pkt.u.request.stub_and_verifier.data) {
633 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
635 memcpy(call->pkt.u.request.stub_and_verifier.data +
636 call->pkt.u.request.stub_and_verifier.length,
637 call2->pkt.u.request.stub_and_verifier.data,
638 call2->pkt.u.request.stub_and_verifier.length);
639 call->pkt.u.request.stub_and_verifier.length +=
640 call2->pkt.u.request.stub_and_verifier.length;
642 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
645 /* this may not be the last pdu in the chain - if its isn't then
646 just put it on the call_list and wait for the rest */
647 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
648 DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
652 switch (call->pkt.ptype) {
653 case DCERPC_PKT_BIND:
654 status = dcesrv_bind(call);
656 case DCERPC_PKT_REQUEST:
657 status = dcesrv_request(call);
660 status = NT_STATUS_INVALID_PARAMETER;
664 /* if we are going to be sending a reply then add
665 it to the list of pending calls. We add it to the end to keep the call
666 list in the order we will answer */
667 if (NT_STATUS_IS_OK(status)) {
668 DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
670 talloc_destroy(mem_ctx);
677 retrieve some output from a dcerpc server. The amount of data that
678 is wanted is in data->length and data->data is already allocated
679 to hold that much data.
681 NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
683 struct dcesrv_call_state *call;
684 struct dcesrv_call_reply *rep;
686 call = dce->call_list;
687 if (!call || !call->replies) {
688 return NT_STATUS_FOOBAR;
692 if (data->length >= rep->data.length) {
693 data->length = rep->data.length;
696 memcpy(data->data, rep->data.data, data->length);
697 rep->data.length -= data->length;
698 rep->data.data += data->length;
700 if (rep->data.length == 0) {
701 /* we're done with this section of the call */
702 DLIST_REMOVE(call->replies, rep);
705 if (call->replies == NULL) {
706 /* we're done with the whole call */
707 DLIST_REMOVE(dce->call_list, call);
708 talloc_destroy(call->mem_ctx);
716 a useful function for implementing the query endpoint op
718 BOOL dcesrv_table_query(const struct dcerpc_interface_table *table,
719 const struct dcesrv_endpoint *ep)
722 const struct dcerpc_endpoint_list *endpoints = table->endpoints;
724 if (ep->type != ENDPOINT_SMB) {
728 for (i=0;i<endpoints->count;i++) {
729 if (strcasecmp(ep->info.smb_pipe, endpoints->names[i]) == 0) {
738 a useful function for implementing the lookup_endpoints op
740 int dcesrv_lookup_endpoints(const struct dcerpc_interface_table *table,
742 struct dcesrv_ep_iface **e)
745 *e = talloc_array_p(mem_ctx, struct dcesrv_ep_iface, table->endpoints->count);
750 for (i=0;i<table->endpoints->count;i++) {
751 (*e)[i].name = table->name;
752 (*e)[i].uuid = table->uuid;
753 (*e)[i].if_version = table->if_version;
754 if (strncmp(table->endpoints->names[i], "TCP-", 4) == 0) {
755 (*e)[i].endpoint.type = ENDPOINT_TCP;
756 (*e)[i].endpoint.info.tcp_port = atoi(table->endpoints->names[i]+4);
758 (*e)[i].endpoint.type = ENDPOINT_SMB;
759 (*e)[i].endpoint.info.smb_pipe = table->endpoints->names[i];
768 initialise the dcerpc server subsystem
770 BOOL dcesrv_init(struct dcesrv_context *dce)
773 rpc_epmapper_init(dce);