diff options
Diffstat (limited to 'source3/rpc_server/epmapper')
-rw-r--r-- | source3/rpc_server/epmapper/srv_epmapper.c | 1063 |
1 files changed, 1063 insertions, 0 deletions
diff --git a/source3/rpc_server/epmapper/srv_epmapper.c b/source3/rpc_server/epmapper/srv_epmapper.c new file mode 100644 index 0000000..cf6b268 --- /dev/null +++ b/source3/rpc_server/epmapper/srv_epmapper.c @@ -0,0 +1,1063 @@ +/* + Unix SMB/CIFS implementation. + + Endpoint server for the epmapper pipe + + Copyright (C) 2010-2011 Andreas Schneider <asn@samba.org> + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "ntdomain.h" +#include "../libcli/security/security.h" +#include "../lib/tsocket/tsocket.h" +#include "auth.h" + +#include "librpc/rpc/dcesrv_core.h" +#include "librpc/gen_ndr/ndr_epmapper.h" +#include "librpc/gen_ndr/ndr_epmapper_scompat.h" +#include "rpc_server/rpc_server.h" +#include "lib/tdb_wrap/tdb_wrap.h" +#include "lib/util/util_tdb.h" +#include "lib/util/strv.h" + +static struct tdb_wrap *epmdb = NULL; + +/* handle types for this module */ +enum handle_types {HTYPE_LOOKUP}; + +typedef uint32_t error_status_t; + +/* An endpoint combined with an interface description */ +struct dcesrv_ep_iface { + const char *name; + struct ndr_syntax_id syntax_id; + struct epm_tower ep; +}; + +/* A rpc service interface like samr, lsarpc or netlogon */ +struct dcesrv_iface { + const char *name; + struct ndr_syntax_id syntax_id; +}; + +struct dcesrv_iface_list { + struct dcesrv_iface_list *next, *prev; + struct dcesrv_iface *iface; +}; + +/* + * An endpoint can serve multiple rpc services interfaces. + * For example \\pipe\netlogon can be used by lsarpc and netlogon. + */ +struct dcesrv_epm_endpoint { + struct dcesrv_epm_endpoint *next, *prev; + + /* The type and the location of the endpoint */ + struct dcerpc_binding *ep_description; + + /* A list of rpc services able to connect to the endpoint */ + struct dcesrv_iface_list *iface_list; +}; + +struct rpc_eps { + struct dcesrv_ep_iface *e; + uint32_t count; +}; + +struct build_ep_list_state { + const struct GUID *uuid; + const char *srv_addr; + TALLOC_CTX *mem_ctx; + struct dcesrv_ep_iface *ifaces; +}; + +static bool build_ep_list_fill_iface( + TALLOC_CTX *mem_ctx, + const struct ndr_syntax_id *syntax_id, + const char *endpoint, + const char *name, + const char *srv_addr, + struct dcesrv_ep_iface *dst) +{ + struct dcesrv_ep_iface iface = { + .syntax_id = *syntax_id, + }; + struct dcerpc_binding *binding = NULL; + enum dcerpc_transport_t transport; + char *name_dup = NULL; + const char *host_addr = NULL; + NTSTATUS status; + + /* copy without const for error path TALLOC_FREE */ + name_dup = talloc_strdup(mem_ctx, name); + if (name_dup == NULL) { + goto fail; + } + iface.name = name_dup; + + status = dcerpc_parse_binding(mem_ctx, endpoint, &binding); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_parse_binding failed: %s\n", + nt_errstr(status)); + goto fail; + } + + status = dcerpc_binding_set_abstract_syntax(binding, syntax_id); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_binding_set_abstract_syntax failed: %s\n", + nt_errstr(status)); + goto fail; + } + + transport = dcerpc_binding_get_transport(binding); + if (transport == NCACN_IP_TCP) { + const char *host = NULL; + + host = dcerpc_binding_get_string_option(binding, "host"); + if (host == NULL) { + host_addr = srv_addr; + } else if (!is_ipaddress_v4(host)) { + host_addr = srv_addr; + } else if (strcmp(host, "0.0.0.0") == 0) { + host_addr = srv_addr; + } + } + + if (host_addr != NULL) { + status = dcerpc_binding_set_string_option( + binding, "host", host_addr); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_binding_set_string_option " + "failed: %s\n", + nt_errstr(status)); + goto fail; + } + } + + status = dcerpc_binding_build_tower(mem_ctx, binding, &iface.ep); + TALLOC_FREE(binding); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_binding_build_tower failed: %s\n", + nt_errstr(status)); + goto fail; + } + + *dst = iface; + return true; + +fail: + TALLOC_FREE(binding); + TALLOC_FREE(name_dup); + TALLOC_FREE(iface.ep.floors); + return false; +} + +static int build_ep_list_fn( + struct tdb_context *tdb, + TDB_DATA key, + TDB_DATA value, + void *private_data) +{ + struct build_ep_list_state *state = private_data; + struct ndr_syntax_id syntax_id = { .if_version = 0 }; + const char *name = NULL; + char *endpoints = NULL; + const char *endpoint = NULL; + bool ok; + + if ((key.dsize == 0) || (key.dptr[key.dsize-1] != '\0') || + (value.dsize == 0) || (value.dptr[value.dsize-1] != '\0')) { + DBG_DEBUG("Invalid record\n"); + return 0; + } + + ok = ndr_syntax_id_from_string((char *)key.dptr, &syntax_id); + if (!ok) { + DBG_DEBUG("Invalid interface: %s\n", (char *)key.dptr); + return 0; + } + + endpoints = (char *)value.dptr; + endpoint = endpoints; + name = endpoints; + + while ((endpoint = strv_len_next(endpoints, value.dsize, endpoint))) { + size_t num_ifaces = talloc_array_length(state->ifaces); + struct dcesrv_ep_iface *tmp = NULL; + + if (num_ifaces+1 < num_ifaces) { + return 1; + } + + tmp = talloc_realloc( + state->mem_ctx, + state->ifaces, + struct dcesrv_ep_iface, + num_ifaces+1); + if (tmp == NULL) { + return 1; + } + state->ifaces = tmp; + + ok = build_ep_list_fill_iface( + state->ifaces, + &syntax_id, + endpoint, + name, + state->srv_addr, + &state->ifaces[num_ifaces]); + if (!ok) { + state->ifaces = talloc_realloc( + state->mem_ctx, + state->ifaces, + struct dcesrv_ep_iface, + num_ifaces); + } + } + + return 0; +} + +/* + * Build a list of all interfaces handled by all endpoint servers. + */ +static uint32_t build_ep_list(TALLOC_CTX *mem_ctx, + const struct GUID *uuid, + const char *srv_addr, + struct dcesrv_ep_iface **peps) +{ + struct build_ep_list_state state = { + .mem_ctx = mem_ctx, .uuid = uuid, .srv_addr = srv_addr, + }; + int ret; + + ret = tdb_traverse_read(epmdb->tdb, build_ep_list_fn, &state); + if (ret == -1) { + DBG_DEBUG("tdb_traverse_read failed\n"); + return 0; + } + + *peps = state.ifaces; + return talloc_array_length(*peps); +} + +/* + * epm_Insert + * + * Add the specified entries to an endpoint map. + */ +error_status_t _epm_Insert(struct pipes_struct *p, + struct epm_Insert *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return EPMAPPER_STATUS_CANT_PERFORM_OP; +} + +/* + * epm_Delete + * + * Delete the specified entries from an endpoint map. + */ +error_status_t _epm_Delete(struct pipes_struct *p, + struct epm_Delete *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return EPMAPPER_STATUS_CANT_PERFORM_OP; +} + +/* + * epm_Lookup + * + * Lookup entries in an endpoint map. + */ +error_status_t _epm_Lookup(struct pipes_struct *p, + struct epm_Lookup *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct dcesrv_connection *dcesrv_conn = dce_call->conn; + struct policy_handle *entry_handle; + struct rpc_eps *eps; + TALLOC_CTX *tmp_ctx; + error_status_t rc; + uint32_t count = 0; + uint32_t num_ents = 0; + uint32_t i; + bool match = false; + bool ok; + NTSTATUS status; + + *r->out.num_ents = 0; + r->out.entries = NULL; + + tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return EPMAPPER_STATUS_NO_MEMORY; + } + + DEBUG(5, ("_epm_Lookup: Trying to lookup max. %u entries.\n", + r->in.max_ents)); + + if (r->in.entry_handle == NULL || + ndr_policy_handle_empty(r->in.entry_handle)) { + const struct tsocket_address *local_address = + dcesrv_connection_get_local_address(dcesrv_conn); + char *srv_addr = NULL; + + DEBUG(7, ("_epm_Lookup: No entry_handle found, creating it.\n")); + + eps = talloc_zero(tmp_ctx, struct rpc_eps); + if (eps == NULL) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + + if (local_address != NULL && + tsocket_address_is_inet(local_address, "ipv4")) + { + srv_addr = tsocket_address_inet_addr_string( + local_address, tmp_ctx); + } + + switch (r->in.inquiry_type) { + case RPC_C_EP_ALL_ELTS: + /* + * Return all elements from the endpoint map. The + * interface_id, vers_option, and object parameters MUST + * be ignored. + */ + eps->count = build_ep_list(eps, + NULL, + srv_addr, + &eps->e); + break; + case RPC_C_EP_MATCH_BY_IF: + /* + * Return endpoint map elements that contain the + * interface identifier specified by the interface_id + * and vers_option values. + * + * RPC_C_EP_MATCH_BY_IF and RPC_C_EP_MATCH_BY_BOTH + * need both the same endpoint list. There is a second + * check for the inquiry_type below which differentiates + * between them. + */ + case RPC_C_EP_MATCH_BY_BOTH: + /* + * Return endpoint map elements that contain the + * interface identifier and object UUID specified by + * interface_id, vers_option, and object. + */ + eps->count = build_ep_list(eps, + &r->in.interface_id->uuid, + srv_addr, + &eps->e); + break; + case RPC_C_EP_MATCH_BY_OBJ: + /* + * Return endpoint map elements that contain the object + * UUID specified by object. + */ + eps->count = build_ep_list(eps, + r->in.object, + srv_addr, + &eps->e); + break; + default: + rc = EPMAPPER_STATUS_CANT_PERFORM_OP; + goto done; + } + + if (eps->count == 0) { + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + ok = create_policy_hnd(p, r->out.entry_handle, HTYPE_LOOKUP, eps); + if (!ok) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + + eps = find_policy_by_hnd(p, + r->out.entry_handle, + HTYPE_LOOKUP, + struct rpc_eps, + &status); + if (!NT_STATUS_IS_OK(status)) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + entry_handle = r->out.entry_handle; + } else { + DEBUG(7, ("_epm_Lookup: Trying to find entry_handle.\n")); + + eps = find_policy_by_hnd(p, + r->in.entry_handle, + HTYPE_LOOKUP, + struct rpc_eps, + &status); + if (!NT_STATUS_IS_OK(status)) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + entry_handle = r->in.entry_handle; + } + + if (eps == NULL || eps->e == NULL) { + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + /* return the next N elements */ + count = r->in.max_ents; + if (count > eps->count) { + count = eps->count; + } + + DEBUG(5, ("_epm_Lookup: Find %u entries\n", count)); + + if (count == 0) { + close_policy_hnd(p, entry_handle); + ZERO_STRUCTP(r->out.entry_handle); + + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + r->out.entries = talloc_array(p->mem_ctx, struct epm_entry_t, count); + if (r->out.entries == NULL) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + + for (i = 0; i < count; i++) { + match = false; + + switch (r->in.inquiry_type) { + case RPC_C_EP_ALL_ELTS: + /* + * Return all elements from the endpoint map. The + * interface_id, vers_option, and object parameters MUST + * be ignored. + */ + match = true; + break; + case RPC_C_EP_MATCH_BY_IF: + /* + * Return endpoint map elements that contain the + * interface identifier specified by the interface_id + * and vers_option values. + */ + if (GUID_equal(&r->in.interface_id->uuid, + &eps->e[i].syntax_id.uuid)) { + match = true; + } + break; + case RPC_C_EP_MATCH_BY_OBJ: + /* + * Return endpoint map elements that contain the object + * UUID specified by object. + */ + if (GUID_equal(r->in.object, + &eps->e[i].syntax_id.uuid)) { + match = true; + } + break; + case RPC_C_EP_MATCH_BY_BOTH: + /* + * Return endpoint map elements that contain the + * interface identifier and object UUID specified by + * interface_id, vers_option, and object. + */ + if (GUID_equal(&r->in.interface_id->uuid, + &eps->e[i].syntax_id.uuid) && + GUID_equal(r->in.object, &eps->e[i].syntax_id.uuid)) { + match = true; + } + break; + default: + return EPMAPPER_STATUS_CANT_PERFORM_OP; + } + + if (match) { + if (r->in.inquiry_type == RPC_C_EP_MATCH_BY_IF || + r->in.inquiry_type == RPC_C_EP_MATCH_BY_OBJ) { + /* Check interface version */ + + match = false; + switch (r->in.vers_option) { + case RPC_C_VERS_ALL: + /* + * Return endpoint map elements that + * contain the specified interface UUID, + * regardless of the version numbers. + */ + match = true; + break; + case RPC_C_VERS_COMPATIBLE: + /* + * Return the endpoint map elements that + * contain the same major versions of + * the specified interface UUID and a + * minor version greater than or equal + * to the minor version of the specified + * UUID. + */ + if (r->in.interface_id->vers_major == + (eps->e[i].syntax_id.if_version >> 16) && + r->in.interface_id->vers_minor <= + (eps->e[i].syntax_id.if_version & 0xFFFF)) { + match = true; + } + break; + case RPC_C_VERS_EXACT: + /* + * Return endpoint map elements that + * contain the specified version of the + * specified interface UUID. + */ + if (r->in.interface_id->vers_major == + (eps->e[i].syntax_id.if_version >> 16) && + r->in.interface_id->vers_minor == + (eps->e[i].syntax_id.if_version & 0xFFFF)) { + match = true; + } + match = true; + break; + case RPC_C_VERS_MAJOR_ONLY: + /* + * Return endpoint map elements that + * contain the same version of the + * specified interface UUID and ignore + * the minor version. + */ + if (r->in.interface_id->vers_major == + (eps->e[i].syntax_id.if_version >> 16)) { + match = true; + } + match = true; + break; + case RPC_C_VERS_UPTO: + /* + * Return endpoint map elements that + * contain a version of the specified + * interface UUID less than or equal to + * the specified major and minor + * version. + */ + if (r->in.interface_id->vers_major > + eps->e[i].syntax_id.if_version >> 16) { + match = true; + } else { + if (r->in.interface_id->vers_major == + (eps->e[i].syntax_id.if_version >> 16) && + r->in.interface_id->vers_minor >= + (eps->e[i].syntax_id.if_version & 0xFFFF)) { + match = true; + } + } + break; + default: + return EPMAPPER_STATUS_CANT_PERFORM_OP; + } + } + } + + if (match) { + ZERO_STRUCT(r->out.entries[num_ents].object); + + DEBUG(10, ("_epm_Lookup: Adding tower for '%s'\n", + eps->e[i].name)); + r->out.entries[num_ents].annotation = talloc_strdup(r->out.entries, + eps->e[i].name); + r->out.entries[num_ents].tower = talloc(r->out.entries, + struct epm_twr_t); + if (r->out.entries[num_ents].tower == NULL) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + r->out.entries[num_ents].tower->tower.floors = talloc_move(r->out.entries[num_ents].tower, &eps->e[i].ep.floors); + r->out.entries[num_ents].tower->tower.num_floors = eps->e[i].ep.num_floors; + r->out.entries[num_ents].tower->tower_length = 0; + + num_ents++; + } + } /* end for loop */ + + *r->out.num_ents = num_ents; + + eps->count -= count; + eps->e += count; + if (eps->count == 0) { + close_policy_hnd(p, entry_handle); + ZERO_STRUCTP(r->out.entry_handle); + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + rc = EPMAPPER_STATUS_OK; +done: + talloc_free(tmp_ctx); + + return rc; +} + +static struct rpc_eps *epm_map_get_towers( + TALLOC_CTX *mem_ctx, + const struct ndr_syntax_id *iface, + enum dcerpc_transport_t transport, + const char *local_address) +{ + struct ndr_syntax_id_buf idbuf; + char *iface_string = ndr_syntax_id_buf_string(iface, &idbuf); + struct rpc_eps *eps = NULL; + uint8_t *buf = NULL; + size_t buflen; + char *bindings = NULL; + char *binding = NULL; + char *name = NULL; + NTSTATUS status; + int ret; + + DBG_DEBUG("Mapping interface %s\n", iface_string); + + eps = talloc_zero(mem_ctx, struct rpc_eps); + if (eps == NULL) { + goto fail; + } + + ret = tdb_fetch_talloc( + epmdb->tdb, string_term_tdb_data(iface_string), eps, &buf); + if (ret != 0) { + DBG_DEBUG("Could not find epm entry for %s: %s\n", + iface_string, + strerror(ret)); + goto fail; + } + buflen = talloc_array_length(buf); + + if ((buflen < 1) || (buf[buflen-1] != '\0')) { + DBG_DEBUG("epm entry for %s invalid\n", iface_string); + goto fail; + } + bindings = (char *)buf; + + name = bindings; /* name comes first */ + binding = name; /* strv_next will skip name */ + + while ((binding = strv_next(bindings, binding)) != NULL) { + struct dcerpc_binding *b = NULL; + enum dcerpc_transport_t found_transport; + struct dcesrv_ep_iface *tmp = NULL, *new_ep = NULL; + + DBG_DEBUG("Found %s for %s\n", binding, name); + + status = dcerpc_parse_binding(mem_ctx, binding, &b); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_parse_binding() for %s failed: %s\n", + binding, + nt_errstr(status)); + goto fail; + } + + found_transport = dcerpc_binding_get_transport(b); + if (found_transport != transport) { + DBG_DEBUG("Transport %d does not match %d\n", + (int)found_transport, + (int)transport); + TALLOC_FREE(b); + continue; + } + + if (found_transport == NCACN_IP_TCP) { + status = dcerpc_binding_set_string_option( + b, "host", local_address); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("Could not set host: %s\n", + nt_errstr(status)); + goto fail; + } + } + + status = dcerpc_binding_set_abstract_syntax(b, iface); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("Could not set abstract syntax: %s\n", + nt_errstr(status)); + goto fail; + } + + tmp = talloc_realloc( + eps, + eps->e, + struct dcesrv_ep_iface, + eps->count+1); + if (tmp == NULL) { + goto fail; + } + eps->e = tmp; + + new_ep = &eps->e[eps->count]; + + new_ep->name = talloc_strdup(eps->e, name); + if (new_ep->name == NULL) { + goto fail; + } + new_ep->syntax_id = *iface; + + status = dcerpc_binding_build_tower(eps->e, b, &new_ep->ep); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_binding_build_tower failed: %s\n", + nt_errstr(status)); + goto fail; + } + + eps->count += 1; + + TALLOC_FREE(b); + } + return eps; + +fail: + TALLOC_FREE(eps); + return NULL; +} + +/* + * epm_Map + * + * Apply some algorithm (using the fields in the map_tower) to an endpoint map + * to produce a list of protocol towers. + */ +error_status_t _epm_Map(struct pipes_struct *p, + struct epm_Map *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct dcesrv_connection *dcesrv_conn = dce_call->conn; + struct policy_handle *entry_handle; + enum dcerpc_transport_t transport; + struct ndr_syntax_id ifid; + struct epm_floor *floors; + struct rpc_eps *eps; + TALLOC_CTX *tmp_ctx; + error_status_t rc; + uint32_t count = 0; + uint32_t num_towers = 0; + uint32_t i; + bool ok; + NTSTATUS status; + + *r->out.num_towers = 0; + r->out.towers = NULL; + + if (r->in.map_tower == NULL || r->in.max_towers == 0 || + r->in.map_tower->tower.num_floors < 3) { + return EPMAPPER_STATUS_NO_MORE_ENTRIES; + } + + tmp_ctx = talloc_stackframe(); + + ZERO_STRUCTP(r->out.entry_handle); + + DEBUG(5, ("_epm_Map: Trying to map max. %u towers.\n", + r->in.max_towers)); + + /* + * A tower has normally up to 6 floors + * + * +-----------------------------------------------------------------+ + * | Floor 1 | Provides the RPC interface identifier. (e.g. UUID for | + * | | netlogon) | + * +---------+-------------------------------------------------------+ + * | Floor 2 | Transfer syntax (NDR encoded) | + * +---------+-------------------------------------------------------+ + * | Floor 3 | RPC protocol identifier (ncacn_tcp_ip, ncacn_np, ...) | + * +---------+-------------------------------------------------------+ + * | Floor 4 | Port address (e.g. TCP Port: 49156) | + * +---------+-------------------------------------------------------+ + * | Floor 5 | Transport (e.g. IP:192.168.51.10) | + * +---------+-------------------------------------------------------+ + * | Floor 6 | Routing | + * +---------+-------------------------------------------------------+ + */ + floors = r->in.map_tower->tower.floors; + + /* We accept NDR as the transfer syntax */ + status = dcerpc_floor_get_uuid_full(&floors[1], &ifid); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_floor_get_uuid_full() failed: %s\n", + nt_errstr(status)); + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + if (floors[1].lhs.protocol != EPM_PROTOCOL_UUID || + !ndr_syntax_id_equal(&ifid, &ndr_transfer_syntax_ndr)) { + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + /* We only talk to sane transports */ + transport = dcerpc_transport_by_tower(&r->in.map_tower->tower); + if (transport == NCA_UNKNOWN) { + DEBUG(2, ("epm_Map: Client requested unknown transport with " + "levels: ")); + for (i = 2; i < r->in.map_tower->tower.num_floors; i++) { + DEBUG(2, ("%d, ", r->in.map_tower->tower.floors[i].lhs.protocol)); + } + DEBUG(2, ("\n")); + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + if (r->in.entry_handle == NULL || + ndr_policy_handle_empty(r->in.entry_handle)) { + const struct tsocket_address *local_addr = + dcesrv_connection_get_local_address(dcesrv_conn); + char *local_address = NULL; + struct ndr_syntax_id_buf buf; + char *if_string = NULL; + + DEBUG(7, ("_epm_Map: No entry_handle found, creating it.\n")); + + status = dcerpc_floor_get_uuid_full(&floors[0], &ifid); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_floor_get_uuid_full() failed: %s\n", + nt_errstr(status)); + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + if_string = ndr_syntax_id_buf_string(&ifid, &buf); + + DBG_INFO("Mapping interface %s\n", if_string); + + if ((transport == NCACN_IP_TCP) && + tsocket_address_is_inet(local_addr, "ip")) { + /* + * We don't have the host ip in the epm + * database. For NCACN_IP_TCP, add the IP that + * the client connected to. + */ + local_address = tsocket_address_inet_addr_string( + local_addr, tmp_ctx); + } + + eps = epm_map_get_towers( + tmp_ctx, &ifid, transport, local_address); + if (eps == NULL) { + DBG_DEBUG("No bindings found\n"); + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + ok = create_policy_hnd(p, r->out.entry_handle, HTYPE_LOOKUP, eps); + if (!ok) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + + eps = find_policy_by_hnd(p, + r->out.entry_handle, + HTYPE_LOOKUP, + struct rpc_eps, + &status); + if (!NT_STATUS_IS_OK(status)) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + entry_handle = r->out.entry_handle; + } else { + DEBUG(7, ("_epm_Map: Trying to find entry_handle.\n")); + + eps = find_policy_by_hnd(p, + r->in.entry_handle, + HTYPE_LOOKUP, + struct rpc_eps, + &status); + if (!NT_STATUS_IS_OK(status)) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + entry_handle = r->in.entry_handle; + } + + if (eps == NULL || eps->e == NULL) { + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + /* return the next N elements */ + count = r->in.max_towers; + if (count > eps->count) { + count = eps->count; + } + + if (count == 0) { + close_policy_hnd(p, entry_handle); + ZERO_STRUCTP(r->out.entry_handle); + + rc = EPMAPPER_STATUS_NO_MORE_ENTRIES; + goto done; + } + + r->out.towers = talloc_array(p->mem_ctx, struct epm_twr_p_t, count); + if (r->out.towers == NULL) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + + for (i = 0; i < count; i++) { + DEBUG(7, ("_epm_Map: Map tower for '%s'\n", + eps->e[i].name)); + + r->out.towers[num_towers].twr = talloc(r->out.towers, + struct epm_twr_t); + if (r->out.towers[num_towers].twr == NULL) { + rc = EPMAPPER_STATUS_NO_MEMORY; + goto done; + } + r->out.towers[num_towers].twr->tower.floors = talloc_move(r->out.towers[num_towers].twr, &eps->e[i].ep.floors); + r->out.towers[num_towers].twr->tower.num_floors = eps->e[i].ep.num_floors; + r->out.towers[num_towers].twr->tower_length = 0; + + num_towers++; + } + + *r->out.num_towers = num_towers; + + eps->count -= count; + eps->e += count; + if (eps->count == 0) { + close_policy_hnd(p, entry_handle); + ZERO_STRUCTP(r->out.entry_handle); + } + + rc = EPMAPPER_STATUS_OK; +done: + talloc_free(tmp_ctx); + + return rc; +} + +/* + * epm_LookupHandleFree + */ +error_status_t _epm_LookupHandleFree(struct pipes_struct *p, + struct epm_LookupHandleFree *r) +{ + if (r->in.entry_handle == NULL) { + return EPMAPPER_STATUS_OK; + } + + if (is_valid_policy_hnd(r->in.entry_handle)) { + close_policy_hnd(p, r->in.entry_handle); + } + + r->out.entry_handle = r->in.entry_handle; + + return EPMAPPER_STATUS_OK; +} + + +/* + * epm_InqObject + * + * A client implementation SHOULD NOT call this method. These extensions do not + * provide an alternative method. + */ +error_status_t _epm_InqObject(struct pipes_struct *p, + struct epm_InqObject *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return EPMAPPER_STATUS_CANT_PERFORM_OP; +} + + +/* + * epm_MgmtDelete + * + * A client implementation SHOULD NOT call this method. These extensions do not + * provide an alternative method. +*/ +error_status_t _epm_MgmtDelete(struct pipes_struct *p, + struct epm_MgmtDelete *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return EPMAPPER_STATUS_CANT_PERFORM_OP; +} + + +/* + epm_MapAuth +*/ +error_status_t _epm_MapAuth(struct pipes_struct *p, + struct epm_MapAuth *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return EPMAPPER_STATUS_CANT_PERFORM_OP; +} + +static NTSTATUS epmapper__op_shutdown_server(struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint_server *ep_server); + +#define DCESRV_INTERFACE_EPMAPPER_SHUTDOWN_SERVER \ + epmapper_shutdown_server + +static NTSTATUS epmapper_shutdown_server(struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint_server *ep_server) +{ + return epmapper__op_shutdown_server(dce_ctx, ep_server); +} + +static NTSTATUS epmapper__op_init_server( + struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint_server *ep_server); + +static NTSTATUS epmapper_init_server( + struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint_server *ep_server) +{ + char *epmdb_path = NULL; + NTSTATUS status; + + epmdb_path = lock_path(dce_ctx, "epmdb.tdb"); + if (epmdb_path == NULL) { + return NT_STATUS_NO_MEMORY; + } + + epmdb = tdb_wrap_open( + dce_ctx, + epmdb_path, + 0, + TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, + O_RDONLY, + 0644); + if (epmdb == NULL) { + DBG_DEBUG("Could not open epmdb.tdb: %s\n", strerror(errno)); + return map_nt_error_from_unix(errno); + } + TALLOC_FREE(epmdb_path); + + status = epmapper__op_init_server(dce_ctx, ep_server); + return status; +} + +#define DCESRV_INTERFACE_EPMAPPER_INIT_SERVER epmapper_init_server + +/* include the generated boilerplate */ +#include "librpc/gen_ndr/ndr_epmapper_scompat.c" + +/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */ |