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 server_context *smb,
29 const struct dcesrv_endpoint *endpoint)
31 struct dce_endpoint *ep;
32 for (ep=smb->dcesrv.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 server_context *smb,
58 const struct dcesrv_endpoint_ops *ops)
60 struct dce_endpoint *ep;
61 ep = malloc(sizeof(*ep));
65 ep->endpoint_ops = ops;
66 DLIST_ADD(smb->dcesrv.endpoint_list, ep);
71 connect to a dcerpc endpoint
73 NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
74 const struct dcesrv_endpoint *endpoint,
75 struct dcesrv_state **p)
79 const struct dcesrv_endpoint_ops *ops;
81 /* make sure this endpoint exists */
82 ops = find_endpoint(smb, endpoint);
84 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
87 mem_ctx = talloc_init("dcesrv_endpoint_connect");
89 return NT_STATUS_NO_MEMORY;
92 *p = talloc_p(mem_ctx, struct dcesrv_state);
94 talloc_destroy(mem_ctx);
95 return NT_STATUS_NO_MEMORY;
99 (*p)->mem_ctx = mem_ctx;
100 (*p)->endpoint = *endpoint;
102 (*p)->private = NULL;
103 (*p)->call_list = NULL;
104 (*p)->cli_max_recv_frag = 0;
106 (*p)->dispatch = NULL;
107 (*p)->handles = NULL;
108 (*p)->next_handle = 0;
110 /* make sure the endpoint server likes the connection */
111 status = ops->connect(*p);
112 if (!NT_STATUS_IS_OK(status)) {
113 talloc_destroy(mem_ctx);
122 disconnect a link to an endpoint
124 void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
126 p->ops->disconnect(p);
128 /* destroy any handles */
130 TALLOC_CTX *m = p->handles->mem_ctx;
131 DLIST_REMOVE(p->handles, p->handles);
135 talloc_destroy(p->mem_ctx);
139 return a dcerpc fault
141 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
143 struct ndr_push *push;
144 struct dcerpc_packet pkt;
145 struct dcesrv_call_reply *rep;
148 /* setup a bind_ack */
150 pkt.rpc_vers_minor = 0;
151 pkt.drep[0] = 0x10; /* Little endian */
156 pkt.call_id = call->pkt.call_id;
157 pkt.ptype = DCERPC_PKT_FAULT;
158 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
159 pkt.u.fault.alloc_hint = 0;
160 pkt.u.fault.context_id = 0;
161 pkt.u.fault.cancel_count = 0;
162 pkt.u.fault.status = fault_code;
164 /* now form the NDR for the fault */
165 push = ndr_push_init_ctx(call->mem_ctx);
167 return NT_STATUS_NO_MEMORY;
170 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
171 if (!NT_STATUS_IS_OK(status)) {
175 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
177 return NT_STATUS_NO_MEMORY;
180 rep->data = ndr_push_blob(push);
181 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
183 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
190 return a dcerpc fault from a ntstatus code
192 static NTSTATUS dcesrv_fault_nt(struct dcesrv_call_state *call, NTSTATUS status)
194 uint32 fault_code = DCERPC_FAULT_OTHER;
196 /* TODO: we need to expand this table to include more mappings */
197 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
198 fault_code = DCERPC_FAULT_CONTEXT_MISMATCH;
201 return dcesrv_fault(call, fault_code);
206 return a dcerpc bind_nak
208 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
210 struct ndr_push *push;
211 struct dcerpc_packet pkt;
212 struct dcesrv_call_reply *rep;
215 /* setup a bind_ack */
217 pkt.rpc_vers_minor = 0;
218 pkt.drep[0] = 0x10; /* Little endian */
223 pkt.call_id = call->pkt.call_id;
224 pkt.ptype = DCERPC_PKT_BIND_NAK;
225 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
226 pkt.u.bind_nak.reject_reason = reason;
227 pkt.u.bind_nak.num_versions = 0;
229 /* now form the NDR for the bind_nak */
230 push = ndr_push_init_ctx(call->mem_ctx);
232 return NT_STATUS_NO_MEMORY;
235 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
236 if (!NT_STATUS_IS_OK(status)) {
240 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
242 return NT_STATUS_NO_MEMORY;
245 rep->data = ndr_push_blob(push);
246 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
248 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
255 handle a bind request
257 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
259 const char *uuid, *transfer_syntax;
260 uint32 if_version, transfer_syntax_version;
261 struct dcerpc_packet pkt;
262 struct ndr_push *push;
263 struct dcesrv_call_reply *rep;
265 uint32 result=0, reason=0;
267 if (call->pkt.u.bind.num_contexts != 1 ||
268 call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
269 return dcesrv_bind_nak(call, 0);
272 if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.major_version;
273 uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
275 return dcesrv_bind_nak(call, 0);
278 transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].major_version;
279 transfer_syntax = GUID_string(call->mem_ctx,
280 &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
281 if (!transfer_syntax ||
282 strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
283 NDR_GUID_VERSION != transfer_syntax_version) {
284 /* we only do NDR encoded dcerpc */
285 return dcesrv_bind_nak(call, 0);
288 if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) {
289 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
290 /* we don't know about that interface */
291 result = DCERPC_BIND_PROVIDER_REJECT;
292 reason = DCERPC_BIND_REASON_ASYNTAX;
295 if (call->dce->cli_max_recv_frag == 0) {
296 call->dce->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
299 /* setup a bind_ack */
301 pkt.rpc_vers_minor = 0;
302 pkt.drep[0] = 0x10; /* Little endian */
307 pkt.call_id = call->pkt.call_id;
308 pkt.ptype = DCERPC_PKT_BIND_ACK;
309 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
310 pkt.u.bind_ack.max_xmit_frag = 0x2000;
311 pkt.u.bind_ack.max_recv_frag = 0x2000;
312 pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
313 if (call->dce->ndr) {
314 pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s",
315 call->dce->ndr->name);
317 pkt.u.bind_ack.secondary_address = "";
319 pkt.u.bind_ack.num_results = 1;
320 pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx);
321 if (!pkt.u.bind_ack.ctx_list) {
322 return NT_STATUS_NO_MEMORY;
324 pkt.u.bind_ack.ctx_list[0].result = result;
325 pkt.u.bind_ack.ctx_list[0].reason = reason;
326 GUID_from_string(uuid, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
327 pkt.u.bind_ack.ctx_list[0].syntax.major_version = if_version;
328 pkt.u.bind_ack.ctx_list[0].syntax.minor_version = 0;
329 pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
331 /* now form the NDR for the bind_ack */
332 push = ndr_push_init_ctx(call->mem_ctx);
334 return NT_STATUS_NO_MEMORY;
337 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
338 if (!NT_STATUS_IS_OK(status)) {
342 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
344 return NT_STATUS_NO_MEMORY;
347 rep->data = ndr_push_blob(push);
348 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
350 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
357 handle a dcerpc request packet
359 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
361 struct ndr_pull *pull;
362 struct ndr_push *push;
368 opnum = call->pkt.u.request.opnum;
370 if (opnum >= call->dce->ndr->num_calls) {
371 return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
374 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
376 return NT_STATUS_NO_MEMORY;
379 r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size);
381 return NT_STATUS_NO_MEMORY;
384 /* unravel the NDR for the packet */
385 status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
386 if (!NT_STATUS_IS_OK(status)) {
387 return dcesrv_fault(call, DCERPC_FAULT_NDR);
390 /* call the dispatch function */
391 status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
392 if (!NT_STATUS_IS_OK(status)) {
393 return dcesrv_fault_nt(call, status);
396 /* form the reply NDR */
397 push = ndr_push_init_ctx(call->mem_ctx);
399 return NT_STATUS_NO_MEMORY;
402 status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
403 if (!NT_STATUS_IS_OK(status)) {
404 return dcesrv_fault(call, DCERPC_FAULT_NDR);
407 stub = ndr_push_blob(push);
411 struct dcesrv_call_reply *rep;
412 struct dcerpc_packet pkt;
414 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
416 return NT_STATUS_NO_MEMORY;
419 length = stub.length;
420 if (length + DCERPC_RESPONSE_LENGTH > call->dce->cli_max_recv_frag) {
421 length = call->dce->cli_max_recv_frag - DCERPC_RESPONSE_LENGTH;
424 /* form the dcerpc response packet */
426 pkt.rpc_vers_minor = 0;
427 pkt.drep[0] = 0x10; /* Little endian */
432 pkt.call_id = call->pkt.call_id;
433 pkt.ptype = DCERPC_PKT_RESPONSE;
435 if (!call->replies) {
436 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
438 if (length == stub.length) {
439 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
441 pkt.u.response.alloc_hint = stub.length;
442 pkt.u.response.context_id = call->pkt.u.request.context_id;
443 pkt.u.response.cancel_count = 0;
444 pkt.u.response.stub_and_verifier.data = stub.data;
445 pkt.u.response.stub_and_verifier.length = length;
447 push = ndr_push_init_ctx(call->mem_ctx);
449 return NT_STATUS_NO_MEMORY;
452 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
453 if (!NT_STATUS_IS_OK(status)) {
457 rep->data = ndr_push_blob(push);
458 SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length);
460 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
463 stub.length -= length;
464 } while (stub.length != 0);
471 provide some input to a dcerpc endpoint server. This passes data
472 from a dcerpc client into the server
474 NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
476 struct ndr_pull *ndr;
479 struct dcesrv_call_state *call;
481 mem_ctx = talloc_init("dcesrv_input");
483 return NT_STATUS_NO_MEMORY;
485 call = talloc_p(mem_ctx, struct dcesrv_call_state);
487 talloc_destroy(mem_ctx);
488 return NT_STATUS_NO_MEMORY;
490 call->mem_ctx = mem_ctx;
492 call->replies = NULL;
494 ndr = ndr_pull_init_blob(data, mem_ctx);
496 talloc_destroy(mem_ctx);
497 return NT_STATUS_NO_MEMORY;
500 status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
501 if (!NT_STATUS_IS_OK(status)) {
502 talloc_destroy(mem_ctx);
506 /* see if this is a continued packet */
507 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
508 struct dcesrv_call_state *call2 = call;
511 /* we only allow fragmented requests, no other packet types */
512 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
513 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
516 /* this is a continuation of an existing call - find the call then
517 tack it on the end */
518 call = dcesrv_find_call(dce, call2->pkt.call_id);
520 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
523 if (call->pkt.ptype != call2->pkt.ptype) {
524 /* trying to play silly buggers are we? */
525 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
528 alloc_size = call->pkt.u.request.stub_and_verifier.length +
529 call2->pkt.u.request.stub_and_verifier.length;
530 if (call->pkt.u.request.alloc_hint > alloc_size) {
531 alloc_size = call->pkt.u.request.alloc_hint;
534 call->pkt.u.request.stub_and_verifier.data =
535 talloc_realloc(call->mem_ctx,
536 call->pkt.u.request.stub_and_verifier.data, alloc_size);
537 if (!call->pkt.u.request.stub_and_verifier.data) {
538 return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
540 memcpy(call->pkt.u.request.stub_and_verifier.data +
541 call->pkt.u.request.stub_and_verifier.length,
542 call2->pkt.u.request.stub_and_verifier.data,
543 call2->pkt.u.request.stub_and_verifier.length);
544 call->pkt.u.request.stub_and_verifier.length +=
545 call2->pkt.u.request.stub_and_verifier.length;
547 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
550 /* this may not be the last pdu in the chain - if its isn't then
551 just put it on the call_list and wait for the rest */
552 if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
553 DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
557 switch (call->pkt.ptype) {
558 case DCERPC_PKT_BIND:
559 status = dcesrv_bind(call);
561 case DCERPC_PKT_REQUEST:
562 status = dcesrv_request(call);
565 status = NT_STATUS_INVALID_PARAMETER;
569 /* if we are going to be sending a reply then add
570 it to the list of pending calls. We add it to the end to keep the call
571 list in the order we will answer */
572 if (NT_STATUS_IS_OK(status)) {
573 DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
575 talloc_destroy(mem_ctx);
582 retrieve some output from a dcerpc server. The amount of data that
583 is wanted is in data->length and data->data is already allocated
584 to hold that much data.
586 NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
588 struct dcesrv_call_state *call;
589 struct dcesrv_call_reply *rep;
591 call = dce->call_list;
592 if (!call || !call->replies) {
593 return NT_STATUS_FOOBAR;
597 if (data->length >= rep->data.length) {
598 data->length = rep->data.length;
601 memcpy(data->data, rep->data.data, data->length);
602 rep->data.length -= data->length;
603 rep->data.data += data->length;
605 if (rep->data.length == 0) {
606 /* we're done with this section of the call */
607 DLIST_REMOVE(call->replies, rep);
610 if (call->replies == NULL) {
611 /* we're done with the whole call */
612 DLIST_REMOVE(dce->call_list, call);
613 talloc_destroy(call->mem_ctx);
621 a useful function for implementing the query endpoint op
623 BOOL dcesrv_table_query(const struct dcerpc_interface_table *table,
624 const struct dcesrv_endpoint *ep)
627 const struct dcerpc_endpoint_list *endpoints = table->endpoints;
629 if (ep->type != ENDPOINT_SMB) {
633 for (i=0;i<endpoints->count;i++) {
634 if (strcasecmp(ep->info.smb_pipe, endpoints->names[i]) == 0) {
643 a useful function for implementing the lookup_endpoints op
645 int dcesrv_lookup_endpoints(const struct dcerpc_interface_table *table,
647 struct dcesrv_ep_iface **e)
649 *e = talloc_p(mem_ctx, struct dcesrv_ep_iface);
654 (*e)->uuid = table->uuid;
655 (*e)->if_version = table->if_version;
656 (*e)->endpoint.type = ENDPOINT_SMB;
657 (*e)->endpoint.info.smb_pipe = table->endpoints->names[0];
664 initialise the dcerpc server subsystem
666 BOOL dcesrv_init(struct server_context *smb)
669 rpc_epmapper_init(smb);