From 0e5303ba662019626b11aa7d1fc531fccad74b9b Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Apr 23 2020 11:40:44 +0000 Subject: LDAP: Add support for ipnetwork lookups (no enumeration) Signed-off-by: Samuel Cabrero Reviewed-by: Pavel Březina --- diff --git a/src/providers/ldap/sdap_async_ipnetwork.c b/src/providers/ldap/sdap_async_ipnetwork.c index 9a09f11..58d7efe 100644 --- a/src/providers/ldap/sdap_async_ipnetwork.c +++ b/src/providers/ldap/sdap_async_ipnetwork.c @@ -21,10 +21,32 @@ */ #include "providers/ldap/sdap_async_private.h" +#include "providers/ldap/ldap_common.h" +#include "db/sysdb_ipnetworks.h" struct sdap_get_ipnetwork_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct sdap_handle *sh; + struct sss_domain_info *dom; + struct sysdb_ctx *sysdb; + const char **attrs; + const char *base_filter; + char *filter; + int timeout; + bool enumeration; + + char *higher_usn; + struct sysdb_attrs **entries; + size_t num_entries; + + size_t base_iter; + struct sdap_search_base **search_bases; }; +static errno_t +sdap_get_ipnetwork_next_base(struct tevent_req *req); + struct tevent_req * sdap_get_ipnetwork_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -48,10 +70,411 @@ sdap_get_ipnetwork_send(TALLOC_CTX *mem_ctx, return NULL; } - /* TODO */ + state->ev = ev; + state->opts = opts; + state->dom = dom; + state->sh = sh; + state->sysdb = sysdb; + state->attrs = attrs; + state->higher_usn = NULL; + state->entries = NULL; + state->num_entries = 0; + state->timeout = timeout; + state->base_filter = filter; + state->base_iter = 0; + state->search_bases = search_bases; + state->enumeration = enumeration; + + if (state->search_bases == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "IP network lookup request without a search base\n"); + ret = EINVAL; + goto done; + } + + ret = sdap_get_ipnetwork_next_base(req); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + tevent_req_post(req, state->ev); + } + + return req; +} + +static void +sdap_get_ipnetwork_process(struct tevent_req *subreq); + +static errno_t +sdap_get_ipnetwork_next_base(struct tevent_req *req) +{ + struct tevent_req *subreq; + struct sdap_get_ipnetwork_state *state; + + state = tevent_req_data(req, struct sdap_get_ipnetwork_state); + + talloc_zfree(state->filter); + state->filter = sdap_combine_filters(state, state->base_filter, + state->search_bases[state->base_iter]->filter); + if (state->filter == NULL) { + return ENOMEM; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Searching for IP network with base [%s]\n", + state->search_bases[state->base_iter]->basedn); + + subreq = sdap_get_generic_send( + state, state->ev, state->opts, state->sh, + state->search_bases[state->base_iter]->basedn, + state->search_bases[state->base_iter]->scope, + state->filter, state->attrs, + state->opts->ipnetwork_map, + SDAP_OPTS_IPNETWORK, + state->timeout, + state->enumeration); /* If we're enumerating, we need paging */ + if (subreq == NULL) { + return ENOMEM; + } + tevent_req_set_callback(subreq, sdap_get_ipnetwork_process, req); + + return EOK; +} + +static errno_t +sdap_save_ipnetworks(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_options *opts, + struct sysdb_attrs **entries, + size_t num_entries, + char **_usn_value); + +static void +sdap_get_ipnetwork_process(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_get_ipnetwork_state *state; + int ret; + size_t count, i; + struct sysdb_attrs **entries; + bool next_base = false; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_get_ipnetwork_state); + + ret = sdap_get_generic_recv(subreq, state, &count, &entries); + talloc_zfree(subreq); + + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Search for IP networks returned %zu results.\n", + count); + + if (state->enumeration || count == 0) { + /* No entries found in this search or enumerating */ + next_base = true; + } + + /* Add this batch of entries to the list */ + if (count > 0) { + state->entries = talloc_realloc(state, state->entries, + struct sysdb_attrs *, + state->num_entries + count + 1); + if (state->entries == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + /* Steal the new entries into the list */ + for (i = 0; i < count; i++) { + state->entries[state->num_entries + i] = + talloc_steal(state->entries, entries[i]); + } + + state->num_entries += count; + state->entries[state->num_entries] = NULL; + } + + if (next_base) { + state->base_iter++; + if (state->search_bases[state->base_iter]) { + /* There are more search bases to try */ + ret = sdap_get_ipnetwork_next_base(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } + return; + } + } + + /* No more search bases + * Return ENOENT if no entries were found + */ + if (state->num_entries == 0) { + tevent_req_error(req, ENOENT); + return; + } + + ret = sdap_save_ipnetworks(state, state->sysdb, state->dom, state->opts, + state->entries, state->num_entries, + &state->higher_usn); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to store IP networks.\n"); + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "Saved %zu IP networks\n", state->num_entries); tevent_req_done(req); - return tevent_req_post(req, ev); +} + + +static errno_t +sdap_save_ipnetwork(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sss_domain_info *dom, + struct sysdb_attrs *attrs, + char **_usn_value, + time_t now); + +static errno_t +sdap_save_ipnetworks(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_options *opts, + struct sysdb_attrs **entries, + size_t num_entries, + char **_usn_value) +{ + errno_t ret, sret; + time_t now; + size_t i; + bool in_transaction = false; + char *higher_usn = NULL; + char *usn_value; + TALLOC_CTX *tmp_ctx; + + if (num_entries == 0) { + /* Nothing to do */ + return ENOENT; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n"); + goto done; + } + + in_transaction = true; + + now = time(NULL); + for (i = 0; i < num_entries; i++) { + usn_value = NULL; + + ret = sdap_save_ipnetwork(tmp_ctx, sysdb, opts, dom, entries[i], + &usn_value, now); + + /* Do not fail completely on errors. + * Just report the failure to save and go on */ + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to store IP network %zu. Ignoring.\n", i); + } else { + DEBUG(SSSDBG_TRACE_INTERNAL, + "IP network [%zu/%zu] saved\n", i, num_entries); + } + + if (usn_value != NULL) { + if (higher_usn != NULL) { + if ((strlen(usn_value) > strlen(higher_usn)) || + (strcmp(usn_value, higher_usn) > 0)) { + talloc_zfree(higher_usn); + higher_usn = usn_value; + } else { + talloc_zfree(usn_value); + } + } else { + higher_usn = usn_value; + } + } + } + + ret = sysdb_transaction_commit(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n"); + goto done; + } + in_transaction = false; + + if (_usn_value != NULL) { + *_usn_value = talloc_steal(mem_ctx, higher_usn); + } + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to cancel transaction!\n"); + } + } + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +sdap_save_ipnetwork(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sss_domain_info *dom, + struct sysdb_attrs *attrs, + char **_usn_value, + time_t now) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx = NULL; + struct sysdb_attrs *net_attrs; + struct ldb_message_element *el; + char *usn_value = NULL; + const char *name = NULL; + const char *address = NULL; + const char **aliases = NULL; + const char **cased_aliases = NULL; + const char **store_aliases = NULL; + char **missing; + uint64_t cache_timeout; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + net_attrs = sysdb_new_attrs(tmp_ctx); + if (net_attrs == NULL) { + ret = ENOMEM; + goto done; + } + + /* Identify the primary name of this network */ + ret = sysdb_attrs_primary_name(sysdb, attrs, + opts->ipnetwork_map[SDAP_AT_IPNETWORK_NAME].name, + &name); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Could not determine the primary name of the IP network\n"); + goto done; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "IP network primary name: [%s]\n", name); + + /* Handle any available aliases */ + ret = sysdb_attrs_get_aliases(tmp_ctx, attrs, name, + !dom->case_sensitive, + &aliases); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to identify IP network aliases: [%s]\n", + strerror(ret)); + goto done; + } + + /* Get the address */ + ret = sysdb_attrs_get_string(attrs, SYSDB_IP_NETWORK_ATTR_NUMBER, &address); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to identify IP network number: [%s]\n", + strerror(ret)); + goto done; + } + + if (dom->case_sensitive == false) { + /* Don't perform the extra mallocs if not necessary */ + ret = sss_get_cased_name_list(tmp_ctx, aliases, + dom->case_sensitive, &cased_aliases); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to get case_sensitive aliases: [%s]\n", + strerror(ret)); + goto done; + } + } + + store_aliases = dom->case_sensitive ? aliases : cased_aliases; + + /* Get the USN value, if available */ + ret = sysdb_attrs_get_el(attrs, + opts->ipnetwork_map[SDAP_AT_IPNETWORK_USN].sys_name, + &el); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to retrieve USN value: [%s]\n", + strerror(ret)); + goto done; + } + + if (ret == ENOENT || el->num_values == 0) { + DEBUG(SSSDBG_TRACE_LIBS, + "Original USN value is not available for [%s].\n", + name); + } else { + ret = sysdb_attrs_add_string(net_attrs, + opts->ipnetwork_map[SDAP_AT_IPNETWORK_USN].sys_name, + (const char*)el->values[0].data); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to add USN value: [%s]\n", + strerror(ret)); + goto done; + } + usn_value = talloc_strdup(tmp_ctx, (const char*)el->values[0].data); + if (usn_value == NULL) { + ret = ENOMEM; + goto done; + } + } + + /* Make sure to remove any extra attributes from the sysdb + * that have been removed from LDAP + */ + ret = list_missing_attrs(net_attrs, opts->ipnetwork_map, + SDAP_OPTS_IPNETWORK, + attrs, &missing); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to identify removed attributes: [%s]\n", + strerror(ret)); + goto done; + } + + cache_timeout = dom->resolver_timeout; + + ret = sysdb_store_ipnetwork(dom, name, store_aliases, address, + net_attrs, missing, cache_timeout, now); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to store IP network in the sysdb: [%s]\n", + strerror(ret)); + goto done; + } + + *_usn_value = talloc_steal(mem_ctx, usn_value); + +done: + talloc_free(tmp_ctx); + return ret; } errno_t