summaryrefslogtreecommitdiff
path: root/source4/rpc_server
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2003-12-12 03:59:09 +0000
committerAndrew Tridgell <tridge@samba.org>2003-12-12 03:59:09 +0000
commit16309de71d6c8de96e869aeaab0b879185991d87 (patch)
treed122fe6bd25cd87df5422b0af661c9e93db31ef8 /source4/rpc_server
parentfcc4efd1ea637c810eed8444080b87d7f92c837a (diff)
downloadsamba-16309de71d6c8de96e869aeaab0b879185991d87.tar.gz
samba-16309de71d6c8de96e869aeaab0b879185991d87.tar.bz2
samba-16309de71d6c8de96e869aeaab0b879185991d87.zip
* the RPC-ECHO pipe now works in smbd, as long as the data sizes
don't cause fragmented pdus (I'll add fragments shortly) * change data_blob_talloc() to not zero memory when the 2nd argument is NULL. The zeroing just masks bugs, and can't even allow a DOS attack * modified pidl to ensure that [ref] arguments to the out side of functions are allocated when parsing the in side. This allows rpc backends to assume that [ref] variables are all setup. Doesn't work correctly for [ref] arrays yet * changed DLIST_ADD_END() to take the type instead of a tmp variable. This means you don't need to declare a silly tmp variable in the caller (This used to be commit 46e0a358198eeb9af1907ee2a29025d3ab23b6d1)
Diffstat (limited to 'source4/rpc_server')
-rw-r--r--source4/rpc_server/dcerpc_server.c308
-rw-r--r--source4/rpc_server/dcerpc_server.h31
-rw-r--r--source4/rpc_server/rpc_echo.c79
3 files changed, 408 insertions, 10 deletions
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
index e1d6da2292..f5e7ff858e 100644
--- a/source4/rpc_server/dcerpc_server.c
+++ b/source4/rpc_server/dcerpc_server.c
@@ -30,7 +30,7 @@ static const struct dcesrv_endpoint_ops *find_endpoint(struct server_context *sm
{
struct dce_endpoint *ep;
for (ep=smb->dcesrv.endpoint_list; ep; ep=ep->next) {
- if (ep->endpoint_ops->query(endpoint)) {
+ if (ep->endpoint_ops->query_endpoint(endpoint)) {
return ep->endpoint_ops;
}
}
@@ -86,6 +86,7 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
(*p)->endpoint = *endpoint;
(*p)->ops = ops;
(*p)->private = NULL;
+ (*p)->call_list = NULL;
/* make sure the endpoint server likes the connection */
status = ops->connect(*p);
@@ -107,23 +108,318 @@ void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
talloc_destroy(p->mem_ctx);
}
+/*
+ return a dcerpc fault
+*/
+static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
+{
+ struct ndr_push *push;
+ struct dcerpc_packet pkt;
+ NTSTATUS status;
+
+ /* setup a bind_ack */
+ pkt.rpc_vers = 5;
+ pkt.rpc_vers_minor = 0;
+ pkt.drep[0] = 0x10; /* Little endian */
+ pkt.drep[1] = 0;
+ pkt.drep[2] = 0;
+ pkt.drep[3] = 0;
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_FAULT;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.fault.alloc_hint = 0;
+ pkt.u.fault.context_id = 0;
+ pkt.u.fault.cancel_count = 0;
+ pkt.u.fault.status = fault_code;
+
+ /* now form the NDR for the bind_ack */
+ push = ndr_push_init_ctx(call->mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ call->data = ndr_push_blob(push);
+ SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a bind request
+*/
+static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
+{
+ const char *uuid, *transfer_syntax;
+ uint32 if_version, transfer_syntax_version;
+ struct dcerpc_packet pkt;
+ struct ndr_push *push;
+ NTSTATUS status;
+
+ if (call->pkt.u.bind.num_contexts != 1 ||
+ call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.major_version;
+ uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
+ if (!uuid) {
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].major_version;
+ transfer_syntax = GUID_string(call->mem_ctx,
+ &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
+ if (!transfer_syntax ||
+ strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
+ NDR_GUID_VERSION != transfer_syntax_version) {
+ /* we only do NDR encoded dcerpc */
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) {
+ DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
+ /* we don't know about that interface */
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ /* setup a bind_ack */
+ pkt.rpc_vers = 5;
+ pkt.rpc_vers_minor = 0;
+ pkt.drep[0] = 0x10; /* Little endian */
+ pkt.drep[1] = 0;
+ pkt.drep[2] = 0;
+ pkt.drep[3] = 0;
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_BIND_ACK;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.bind_ack.max_xmit_frag = 0x2000;
+ pkt.u.bind_ack.max_recv_frag = 0x2000;
+ pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
+ pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s",
+ call->dce->ndr->name);
+ pkt.u.bind_ack.num_results = 1;
+ pkt.u.bind_ack.ctx_list = talloc(call->mem_ctx, sizeof(struct dcerpc_ack_ctx));
+ if (!pkt.u.bind_ack.ctx_list) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pkt.u.bind_ack.ctx_list[0].result = 0;
+ pkt.u.bind_ack.ctx_list[0].reason = 0;
+ GUID_from_string(uuid, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
+ pkt.u.bind_ack.ctx_list[0].syntax.major_version = if_version;
+ pkt.u.bind_ack.ctx_list[0].syntax.minor_version = 0;
+ pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
+
+ /* now form the NDR for the bind_ack */
+ push = ndr_push_init_ctx(call->mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ call->data = ndr_push_blob(push);
+ SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a dcerpc request packet
+*/
+static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
+{
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ uint16 opnum;
+ void *r;
+ NTSTATUS status;
+ DATA_BLOB stub;
+ struct dcerpc_packet pkt;
+
+ if (call->pkt.pfc_flags != (DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST)) {
+ /* we don't do fragments in the server yet */
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ opnum = call->pkt.u.request.opnum;
+
+ if (opnum >= call->dce->ndr->num_calls) {
+ return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
+ }
+
+ pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size);
+ if (!r) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* unravel the NDR for the packet */
+ status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ /* call the dispatch function */
+ status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ /* form the reply NDR */
+ push = ndr_push_init_ctx(call->mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, DCERPC_FAULT_TODO);
+ }
+
+ stub = ndr_push_blob(push);
+
+ /* form the dcerpc response packet */
+ pkt.rpc_vers = 5;
+ pkt.rpc_vers_minor = 0;
+ pkt.drep[0] = 0x10; /* Little endian */
+ pkt.drep[1] = 0;
+ pkt.drep[2] = 0;
+ pkt.drep[3] = 0;
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_RESPONSE;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.response.alloc_hint = stub.length;
+ pkt.u.response.context_id = call->pkt.u.request.context_id;
+ pkt.u.response.cancel_count = 0;
+ pkt.u.response.stub_and_verifier = stub;
+
+ push = ndr_push_init_ctx(call->mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ call->data = ndr_push_blob(push);
+ SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length);
+
+ return NT_STATUS_OK;
+}
+
/*
provide some input to a dcerpc endpoint server. This passes data
from a dcerpc client into the server
*/
-NTSTATUS dcesrv_input(struct dcesrv_state *p, const DATA_BLOB *data)
+NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
{
- return NT_STATUS_NOT_IMPLEMENTED;
+ struct ndr_pull *ndr;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct dcesrv_call_state *call;
+
+ mem_ctx = talloc_init("dcesrv_input");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ call = talloc(mem_ctx, sizeof(*call));
+ if (!call) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ call->mem_ctx = mem_ctx;
+ call->dce = dce;
+
+ ndr = ndr_pull_init_blob(data, mem_ctx);
+ if (!ndr) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_destroy(mem_ctx);
+ return status;
+ }
+
+ /* TODO: at this point we should see if the packet is a
+ continuation of an existing call, but I'm too lazy for that
+ right now ... maybe tomorrow */
+
+
+ switch (call->pkt.ptype) {
+ case DCERPC_PKT_BIND:
+ status = dcesrv_bind(call);
+ break;
+ case DCERPC_PKT_REQUEST:
+ status = dcesrv_request(call);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ /* if we are going to be sending a reply then add
+ it to the list of pending calls. We add it to the end to keep the call
+ list in the order we will answer */
+ if (NT_STATUS_IS_OK(status)) {
+ DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
+ } else {
+ talloc_destroy(mem_ctx);
+ }
+
+ return status;
}
/*
retrieve some output from a dcerpc server. The amount of data that
- is wanted is in data->length
+ is wanted is in data->length and data->data is already allocated
+ to hold that much data.
*/
-NTSTATUS dcesrv_output(struct dcesrv_state *p, DATA_BLOB *data)
+NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
{
- return NT_STATUS_NOT_IMPLEMENTED;
+ struct dcesrv_call_state *call;
+
+ call = dce->call_list;
+ if (!call) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (data->length >= call->data.length) {
+ data->length = call->data.length;
+ }
+
+ memcpy(data->data, call->data.data, data->length);
+ call->data.length -= data->length;
+ call->data.data += data->length;
+
+ if (call->data.length == 0) {
+ /* we're done with this call */
+ DLIST_REMOVE(dce->call_list, call);
+ talloc_destroy(call->mem_ctx);
+ }
+
+ return NT_STATUS_OK;
}
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h
index b28f40768a..9895254cad 100644
--- a/source4/rpc_server/dcerpc_server.h
+++ b/source4/rpc_server/dcerpc_server.h
@@ -32,6 +32,19 @@ struct dcesrv_endpoint {
} info;
};
+struct dcesrv_state;
+
+/* the dispatch functions for an interface take this form */
+typedef NTSTATUS (*dcesrv_dispatch_fn_t)(struct dcesrv_state *, TALLOC_CTX *, void *);
+
+/* the state of an ongoing dcerpc call */
+struct dcesrv_call_state {
+ struct dcesrv_call_state *next, *prev;
+ struct dcesrv_state *dce;
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_packet pkt;
+ DATA_BLOB data;
+};
/* the state associated with a dcerpc server connection */
struct dcesrv_state {
@@ -43,15 +56,29 @@ struct dcesrv_state {
/* endpoint operations provided by the endpoint server */
const struct dcesrv_endpoint_ops *ops;
+ /* the ndr function table for the chosen interface */
+ const struct dcerpc_interface_table *ndr;
+
+ /* the dispatch table for the chosen interface. Must contain
+ enough entries for all entries in the ndr table */
+ const dcesrv_dispatch_fn_t *dispatch;
+
+ /* the state of the current calls */
+ struct dcesrv_call_state *call_list;
+
/* private data for the endpoint server */
void *private;
};
struct dcesrv_endpoint_ops {
- /* the query function is used to ask an endpoint server if it
+ /* this function is used to ask an endpoint server if it
handles a particular endpoint */
- BOOL (*query)(const struct dcesrv_endpoint *);
+ BOOL (*query_endpoint)(const struct dcesrv_endpoint *);
+
+ /* this function sets up the dispatch table for this
+ connection */
+ BOOL (*set_interface)(struct dcesrv_state *, const char *, uint32);
/* connect() is called when a connection is made to an endpoint */
NTSTATUS (*connect)(struct dcesrv_state *);
diff --git a/source4/rpc_server/rpc_echo.c b/source4/rpc_server/rpc_echo.c
index 51914f7946..37b72f764b 100644
--- a/source4/rpc_server/rpc_echo.c
+++ b/source4/rpc_server/rpc_echo.c
@@ -23,20 +23,94 @@
#include "includes.h"
+static NTSTATUS echo_AddOne(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_AddOne *r)
+{
+ *r->out.v = *r->in.v + 1;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS echo_EchoData(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_EchoData *r)
+{
+ r->out.out_data = talloc(mem_ctx, r->in.len);
+ if (!r->out.out_data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(r->out.out_data, r->in.in_data, r->in.len);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS echo_SinkData(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_SinkData *r)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS echo_SourceData(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_SourceData *r)
+{
+ int i;
+ r->out.data = talloc(mem_ctx, r->in.len);
+ if (!r->out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<r->in.len;i++) {
+ r->out.data[i] = i;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS echo_TestCall(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct TestCall *r)
+{
+ return NT_STATUS_BAD_NETWORK_NAME;
+}
+
+static NTSTATUS echo_TestCall2(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct TestCall2 *r)
+{
+ return NT_STATUS_BAD_NETWORK_NAME;
+}
+
+
+
/**************************************************************************
all the code below this point is boilerplate that will be auto-generated
***************************************************************************/
+static const dcesrv_dispatch_fn_t dispatch_table[] = {
+ (dcesrv_dispatch_fn_t)echo_AddOne,
+ (dcesrv_dispatch_fn_t)echo_EchoData,
+ (dcesrv_dispatch_fn_t)echo_SinkData,
+ (dcesrv_dispatch_fn_t)echo_SourceData,
+ (dcesrv_dispatch_fn_t)echo_TestCall,
+ (dcesrv_dispatch_fn_t)echo_TestCall2
+};
+
/*
return True if we want to handle the given endpoint
*/
-static BOOL op_query(const struct dcesrv_endpoint *ep)
+static BOOL op_query_endpoint(const struct dcesrv_endpoint *ep)
{
return dcesrv_table_query(&dcerpc_table_rpcecho, ep);
}
+/*
+ setup for a particular rpc interface
+*/
+static BOOL op_set_interface(struct dcesrv_state *dce, const char *uuid, uint32 if_version)
+{
+ if (strcasecmp(uuid, dcerpc_table_rpcecho.uuid) != 0 ||
+ if_version != dcerpc_table_rpcecho.if_version) {
+ DEBUG(2,("Attempt to use unknown interface %s/%d\n", uuid, if_version));
+ return False;
+ }
+
+ dce->ndr = &dcerpc_table_rpcecho;
+ dce->dispatch = dispatch_table;
+
+ return True;
+}
+
/* op_connect is called when a connection is made to an endpoint */
static NTSTATUS op_connect(struct dcesrv_state *dce)
@@ -51,7 +125,8 @@ static void op_disconnect(struct dcesrv_state *dce)
static const struct dcesrv_endpoint_ops rpc_echo_ops = {
- op_query,
+ op_query_endpoint,
+ op_set_interface,
op_connect,
op_disconnect
};