diff options
author | Andrew Tridgell <tridge@samba.org> | 2003-12-13 02:20:40 +0000 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2003-12-13 02:20:40 +0000 |
commit | 340d9b71f9e75d634389104da5949ba59669ede2 (patch) | |
tree | 1bc2c22f57eade574841d8e4d9f3f5a5ff1c1022 /source4/rpc_server | |
parent | f7065cc0a5555a32499908a499f926ede3f7d851 (diff) | |
download | samba-340d9b71f9e75d634389104da5949ba59669ede2.tar.gz samba-340d9b71f9e75d634389104da5949ba59669ede2.tar.bz2 samba-340d9b71f9e75d634389104da5949ba59669ede2.zip |
added a basic dcerpc endpoint mapper to Samba4. Currently only
implements the epm_Lookup() call, I'll add the other important calls
soon. I was rather pleased to find that epm_Lookup() worked first
time, which is particularly surprising given its complexity.
This required quite a bit of new infrastructure:
* a generic way of handling dcerpc policy handles in the rpc server
* added type checked varients of talloc. These are much less error
prone. I'd like to move to using these for nearly all uses of
talloc.
* added more dcerpc fault handling code, and translation from
NTSTATUS to a dcerpc fault code
* added data_blob_talloc_zero() for allocating an initially zero
blob
* added a endpoint enumeration hook in the dcerpc endpoint server
operations
(This used to be commit 3f85f9b782dc17417baf1ca557fcae22f5b6a83a)
Diffstat (limited to 'source4/rpc_server')
-rw-r--r-- | source4/rpc_server/dcerpc_server.c | 64 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_server.h | 31 | ||||
-rw-r--r-- | source4/rpc_server/echo/rpc_echo.c | 8 | ||||
-rw-r--r-- | source4/rpc_server/epmapper/rpc_epmapper.c | 274 | ||||
-rw-r--r-- | source4/rpc_server/handles.c | 92 |
5 files changed, 459 insertions, 10 deletions
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 4711f4e6ff..e2a6ab0132 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -89,12 +89,13 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb, return NT_STATUS_NO_MEMORY; } - *p = talloc(mem_ctx, sizeof(struct dcesrv_state)); + *p = talloc_p(mem_ctx, struct dcesrv_state); if (! *p) { talloc_destroy(mem_ctx); return NT_STATUS_NO_MEMORY; } + (*p)->smb = smb; (*p)->mem_ctx = mem_ctx; (*p)->endpoint = *endpoint; (*p)->ops = ops; @@ -103,6 +104,7 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb, (*p)->cli_max_recv_frag = 0; (*p)->ndr = NULL; (*p)->dispatch = NULL; + (*p)->handles = NULL; /* make sure the endpoint server likes the connection */ status = ops->connect(*p); @@ -121,6 +123,14 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb, void dcesrv_endpoint_disconnect(struct dcesrv_state *p) { p->ops->disconnect(p); + + /* destroy any handles */ + while (p->handles) { + TALLOC_CTX *m = p->handles->mem_ctx; + DLIST_REMOVE(p->handles, p->handles); + talloc_destroy(m); + } + talloc_destroy(p->mem_ctx); } @@ -161,7 +171,7 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code) return status; } - rep = talloc(call->mem_ctx, sizeof(*rep)); + rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply); if (!rep) { return NT_STATUS_NO_MEMORY; } @@ -176,6 +186,22 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code) /* + return a dcerpc fault from a ntstatus code +*/ +static NTSTATUS dcesrv_fault_nt(struct dcesrv_call_state *call, NTSTATUS status) +{ + uint32 fault_code = DCERPC_FAULT_OTHER; + + /* TODO: we need to expand this table to include more mappings */ + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { + fault_code = DCERPC_FAULT_CONTEXT_MISMATCH; + } + + return dcesrv_fault(call, fault_code); +} + + +/* return a dcerpc bind_nak */ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason) @@ -210,7 +236,7 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason) return status; } - rep = talloc(call->mem_ctx, sizeof(*rep)); + rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply); if (!rep) { return NT_STATUS_NO_MEMORY; } @@ -290,7 +316,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) pkt.u.bind_ack.secondary_address = ""; } pkt.u.bind_ack.num_results = 1; - pkt.u.bind_ack.ctx_list = talloc(call->mem_ctx, sizeof(struct dcerpc_ack_ctx)); + pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx); if (!pkt.u.bind_ack.ctx_list) { return NT_STATUS_NO_MEMORY; } @@ -312,7 +338,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) return status; } - rep = talloc(call->mem_ctx, sizeof(*rep)); + rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply); if (!rep) { return NT_STATUS_NO_MEMORY; } @@ -363,7 +389,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) /* 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_NDR); + return dcesrv_fault_nt(call, status); } /* form the reply NDR */ @@ -384,7 +410,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) struct dcesrv_call_reply *rep; struct dcerpc_packet pkt; - rep = talloc(call->mem_ctx, sizeof(*rep)); + rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply); if (!rep) { return NT_STATUS_NO_MEMORY; } @@ -455,7 +481,7 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data) if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } - call = talloc(mem_ctx, sizeof(*call)); + call = talloc_p(mem_ctx, struct dcesrv_call_state); if (!call) { talloc_destroy(mem_ctx); return NT_STATUS_NO_MEMORY; @@ -613,10 +639,32 @@ BOOL dcesrv_table_query(const struct dcerpc_interface_table *table, /* + a useful function for implementing the lookup_endpoints op + */ +int dcesrv_lookup_endpoints(const struct dcerpc_interface_table *table, + TALLOC_CTX *mem_ctx, + struct dcesrv_ep_iface **e) +{ + *e = talloc_p(mem_ctx, struct dcesrv_ep_iface); + if (! *e) { + return -1; + } + + (*e)->uuid = table->uuid; + (*e)->if_version = table->if_version; + (*e)->endpoint.type = ENDPOINT_SMB; + (*e)->endpoint.info.smb_pipe = table->endpoints->names[0]; + + return 1; +} + + +/* initialise the dcerpc server subsystem */ BOOL dcesrv_init(struct server_context *smb) { rpc_echo_init(smb); + rpc_epmapper_init(smb); return True; } diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index 165eddc5e6..3f2f5d039f 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -23,7 +23,8 @@ enum endpoint_type {ENDPOINT_SMB, ENDPOINT_TCP}; -/* a description of a single dcerpc endpoint */ +/* a description of a single dcerpc endpoint. Not as flexible as a full epm tower, + but much easier to work with */ struct dcesrv_endpoint { enum endpoint_type type; union { @@ -32,6 +33,13 @@ struct dcesrv_endpoint { } info; }; +/* a endpoint combined with an interface description */ +struct dcesrv_ep_iface { + struct dcesrv_endpoint endpoint; + const char *uuid; + uint32 if_version; +}; + struct dcesrv_state; /* the dispatch functions for an interface take this form */ @@ -50,8 +58,20 @@ struct dcesrv_call_state { } *replies; }; + +/* a dcerpc handle in internal format */ +struct dcesrv_handle { + struct dcesrv_handle *next, *prev; + struct policy_handle wire_handle; + TALLOC_CTX *mem_ctx; + void *data; +}; + /* the state associated with a dcerpc server connection */ struct dcesrv_state { + /* the top level context for this server */ + struct server_context *smb; + TALLOC_CTX *mem_ctx; /* the endpoint that was opened */ @@ -75,6 +95,11 @@ struct dcesrv_state { /* private data for the endpoint server */ void *private; + + /* current rpc handles - this is really the wrong scope for + them, but it will do for now */ + uint32 next_handle; + struct dcesrv_handle *handles; }; @@ -92,6 +117,10 @@ struct dcesrv_endpoint_ops { /* disconnect() is called when the endpoint is disconnected */ void (*disconnect)(struct dcesrv_state *); + + /* this function is used to ask an endpoint server for a list + of endpoints it wants to handle */ + int (*lookup_endpoints)(TALLOC_CTX *mem_ctx, struct dcesrv_ep_iface **); }; diff --git a/source4/rpc_server/echo/rpc_echo.c b/source4/rpc_server/echo/rpc_echo.c index 42856737c4..f741e63d62 100644 --- a/source4/rpc_server/echo/rpc_echo.c +++ b/source4/rpc_server/echo/rpc_echo.c @@ -163,11 +163,17 @@ static void op_disconnect(struct dcesrv_state *dce) } +static int op_lookup_endpoints(TALLOC_CTX *mem_ctx, struct dcesrv_ep_iface **e) +{ + return dcesrv_lookup_endpoints(&dcerpc_table_rpcecho, mem_ctx, e); +} + static const struct dcesrv_endpoint_ops rpc_echo_ops = { op_query_endpoint, op_set_interface, op_connect, - op_disconnect + op_disconnect, + op_lookup_endpoints }; /* diff --git a/source4/rpc_server/epmapper/rpc_epmapper.c b/source4/rpc_server/epmapper/rpc_epmapper.c new file mode 100644 index 0000000000..945823c013 --- /dev/null +++ b/source4/rpc_server/epmapper/rpc_epmapper.c @@ -0,0 +1,274 @@ +/* + Unix SMB/CIFS implementation. + + endpoint server for the epmapper pipe + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/* handle types for this module */ +enum handle_types {HTYPE_LOOKUP}; + +static NTSTATUS epm_Insert(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, + struct epm_Lookup *r) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS epm_Delete(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, + struct epm_Lookup *r) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/* + implement epm_Lookup. This call is used to enumerate the interfaces + available on a rpc server +*/ +static NTSTATUS epm_Lookup(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, + struct epm_Lookup *r) +{ + struct dcesrv_handle *h; + struct rpc_eps { + uint32 count; + struct dcesrv_ep_iface *e; + } *eps; + uint32 num_ents; + int i; + + h = dcesrv_handle_fetch(dce, r->in.entry_handle, HTYPE_LOOKUP); + if (!h) { + return NT_STATUS_INVALID_HANDLE; + } + + eps = h->data; + + if (!eps) { + /* this is the first call - fill the list. Subsequent calls + will feed from this list, stored in the handle */ + struct dce_endpoint *d; + struct dcesrv_ep_iface *e; + + eps = talloc_p(h->mem_ctx, struct rpc_eps); + if (!eps) { + return NT_STATUS_NO_MEMORY; + } + eps->count = 0; + eps->e = NULL; + h->data = eps; + + for (d=dce->smb->dcesrv.endpoint_list; d; d=d->next) { + int count = d->endpoint_ops->lookup_endpoints(h->mem_ctx, &e); + if (count > 0) { + eps->e = talloc_realloc_p(h->mem_ctx, + eps->e, + struct dcesrv_ep_iface, + eps->count + count); + if (!eps->e) { + return NT_STATUS_NO_MEMORY; + } + memcpy(eps->e + eps->count, e, sizeof(*e) * count); + eps->count += count; + } + } + } + + /* return the next N elements */ + num_ents = r->in.max_ents; + if (num_ents > eps->count) { + num_ents = eps->count; + } + + *r->out.entry_handle = h->wire_handle; + r->out.num_ents = num_ents; + r->out.status = 0; + + if (num_ents == 0) { + r->out.entries = NULL; + return NT_STATUS_OK; + } + + r->out.entries = talloc_array_p(mem_ctx, struct epm_entry_t, num_ents); + if (!r->out.entries) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0;i<num_ents;i++) { + struct epm_twr_t *t; + struct epm_towers *twr; + + ZERO_STRUCT(r->out.entries[i].object); + r->out.entries[i].annotation = ""; + t = talloc_p(mem_ctx, struct epm_twr_t); + if (!twr) { + return NT_STATUS_NO_MEMORY; + } + r->out.entries[i].tower = t; + twr = &t->towers; + twr->num_floors = 5; + twr->floors = talloc_array_p(mem_ctx, struct epm_floor, 5); + if (!twr->floors) { + return NT_STATUS_NO_MEMORY; + } + + twr->floors[0].lhs.protocol = EPM_PROTOCOL_UUID; + GUID_from_string(eps->e[i].uuid, &twr->floors[0].lhs.info.uuid.uuid); + twr->floors[0].lhs.info.uuid.version = eps->e[i].if_version; + twr->floors[0].rhs.rhs_data = data_blob_talloc_zero(mem_ctx, 2); + + /* encoded with NDR ... */ + twr->floors[1].lhs.protocol = EPM_PROTOCOL_UUID; + GUID_from_string(NDR_GUID, &twr->floors[1].lhs.info.uuid.uuid); + twr->floors[1].lhs.info.uuid.version = NDR_GUID_VERSION; + twr->floors[1].rhs.rhs_data = data_blob_talloc_zero(mem_ctx, 2); + + /* on an RPC connection ... */ + twr->floors[2].lhs.protocol = EPM_PROTOCOL_RPC_C; + twr->floors[2].lhs.info.lhs_data = data_blob(NULL, 0); + twr->floors[2].rhs.rhs_data = data_blob_talloc_zero(mem_ctx, 2); + + /* on a SMB pipe ... */ + twr->floors[3].lhs.protocol = EPM_PROTOCOL_SMB; + twr->floors[3].lhs.info.lhs_data = data_blob(NULL, 0); + twr->floors[3].rhs.rhs_data.data = talloc_asprintf(mem_ctx, "\\PIPE\\%s", + eps->e[i].endpoint.info.smb_pipe); + twr->floors[3].rhs.rhs_data.length = strlen(twr->floors[3].rhs.rhs_data.data); + + /* on an NetBIOS link ... */ + twr->floors[4].lhs.protocol = EPM_PROTOCOL_NETBIOS; + twr->floors[4].lhs.info.lhs_data = data_blob(NULL, 0); + twr->floors[4].rhs.rhs_data.data = talloc_asprintf(mem_ctx, "\\\\%s", + lp_netbios_name()); + twr->floors[4].rhs.rhs_data.length = strlen(twr->floors[4].rhs.rhs_data.data); + } + + eps->count -= num_ents; + eps->e += num_ents; + + return NT_STATUS_OK; +} + + +/* + implement epm_Map. This is used to find the specific endpoint to talk to given + a generic protocol tower +*/ +static NTSTATUS epm_Map(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, + struct epm_Lookup *r) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS epm_LookupHandleFree(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, + struct epm_Lookup *r) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS epm_InqObject(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, + struct epm_Lookup *r) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS epm_MgmtDelete(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, + struct epm_Lookup *r) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/************************************************************************** + 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)epm_Insert, + (dcesrv_dispatch_fn_t)epm_Delete, + (dcesrv_dispatch_fn_t)epm_Lookup, + (dcesrv_dispatch_fn_t)epm_Map, + (dcesrv_dispatch_fn_t)epm_LookupHandleFree, + (dcesrv_dispatch_fn_t)epm_InqObject, + (dcesrv_dispatch_fn_t)epm_MgmtDelete +}; + + +/* + return True if we want to handle the given endpoint +*/ +static BOOL op_query_endpoint(const struct dcesrv_endpoint *ep) +{ + return dcesrv_table_query(&dcerpc_table_epmapper, 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_epmapper.uuid) != 0 || + if_version != dcerpc_table_epmapper.if_version) { + DEBUG(2,("Attempt to use unknown interface %s/%d\n", uuid, if_version)); + return False; + } + + dce->ndr = &dcerpc_table_epmapper; + 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) +{ + return NT_STATUS_OK; +} + +static void op_disconnect(struct dcesrv_state *dce) +{ + /* nothing to do */ +} + + +static int op_lookup_endpoints(TALLOC_CTX *mem_ctx, struct dcesrv_ep_iface **e) +{ + return dcesrv_lookup_endpoints(&dcerpc_table_epmapper, mem_ctx, e); +} + + +static const struct dcesrv_endpoint_ops rpc_epmapper_ops = { + op_query_endpoint, + op_set_interface, + op_connect, + op_disconnect, + op_lookup_endpoints +}; + +/* + register with the dcerpc server +*/ +void rpc_epmapper_init(struct server_context *smb) +{ + if (!dcesrv_endpoint_register(smb, &rpc_epmapper_ops)) { + DEBUG(1,("Failed to register epmapper endpoint\n")); + } +} diff --git a/source4/rpc_server/handles.c b/source4/rpc_server/handles.c new file mode 100644 index 0000000000..16e4175184 --- /dev/null +++ b/source4/rpc_server/handles.c @@ -0,0 +1,92 @@ +/* + Unix SMB/CIFS implementation. + + server side dcerpc handle code + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + allocate a new rpc handle +*/ +struct dcesrv_handle *dcesrv_handle_new(struct dcesrv_state *dce, + uint8 handle_type) +{ + TALLOC_CTX *mem_ctx; + struct dcesrv_handle *h; + + mem_ctx = talloc_init("rpc handle type %d\n", handle_type); + if (!mem_ctx) { + return NULL; + } + h = talloc(mem_ctx, sizeof(*h)); + if (!h) { + talloc_destroy(mem_ctx); + return NULL; + } + h->mem_ctx = mem_ctx; + h->data = NULL; + + memset(h->wire_handle.data, 'H', sizeof(h->wire_handle.data)); + strncpy(h->wire_handle.data, dce->ndr->name, 11); + h->wire_handle.data[11] = handle_type; + + /* TODO: check for wraparound here */ + SIVAL(&h->wire_handle.data, 12, random()); + SIVAL(&h->wire_handle.data, 16, dce->next_handle); + dce->next_handle++; + + DLIST_ADD(dce->handles, h); + + return h; +} + +/* + destroy a rpc handle +*/ +void dcesrv_handle_destroy(struct dcesrv_state *dce, + struct dcesrv_handle *h) +{ + DLIST_REMOVE(dce->handles, h); + talloc_destroy(h->mem_ctx); +} + + +/* + find an internal handle given a wire handle. If the wire handle is NULL then + allocate a new handle +*/ +struct dcesrv_handle *dcesrv_handle_fetch(struct dcesrv_state *dce, + struct policy_handle *p, + uint8 handle_type) +{ + struct dcesrv_handle *h; + + if (all_zero(p->data, sizeof(p->data))) { + return dcesrv_handle_new(dce, handle_type); + } + + for (h=dce->handles; h; h=h->next) { + if (memcmp(h->wire_handle.data, p->data, sizeof(p->data)) == 0) { + return h; + } + } + + return NULL; +} |