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)->partial_input = data_blob(NULL, 0);
132 (*p)->auth_state.ntlmssp_state = NULL;
133 (*p)->auth_state.auth_info = NULL;
135 /* make sure the endpoint server likes the connection */
136 status = ops->connect(*p);
137 if (!NT_STATUS_IS_OK(status)) {
138 talloc_destroy(mem_ctx);
146 connect to a dcerpc endpoint
148 NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce,
149 const struct dcesrv_endpoint *endpoint,
150 struct dcesrv_state **p)
152 const struct dcesrv_endpoint_ops *ops;
154 /* make sure this endpoint exists */
155 ops = find_endpoint(dce, endpoint);
157 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
160 return dcesrv_endpoint_connect_ops(dce, endpoint, ops, p);
165 disconnect a link to an endpoint
167 void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
169 p->ops->disconnect(p);
171 /* destroy any handles */
173 TALLOC_CTX *m = p->handles->mem_ctx;
174 DLIST_REMOVE(p->handles, p->handles);
178 talloc_destroy(p->mem_ctx);
182 return a dcerpc fault
184 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
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 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
208 return NT_STATUS_NO_MEMORY;
211 status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL, 0);
212 if (!NT_STATUS_IS_OK(status)) {
216 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
218 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
225 return a dcerpc fault from a ntstatus code
227 static NTSTATUS dcesrv_fault_nt(struct dcesrv_call_state *call, NTSTATUS status)
229 uint32 fault_code = DCERPC_FAULT_OTHER;
231 /* TODO: we need to expand this table to include more mappings */
232 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
233 fault_code = DCERPC_FAULT_CONTEXT_MISMATCH;
236 return dcesrv_fault(call, fault_code);
241 return a dcerpc bind_nak
243 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
245 struct dcerpc_packet pkt;
246 struct dcesrv_call_reply *rep;
249 /* setup a bind_ack */
251 pkt.rpc_vers_minor = 0;
252 pkt.drep[0] = 0x10; /* Little endian */
257 pkt.call_id = call->pkt.call_id;
258 pkt.ptype = DCERPC_PKT_BIND_NAK;
259 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
260 pkt.u.bind_nak.reject_reason = reason;
261 pkt.u.bind_nak.num_versions = 0;
263 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
265 return NT_STATUS_NO_MEMORY;
268 status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL, 0);
269 if (!NT_STATUS_IS_OK(status)) {
273 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
275 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
282 handle a bind request
284 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
286 const char *uuid, *transfer_syntax;
287 uint32 if_version, transfer_syntax_version;
288 struct dcerpc_packet pkt;
289 struct dcesrv_call_reply *rep;
291 uint32 result=0, reason=0;
293 if (call->pkt.u.bind.num_contexts != 1 ||
294 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
295 return dcesrv_bind_nak(call, 0);
298 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
299 uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
301 return dcesrv_bind_nak(call, 0);
304 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
305 transfer_syntax = GUID_string(call->mem_ctx,
306 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
307 if (!transfer_syntax ||
308 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
309 NDR_GUID_VERSION != transfer_syntax_version) {
310 /* we only do NDR encoded dcerpc */
311 return dcesrv_bind_nak(call, 0);
314 if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) {
315 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
316 /* we don't know about that interface */
317 result = DCERPC_BIND_PROVIDER_REJECT;
318 reason = DCERPC_BIND_REASON_ASYNTAX;
321 if (call->dce->cli_max_recv_frag == 0) {
322 call->dce->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
325 /* handle any authentication that is being requested */
326 if (!dcesrv_auth_bind(call)) {
327 return dcesrv_bind_nak(call, 0);
330 /* setup a bind_ack */
332 pkt.rpc_vers_minor = 0;
333 pkt.drep[0] = 0x10; /* Little endian */
338 pkt.call_id = call->pkt.call_id;
339 pkt.ptype = DCERPC_PKT_BIND_ACK;
340 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
341 pkt.u.bind_ack.max_xmit_frag = 0x2000;
342 pkt.u.bind_ack.max_recv_frag = 0x2000;
343 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
344 if (call->dce->ndr) {
345 pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s",
346 call->dce->ndr->name);
348 pkt.u.bind_ack.secondary_address = "";
350 pkt.u.bind_ack.num_results = 1;
351 pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx);
352 if (!pkt.u.bind_ack.ctx_list) {
353 return NT_STATUS_NO_MEMORY;
355 pkt.u.bind_ack.ctx_list[0].result = result;
356 pkt.u.bind_ack.ctx_list[0].reason = reason;
357 GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
358 pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
359 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
361 if (!dcesrv_auth_bind_ack(call, &pkt)) {
362 return dcesrv_bind_nak(call, 0);
365 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
367 return NT_STATUS_NO_MEMORY;
370 status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt,
371 call->dce->auth_state.auth_info, 0);
372 if (!NT_STATUS_IS_OK(status)) {
376 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
378 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
379 DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *);
386 handle a auth3 request
388 static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
390 /* handle the auth3 in the auth code */
391 if (!dcesrv_auth_auth3(call)) {
392 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
395 talloc_destroy(call->mem_ctx);
397 /* we don't send a reply to a auth3 request, except by a
404 handle a dcerpc request packet
406 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
408 struct ndr_pull *pull;
409 struct ndr_push *push;
415 opnum = call->pkt.u.request.opnum;
417 if (opnum >= call->dce->ndr->num_calls) {
418 return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
421 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
423 return NT_STATUS_NO_MEMORY;
426 r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size);
428 return NT_STATUS_NO_MEMORY;
431 /* unravel the NDR for the packet */
432 status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
433 if (!NT_STATUS_IS_OK(status)) {
434 return dcesrv_fault(call, DCERPC_FAULT_NDR);
437 /* call the dispatch function */
438 status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
439 if (!NT_STATUS_IS_OK(status)) {
440 return dcesrv_fault_nt(call, status);
443 /* form the reply NDR */
444 push = ndr_push_init_ctx(call->mem_ctx);
446 return NT_STATUS_NO_MEMORY;
449 status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
450 if (!NT_STATUS_IS_OK(status)) {
451 return dcesrv_fault(call, DCERPC_FAULT_NDR);
454 stub = ndr_push_blob(push);
458 struct dcesrv_call_reply *rep;
459 struct dcerpc_packet pkt;
461 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
463 return NT_STATUS_NO_MEMORY;
466 length = stub.length;
467 if (length + DCERPC_RESPONSE_LENGTH > call->dce->cli_max_recv_frag) {
468 /* the 32 is to cope with signing data */
469 length = call->dce->cli_max_recv_frag -
470 (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
473 /* form the dcerpc response packet */
475 pkt.rpc_vers_minor = 0;
476 pkt.drep[0] = 0x10; /* Little endian */
481 pkt.call_id = call->pkt.call_id;
482 pkt.ptype = DCERPC_PKT_RESPONSE;
484 if (!call->replies) {
485 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
487 if (length == stub.length) {
488 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
490 pkt.u.response.alloc_hint = stub.length;
491 pkt.u.response.context_id = call->pkt.u.request.context_id;
492 pkt.u.response.cancel_count = 0;
493 pkt.u.response.stub_and_verifier.data = stub.data;
494 pkt.u.response.stub_and_verifier.length = length;
496 if (!dcesrv_auth_response(call, &rep->data, &pkt)) {
497 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
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);
508 DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *);
515 work out if we have a full packet yet
517 static BOOL dce_full_packet(const DATA_BLOB *data)
519 if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
522 if (SVAL(data->data, DCERPC_FRAG_LEN_OFFSET) > data->length) {
529 we might have consumed only part of our input - advance past that part
531 static void dce_partial_advance(struct dcesrv_state *dce, uint32 offset)
535 if (dce->partial_input.length == offset) {
536 free(dce->partial_input.data);
537 dce->partial_input = data_blob(NULL, 0);
541 blob = dce->partial_input;
542 dce->partial_input = data_blob(blob.data + offset,
543 blob.length - offset);
548 process some input to a dcerpc endpoint server.
550 NTSTATUS dcesrv_input_process(struct dcesrv_state *dce)
552 struct ndr_pull *ndr;
555 struct dcesrv_call_state *call;
558 mem_ctx = talloc_init("dcesrv_input");
560 return NT_STATUS_NO_MEMORY;
562 call = talloc_p(mem_ctx, struct dcesrv_call_state);
564 talloc_free(dce->mem_ctx, dce->partial_input.data);
565 talloc_destroy(mem_ctx);
566 return NT_STATUS_NO_MEMORY;
568 call->mem_ctx = mem_ctx;
570 call->replies = NULL;
572 blob = dce->partial_input;
573 blob.length = SVAL(blob.data, DCERPC_FRAG_LEN_OFFSET);
575 ndr = ndr_pull_init_blob(&blob, mem_ctx);
577 talloc_free(dce->mem_ctx, dce->partial_input.data);
578 talloc_destroy(mem_ctx);
579 return NT_STATUS_NO_MEMORY;
582 status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
583 if (!NT_STATUS_IS_OK(status)) {
584 talloc_free(dce->mem_ctx, dce->partial_input.data);
585 talloc_destroy(mem_ctx);
589 dce_partial_advance(dce, blob.length);
591 /* we have to check the signing here, before combining the
593 if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
594 !dcesrv_auth_request(call)) {
595 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
598 /* see if this is a continued packet */
599 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
600 struct dcesrv_call_state *call2 = call;
603 /* we only allow fragmented requests, no other packet types */
604 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
605 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
608 /* this is a continuation of an existing call - find the call then
609 tack it on the end */
610 call = dcesrv_find_call(dce, call2->pkt.call_id);
612 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
615 if (call->pkt.ptype != call2->pkt.ptype) {
616 /* trying to play silly buggers are we? */
617 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
620 alloc_size = call->pkt.u.request.stub_and_verifier.length +
621 call2->pkt.u.request.stub_and_verifier.length;
622 if (call->pkt.u.request.alloc_hint > alloc_size) {
623 alloc_size = call->pkt.u.request.alloc_hint;
626 call->pkt.u.request.stub_and_verifier.data =
627 talloc_realloc(call->mem_ctx,
628 call->pkt.u.request.stub_and_verifier.data, alloc_size);
629 if (!call->pkt.u.request.stub_and_verifier.data) {
630 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
632 memcpy(call->pkt.u.request.stub_and_verifier.data +
633 call->pkt.u.request.stub_and_verifier.length,
634 call2->pkt.u.request.stub_and_verifier.data,
635 call2->pkt.u.request.stub_and_verifier.length);
636 call->pkt.u.request.stub_and_verifier.length +=
637 call2->pkt.u.request.stub_and_verifier.length;
639 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
642 /* this may not be the last pdu in the chain - if its isn't then
643 just put it on the call_list and wait for the rest */
644 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
645 DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
649 switch (call->pkt.ptype) {
650 case DCERPC_PKT_BIND:
651 status = dcesrv_bind(call);
653 case DCERPC_PKT_AUTH3:
654 status = dcesrv_auth3(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 talloc_destroy(mem_ctx);
676 provide some input to a dcerpc endpoint server. This passes data
677 from a dcerpc client into the server
679 NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
683 dce->partial_input.data = Realloc(dce->partial_input.data,
684 dce->partial_input.length + data->length);
685 if (!dce->partial_input.data) {
686 return NT_STATUS_NO_MEMORY;
688 memcpy(dce->partial_input.data + dce->partial_input.length,
689 data->data, data->length);
690 dce->partial_input.length += data->length;
692 while (dce_full_packet(&dce->partial_input)) {
693 status = dcesrv_input_process(dce);
694 if (!NT_STATUS_IS_OK(status)) {
703 retrieve some output from a dcerpc server. The amount of data that
704 is wanted is in data->length and data->data is already allocated
705 to hold that much data.
707 NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
709 struct dcesrv_call_state *call;
710 struct dcesrv_call_reply *rep;
712 call = dce->call_list;
713 if (!call || !call->replies) {
714 return NT_STATUS_FOOBAR;
718 if (data->length >= rep->data.length) {
719 data->length = rep->data.length;
722 memcpy(data->data, rep->data.data, data->length);
723 rep->data.length -= data->length;
724 rep->data.data += data->length;
726 if (rep->data.length == 0) {
727 /* we're done with this section of the call */
728 DLIST_REMOVE(call->replies, rep);
731 if (call->replies == NULL) {
732 /* we're done with the whole call */
733 DLIST_REMOVE(dce->call_list, call);
734 talloc_destroy(call->mem_ctx);
742 a useful function for implementing the query endpoint op
744 BOOL dcesrv_table_query(const struct dcerpc_interface_table *table,
745 const struct dcesrv_endpoint *ep)
748 const struct dcerpc_endpoint_list *endpoints = table->endpoints;
750 if (ep->type != ENDPOINT_SMB) {
754 for (i=0;i<endpoints->count;i++) {
755 if (strcasecmp(ep->info.smb_pipe, endpoints->names[i]) == 0) {
764 a useful function for implementing the lookup_endpoints op
766 int dcesrv_lookup_endpoints(const struct dcerpc_interface_table *table,
768 struct dcesrv_ep_iface **e)
771 *e = talloc_array_p(mem_ctx, struct dcesrv_ep_iface, table->endpoints->count);
776 for (i=0;i<table->endpoints->count;i++) {
777 (*e)[i].name = table->name;
778 (*e)[i].uuid = table->uuid;
779 (*e)[i].if_version = table->if_version;
780 if (strncmp(table->endpoints->names[i], "TCP-", 4) == 0) {
781 (*e)[i].endpoint.type = ENDPOINT_TCP;
782 (*e)[i].endpoint.info.tcp_port = atoi(table->endpoints->names[i]+4);
784 (*e)[i].endpoint.type = ENDPOINT_SMB;
785 (*e)[i].endpoint.info.smb_pipe = table->endpoints->names[i];
793 BOOL dcesrv_set_interface(struct dcesrv_state *dce,
794 const char *uuid, uint32 if_version,
795 const struct dcerpc_interface_table *table,
796 const dcesrv_dispatch_fn_t *dispatch_table)
798 if (strcasecmp(table->uuid, uuid) != 0 || if_version != table->if_version) {
799 DEBUG(2,("Attempt to use unknown interface %s/%d\n", uuid, if_version));
804 dce->dispatch = dispatch_table;
810 initialise the dcerpc server subsystem
812 BOOL dcesrv_init(struct dcesrv_context *dce)
814 rpc_rpcecho_init(dce);
815 rpc_epmapper_init(dce);