diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/providers/ldap/sdap_async_autofs.c | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/providers/ldap/sdap_async_autofs.c')
-rw-r--r-- | src/providers/ldap/sdap_async_autofs.c | 1479 |
1 files changed, 1479 insertions, 0 deletions
diff --git a/src/providers/ldap/sdap_async_autofs.c b/src/providers/ldap/sdap_async_autofs.c new file mode 100644 index 0000000..8a542f9 --- /dev/null +++ b/src/providers/ldap/sdap_async_autofs.c @@ -0,0 +1,1479 @@ +/* + SSSD + + Async LDAP Helper routines for autofs + + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2012 Red Hat + + 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 "util/util.h" +#include "db/sysdb.h" +#include "providers/ldap/sdap_async_private.h" +#include "db/sysdb_autofs.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap_autofs.h" +#include "providers/ldap/sdap_ops.h" + +/* ====== Utility functions ====== */ +static const char * +get_autofs_map_name(struct sysdb_attrs *map, struct sdap_options *opts) +{ + errno_t ret; + struct ldb_message_element *el; + + ret = sysdb_attrs_get_el(map, + opts->autofs_mobject_map[SDAP_AT_AUTOFS_MAP_NAME].sys_name, + &el); + if (ret) return NULL; + if (el->num_values == 0) return NULL; + + return (const char *)el->values[0].data; +} + +static const char * +get_autofs_entry_attr(struct sysdb_attrs *entry, struct sdap_options *opts, + enum sdap_autofs_entry_attrs attr) +{ + errno_t ret; + struct ldb_message_element *el; + + ret = sysdb_attrs_get_el(entry, + opts->autofs_entry_map[attr].sys_name, + &el); + if (ret) return NULL; + if (el->num_values != 1) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Expected one entry got %d\n", el->num_values); + return NULL; + } + + return (const char *)el->values[0].data; +} + +static const char * +get_autofs_entry_key(struct sysdb_attrs *entry, struct sdap_options *opts) +{ + return get_autofs_entry_attr(entry, opts, SDAP_AT_AUTOFS_ENTRY_KEY); +} + +static const char * +get_autofs_entry_value(struct sysdb_attrs *entry, struct sdap_options *opts) +{ + return get_autofs_entry_attr(entry, opts, SDAP_AT_AUTOFS_ENTRY_VALUE); +} + +static errno_t +add_autofs_entry(struct sss_domain_info *domain, + const char *map, + struct sdap_options *opts, + struct sysdb_attrs *entry, + time_t now) +{ + const char *key; + const char *value; + + key = get_autofs_entry_key(entry, opts); + if (!key) { + DEBUG(SSSDBG_OP_FAILURE, "Could not get autofs entry key\n"); + return EINVAL; + } + + value = get_autofs_entry_value(entry, opts); + if (!value) { + DEBUG(SSSDBG_OP_FAILURE, "Could not get autofs entry value\n"); + return EINVAL; + } + + return sysdb_save_autofsentry(domain, map, key, value, NULL, + domain->autofsmap_timeout, now); +} + +static errno_t +save_autofs_entries(struct sss_domain_info *domain, + struct sdap_options *opts, + const char *map, + char **add_dn_list, + hash_table_t *entry_hash) +{ + hash_key_t key; + hash_value_t value; + size_t i; + int hret; + errno_t ret; + struct sysdb_attrs *entry; + time_t now; + + if (!add_dn_list) { + return EOK; + } + + now = time(NULL); + + for (i=0; add_dn_list[i]; i++) { + key.type = HASH_KEY_STRING; + key.str = (char *) add_dn_list[i]; + + hret = hash_lookup(entry_hash, &key, &value); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot retrieve entry [%s] from hash\n", add_dn_list[i]); + continue; + } + + entry = talloc_get_type(value.ptr, struct sysdb_attrs); + if (!entry) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot retrieve entry [%s] from ptr\n", add_dn_list[i]); + continue; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Saving autofs entry [%s]\n", add_dn_list[i]); + ret = add_autofs_entry(domain, map, opts, entry, now); + if (ret) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot save entry [%s] to cache\n", add_dn_list[i]); + continue; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Saved entry [%s]\n", add_dn_list[i]); + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "All entries saved\n"); + return EOK; +} + +static errno_t +del_autofs_entries(struct sss_domain_info *dom, + char **del_dn_list) +{ + size_t i; + errno_t ret; + + for (i=0; del_dn_list[i]; i++) { + DEBUG(SSSDBG_TRACE_FUNC, + "Removing autofs entry [%s]\n", del_dn_list[i]); + + ret = sysdb_del_autofsentry(dom, del_dn_list[i]); + if (ret) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot delete entry %s\n", del_dn_list[i]); + continue; + } + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "All entries removed\n"); + return EOK; +} + +static errno_t +save_autofs_map(struct sss_domain_info *dom, + struct sdap_options *opts, + struct sysdb_attrs *map, + bool enumerated) +{ + const char *mapname; + const char *origdn; + errno_t ret; + time_t now; + + mapname = get_autofs_map_name(map, opts); + if (!mapname) return EINVAL; + + ret = sysdb_attrs_get_string(map, SYSDB_ORIG_DN, &origdn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get original dn [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + now = time(NULL); + + ret = sysdb_save_autofsmap(dom, mapname, mapname, origdn, + NULL, dom->autofsmap_timeout, now, enumerated); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +struct automntmaps_process_members_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct sdap_handle *sh; + struct sss_domain_info *dom; + int timeout; + + const char *orig_dn; + char *base_filter; + char *filter; + const char **attrs; + size_t base_iter; + struct sdap_search_base **search_bases; + + struct sysdb_attrs *map; + + struct sysdb_attrs **entries; + size_t entries_count; +}; + +static void +automntmaps_process_members_done(struct tevent_req *subreq); +static errno_t +automntmaps_process_members_next_base(struct tevent_req *req); + +static struct tevent_req * +automntmaps_process_members_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct sdap_handle *sh, + struct sss_domain_info *dom, + struct sdap_search_base **search_bases, + int timeout, + struct sysdb_attrs *map) +{ + errno_t ret; + struct tevent_req *req; + struct automntmaps_process_members_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct automntmaps_process_members_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->dom = dom; + state->sh = sh; + state->timeout = timeout; + state->base_iter = 0; + state->map = map; + state->search_bases = search_bases; + + state->base_filter = talloc_asprintf(state, "(&(%s=*)(objectclass=%s))", + opts->autofs_entry_map[SDAP_AT_AUTOFS_ENTRY_KEY].name, + opts->autofs_entry_map[SDAP_OC_AUTOFS_ENTRY].name); + if (!state->base_filter) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build filter\n"); + ret = ENOMEM; + goto immediate; + } + + ret = build_attrs_from_map(state, opts->autofs_entry_map, + SDAP_OPTS_AUTOFS_ENTRY, NULL, + &state->attrs, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build attributes from map\n"); + ret = ENOMEM; + goto immediate; + } + + + ret = sysdb_attrs_get_string(state->map, SYSDB_ORIG_DN, &state->orig_dn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get originalDN\n"); + goto immediate; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Examining autofs map [%s]\n", state->orig_dn); + + ret = automntmaps_process_members_next_base(req); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "search failed [%d]: %s\n", ret, strerror(ret)); + goto immediate; + } + + return req; + +immediate: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } + tevent_req_post(req, ev); + return req; +} + +static errno_t +automntmaps_process_members_next_base(struct tevent_req *req) +{ + struct tevent_req *subreq; + struct automntmaps_process_members_state *state = + tevent_req_data(req, struct automntmaps_process_members_state); + + talloc_zfree(state->filter); + state->filter = sdap_combine_filters(state, state->base_filter, + state->search_bases[state->base_iter]->filter); + if (!state->filter) { + return ENOMEM; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Searching for automount map entries with base [%s]\n", + state->search_bases[state->base_iter]->basedn); + + subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, + state->orig_dn, + state->search_bases[state->base_iter]->scope, + state->filter, state->attrs, + state->opts->autofs_entry_map, + SDAP_OPTS_AUTOFS_ENTRY, + state->timeout, true); + if (!subreq) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot start search for entries\n"); + return EIO; + } + tevent_req_set_callback(subreq, automntmaps_process_members_done, req); + + return EOK; +} + +static void +automntmaps_process_members_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct automntmaps_process_members_state *state = + tevent_req_data(req, struct automntmaps_process_members_state); + errno_t ret; + struct sysdb_attrs **entries; + size_t entries_count, i; + + ret = sdap_get_generic_recv(subreq, state, + &entries_count, &entries); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (entries_count > 0) { + state->entries = talloc_realloc(state, state->entries, + struct sysdb_attrs *, + state->entries_count + entries_count + 1); + if (state->entries == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + for (i=0; i < entries_count; i++) { + state->entries[state->entries_count + i] = + talloc_steal(state->entries, entries[i]); + } + + state->entries_count += entries_count; + state->entries[state->entries_count] = NULL; + } + + state->base_iter++; + if (state->search_bases[state->base_iter]) { + ret = automntmaps_process_members_next_base(req); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "No more search bases to try\n"); + + DEBUG(SSSDBG_TRACE_FUNC, + "Search for autofs entries, returned %zu results.\n", + state->entries_count); + + tevent_req_done(req); + return; +} + +static errno_t +automntmaps_process_members_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *entries_count, + struct sysdb_attrs ***entries) +{ + struct automntmaps_process_members_state *state; + state = tevent_req_data(req, struct automntmaps_process_members_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (entries_count) { + *entries_count = state->entries_count; + } + + if (entries) { + *entries = talloc_steal(mem_ctx, state->entries); + } + + return EOK; +} + +struct sdap_get_automntmap_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct sdap_handle *sh; + struct sss_domain_info *dom; + const char **attrs; + const char *base_filter; + char *filter; + int timeout; + + char *higher_timestamp; + + struct sysdb_attrs **map; + size_t count; + + struct sysdb_attrs **entries; + size_t entries_count; + + size_t base_iter; + struct sdap_search_base **search_bases; +}; + +static errno_t +sdap_get_automntmap_next_base(struct tevent_req *req); +static void +sdap_get_automntmap_process(struct tevent_req *subreq); + +static struct tevent_req * +sdap_get_automntmap_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sdap_options *opts, + struct sdap_search_base **search_bases, + struct sdap_handle *sh, + const char **attrs, + const char *filter, + int timeout) +{ + errno_t ret; + struct tevent_req *req; + struct sdap_get_automntmap_state *state; + + req = tevent_req_create(memctx, &state, struct sdap_get_automntmap_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->dom = dom; + state->sh = sh; + state->attrs = attrs; + state->higher_timestamp = NULL; + state->map = NULL; + state->count = 0; + state->timeout = timeout; + state->base_filter = filter; + state->base_iter = 0; + state->search_bases = search_bases; + + ret = sdap_get_automntmap_next_base(req); + if (ret != EOK) { + tevent_req_error(req, ret); + tevent_req_post(req, state->ev); + } + return req; +} + +static errno_t +sdap_get_automntmap_next_base(struct tevent_req *req) +{ + struct tevent_req *subreq; + struct sdap_get_automntmap_state *state; + + state = tevent_req_data(req, struct sdap_get_automntmap_state); + + talloc_zfree(state->filter); + state->filter = sdap_combine_filters(state, state->base_filter, + state->search_bases[state->base_iter]->filter); + if (!state->filter) { + return ENOMEM; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Searching for automount maps 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->autofs_mobject_map, SDAP_OPTS_AUTOFS_MAP, + state->timeout, + false); + if (!subreq) { + return EIO; + } + tevent_req_set_callback(subreq, sdap_get_automntmap_process, req); + + return EOK; +} + +static void +sdap_get_automntmap_done(struct tevent_req *subreq); + +static void +sdap_get_automntmap_process(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_get_automntmap_state *state = tevent_req_data(req, + struct sdap_get_automntmap_state); + errno_t ret; + + ret = sdap_get_generic_recv(subreq, state, + &state->count, &state->map); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Search for autofs maps, returned %zu results.\n", state->count); + + if (state->count == 0) { + /* No maps found in this search */ + state->base_iter++; + if (state->search_bases[state->base_iter]) { + /* There are more search bases to try */ + ret = sdap_get_automntmap_next_base(req); + if (ret != EOK) { + tevent_req_error(req, ENOENT); + } + return; + } + + tevent_req_error(req, ENOENT); + return; + } else if (state->count > 1) { + DEBUG(SSSDBG_OP_FAILURE, + "The search yielded more than one autofs map\n"); + tevent_req_error(req, EIO); + return; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "Processing autofs maps\n"); + subreq = automntmaps_process_members_send(state, state->ev, state->opts, + state->sh, state->dom, + state->search_bases, + state->timeout, + state->map[0]); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sdap_get_automntmap_done, req); + + return; +} + +static void +sdap_get_automntmap_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_get_automntmap_state *state = tevent_req_data(req, + struct sdap_get_automntmap_state); + errno_t ret; + + ret = automntmaps_process_members_recv(subreq, state, &state->entries_count, + &state->entries); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "automount map members received\n"); + tevent_req_done(req); + return; +} + +static errno_t +sdap_get_automntmap_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct sysdb_attrs **map, + size_t *entries_count, + struct sysdb_attrs ***entries) +{ + struct sdap_get_automntmap_state *state = tevent_req_data(req, + struct sdap_get_automntmap_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (map) { + *map = talloc_steal(mem_ctx, state->map[0]); + } + + if (entries_count) { + *entries_count = state->entries_count; + } + + if (entries) { + *entries = talloc_steal(mem_ctx, state->entries); + } + + return EOK; +} + +struct sdap_autofs_setautomntent_state { + char *filter; + const char **attrs; + struct sdap_options *opts; + struct sdap_handle *sh; + struct sysdb_ctx *sysdb; + struct sdap_id_op *sdap_op; + struct sss_domain_info *dom; + + const char *mapname; + struct sysdb_attrs *map; + struct sysdb_attrs **entries; + size_t entries_count; + + int dp_error; +}; + +static void +sdap_autofs_setautomntent_done(struct tevent_req *subreq); + +struct tevent_req * +sdap_autofs_setautomntent_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + struct sdap_handle *sh, + struct sdap_id_op *op, + struct sdap_options *opts, + const char *mapname) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sdap_autofs_setautomntent_state *state; + char *clean_mapname; + errno_t ret; + + req = tevent_req_create(memctx, &state, + struct sdap_autofs_setautomntent_state); + if (!req) return NULL; + + if (!mapname) { + DEBUG(SSSDBG_CRIT_FAILURE, "No map name given\n"); + ret = EINVAL; + goto fail; + } + + state->sh = sh; + state->sysdb = sysdb; + state->opts = opts; + state->sdap_op = op; + state->dom = dom; + state->mapname = mapname; + + ret = sss_filter_sanitize(state, mapname, &clean_mapname); + if (ret != EOK) { + goto fail; + } + + state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + state->opts->autofs_mobject_map[SDAP_AT_AUTOFS_MAP_NAME].name, + clean_mapname, + state->opts->autofs_mobject_map[SDAP_OC_AUTOFS_MAP].name); + if (!state->filter) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build filter\n"); + ret = ENOMEM; + goto fail; + } + talloc_free(clean_mapname); + + ret = build_attrs_from_map(state, state->opts->autofs_mobject_map, + SDAP_OPTS_AUTOFS_MAP, NULL, + &state->attrs, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build attributes from map\n"); + ret = ENOMEM; + goto fail; + } + + subreq = sdap_get_automntmap_send(state, ev, dom, + state->opts, + state->opts->sdom->autofs_search_bases, + state->sh, + state->attrs, state->filter, + dp_opt_get_int(state->opts->basic, + SDAP_SEARCH_TIMEOUT)); + if (!subreq) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_get_automntmap_send failed\n"); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sdap_autofs_setautomntent_done, req); + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static errno_t +sdap_autofs_setautomntent_save(struct tevent_req *req); + +static void +sdap_autofs_setautomntent_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_autofs_setautomntent_state *state = tevent_req_data(req, + struct sdap_autofs_setautomntent_state); + + ret = sdap_get_automntmap_recv(subreq, state, &state->map, + &state->entries_count, &state->entries); + talloc_zfree(subreq); + if (ret != EOK) { + if (ret == ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not find automount map\n"); + } else { + DEBUG(SSSDBG_OP_FAILURE, + "sdap_get_automntmap_recv failed [%d]: %s\n", + ret, strerror(ret)); + } + tevent_req_error(req, ret); + return; + } + + ret = sdap_autofs_setautomntent_save(req); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not save automount map\n"); + tevent_req_error(req, ret); + return; + } + + state->dp_error = DP_ERR_OK; + tevent_req_done(req); + return; +} + +static errno_t +sdap_autofs_setautomntent_save(struct tevent_req *req) +{ + struct sdap_autofs_setautomntent_state *state = tevent_req_data(req, + struct sdap_autofs_setautomntent_state); + errno_t ret, tret; + bool in_transaction = false; + TALLOC_CTX *tmp_ctx; + struct ldb_message **entries = NULL; + size_t count; + const char *key; + const char *val; + char **sysdb_entrylist = NULL; + char **ldap_entrylist = NULL; + char **add_entries = NULL; + char **del_entries = NULL; + size_t i, j; + + hash_table_t *entry_hash = NULL; + hash_key_t hkey; + hash_value_t value; + int hret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + DEBUG(SSSDBG_TRACE_LIBS, + "Got %zu map entries from LDAP\n", state->entries_count); + if (state->entries_count == 0) { + /* No entries for this map in LDAP. + * We need to ensure that there are no entries + * in the sysdb either. + */ + ldap_entrylist = NULL; + } else { + ldap_entrylist = talloc_array(tmp_ctx, char *, + state->entries_count+1); + if (!ldap_entrylist) { + ret = ENOMEM; + goto done; + } + + ret = sss_hash_create(state, 0, &entry_hash); + if (ret) { + goto done; + } + + /* Get a list of the map members by DN */ + for (i=0, j=0; i < state->entries_count; i++) { + key = get_autofs_entry_key(state->entries[i], state->opts); + val = get_autofs_entry_value(state->entries[i], state->opts); + if (!key || !val) { + DEBUG(SSSDBG_MINOR_FAILURE, "Malformed entry, skipping\n"); + continue; + } + + ldap_entrylist[j] = sysdb_autofsentry_strdn(ldap_entrylist, + state->dom, + state->mapname, + key, val); + if (!ldap_entrylist[j]) { + ret = ENOMEM; + goto done; + } + + hkey.type = HASH_KEY_STRING; + hkey.str = ldap_entrylist[j]; + value.type = HASH_VALUE_PTR; + value.ptr = state->entries[i]; + + hret = hash_enter(entry_hash, &hkey, &value); + if (hret != HASH_SUCCESS) { + ret = EIO; + goto done; + } + + j++; + } + /* terminate array with NULL after the last retrieved entry */ + ldap_entrylist[j] = NULL; + } + + ret = sysdb_autofs_entries_by_map(tmp_ctx, state->dom, state->mapname, + &count, &entries); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + "cache lookup for the map failed [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Got %zu map entries from sysdb\n", count); + if (count == 0) { + /* No map members for this map in sysdb currently */ + sysdb_entrylist = NULL; + } else { + sysdb_entrylist = talloc_array(state, char *, count+1); + if (!sysdb_entrylist) { + ret = ENOMEM; + goto done; + } + + /* Get a list of the map members by DN */ + for (i=0; i < count; i++) { + sysdb_entrylist[i] = talloc_strdup(sysdb_entrylist, + ldb_dn_get_linearized(entries[i]->dn)); + if (!sysdb_entrylist[i]) { + ret = ENOMEM; + goto done; + } + } + sysdb_entrylist[count] = NULL; + } + + /* Find the differences between the sysdb and LDAP lists + * Entries in the sysdb only must be removed. + */ + ret = diff_string_lists(tmp_ctx, ldap_entrylist, sysdb_entrylist, + &add_entries, &del_entries, NULL); + if (ret != EOK) goto done; + + ret = sysdb_transaction_start(state->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot start sysdb transaction [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + in_transaction = true; + + /* Save the map itself */ + ret = save_autofs_map(state->dom, state->opts, state->map, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot save autofs map entry [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + + /* Create entries that don't exist yet */ + if (add_entries && add_entries[0]) { + ret = save_autofs_entries(state->dom, state->opts, + state->mapname, add_entries, + entry_hash); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot save autofs entries [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + } + + /* Delete entries that don't exist anymore */ + if (del_entries && del_entries[0]) { + ret = del_autofs_entries(state->dom, del_entries); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot delete autofs entries [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + } + + + ret = sysdb_transaction_commit(state->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot commit sysdb transaction [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + in_transaction = false; + + ret = EOK; +done: + if (in_transaction) { + tret = sysdb_transaction_cancel(state->sysdb); + if (tret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot cancel sysdb transaction [%d]: %s\n", + ret, strerror(ret)); + } + } + talloc_zfree(tmp_ctx); + return ret; +} + +errno_t +sdap_autofs_setautomntent_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct sdap_autofs_get_map_state { + struct sdap_id_ctx *id_ctx; + struct sdap_options *opts; + struct sdap_id_op *sdap_op; + const char *mapname; + int dp_error; +}; + +static errno_t sdap_autofs_get_map_retry(struct tevent_req *req); +static void sdap_autofs_get_map_connect_done(struct tevent_req *subreq); +static void sdap_autofs_get_map_done(struct tevent_req *subreq); + +struct tevent_req *sdap_autofs_get_map_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + const char *mapname) +{ + struct tevent_req *req; + struct sdap_autofs_get_map_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sdap_autofs_get_map_state); + if (!req) { + return NULL; + } + + state->id_ctx = id_ctx; + state->opts = id_ctx->opts; + state->mapname = mapname; + state->dp_error = DP_ERR_FATAL; + + state->sdap_op = sdap_id_op_create(state, id_ctx->conn->conn_cache); + if (!state->sdap_op) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n"); + ret = ENOMEM; + goto done; + } + + ret = sdap_autofs_get_map_retry(req); + if (ret == EAGAIN) { + /* asynchronous processing */ + return req; + } + +done: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, id_ctx->be->ev); + + return req; +} + +static errno_t sdap_autofs_get_map_retry(struct tevent_req *req) +{ + struct sdap_autofs_get_map_state *state; + struct tevent_req *subreq; + int ret; + + state = tevent_req_data(req, struct sdap_autofs_get_map_state); + + subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed: " + "%d(%s)\n", ret, strerror(ret)); + return ret; + } + + tevent_req_set_callback(subreq, sdap_autofs_get_map_connect_done, req); + + return EAGAIN; +} + +static void sdap_autofs_get_map_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_autofs_get_map_state *state; + char *filter; + char *safe_mapname; + const char **attrs; + int dp_error; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_autofs_get_map_state); + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "LDAP connection failed " + "[%d]: %s\n", ret, strerror(ret)); + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "LDAP connection successful\n"); + + ret = sss_filter_sanitize(state, state->mapname, &safe_mapname); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + state->opts->autofs_mobject_map[SDAP_AT_AUTOFS_MAP_NAME].name, + safe_mapname, + state->opts->autofs_mobject_map[SDAP_OC_AUTOFS_MAP].name); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build filter\n"); + tevent_req_error(req, ret); + return; + } + + ret = build_attrs_from_map(state, state->opts->autofs_mobject_map, + SDAP_OPTS_AUTOFS_MAP, NULL, &attrs, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build attributes from map\n"); + tevent_req_error(req, ret); + return; + } + + subreq = sdap_search_bases_return_first_send(state, state->id_ctx->be->ev, + state->opts, sdap_id_op_handle(state->sdap_op), + state->opts->sdom->autofs_search_bases, + state->opts->autofs_mobject_map, false, + dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT), + filter, attrs, NULL); + if (subreq == NULL) { + state->dp_error = DP_ERR_FATAL; + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_set_callback(subreq, sdap_autofs_get_map_done, req); +} + +static void sdap_autofs_get_map_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_autofs_get_map_state *state; + struct sysdb_attrs **reply; + size_t reply_count; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_autofs_get_map_state); + + ret = sdap_search_bases_return_first_recv(subreq, state, &reply_count, + &reply); + talloc_zfree(subreq); + + ret = sdap_id_op_done(state->sdap_op, ret, &state->dp_error); + if (state->dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = sdap_autofs_get_map_retry(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } + return; + } else if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (reply_count == 0) { + ret = sysdb_delete_autofsmap(state->id_ctx->be->domain, state->mapname); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot delete autofs map %s [%d]: %s\n", + state->mapname, ret, strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; + } + + ret = save_autofs_map(state->id_ctx->be->domain, state->opts, reply[0], false); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot save autofs map %s [%d]: %s\n", + state->mapname, ret, strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t sdap_autofs_get_map_recv(struct tevent_req *req, + int *dp_error) +{ + struct sdap_autofs_get_map_state *state; + + state = tevent_req_data(req, struct sdap_autofs_get_map_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *dp_error = state->dp_error; + + return EOK; +} + +struct sdap_autofs_get_entry_state { + struct sdap_id_ctx *id_ctx; + struct sdap_options *opts; + struct sdap_id_op *sdap_op; + const char *mapname; + const char *entryname; + int dp_error; +}; + +static errno_t sdap_autofs_get_entry_retry(struct tevent_req *req); +static void sdap_autofs_get_entry_connect_done(struct tevent_req *subreq); +static void sdap_autofs_get_entry_done(struct tevent_req *subreq); + +struct tevent_req *sdap_autofs_get_entry_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + const char *mapname, + const char *entryname) +{ + struct tevent_req *req; + struct sdap_autofs_get_entry_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sdap_autofs_get_entry_state); + if (!req) { + return NULL; + } + + state->id_ctx = id_ctx; + state->opts = id_ctx->opts; + state->mapname = mapname; + state->entryname = entryname; + state->dp_error = DP_ERR_FATAL; + + state->sdap_op = sdap_id_op_create(state, id_ctx->conn->conn_cache); + if (!state->sdap_op) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n"); + ret = ENOMEM; + goto done; + } + + ret = sdap_autofs_get_entry_retry(req); + if (ret == EAGAIN) { + /* asynchronous processing */ + return req; + } + +done: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, id_ctx->be->ev); + + return req; +} + +static errno_t sdap_autofs_get_entry_retry(struct tevent_req *req) +{ + struct sdap_autofs_get_entry_state *state; + struct tevent_req *subreq; + int ret; + + state = tevent_req_data(req, struct sdap_autofs_get_entry_state); + + subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed: " + "%d(%s)\n", ret, strerror(ret)); + return ret; + } + + tevent_req_set_callback(subreq, sdap_autofs_get_entry_connect_done, req); + + return EAGAIN; +} + +static void sdap_autofs_get_entry_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_autofs_get_entry_state *state; + struct ldb_message *map; + char *filter; + char *safe_entryname; + const char **attrs; + const char *base_dn; + int dp_error; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_autofs_get_entry_state); + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "LDAP connection failed " + "[%d]: %s\n", ret, strerror(ret)); + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "LDAP connection successful\n"); + + ret = sysdb_get_map_byname(state, state->id_ctx->be->domain, + state->mapname, &map); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Map %s does not exist!\n", state->mapname); + tevent_req_error(req, ret); + return; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get map %s [%d]: %s\n", + state->mapname, ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + base_dn = ldb_msg_find_attr_as_string(map, SYSDB_ORIG_DN, NULL); + if (base_dn == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get originalDN\n"); + tevent_req_error(req, ERR_INTERNAL); + return; + } + + ret = sss_filter_sanitize(state, state->entryname, &safe_entryname); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + state->opts->autofs_entry_map[SDAP_AT_AUTOFS_ENTRY_KEY].name, + safe_entryname, + state->opts->autofs_entry_map[SDAP_OC_AUTOFS_ENTRY].name); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build filter\n"); + tevent_req_error(req, ret); + return; + } + + ret = build_attrs_from_map(state, state->opts->autofs_entry_map, + SDAP_OPTS_AUTOFS_ENTRY, NULL, &attrs, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build attributes from map\n"); + tevent_req_error(req, ret); + return; + } + + subreq = sdap_search_bases_return_first_send(state, state->id_ctx->be->ev, + state->opts, sdap_id_op_handle(state->sdap_op), + state->opts->sdom->autofs_search_bases, + state->opts->autofs_entry_map, false, + dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT), + filter, attrs, base_dn); + if (subreq == NULL) { + state->dp_error = DP_ERR_FATAL; + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_set_callback(subreq, sdap_autofs_get_entry_done, req); +} + +static errno_t sdap_autofs_save_entry(struct sss_domain_info *domain, + struct sdap_options *opts, + struct sysdb_attrs *newentry, + const char *mapname, + const char *entryname); + +static void sdap_autofs_get_entry_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_autofs_get_entry_state *state; + struct sysdb_attrs **reply; + size_t reply_count; + size_t i; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_autofs_get_entry_state); + + ret = sdap_search_bases_return_first_recv(subreq, state, &reply_count, + &reply); + talloc_zfree(subreq); + + ret = sdap_id_op_done(state->sdap_op, ret, &state->dp_error); + if (state->dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = sdap_autofs_get_entry_retry(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } + return; + } else if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + /* This will delete the entry if it already exist. */ + if (reply_count == 0) { + ret = sdap_autofs_save_entry(state->id_ctx->be->domain, state->opts, + NULL, state->mapname, state->entryname); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + goto done; + } + + /* If other attribute then automountKey is in the distinguished name and + * there are multiple entries with different casing then we may get more + * then one result. */ + for (i = 0; i < reply_count; i++) { + ret = sdap_autofs_save_entry(state->id_ctx->be->domain, state->opts, + reply[i], state->mapname, + state->entryname); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + } + +done: + tevent_req_done(req); + return; +} + +errno_t sdap_autofs_get_entry_recv(struct tevent_req *req, + int *dp_error) +{ + struct sdap_autofs_get_entry_state *state; + + state = tevent_req_data(req, struct sdap_autofs_get_entry_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *dp_error = state->dp_error; + + return EOK; +} + +static errno_t sdap_autofs_save_entry(struct sss_domain_info *domain, + struct sdap_options *opts, + struct sysdb_attrs *newentry, + const char *mapname, + const char *entryname) +{ + bool in_transaction = false; + errno_t ret; + int tret; + + ret = sysdb_transaction_start(domain->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot start sysdb transaction [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + in_transaction = true; + + /* Delete existing entry to cover case where new entry has the same key + * but different automountInformation. Because the dn is created from the + * combination of key and information it would be possible to end up with + * two entries with same key but different information otherwise. + */ + ret = sysdb_del_autofsentry_by_key(domain, mapname, entryname); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot delete entry %s:%s\n", + mapname, entryname); + goto done; + } + + if (newentry != NULL) { + ret = add_autofs_entry(domain, mapname, opts, newentry, time(NULL)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot save autofs entry %s:%s [%d]: %s\n", + mapname, entryname, ret, sss_strerror(ret)); + goto done; + } + } + + ret = sysdb_transaction_commit(domain->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot commit sysdb transaction [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + in_transaction = false; + + ret = EOK; + +done: + if (in_transaction) { + tret = sysdb_transaction_cancel(domain->sysdb); + if (tret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot cancel sysdb transaction " + "[%d]: %s\n", ret, sss_strerror(ret)); + } + } + + return ret; +} |