diff options
Diffstat (limited to '')
54 files changed, 17135 insertions, 0 deletions
diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c new file mode 100644 index 0000000..b827595 --- /dev/null +++ b/src/responder/common/cache_req/cache_req.c @@ -0,0 +1,1612 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <ldb.h> +#include <talloc.h> +#include <tevent.h> +#include <errno.h> + +#include "util/util.h" +#include "util/sss_chain_id.h" +#include "responder/common/responder.h" +#include "responder/common/cache_req/cache_req_private.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const struct cache_req_plugin * +cache_req_get_plugin(enum cache_req_type type) +{ + static const struct cache_req_plugin *plugins[CACHE_REQ_SENTINEL] = { + &cache_req_user_by_name, + &cache_req_user_by_upn, + &cache_req_user_by_id, + &cache_req_user_by_cert, + &cache_req_user_by_filter, + + &cache_req_group_by_name, + &cache_req_group_by_id, + &cache_req_group_by_filter, + + &cache_req_initgroups_by_name, + &cache_req_initgroups_by_upn, + +#ifdef BUILD_SUBID + &cache_req_subid_ranges_by_name, +#endif + + &cache_req_object_by_sid, + &cache_req_object_by_name, + &cache_req_object_by_id, + + &cache_req_enum_users, + &cache_req_enum_groups, + &cache_req_enum_svc, + &cache_req_enum_ip_hosts, + &cache_req_enum_ip_networks, + + &cache_req_svc_by_name, + &cache_req_svc_by_port, + + &cache_req_netgroup_by_name, + + &cache_req_ssh_host_id_by_name, + + &cache_req_autofs_map_entries, + &cache_req_autofs_map_by_name, + &cache_req_autofs_entry_by_name, + + &cache_req_ip_host_by_name, + &cache_req_ip_host_by_addr, + &cache_req_ip_network_by_name, + &cache_req_ip_network_by_addr, + }; + + if (type >= CACHE_REQ_SENTINEL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Bug: invalid plugin type!"); + return NULL; + } + + return plugins[type]; +} + +static errno_t cache_req_set_plugin(struct cache_req *cr, + enum cache_req_type type) +{ + const struct cache_req_plugin *plugin; + + plugin = cache_req_get_plugin(type); + if (plugin == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Bug: unset plugin!"); + return EINVAL; + } + + cr->reqname = plugin->name; + cr->plugin = plugin; + + CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, "Setting \"%s\" plugin\n", + plugin->name); + + return EOK; +} + +static const char * +cache_req_dom_type_as_str(struct cache_req *cr) +{ + if (cr == NULL) { + return "BUG: Invalid cache_req pointer\n"; + } + switch (cr->req_dom_type) { + case CACHE_REQ_POSIX_DOM: + return "POSIX-only"; + case CACHE_REQ_APPLICATION_DOM: + return "Application-only"; + case CACHE_REQ_ANY_DOM: + return "Any"; + } + + return "Unknown"; +} + +static struct cache_req * +cache_req_create(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct cache_req_data *data, + struct sss_nc_ctx *ncache, + int midpoint, + enum cache_req_dom_type req_dom_type) +{ + struct cache_req *cr; + bool bypass_cache; + errno_t ret; + + cr = talloc_zero(mem_ctx, struct cache_req); + if (cr == NULL) { + return NULL; + } + + cr->rctx = rctx; + cr->data = data; + cr->ncache = ncache; + cr->midpoint = midpoint; + cr->req_dom_type = req_dom_type; + cr->req_start = time(NULL); + + /* It is perfectly fine to just overflow here. */ + cr->reqid = rctx->cache_req_num++; + + ret = cache_req_set_plugin(cr, data->type); + if (ret != EOK) { + talloc_free(cr); + return NULL; + } + + bypass_cache = cr->plugin->bypass_cache || cr->data->bypass_cache; + if (bypass_cache && cr->data->bypass_dp) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Cannot bypass cache and dp at the same time!"); + talloc_free(cr); + return NULL; + } + if (rctx->cache_first) { + cr->cache_behavior = CACHE_REQ_CACHE_FIRST; + } + /* it is ok to override cache_first here */ + if (bypass_cache) { + cr->cache_behavior = CACHE_REQ_BYPASS_CACHE; + } else if (cr->data->bypass_dp) { + cr->cache_behavior = CACHE_REQ_BYPASS_PROVIDER; + } + + return cr; +} + +static errno_t +cache_req_set_name(struct cache_req *cr, const char *name) +{ + const char *dup_name; + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Setting name [%s]\n", name); + + dup_name = talloc_strdup(cr->data, name); + if (dup_name == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, "Unable to set name!\n"); + return ENOMEM; + } + + talloc_zfree(cr->data->name.name); + cr->data->name.name = dup_name; + + return EOK; +} + +static bool +cache_req_validate_domain_enumeration(struct cache_req *cr, + struct sss_domain_info *domain) +{ + if (!cr->plugin->require_enumeration) { + return true; + } + + if (domain->enumerate == false) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Domain %s does not support " + "enumeration, skipping...\n", domain->name); + if (cr->rctx->enumeration_warn_logged == false) { + sss_log(SSS_LOG_NOTICE, "Enumeration requested but not enabled\n"); + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Enumeration requested but not enabled\n"); + cr->rctx->enumeration_warn_logged = true; + } + return false; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Domain %s supports enumeration\n", + domain->name); + + return true; +} + +static bool +cache_req_validate_domain_type(struct cache_req *cr, + struct sss_domain_info *domain) +{ + bool valid = false; + + switch (cr->req_dom_type) { + case CACHE_REQ_POSIX_DOM: + valid = domain->type == DOM_TYPE_POSIX ? true : false; + break; + case CACHE_REQ_APPLICATION_DOM: + valid = domain->type == DOM_TYPE_APPLICATION ? true : false; + break; + case CACHE_REQ_ANY_DOM: + valid = true; + break; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Request type %s for domain %s type %s is %svalid\n", + cache_req_dom_type_as_str(cr), + domain->name, + sss_domain_type_str(domain), + valid ? "" : "not "); + return valid; +} + +static bool +cache_req_validate_domain(struct cache_req *cr, + struct sss_domain_info *domain) +{ + bool ok; + + ok = cache_req_validate_domain_enumeration(cr, domain); + if (ok == false) { + return false; + } + + ok = cache_req_validate_domain_type(cr, domain); + if (ok == false) { + return false; + } + + ok = !cr->data->hybrid_lookup || domain->mpg_mode == MPG_HYBRID; + if (ok == false) { + return false; + } + + return true; +} + +static errno_t +cache_req_is_well_known_object(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_result **_result) +{ + errno_t ret; + + if (cr->plugin->is_well_known_fn == NULL) { + return ENOENT; + } + + ret = cr->plugin->is_well_known_fn(mem_ctx, cr, cr->data, _result); + if (ret == EOK) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Object is well known!\n"); + (*_result)->well_known_object = true; + } else if (ret != ENOENT) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Unable to prepare data [%d]: %s\n", + ret, sss_strerror(ret)); + } + + return ret; +} + +static errno_t +cache_req_prepare_domain_data(struct cache_req *cr, + struct sss_domain_info *domain) +{ + errno_t ret; + + if (cr->plugin->prepare_domain_data_fn == NULL) { + return EOK; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Preparing input data for domain [%s] rules\n", + domain->name); + + ret = cr->plugin->prepare_domain_data_fn(cr, cr->data, domain); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Unable to prepare data [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + return EOK; +} + +static errno_t +cache_req_create_debug_name(struct cache_req *cr, + struct sss_domain_info *domain) +{ + if (cr->plugin->create_debug_name_fn == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Bug: no create debug name function specified!\n"); + return ERR_INTERNAL; + } + + talloc_zfree(cr->debugobj); + + cr->debugobj = cr->plugin->create_debug_name_fn(cr, cr->data, domain); + if (cr->debugobj == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Unable to create debug name!\n"); + return ENOMEM; + } + + return EOK; +} + +static errno_t +cache_req_set_domain(struct cache_req *cr, + struct sss_domain_info *domain) +{ + errno_t ret; + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Using domain [%s]\n", domain->name); + + ret = cache_req_prepare_domain_data(cr, domain); + if (ret != EOK) { + return ret; + } + + ret = cache_req_create_debug_name(cr, domain); + if (ret != EOK) { + return ret; + } + + cr->domain = domain; + + return EOK; +} + +static void cache_req_global_ncache_add(struct cache_req *cr) +{ + errno_t ret; + + if (cr->plugin->global_ncache_add_fn == NULL) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, + "This request type does not support " + "global negative cache\n"); + return; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Adding [%s] to global " + "negative cache\n", cr->debugobj); + + ret = cr->plugin->global_ncache_add_fn(cr->ncache, cr->data); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr, + "Cannot set negative cache for [%s] [%d]: %s\n", + cr->debugobj, ret, sss_strerror(ret)); + /* not fatal */ + } + + return; +} + +static bool cache_req_check_acct_domain_lookup_type(struct cache_req *cr, + struct sss_domain_info *dom) +{ + struct sss_domain_info *head; + int nret; + + head = get_domains_head(dom); + if (head == NULL) { + return false; + } + + nret = sss_ncache_check_domain_locate_type(cr->rctx->ncache, + head, + cr->plugin->name); + if (nret == ENOENT) { + return true; + } + return false; +} + +static errno_t cache_req_set_acct_domain_lookup_type(struct cache_req *cr, + struct sss_domain_info *dom) +{ + struct sss_domain_info *head; + + head = get_domains_head(dom); + if (head == NULL) { + return EINVAL; + } + + return sss_ncache_set_domain_locate_type(cr->rctx->ncache, + head, + cr->plugin->name); +} + +static void cache_req_domain_set_locate_flag(struct cache_req_domain *domains, + struct cache_req *cr) +{ + struct cache_req_domain *crd_iter; + + DLIST_FOR_EACH(crd_iter, domains) { + if (cache_req_check_acct_domain_lookup_type(cr, crd_iter->domain)) { + crd_iter->locate_domain = true; + } + } +} + +static bool +cache_req_assume_upn(struct cache_req *cr) +{ + errno_t ret; + + if (cr->plugin->allow_switch_to_upn == false + || cr->data->name.input == NULL + || strchr(cr->data->name.input, '@') == NULL) { + return false; + } + + ret = cache_req_set_plugin(cr, cr->plugin->upn_equivalent); + if (ret != EOK) { + return false; + } + + ret = cache_req_set_name(cr, cr->data->name.input); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_set_name() failed\n"); + return false; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Assuming UPN [%s]\n", + cr->data->name.input); + + return true; +} + +struct cache_req_locate_dom_state { + /* input data */ + struct tevent_context *ev; + struct cache_req *cr; + struct cache_req_domain *req_domains; + + /* Return values in case the first cache lookup succeeds */ + struct ldb_result *result; + bool dp_success; +}; + +static void cache_req_locate_dom_cache_done(struct tevent_req *subreq); +static void cache_req_locate_dom_done(struct tevent_req *subreq); +static void cache_req_locate_dom_mark_neg_all( + struct cache_req_locate_dom_state *state); +static void cache_req_locate_dom_mark_neg_domains( + struct cache_req_locate_dom_state *state, + const char *found_domain_name); + +static struct tevent_req *cache_req_locate_dom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr, + struct cache_req_domain *req_domains) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct cache_req_locate_dom_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct cache_req_locate_dom_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + state->ev = ev; + state->cr = cr; + state->req_domains = req_domains; + + /* It is wasteful to run the domain locator request if the results are + * present in the cache, because the domain locator always contacts + * the DP. Therefore, first run a cache-only search and only if the + * requested data is not available, run the locator + * + * FIXME - this could be optimized further if we are running the + * second iteration with cache_first, then we don't need to search + * again + */ + subreq = cache_req_search_send(state, + state->ev, + state->cr, + false, + true); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + tevent_req_set_callback(subreq, cache_req_locate_dom_cache_done, req); + + return req; + +immediately: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void cache_req_locate_dom_cache_done(struct tevent_req *subreq) +{ + struct cache_req_locate_dom_state *state = NULL; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_locate_dom_state); + + ret = cache_req_search_recv(state, subreq, &state->result, &state->dp_success); + talloc_zfree(subreq); + + switch (ret) { + case EOK: + /* Just finish the request and let the caller handle the result */ + DEBUG(SSSDBG_TRACE_INTERNAL, "Result found in the cache\n"); + tevent_req_done(req); + return; + case ERR_ID_OUTSIDE_RANGE: + case ENOENT: + /* Not cached and locator was requested, run the locator + * DP request plugin + */ + subreq = cache_req_locate_domain_send(state, + state->ev, + state->cr); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, cache_req_locate_dom_done, req); + return; + default: + DEBUG(SSSDBG_OP_FAILURE, + "cache_req_search_recv returned [%d]: %s\n", ret, sss_strerror(ret)); + break; + } + + tevent_req_error(req, ret); + return; +} + +static void cache_req_locate_dom_done(struct tevent_req *subreq) +{ + struct cache_req_locate_dom_state *state; + struct tevent_req *req; + errno_t ret; + char *found_domain_name; + int nret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_locate_dom_state); + + ret = cache_req_locate_domain_recv(state, subreq, &found_domain_name); + talloc_zfree(subreq); + switch (ret) { + case ERR_GET_ACCT_DOM_NOT_SUPPORTED: + nret = cache_req_set_acct_domain_lookup_type(state->cr, + state->cr->domain); + if (nret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to disable domain locating functionality for %s\n", + state->cr->plugin->name); + } + DEBUG(SSSDBG_CONF_SETTINGS, + "Disabled domain locating functionality for %s\n", + state->cr->plugin->name); + break; + case ERR_NOT_FOUND: + cache_req_locate_dom_mark_neg_all(state); + break; + case EOK: + cache_req_locate_dom_mark_neg_domains(state, found_domain_name); + break; + default: + /* We explicitly ignore errors here */ + break; + } + + tevent_req_done(req); + return; +} + +static void cache_req_locate_dom_mark_neg_all( + struct cache_req_locate_dom_state *state) +{ + struct cache_req_domain *iter; + + DLIST_FOR_EACH(iter, state->req_domains) { + if (get_domains_head(state->cr->domain) != get_domains_head(iter->domain)) { + /* Only add to negative cache for domains from the same "main" + * domain" */ + continue; + } + cache_req_search_ncache_add_to_domain(state->cr, iter->domain); + } +} + +static void cache_req_locate_dom_mark_neg_domains( + struct cache_req_locate_dom_state *state, + const char *found_domain_name) +{ + struct sss_domain_info *found_domain; + struct cache_req_domain *iter; + + found_domain = find_domain_by_name(get_domains_head(state->cr->domain), + found_domain_name, + true); + if (found_domain == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot find domain %s\n", found_domain_name); + return; + } + + /* Set negcache in all subdomains of the one being examined + * except the found one */ + DLIST_FOR_EACH(iter, state->req_domains) { + if (strcasecmp(found_domain_name, + iter->domain->name) == 0) { + continue; + } + + if (get_domains_head(found_domain) != get_domains_head(iter->domain)) { + /* Don't set negative cache for domains outside the main + * domain/subdomain tree b/c the locator request is not + * authoritative for them + */ + continue; + } + cache_req_search_ncache_add_to_domain(state->cr, iter->domain); + } +} + +static errno_t cache_req_locate_dom_cache_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ldb_result **_result, + bool *_dp_success) +{ + struct cache_req_locate_dom_state *state; + + state = tevent_req_data(req, struct cache_req_locate_dom_state); + + if (_dp_success != NULL) { + *_dp_success = state->dp_success; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_result != NULL) { + *_result = talloc_steal(mem_ctx, state->result); + } + + return EOK; +} + +struct cache_req_search_domains_state { + /* input data */ + struct tevent_context *ev; + struct cache_req *cr; + + /* work data */ + struct cache_req_domain *cr_domain; + struct cache_req_domain *req_domains; + struct sss_domain_info *selected_domain; + struct cache_req_result **results; + size_t num_results; + bool check_next; + bool dp_success; + bool first_iteration; +}; + +static errno_t cache_req_search_domains_next(struct tevent_req *req); +static errno_t cache_req_handle_result(struct tevent_req *req, + struct ldb_result *result); + +static void cache_req_search_domains_locate_done(struct tevent_req *subreq); + +static void cache_req_search_domains_done(struct tevent_req *subreq); + +static bool +cache_req_dp_contacted(struct cache_req_search_domains_state *state) +{ + switch (state->cr->cache_behavior) { + case CACHE_REQ_CACHE_FIRST: + if (state->first_iteration) { + /* This is the first iteration so provider was bypassed. */ + return false; + } + + /* This is the second iteration so the provider was contacted. */ + return true; + case CACHE_REQ_BYPASS_PROVIDER: + return false; + default: + /* Other schemas talks to provider immediately. */ + return true; + } +} + +struct tevent_req * +cache_req_search_domains_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr, + struct cache_req_domain *cr_domain, + bool check_next, + bool first_iteration) +{ + struct tevent_req *req; + struct cache_req_search_domains_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct cache_req_search_domains_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->cr = cr; + + state->cr_domain = cr_domain; + state->req_domains = cr_domain; + state->check_next = check_next; + state->dp_success = true; + state->first_iteration = first_iteration; + + if (cr->plugin->dp_get_domain_send_fn != NULL + && ((state->check_next && cr_domain->next != NULL) + || ((state->cr->cache_behavior == CACHE_REQ_CACHE_FIRST) + && !first_iteration))) { + /* If the request is not qualified with a domain name AND + * there are multiple domains to search OR if this is the second + * pass during the "check-cache-first" schema, it makes sense + * to try to run the domain-locator plugin + */ + cache_req_domain_set_locate_flag(cr_domain, cr); + } + + ret = cache_req_search_domains_next(req); + if (ret == EAGAIN) { + return req; + } + + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + + tevent_req_post(req, ev); + return req; +} + +static errno_t cache_req_search_domains_next(struct tevent_req *req) +{ + struct cache_req_search_domains_state *state; + struct tevent_req *subreq; + struct cache_req *cr; + struct sss_domain_info *domain; + uint32_t next_domain_flag; + bool is_domain_valid; + bool allow_no_fqn; + errno_t ret; + + state = tevent_req_data(req, struct cache_req_search_domains_state); + cr = state->cr; + + next_domain_flag = cr->plugin->get_next_domain_flags; + allow_no_fqn = cr->plugin->allow_missing_fqn; + + while (state->cr_domain != NULL) { + domain = state->cr_domain->domain; + + if (domain == NULL) { + break; + } + + /* As the cr_domain list is a flatten version of the domains + * list, we have to ensure to only go through the subdomains in + * case it's specified in the plugin to do so. + */ + if (next_domain_flag == 0 && IS_SUBDOMAIN(domain)) { + state->cr_domain = state->cr_domain->next; + continue; + } + + /* Check if this domain is valid for this request. */ + is_domain_valid = cache_req_validate_domain(cr, domain); + if (!is_domain_valid) { + state->cr_domain = state->cr_domain->next; + continue; + } + + /* If not specified otherwise, we skip domains that require fully + * qualified names on domain less search. We do not descend into + * subdomains here since those are implicitly qualified. + */ + if (state->check_next && !allow_no_fqn && state->cr_domain->fqnames) { + state->cr_domain = state->cr_domain->next; + continue; + } + + state->selected_domain = domain; + + ret = cache_req_set_domain(cr, domain); + if (ret != EOK) { + return ret; + } + + if (state->cr_domain->locate_domain) { + subreq = cache_req_locate_dom_send(state, + state->ev, + cr, + state->req_domains); + if (subreq == NULL) { + return ENOMEM; + } + tevent_req_set_callback(subreq, cache_req_search_domains_locate_done, req); + return EAGAIN; + } + + subreq = cache_req_search_send(state, state->ev, cr, + state->first_iteration, + false); + if (subreq == NULL) { + return ENOMEM; + } + tevent_req_set_callback(subreq, cache_req_search_domains_done, req); + + /* we will continue with the following domain the next time */ + if (state->check_next) { + state->cr_domain = state->cr_domain->next; + } + + return EAGAIN; + } + + /* If we've got some result from previous searches we want to return + * EOK here so the whole cache request is successfully finished. */ + if (state->num_results > 0) { + return EOK; + } + + /* We have searched all available domains and no result was found. + * + * If the plug-in uses a negative cache which is shared among all domains + * (e.g. unique identifiers such as user or group id or sid), we add it + * here and return object not found error. + * + * However, we can only set the negative cache if all data provider + * requests succeeded because only then we can be sure that it does + * not exist- + */ + if (cache_req_dp_contacted(state) && state->dp_success) { + cache_req_global_ncache_add(cr); + } + + return ENOENT; +} + +static void cache_req_search_domains_locate_done(struct tevent_req *subreq) +{ + struct cache_req_search_domains_state *state; + struct ldb_result *result = NULL; + struct tevent_req *req; + bool dp_success; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_search_domains_state); + + ret = cache_req_locate_dom_cache_recv(state, subreq, &result, &dp_success); + talloc_zfree(subreq); + + /* Remember if any DP request fails, but here it shouldn't matter + * as the only DP request that should realistically happen is midpoint + * refresh */ + state->dp_success = !dp_success ? false : state->dp_success; + + /* Don't locate the domain again */ + state->cr_domain->locate_domain = false; + + switch (ret) { + case EOK: + if (result != NULL) { + /* Handle result as normally */ + ret = cache_req_handle_result(req, result); + if (ret != EAGAIN) { + goto done; + } + } + break; + default: + /* Some serious error has happened. Finish. */ + goto done; + } + + /* This is a domain less search, continue with the next domain. */ + ret = cache_req_search_domains_next(req); + +done: + switch (ret) { + case EOK: + tevent_req_done(req); + break; + case EAGAIN: + break; + default: + tevent_req_error(req, ret); + break; + } + return; +} + +static errno_t cache_req_handle_result(struct tevent_req *req, + struct ldb_result *result) +{ + struct cache_req_search_domains_state *state; + errno_t ret; + + state = tevent_req_data(req, struct cache_req_search_domains_state); + + /* We got some data from this search. Save it. */ + ret = cache_req_create_and_add_result(state, + state->cr, + state->selected_domain, + result, + state->cr->data->name.lookup, + &state->results, + &state->num_results); + if (ret != EOK) { + /* We were unable to save data. */ + return ret; + } + + if (!state->check_next || !state->cr->plugin->search_all_domains) { + /* We are not interested in more results. */ + return EOK; + } + + return EAGAIN; +} + +static void cache_req_search_domains_done(struct tevent_req *subreq) +{ + struct cache_req_search_domains_state *state; + struct ldb_result *result; + struct tevent_req *req; + bool dp_success; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_search_domains_state); + + ret = cache_req_search_recv(state, subreq, &result, &dp_success); + talloc_zfree(subreq); + + /* Remember if any DP request fails, if DP was contacted. */ + if (cache_req_dp_contacted(state)) { + state->dp_success = !dp_success ? false : state->dp_success; + } + + switch (ret) { + case EOK: + ret = cache_req_handle_result(req, result); + if (ret != EAGAIN) { + goto done; + } + break; + case ERR_ID_OUTSIDE_RANGE: + case ENOENT: + if (state->check_next == false) { + if (cache_req_dp_contacted(state) + && !state->dp_success + && state->cr->data->propogate_offline_status) { + /* Not found and data provider request failed so we were + * unable to fetch the data. */ + ret = ERR_OFFLINE; + goto done; + } + + /* Not found. */ + ret = ENOENT; + goto done; + } + + /* Continue with next domain. */ + break; + default: + /* Some serious error has happened. Finish. */ + goto done; + } + + /* This is a domain less search, continue with the next domain. */ + ret = cache_req_search_domains_next(req); + +done: + if (ret == ENOENT && state->results != NULL) { + /* We have at least one result. */ + ret = EOK; + } + + switch (ret) { + case EOK: + tevent_req_done(req); + break; + case EAGAIN: + break; + default: + if (cache_req_dp_contacted(state) + && ret == ENOENT + && !state->dp_success + && state->cr->data->propogate_offline_status) { + /* Not found and data provider request failed so we were + * unable to fetch the data. */ + ret = ERR_OFFLINE; + } + tevent_req_error(req, ret); + break; + } + + return; +} + +static errno_t +cache_req_search_domains_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req_result ***_results, + size_t *_num_results) +{ + struct cache_req_search_domains_state *state; + + state = tevent_req_data(req, struct cache_req_search_domains_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_results != NULL) { + *_results = talloc_steal(mem_ctx, state->results); + } + if (_num_results != NULL) { + *_num_results = state->num_results; + } + + return EOK; +} + +struct cache_req_state { + /* input data */ + struct tevent_context *ev; + struct cache_req *cr; + const char *domain_name; + + /* work data */ + struct cache_req_domain *cr_domains; + struct cache_req_result **results; + size_t num_results; + bool first_iteration; +}; + +static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req *cr, + const char *domain); + +static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req *cr, + const char *domain); + +static void cache_req_domains_updated(struct tevent_req *subreq); + +static void cache_req_input_parsed(struct tevent_req *subreq); + +static errno_t cache_req_select_domains(struct tevent_req *req, + const char *domain_name, + char **requested_domains); + +static errno_t +cache_req_search_domains(struct tevent_req *req, + struct cache_req_domain *oredered_domain, + bool check_next); + +static void cache_req_process_result(struct tevent_req *subreq); + +static void cache_req_done(struct tevent_req *subreq); + +struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int midpoint, + enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data) +{ + struct cache_req_state *state; + struct cache_req_result *result; + struct cache_req *cr; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct cache_req_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->cr = cr = cache_req_create(state, rctx, data, + ncache, midpoint, req_dom_type); + if (state->cr == NULL) { + ret = ENOMEM; + goto done; + } + state->first_iteration = true; + + SSS_REQ_TRACE_CID_CR(SSSDBG_TRACE_FUNC, cr, "New request [CID #%lu] '%s'\n", + sss_chain_id_get(), cr->reqname); + + ret = cache_req_is_well_known_object(state, cr, &result); + if (ret == EOK) { + ret = cache_req_add_result(state, result, &state->results, + &state->num_results); + goto done; + } else if (ret != ENOENT) { + goto done; + } + + state->domain_name = domain; + ret = cache_req_process_input(state, req, cr, domain); + if (ret != EOK) { + goto done; + } + + ret = cache_req_select_domains(req, state->domain_name, + cr->data->requested_domains); + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req *cr, + const char *domain) +{ + struct tevent_req *subreq; + const char *default_domain; + errno_t ret; + + if (cr->data->name.input == NULL) { + /* Call cache_req_update_domains() in order to get a up to date list + * of domains and subdomains, if needed. Otherwise just return EOK as + * the input was not a name, thus there's no need to process it + * further. */ + return cache_req_update_domains(mem_ctx, req, cr, domain); + } + + if (cr->plugin->parse_name == false) { + /* Call cache_req_update_domains() in order to get a up to date list + * of domains and subdomains, if needed. Otherwise, just use the input + * name as it is. */ + ret = cache_req_update_domains(mem_ctx, req, cr, domain); + if (ret != EOK) { + return ret; + } + + return cache_req_set_name(cr, cr->data->name.input); + } + + default_domain = NULL; + if (!cr->plugin->ignore_default_domain) { + default_domain = cr->rctx->default_domain; + } + + /* Parse name since it may contain a domain name. */ + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Parsing input name [%s]\n", cr->data->name.input); + + subreq = sss_parse_inp_send(mem_ctx, cr->rctx, default_domain, + cr->data->name.input); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_inp_send() failed\n"); + return ENOMEM; + } + + tevent_req_set_callback(subreq, cache_req_input_parsed, req); + + return EAGAIN; +} + +static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req *cr, + const char *domain) +{ + struct tevent_req *subreq; + + if (cr->rctx->get_domains_last_call.tv_sec != 0) { + return EOK; + } + + subreq = sss_dp_get_domains_send(mem_ctx, cr->rctx, false, domain); + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, cache_req_domains_updated, req); + return EAGAIN; +} + +static void cache_req_domains_updated(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct cache_req_state *state; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_state); + + ret = sss_dp_get_domains_recv(subreq); + talloc_free(subreq); + if (ret != EOK) { + goto done; + } + + if (state->cr->data->name.input == NULL) { + /* Input was not name, there is no need to process it further. */ + goto immediately; + } + + if (state->cr->plugin->parse_name == false || state->domain_name != NULL) { + /* We do not want to parse the name. */ + ret = cache_req_set_name(state->cr, state->cr->data->name.input); + if (ret != EOK) { + goto done; + } + } + +immediately: + ret = cache_req_select_domains(req, state->domain_name, + state->cr->data->requested_domains); + +done: + if (ret != EOK && ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } +} + +static void cache_req_input_parsed(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct cache_req_state *state; + char *name; + char *domain = NULL; + bool maybe_upn; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_state); + + ret = sss_parse_inp_recv(subreq, state, &name, &domain); + + if (state->domain_name != NULL && domain != NULL + && strcmp(state->domain_name, domain) != 0){ + DEBUG(SSSDBG_CRIT_FAILURE, + "Mismatch between input domain name [%s] and parsed domain name [%s]\n", + state->domain_name, domain); + tevent_req_error(req, ERR_INPUT_PARSE); + return; + } + + switch (ret) { + case EOK: + ret = cache_req_set_name(state->cr, name); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + break; + case ERR_DOMAIN_NOT_FOUND: + maybe_upn = cache_req_assume_upn(state->cr); + if (!maybe_upn) { + tevent_req_error(req, ret); + return; + } + + domain = NULL; + break; + default: + tevent_req_error(req, ret); + return; + } + + if (state->domain_name == NULL) { + state->domain_name = domain; + } + ret = cache_req_select_domains(req, state->domain_name, + state->cr->data->requested_domains); + if (ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } +} + +static errno_t cache_req_select_domains(struct tevent_req *req, + const char *domain_name, + char **requested_domains) +{ + struct cache_req_state *state = NULL; + struct cache_req_domain *cr_domain; + bool check_next; + errno_t ret; + + state = tevent_req_data(req, struct cache_req_state); + + if (state->cr->cache_behavior != CACHE_REQ_CACHE_FIRST) { + + if (!state->first_iteration) { + /* We're done here. */ + return EOK; + } + } + + ret = cache_req_domain_copy_cr_domains(state, + state->cr->rctx->cr_domains, + requested_domains, + &state->cr_domains); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_copy_cr_domains() failed\n"); + return EINVAL; + } + + if (domain_name != NULL) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Performing a single domain search\n"); + + cr_domain = cache_req_domain_get_domain_by_name( + state->cr_domains, domain_name); + if (cr_domain == NULL) { + return ERR_DOMAIN_NOT_FOUND; + } + check_next = false; + } else { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Performing a multi-domain search\n"); + + cr_domain = state->cr_domains; + check_next = true; + } + + return cache_req_search_domains(req, cr_domain, check_next); +} + +static errno_t +cache_req_search_domains(struct tevent_req *req, + struct cache_req_domain *cr_domain, + bool check_next) +{ + struct tevent_req *subreq; + struct cache_req_state *state = NULL; + const char *cache_action; + const char *provider_action; + + state = tevent_req_data(req, struct cache_req_state); + + switch (state->cr->cache_behavior) { + case CACHE_REQ_CACHE_FIRST: + cache_action = (state->first_iteration) ? "check" : "bypass"; + provider_action = (state->first_iteration) ? "bypass" : "check"; + break; + case CACHE_REQ_BYPASS_CACHE: + cache_action = "bypass"; + provider_action = "check"; + break; + case CACHE_REQ_BYPASS_PROVIDER: + cache_action = "check"; + provider_action = "bypass"; + break; + default: + cache_action = "check"; + provider_action = "check"; + break; + } + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Search will %s the cache and %s the data provider\n", + cache_action, provider_action); + + subreq = cache_req_search_domains_send(state, state->ev, state->cr, + cr_domain, check_next, + state->first_iteration); + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, cache_req_process_result, req); + return EAGAIN; +} + +static void cache_req_process_result(struct tevent_req *subreq) +{ + struct cache_req_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_state); + + ret = cache_req_search_domains_recv(state, subreq, + &state->results, &state->num_results); + talloc_zfree(subreq); + + if (ret == ENOENT && state->first_iteration) { + /* Try again different search schema. */ + state->first_iteration = false; + ret = cache_req_select_domains(req, state->domain_name, + state->cr->data->requested_domains); + if (ret == EOK) { + /* We're done searching and we have found nothing. */ + ret = ENOENT; + } + } + + /* Have have tried all domains and found nothing. Let's try UPN search. */ + if (ret == ENOENT) { + if (state->domain_name != NULL) { + /* Lookup domain was specified as input. Since we haven't + * found anything yet we may want to try UPN search with + * some plug-ins. */ + + if (cache_req_assume_upn(state->cr)) { + /* Try UPN now. */ + state->first_iteration = true; + ret = cache_req_select_domains(req, NULL, + state->cr->data->requested_domains); + } + } + } + + /* Overlay each result with session recording flag */ + if (ret == EOK) { + subreq = cache_req_sr_overlay_send(state, state->ev, state->cr, + state->results, + state->num_results); + if (subreq == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Failed creating a session recording " + "overlay request\n"); + ret = ENOMEM; + } else { + tevent_req_set_callback(subreq, cache_req_done, req); + ret = EAGAIN; + } + } + + switch (ret) { + case EAGAIN: + break; + case ENOENT: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Not found\n"); + tevent_req_error(req, ret); + break; + default: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Finished: Error %d: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + break; + } + + return; +} + +static void cache_req_done(struct tevent_req *subreq) +{ + struct cache_req_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_state); + ret = cache_req_sr_overlay_recv(subreq); + talloc_zfree(subreq); + + switch (ret) { + case EOK: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Success\n"); + tevent_req_done(req); + break; + default: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Finished: Error %d: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + break; + } +} + +uint32_t cache_req_get_reqid(struct tevent_req *req) +{ + const struct cache_req_state *state; + + state = tevent_req_data(req, struct cache_req_state); + + if (state && state->cr) { + return state->cr->reqid; + } + + return 0; +} + +errno_t cache_req_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req_result ***_results) +{ + struct cache_req_state *state; + + state = tevent_req_data(req, struct cache_req_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_results != NULL) { + *_results = talloc_steal(mem_ctx, state->results); + } + + return EOK; +} + +errno_t cache_req_single_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req_result **_result) +{ + struct cache_req_state *state; + + state = tevent_req_data(req, struct cache_req_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_result != NULL) { + *_result = talloc_steal(mem_ctx, state->results[0]); + } + + return EOK; +} + +struct tevent_req * +cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data) +{ + struct tevent_req *req; + + req = cache_req_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + req_dom_type, domain, data); + if (req == NULL) { + talloc_zfree(data); + return NULL; + } + + talloc_steal(req, data); + + return req; +} diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h new file mode 100644 index 0000000..a0c6879 --- /dev/null +++ b/src/responder/common/cache_req/cache_req.h @@ -0,0 +1,521 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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/>. +*/ + +#ifndef _CACHE_REQ_H_ +#define _CACHE_REQ_H_ + +#include "util/util.h" +#include "confdb/confdb.h" +#include "responder/common/negcache.h" + +enum cache_req_type { + CACHE_REQ_USER_BY_NAME, + CACHE_REQ_USER_BY_UPN, + CACHE_REQ_USER_BY_ID, + CACHE_REQ_USER_BY_CERT, + CACHE_REQ_USER_BY_FILTER, + + CACHE_REQ_GROUP_BY_NAME, + CACHE_REQ_GROUP_BY_ID, + CACHE_REQ_GROUP_BY_FILTER, + + CACHE_REQ_INITGROUPS, + CACHE_REQ_INITGROUPS_BY_UPN, + +#ifdef BUILD_SUBID + CACHE_REQ_SUBID_RANGES_BY_NAME, +#endif + + CACHE_REQ_OBJECT_BY_SID, + CACHE_REQ_OBJECT_BY_NAME, + CACHE_REQ_OBJECT_BY_ID, + + CACHE_REQ_ENUM_USERS, + CACHE_REQ_ENUM_GROUPS, + CACHE_REQ_ENUM_SVC, + CACHE_REQ_ENUM_HOST, + CACHE_REQ_ENUM_IP_NETWORK, + + CACHE_REQ_SVC_BY_NAME, + CACHE_REQ_SVC_BY_PORT, + + CACHE_REQ_NETGROUP_BY_NAME, + + CACHE_REQ_SSH_HOST_ID_BY_NAME, + + CACHE_REQ_AUTOFS_MAP_ENTRIES, + CACHE_REQ_AUTOFS_MAP_BY_NAME, + CACHE_REQ_AUTOFS_ENTRY_BY_NAME, + + CACHE_REQ_IP_HOST_BY_NAME, + CACHE_REQ_IP_HOST_BY_ADDR, + CACHE_REQ_IP_NETWORK_BY_NAME, + CACHE_REQ_IP_NETWORK_BY_ADDR, + + CACHE_REQ_SENTINEL +}; + +/* Whether to limit the request type to a certain domain type + * (POSIX/non-POSIX) + */ +enum cache_req_dom_type { + /* Only look up data in POSIX domains */ + CACHE_REQ_POSIX_DOM, + /* Only look up data in application domains */ + CACHE_REQ_APPLICATION_DOM, + /* Look up data in any domain type */ + CACHE_REQ_ANY_DOM +}; + +/* Controls behavior about how to use cached information during + * a lookup, this is to fine tune some behaviors for specific + * situations + */ +enum cache_req_behavior { + CACHE_REQ_NORMAL, + CACHE_REQ_CACHE_FIRST, + CACHE_REQ_BYPASS_CACHE, + CACHE_REQ_BYPASS_PROVIDER, +}; + +/* Input data. */ + +struct cache_req_data; + +struct cache_req_data * +cache_req_data_attr(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *attr, + const char *filter); + +struct cache_req_data * +cache_req_data_name(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name); + +struct cache_req_data * +cache_req_data_name_attrs(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name, + const char **attrs); + +struct cache_req_data * +cache_req_data_id(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + uint32_t id); + +struct cache_req_data * +cache_req_data_id_attrs(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + uint32_t id, + const char **attrs); + +struct cache_req_data * +cache_req_data_cert(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *cert); + +struct cache_req_data * +cache_req_data_sid(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *sid, + const char **attrs); + +struct cache_req_data * +cache_req_data_addr(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + uint32_t af, + uint32_t addrlen, + uint8_t *addr); + +struct cache_req_data * +cache_req_data_enum(TALLOC_CTX *mem_ctx, + enum cache_req_type type); + +struct cache_req_data * +cache_req_data_svc(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name, + const char *protocol, + uint16_t port); + +struct cache_req_data * +cache_req_data_ssh_host_id(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name, + const char *alias, + const char **attrs); + +struct cache_req_data * +cache_req_data_autofs_entry(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *mapname, + const char *entryname); + +void +cache_req_data_set_bypass_cache(struct cache_req_data *data, + bool bypass_cache); + +void +cache_req_data_set_bypass_dp(struct cache_req_data *data, + bool bypass_dp); + +void +cache_req_data_set_requested_domains(struct cache_req_data *data, + char **requested_domains); + +void +cache_req_data_set_propogate_offline_status(struct cache_req_data *data, + bool propogate_offline_status); + +void +cache_req_data_set_hybrid_lookup(struct cache_req_data *data, + bool hybrid_lookup); + +enum cache_req_type +cache_req_data_get_type(struct cache_req_data *data); + +/* Output data. */ + +struct cache_req_result { + /** + * SSSD domain where the result was obtained. + */ + struct sss_domain_info *domain; + + /** + * Result from ldb lookup. + */ + struct ldb_result *ldb_result; + + /** + * Shortcuts into ldb_result. This shortens the code a little since + * callers usually don't don't need to work with ldb_result directly. + */ + unsigned int count; + struct ldb_message **msgs; + + /** + * If name was used as a lookup parameter, @lookup_name contains name + * normalized to @domain rules. + */ + const char *lookup_name; + + /** + * If true the result contain attributes of a well known object. + * Since this result is manually created it may not contain all + * requested attributes, depending on the plug-in. + */ + bool well_known_object; + + /* If this is a well known object, it may not be part of any particular + * SSSD domain, but still may be associated with a well known domain + * name such as "BUILTIN", or "LOCAL AUTHORITY". + */ + const char *well_known_domain; +}; + +/** + * Shallow copy of cache request result, limiting the result to a maximum + * numbers of records. + */ +struct cache_req_result * +cache_req_copy_limited_result(TALLOC_CTX *mem_ctx, + struct cache_req_result *result, + uint32_t start, + uint32_t limit); + +/* Generic request. */ + +struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int midpoint, + enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data); + +uint32_t cache_req_get_reqid(struct tevent_req *req); + +errno_t cache_req_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req_result ***_results); + +errno_t cache_req_single_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct cache_req_result **_result); + +/* Plug-ins. */ + +struct tevent_req * +cache_req_user_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name); + +#define cache_req_user_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs); + +#define cache_req_user_by_name_attrs_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_user_by_upn_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *upn); + +#define cache_req_user_by_upn_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result); + +struct tevent_req * +cache_req_user_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + uid_t uid); + +#define cache_req_user_by_id_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result); + +struct tevent_req * +cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *pem_cert); + +#define cache_req_user_by_cert_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_group_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name); + +#define cache_req_group_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_group_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + gid_t gid); + +#define cache_req_group_by_id_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name); + +#define cache_req_initgr_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *attr, + const char *filter); + +#define cache_req_user_by_filter_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *filter); + +#define cache_req_group_by_filter_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_object_by_sid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *sid, + const char **attrs); + +#define cache_req_object_by_sid_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_object_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs); + +#define cache_req_object_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_object_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + uint32_t id, + const char **attrs); + +#define cache_req_object_by_id_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_svc_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char *protocol); + +#define cache_req_svc_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_svc_by_port_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + uint16_t port, + const char *protocol); + +#define cache_req_svc_by_port_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_netgroup_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name); + +#define cache_req_netgroup_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_ssh_host_id_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char *alias, + const char **attrs); + +#define cache_req_ssh_host_id_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_autofs_map_entries_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name); + +#define cache_req_autofs_map_entries_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_autofs_map_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name); + +#define cache_req_autofs_map_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +struct tevent_req * +cache_req_autofs_entry_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *mapname, + const char *entryname); + +#define cache_req_autofs_entry_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + +#endif /* _CACHE_REQ_H_ */ diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c new file mode 100644 index 0000000..c506de5 --- /dev/null +++ b/src/responder/common/cache_req/cache_req_data.c @@ -0,0 +1,519 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> + +#include "db/sysdb.h" +#include "responder/common/cache_req/cache_req_private.h" + +static const char ** +cache_req_data_create_attrs(TALLOC_CTX *mem_ctx, + const char **requested) +{ + static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS, SYSDB_NAME, + OVERRIDE_PREFIX SYSDB_NAME, + SYSDB_DEFAULT_OVERRIDE_NAME }; + static size_t defnum = sizeof(defattrs) / sizeof(defattrs[0]); + const char **attrs; + size_t reqnum; + size_t total; + size_t i; + + for (reqnum = 0; requested[reqnum] != NULL; reqnum++); + + total = defnum + reqnum; + + /* We always want to get default attributes. */ + attrs = talloc_zero_array(mem_ctx, const char *, total + 1); + if (attrs == NULL) { + return NULL; + } + + for (i = 0; i < reqnum; i++) { + attrs[i] = talloc_strdup(attrs, requested[i]); + if (attrs[i] == NULL) { + talloc_free(attrs); + return NULL; + } + } + + for (/* continue */; i < total; i++) { + attrs[i] = talloc_strdup(attrs, defattrs[i - reqnum]); + if (attrs[i] == NULL) { + talloc_free(attrs); + return NULL; + } + } + + return attrs; +} + +static struct cache_req_data * +cache_req_data_create(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const struct cache_req_data *input) +{ + struct cache_req_data *data; + errno_t ret; + + data = talloc_zero(mem_ctx, struct cache_req_data); + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n"); + return NULL; + } + + data->type = type; + data->svc.name = &data->name; + + switch (type) { + case CACHE_REQ_USER_BY_FILTER: + if (input->name.attr == NULL) { + data->name.attr = NULL; + } else { + data->name.attr = talloc_strdup(data, input->name.attr); + if (data->name.attr == NULL) { + ret = ENOMEM; + goto done; + } + } + /* Fallthrough */ + case CACHE_REQ_USER_BY_NAME: + case CACHE_REQ_USER_BY_UPN: + case CACHE_REQ_GROUP_BY_NAME: + case CACHE_REQ_GROUP_BY_FILTER: + case CACHE_REQ_INITGROUPS: + case CACHE_REQ_INITGROUPS_BY_UPN: +#ifdef BUILD_SUBID + case CACHE_REQ_SUBID_RANGES_BY_NAME: +#endif + case CACHE_REQ_NETGROUP_BY_NAME: + case CACHE_REQ_OBJECT_BY_NAME: + case CACHE_REQ_AUTOFS_MAP_ENTRIES: + case CACHE_REQ_AUTOFS_MAP_BY_NAME: + case CACHE_REQ_IP_HOST_BY_NAME: + case CACHE_REQ_IP_NETWORK_BY_NAME: + if (input->name.input == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n"); + ret = ERR_INTERNAL; + goto done; + } + + data->name.input = talloc_strdup(data, input->name.input); + if (data->name.input == NULL) { + ret = ENOMEM; + goto done; + } + break; + case CACHE_REQ_IP_HOST_BY_ADDR: + case CACHE_REQ_IP_NETWORK_BY_ADDR: + data->addr.af = input->addr.af; + data->addr.len = input->addr.len; + data->addr.data = talloc_memdup(data, input->addr.data, + input->addr.len); + if (data->addr.data == NULL) { + ret = ENOMEM; + goto done; + } + break; + case CACHE_REQ_USER_BY_CERT: + if (input->cert == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: certificate cannot be NULL!\n"); + ret = ERR_INTERNAL; + goto done; + } + + data->cert = talloc_strdup(data, input->cert); + if (data->cert == NULL) { + ret = ENOMEM; + goto done; + } + break; + case CACHE_REQ_USER_BY_ID: + case CACHE_REQ_GROUP_BY_ID: + case CACHE_REQ_OBJECT_BY_ID: + data->id = input->id; + break; + case CACHE_REQ_OBJECT_BY_SID: + if (input->sid == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: SID cannot be NULL!\n"); + ret = ERR_INTERNAL; + goto done; + } + + data->sid = talloc_strdup(data, input->sid); + if (data->sid == NULL) { + ret = ENOMEM; + goto done; + } + break; + case CACHE_REQ_ENUM_USERS: + case CACHE_REQ_ENUM_GROUPS: + case CACHE_REQ_ENUM_SVC: + case CACHE_REQ_ENUM_HOST: + case CACHE_REQ_ENUM_IP_NETWORK: + break; + case CACHE_REQ_SVC_BY_NAME: + if ((input->svc.name == NULL) || (input->svc.name->input == NULL)) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n"); + ret = ERR_INTERNAL; + goto done; + } + + data->svc.name->input = talloc_strdup(data, input->svc.name->input); + if (data->svc.name->input == NULL) { + ret = ENOMEM; + goto done; + } + + if (input->svc.protocol.name == NULL) { + break; + } + + data->svc.protocol.name = talloc_strdup(data, input->svc.protocol.name); + if (data->svc.protocol.name == NULL) { + ret = ENOMEM; + goto done; + } + + break; + case CACHE_REQ_SVC_BY_PORT: + if (input->svc.port == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: port cannot be 0!\n"); + ret = ERR_INTERNAL; + goto done; + } + + data->svc.port = input->svc.port; + + if (input->svc.protocol.name == NULL) { + break; + } + + data->svc.protocol.name = talloc_strdup(data, input->svc.protocol.name); + if (data->svc.protocol.name == NULL) { + ret = ENOMEM; + goto done; + } + + break; + case CACHE_REQ_SSH_HOST_ID_BY_NAME: + if (input->name.input == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n"); + ret = ERR_INTERNAL; + goto done; + } + + data->name.input = talloc_strdup(data, input->name.input); + if (data->name.input == NULL) { + ret = ENOMEM; + goto done; + } + + if (input->alias == NULL) { + break; + } + + data->alias = talloc_strdup(data, input->alias); + if (data->alias == NULL) { + ret = ENOMEM; + goto done; + } + break; + case CACHE_REQ_AUTOFS_ENTRY_BY_NAME: + if (input->name.input == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n"); + ret = ERR_INTERNAL; + goto done; + } + + data->name.input = talloc_strdup(data, input->name.input); + if (data->name.input == NULL) { + ret = ENOMEM; + goto done; + } + + data->autofs_entry_name = talloc_strdup(data, input->autofs_entry_name); + if (data->autofs_entry_name == NULL) { + ret = ENOMEM; + goto done; + } + break; + case CACHE_REQ_SENTINEL: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid cache request type!\n"); + ret = ERR_INTERNAL; + goto done; + } + + if (input->attrs != NULL) { + data->attrs = cache_req_data_create_attrs(data, input->attrs); + if (data->attrs == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_zfree(data); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create cache_req data " + "[%d]: %s\n", ret, sss_strerror(ret)); + return NULL; + } + + return data; +} + +struct cache_req_data * +cache_req_data_name(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name) +{ + struct cache_req_data input = {0}; + + input.name.input = name; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_name_attrs(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name, + const char **attrs) +{ + struct cache_req_data input = { 0 }; + + input.name.input = name; + input.attrs = attrs; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_attr(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *attr, + const char *filter) +{ + struct cache_req_data input = {0}; + + input.name.input = filter; + input.name.attr = attr; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_id(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + uint32_t id) +{ + struct cache_req_data input = {0}; + + input.id = id; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_id_attrs(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + uint32_t id, + const char **attrs) +{ + struct cache_req_data input = { 0 }; + + input.id = id; + input.attrs = attrs; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_cert(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *cert) +{ + struct cache_req_data input = {0}; + + input.cert = cert; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_sid(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *sid, + const char **attrs) +{ + struct cache_req_data input = {0}; + + input.sid = sid; + input.attrs = attrs; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_enum(TALLOC_CTX *mem_ctx, + enum cache_req_type type) +{ + struct cache_req_data input = { 0 }; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_svc(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name, + const char *protocol, + uint16_t port) +{ + struct cache_req_data input = { 0 }; + + input.name.input = name; + input.svc.name = &input.name; + input.svc.protocol.name = protocol; + input.svc.port = port; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_ssh_host_id(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *name, + const char *alias, + const char **attrs) +{ + struct cache_req_data input = {0}; + + input.name.input = name; + input.alias = alias; + input.attrs = attrs; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_addr(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + uint32_t af, + uint32_t addrlen, + uint8_t *addr) +{ + struct cache_req_data input = {0}; + + input.addr.af = af; + input.addr.len = addrlen; + input.addr.data = addr; + + return cache_req_data_create(mem_ctx, type, &input); +} + +struct cache_req_data * +cache_req_data_autofs_entry(TALLOC_CTX *mem_ctx, + enum cache_req_type type, + const char *mapname, + const char *entryname) +{ + struct cache_req_data input = {0}; + + input.name.input = mapname; + input.autofs_entry_name = entryname; + + return cache_req_data_create(mem_ctx, type, &input); +} + +void +cache_req_data_set_bypass_cache(struct cache_req_data *data, + bool bypass_cache) +{ + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n"); + return; + } + + data->bypass_cache = bypass_cache; +} + +void +cache_req_data_set_bypass_dp(struct cache_req_data *data, + bool bypass_dp) +{ + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n"); + return; + } + + data->bypass_dp = bypass_dp; +} + +void +cache_req_data_set_requested_domains(struct cache_req_data *data, + char **requested_domains) +{ + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n"); + return; + } + + data->requested_domains = requested_domains; +} + +void +cache_req_data_set_propogate_offline_status(struct cache_req_data *data, + bool propogate_offline_status) +{ + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n"); + return; + } + + data->propogate_offline_status = propogate_offline_status; +} + +void +cache_req_data_set_hybrid_lookup(struct cache_req_data *data, + bool hybrid_lookup) +{ + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n"); + return; + } + + data->hybrid_lookup = hybrid_lookup; +} + + +enum cache_req_type +cache_req_data_get_type(struct cache_req_data *data) +{ + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n"); + return CACHE_REQ_SENTINEL; + } + + return data->type; +} diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c new file mode 100644 index 0000000..1f3b690 --- /dev/null +++ b/src/responder/common/cache_req/cache_req_domain.c @@ -0,0 +1,317 @@ +/* + Authors: + Fabiano Fidêncio <fidencio@redhat.com> + + Copyright (C) 2017 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 "responder/common/cache_req/cache_req_domain.h" + +struct cache_req_domain * +cache_req_domain_get_domain_by_name(struct cache_req_domain *domains, + const char *name) +{ + struct cache_req_domain *dom; + struct cache_req_domain *ret = NULL; + + DLIST_FOR_EACH(dom, domains) { + if (sss_domain_get_state(dom->domain) == DOM_DISABLED) { + continue; + } + + if (strcasecmp(dom->domain->name, name) == 0 || + (dom->domain->flat_name != NULL && + strcasecmp(dom->domain->flat_name, name) == 0)) { + ret = dom; + break; + } + } + + if (ret == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Unknown domains [%s].\n", name); + } + + return ret; +} + +errno_t +cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx, + struct cache_req_domain *src, + char **requested_domains, + struct cache_req_domain **_dest) +{ + struct cache_req_domain *cr_domains = NULL; + struct cache_req_domain *cr_domain; + struct cache_req_domain *iter; + errno_t ret; + + if (src == NULL) { + return EINVAL; + } + + DLIST_FOR_EACH(iter, src) { + if (requested_domains != NULL + && !string_in_list(iter->domain->name, requested_domains, + false)) { + continue; + } + + cr_domain = talloc_zero(mem_ctx, struct cache_req_domain); + if (cr_domain == NULL) { + ret = ENOMEM; + goto done; + } + + cr_domain->domain = iter->domain; + cr_domain->fqnames = iter->fqnames; + + DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *); + } + + if (cr_domains == NULL) { + if (requested_domains != NULL) { + DEBUG(SSSDBG_OP_FAILURE, "No requested domains found, " + "please check configuration options for typos.\n"); + } else { + DEBUG(SSSDBG_OP_FAILURE, "Failed to copy domains.\n"); + } + ret = EINVAL; + goto done; + } + + *_dest = cr_domains; + ret = EOK; + +done: + if (ret != EOK) { + cache_req_domain_list_zfree(&cr_domains); + } + + return ret; +} + +void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains) +{ + struct cache_req_domain *p, *q, *r; + + DLIST_FOR_EACH_SAFE(p, q, *cr_domains) { + r = p; + DLIST_REMOVE(*cr_domains, p); + talloc_zfree(r); + } + + *cr_domains = NULL; +} + +static bool +cache_req_domain_use_fqnames(struct sss_domain_info *domain, + bool enforce_non_fqnames) +{ + struct sss_domain_info *head; + + head = get_domains_head(domain); + + /* + * In order to decide whether fully_qualified_names must be used on the + * lookups we have to take into consideration: + * - use_fully_qualified_name value of the head of the domains; + * (head->fqnames) + * - the presence of a domains' resolution order list; + * (non_fqnames_enforced) + * + * The relationship between those two can be described by: + * - head->fqnames: + * - true: in this case doesn't matter whether it's enforced or not, + * fully-qualified-names will _always_ be used + * - false: in this case (which is also the default case), the usage + * depends on it being enforced; + * + * - enforce_non_fqnames: + * - true: in this case, the usage of fully-qualified-names is not + * needed; + * - false: in this case, the usage of fully-qualified-names will be + * done accordingly to what's set for the domain itself. + */ + if (head->fqnames) { + return true; + } else if (enforce_non_fqnames) { + return false; + } else { + return domain->fqnames; + } +} + +static struct cache_req_domain * +cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, + char **resolution_order) +{ + struct cache_req_domain *cr_domains = NULL; + struct cache_req_domain *cr_domain; + struct sss_domain_info *dom; + char *name; + int flag = SSS_GND_ALL_DOMAINS; + int i; + bool enforce_non_fqnames = false; + bool files_provider = false; + errno_t ret; + + /* Firstly, in case a domains' resolution order is passed ... iterate over + * the list adding its domains to the flatten cache req domains' list */ + if (resolution_order != NULL) { + enforce_non_fqnames = true; + for (i = 0; resolution_order[i] != NULL; i++) { + name = resolution_order[i]; + for (dom = domains; dom; dom = get_next_domain(dom, flag)) { + if (strcasecmp(name, dom->name) != 0) { + continue; + } + + cr_domain = talloc_zero(mem_ctx, struct cache_req_domain); + if (cr_domain == NULL) { + ret = ENOMEM; + goto done; + } + cr_domain->domain = dom; + cr_domain->fqnames = + cache_req_domain_use_fqnames(dom, enforce_non_fqnames); + + /* when using the domain resolution order, using shortnames as + * input is allowed by default. However, we really want to use + * the fully qualified name as output in order to avoid + * conflicts whith users who have the very same name. */ + sss_domain_info_set_output_fqnames(cr_domain->domain, true); + + DLIST_ADD_END(cr_domains, cr_domain, + struct cache_req_domain *); + break; + } + } + } + + /* Then iterate through all the other domains (and subdomains) and add them + * to the flatten cache req domains' list */ + for (dom = domains; dom; dom = get_next_domain(dom, flag)) { + if (string_in_list(dom->name, resolution_order, false)) { + continue; + } + + files_provider = is_files_provider(dom); + + cr_domain = talloc_zero(mem_ctx, struct cache_req_domain); + if (cr_domain == NULL) { + ret = ENOMEM; + goto done; + } + cr_domain->domain = dom; + cr_domain->fqnames = + cache_req_domain_use_fqnames(dom, enforce_non_fqnames); + + /* when using the domain resolution order, using shortnames as input + * is allowed by default. However, we really want to use the fully + * qualified name as output in order to avoid conflicts whith users + * who have the very same name. + * + * NOTE: we do *not* want to use fully qualified names for the + * files provider.*/ + if (resolution_order != NULL) { + if (!files_provider) { + sss_domain_info_set_output_fqnames(cr_domain->domain, true); + } + } + + /* The implicit files provider should always be searched firstly, + * doesn't matter whether the domain_resolution_order set! + * + * By doing this we avoid querying other domains for local users. + */ + if (files_provider) { + DLIST_ADD(cr_domains, cr_domain); + continue; + } + + DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *); + } + + ret = EOK; + +done: + if (ret != EOK) { + cache_req_domain_list_zfree(&cr_domains); + } + + return cr_domains; +} + +errno_t +cache_req_domain_new_list_from_domain_resolution_order( + TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, + const char *domain_resolution_order, + struct cache_req_domain **_cr_domains) +{ + TALLOC_CTX *tmp_ctx; + struct cache_req_domain *cr_domains; + char **list = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + if (domain_resolution_order != NULL) { + if (strcmp(domain_resolution_order, ":") != 0) { + DEBUG(SSSDBG_TRACE_FUNC, + "Domain resolution order list (split by ':'): \"%s\"\n", + domain_resolution_order); + + ret = split_on_separator(tmp_ctx, domain_resolution_order, ':', + true, true, &list, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "split_on_separator() failed [%d]: [%s].\n", + ret, sss_strerror(ret)); + goto done; + } + } else { + DEBUG(SSSDBG_TRACE_FUNC, + "Domain resolution order list: ':' " + "(do not use any specific order)\n"); + } + } else { + DEBUG(SSSDBG_TRACE_FUNC, + "Domain resolution order list: not set\n"); + } + + cr_domains = cache_req_domain_new_list_from_string_list(mem_ctx, domains, + list); + if (cr_domains == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_OP_FAILURE, + "cache_req_domain_new_list_from_domain_resolution_order() " + "failed [%d]: [%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + *_cr_domains = cr_domains; + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h new file mode 100644 index 0000000..72e4995 --- /dev/null +++ b/src/responder/common/cache_req/cache_req_domain.h @@ -0,0 +1,63 @@ +/* + Authors: + Fabiano Fidêncio <fidencio@redhat.com> + + Copyright (C) 2017 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/>. +*/ + +#ifndef _CACHE_REQ_DOMAIN_H_ +#define _CACHE_REQ_DOMAIN_H_ + +#include "responder/common/responder.h" + +struct cache_req_domain { + struct sss_domain_info *domain; + bool fqnames; + bool locate_domain; + + struct cache_req_domain *prev; + struct cache_req_domain *next; +}; + +struct cache_req_domain * +cache_req_domain_get_domain_by_name(struct cache_req_domain *domains, + const char *name); + +/* + * This function may have a side effect of setting the output_fqnames' domain + * property when it's called. + * + * It happens as the output_fqnames' domain property must only be set depending + * on whether a domain resolution order is set or not, and the saner place to + * set it to all domains is when flattening those (thus, in this function). + */ +errno_t +cache_req_domain_new_list_from_domain_resolution_order( + TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, + const char *domain_resolution_order, + struct cache_req_domain **_cr_domains); + +errno_t +cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx, + struct cache_req_domain *src, + char **requested_domains, + struct cache_req_domain **_dest); + +void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains); + + +#endif /* _CACHE_REQ_DOMAIN_H_ */ diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h new file mode 100644 index 0000000..f86a020 --- /dev/null +++ b/src/responder/common/cache_req/cache_req_plugin.h @@ -0,0 +1,331 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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/>. +*/ + +#ifndef _CACHE_REQ_PLUGIN_H_ +#define _CACHE_REQ_PLUGIN_H_ + +#include "responder/common/cache_req/cache_req_private.h" +#include "sss_iface/sss_iface_async.h" + +enum cache_object_status { + CACHE_OBJECT_VALID, + CACHE_OBJECT_EXPIRED, + CACHE_OBJECT_MISSING, + CACHE_OBJECT_MIDPOINT +}; + +/** + * Create cache request result manually, if the searched object is well known + * and thus can not be found in the cache. + * + * + * @return EOK If it is a well known object and a result was created. + * @return ENOENT If it is not a well known object. + * @return Other errno code in case of an error. + */ +typedef errno_t +(*cache_req_is_well_known_result_fn)(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct cache_req_result **_result); + +/** + * Prepare domain data. Some plug-ins may require to alter lookup data + * per specific domain rules, such as case sensitivity, fully qualified + * format etc. + * + * @return EOK If everything went fine. + * @return Other errno code in case of an error. + */ +typedef errno_t +(*cache_req_prepare_domain_data_fn)(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain); + +/** + * Create an object debug name that is used in debug messages to identify + * this object. + * + * @return Debug name or NULL in case of an error. + **/ +typedef const char * +(*cache_req_create_debug_name_fn)(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain); + +/** + * Check if an object is stored in negative cache. + * + * @return EOK If the object is not found. + * @return EEXIST If the object is found in negative cache. + * @return Other errno code in case of an error. + */ +typedef errno_t +(*cache_req_ncache_check_fn)(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data); + +/** + * Add an object into negative cache. + * + * @return EOK If everything went fine. + * @return Other errno code in case of an error. + */ +typedef errno_t +(*cache_req_ncache_add_fn)(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data); + +/** + * Filter the result through the negative cache. + * + * This is useful for plugins which don't use name as an input + * token but can be affected by filter_users and filter_groups + * options. + */ +typedef errno_t +(*cache_req_ncache_filter_fn)(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + const char *name); + +/** + * Add an object into global negative cache. + * + * @return EOK If everything went fine. + * @return Other errno code in case of an error. + */ +typedef errno_t +(*cache_req_global_ncache_add_fn)(struct sss_nc_ctx *ncache, + struct cache_req_data *data); + +/** + * Lookup object in sysdb. + * + * @return EOK If the object is found. + * @return ENOENT If the object is not found. + * @return Other errno code in case of an error. + */ +typedef errno_t +(*cache_req_lookup_fn)(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result); + +/** + * Send Data Provider request. + * + * @return Tevent request on success. + * @return NULL on error. + */ +typedef struct tevent_req * +(*cache_req_dp_send_fn)(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result); + +/** + * Process result of Data Provider request. + * + * Do not free subreq! It will be freed in the caller. + * + * @return True if data provider request succeeded. + * @return False if there was an error. + */ +typedef bool +(*cache_req_dp_recv_fn)(struct tevent_req *subreq, + struct cache_req *cr); + +/** + * Check whether the results of the domain locator can still + * be considered valid or whether it is time to call the request + * again. + * + * @param resp_ctx The responder context. + * @param domain The domain to check. This should be the domain-head, + * because the locator works across a domain and its + * subdomains. + * @param data The cache request data that contains primarily the key + * to look for. + * + * @return True if the locator plugin should be ran again. + * @return False if the lookup should just proceed with the + * data that is already in the negative cache. + */ +typedef bool +(*cache_req_dp_get_domain_check_fn)(struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data); +/** + * Send Data Provider request to locate the domain + * of an entry + * + * @param resp_ctx The responder context. + * @param domain The domain to check. This should be the domain-head, + * because the locator works across a domain and its + * subdomains. + * @param data The cache request data that contains primarily the key + * to look for. + * + * + * @return Tevent request on success. + * @return NULL on error. + */ +typedef struct tevent_req * +(*cache_req_dp_get_domain_send_fn)(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data); + +/** + * Process result of Data Provider find-domain request. + * + * Do not free subreq! It will be freed in the caller. + * + * @param mem_ctx The memory context that owns the _found_domain + * result parameter. + * @param subreq The request to finish. + * @param cr The cache_req being processed. + * @param _found_domain The domain the request account belongs to. This + * parameter can be NULL even on success, in that + * case the account was not found and no lookups are + * needed, all domains can be skipped in this case. + * + * @return EOK if the request did not encounter any error. In this + * case, the _found_domain parameter can be considered authoritative, + * regarless of its value + * @return errno on error. _found_domain should be NULL in this case. + */ +typedef errno_t +(*cache_req_dp_get_domain_recv_fn)(TALLOC_CTX *mem_ctx, + struct tevent_req *subreq, + struct cache_req *cr, + char **_found_domain); + +struct cache_req_plugin { + /** + * Plugin name. + */ + const char *name; + + /** + * Expiration timestamp attribute name. + */ + const char *attr_expiration; + + /** + * Flags that are passed to get_next_domain(). + */ + uint32_t get_next_domain_flags; + + /** + * True if input name should be parsed for domain. + */ + bool parse_name; + + /** + * True if default domain suffix should be ignored when parsing name. + */ + bool ignore_default_domain; + + /** + * True if we always contact data provider. + */ + bool bypass_cache; + + /** + * True if only one result is expected. + */ + bool only_one_result; + + /** + * If true, cache request will iterate over all domains on domain-less + * search and merge acquired results. + */ + bool search_all_domains; + + /** + * True if only domains with enumeration enabled are searched. + */ + bool require_enumeration; + + /** + * Allow missing domain part even if domain requires fully qualified name + * on domain less searches. + */ + bool allow_missing_fqn; + + /** + * True if this plugin can be swapped for equivalent search with UPN. + */ + bool allow_switch_to_upn; + enum cache_req_type upn_equivalent; + + /* Operations */ + cache_req_is_well_known_result_fn is_well_known_fn; + cache_req_prepare_domain_data_fn prepare_domain_data_fn; + cache_req_create_debug_name_fn create_debug_name_fn; + cache_req_global_ncache_add_fn global_ncache_add_fn; + cache_req_ncache_check_fn ncache_check_fn; + cache_req_ncache_add_fn ncache_add_fn; + cache_req_ncache_filter_fn ncache_filter_fn; + cache_req_lookup_fn lookup_fn; + cache_req_dp_send_fn dp_send_fn; + cache_req_dp_recv_fn dp_recv_fn; + cache_req_dp_get_domain_check_fn dp_get_domain_check_fn; + cache_req_dp_get_domain_send_fn dp_get_domain_send_fn; + cache_req_dp_get_domain_recv_fn dp_get_domain_recv_fn; +}; + +extern const struct cache_req_plugin cache_req_user_by_name; +extern const struct cache_req_plugin cache_req_user_by_upn; +extern const struct cache_req_plugin cache_req_user_by_id; +extern const struct cache_req_plugin cache_req_group_by_name; +extern const struct cache_req_plugin cache_req_group_by_id; +extern const struct cache_req_plugin cache_req_initgroups_by_name; +extern const struct cache_req_plugin cache_req_initgroups_by_upn; +#ifdef BUILD_SUBID +extern const struct cache_req_plugin cache_req_subid_ranges_by_name; +#endif +extern const struct cache_req_plugin cache_req_user_by_cert; +extern const struct cache_req_plugin cache_req_user_by_filter; +extern const struct cache_req_plugin cache_req_group_by_filter; +extern const struct cache_req_plugin cache_req_object_by_sid; +extern const struct cache_req_plugin cache_req_object_by_name; +extern const struct cache_req_plugin cache_req_object_by_id; +extern const struct cache_req_plugin cache_req_enum_users; +extern const struct cache_req_plugin cache_req_enum_groups; +extern const struct cache_req_plugin cache_req_enum_svc; +extern const struct cache_req_plugin cache_req_enum_ip_hosts; +extern const struct cache_req_plugin cache_req_enum_ip_networks; +extern const struct cache_req_plugin cache_req_svc_by_name; +extern const struct cache_req_plugin cache_req_svc_by_port; +extern const struct cache_req_plugin cache_req_netgroup_by_name; +extern const struct cache_req_plugin cache_req_ssh_host_id_by_name; +extern const struct cache_req_plugin cache_req_autofs_map_entries; +extern const struct cache_req_plugin cache_req_autofs_map_by_name; +extern const struct cache_req_plugin cache_req_autofs_entry_by_name; +extern const struct cache_req_plugin cache_req_ip_host_by_name; +extern const struct cache_req_plugin cache_req_ip_host_by_addr; +extern const struct cache_req_plugin cache_req_ip_network_by_name; +extern const struct cache_req_plugin cache_req_ip_network_by_addr; + +#endif /* _CACHE_REQ_PLUGIN_H_ */ diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h new file mode 100644 index 0000000..22f197b --- /dev/null +++ b/src/responder/common/cache_req/cache_req_private.h @@ -0,0 +1,227 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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/>. +*/ + +#ifndef _CACHE_REQ_PRIVATE_H_ +#define _CACHE_REQ_PRIVATE_H_ + +#include <stdint.h> + +#include "responder/common/responder.h" +#include "responder/common/cache_req/cache_req.h" + +#define CACHE_REQ_DEBUG(level, cr, fmt, ...) \ + DEBUG(level, "CR #%u: " fmt, (cr)->reqid, ##__VA_ARGS__) + +/* Tracing message, changing this can break log parsing tools */ +#define SSS_REQ_TRACE_CID_CR(level, cr, fmt, ...) \ + CACHE_REQ_DEBUG(level, cr, "REQ_TRACE: " fmt, ##__VA_ARGS__) + +struct cache_req { + /* Provided input. */ + struct cache_req_data *data; + + const struct cache_req_plugin *plugin; + struct resp_ctx *rctx; + struct sss_nc_ctx *ncache; + int midpoint; + + /* Domain related information. */ + struct sss_domain_info *domain; + + /* wanted cache behavior */ + enum cache_req_behavior cache_behavior; + + /* Only contact domains with this type */ + enum cache_req_dom_type req_dom_type; + + /* Debug information */ + uint32_t reqid; + const char *reqname; + const char *debugobj; + + /* Time when the request started. Useful for by-filter lookups */ + time_t req_start; +}; + +/** + * Structure to hold the information the user passed as parameter + * and some strings after processing this information. + */ +struct cache_req_parsed_name { + const char *input; /* Original input. */ + const char *name; /* Parsed name or UPN. */ + const char *lookup; /* Converted per domain rules. */ + const char *attr; /* Attribute name when looking for an attribute */ +}; + +/** + * Structure to hold the input strings that cannot contain domain + * part but are transferred per each domain's case sensitivity. + */ +struct cache_req_cased_name { + const char *name; /* Parsed name or UPN. */ + const char *lookup; /* Converted per domain rules. */ +}; + +/* Input data. */ +struct cache_req_data { + enum cache_req_type type; + struct cache_req_parsed_name name; + uint32_t id; + const char *cert; + const char *sid; + const char *alias; + const char **attrs; + const char *autofs_entry_name; + + struct { + struct cache_req_parsed_name *name; + struct cache_req_cased_name protocol; + uint16_t port; + } svc; + + struct { + uint32_t af; + uint32_t len; + uint8_t *data; + } addr; + + bool bypass_cache; + bool bypass_dp; + + /* if set, only search in the listed domains */ + char **requested_domains; + + /* if set, ERR_OFFLINE is returned if data provider is offline */ + bool propogate_offline_status; + + /* if set, only domains with MPG_HYBRID are searched */ + bool hybrid_lookup; +}; + +struct tevent_req * +cache_req_search_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr, + bool first_iteration, + bool cache_only_override); + +errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ldb_result **_result, + bool *_dp_success); + +struct tevent_req *cache_req_locate_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr); +errno_t cache_req_locate_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **_found_domain); + +struct tevent_req * +cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data); + +void cache_req_search_ncache_add_to_domain(struct cache_req *cr, + struct sss_domain_info *domain); + +errno_t +cache_req_add_result(TALLOC_CTX *mem_ctx, + struct cache_req_result *new_result, + struct cache_req_result ***_results, + size_t *_num_results); + +struct cache_req_result * +cache_req_create_result(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_result *ldb_result, + const char *lookup_name, + const char *well_known_domain); + +errno_t +cache_req_create_and_add_result(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct sss_domain_info *domain, + struct ldb_result *ldb_result, + const char *name, + struct cache_req_result ***_results, + size_t *_num_results); + +struct ldb_result * +cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx, + struct ldb_message **ldb_msgs, + size_t ldb_msg_count); + +struct ldb_result * +cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx, + struct ldb_message *ldb_msg); + +struct cache_req_result * +cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_message *ldb_msg, + const char *lookup_name, + const char *well_known_domain); + +struct tevent_req * +cache_req_sr_overlay_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr, + struct cache_req_result **results, + size_t num_results); + +errno_t +cache_req_sr_overlay_recv(struct tevent_req *req); + +/* Plug-in common. */ + +struct cache_req_result * +cache_req_well_known_sid_result(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + const char *domname, + const char *sid, + const char *name); + +bool +cache_req_common_process_dp_reply(struct cache_req *cr, + errno_t ret, + uint16_t err_maj, + uint32_t err_min, + const char *err_msg); + +bool +cache_req_common_dp_recv(struct tevent_req *subreq, + struct cache_req *cr); + +errno_t +cache_req_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *subreq, + struct cache_req *cr, + char **_domain); + +errno_t cache_req_idminmax_check(struct cache_req_data *data, + struct sss_domain_info *domain); +#endif /* _CACHE_REQ_PRIVATE_H_ */ diff --git a/src/responder/common/cache_req/cache_req_result.c b/src/responder/common/cache_req/cache_req_result.c new file mode 100644 index 0000000..c1a3732 --- /dev/null +++ b/src/responder/common/cache_req/cache_req_result.c @@ -0,0 +1,274 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <ldb.h> +#include <talloc.h> +#include <errno.h> + +#include "util/util.h" +#include "responder/common/cache_req/cache_req_private.h" + +errno_t +cache_req_add_result(TALLOC_CTX *mem_ctx, + struct cache_req_result *new_result, + struct cache_req_result ***_results, + size_t *_num_results) +{ + struct cache_req_result **results = *_results; + size_t idx; + size_t count; + + /* Make space for new results. */ + idx = *_num_results; + count = *_num_results + 1; + + results = talloc_realloc(mem_ctx, results, struct cache_req_result *, + count + 1); + if (results == NULL) { + return ENOMEM; + } + + results[idx] = talloc_steal(results, new_result); + results[idx + 1] = NULL; + + *_results = results; + *_num_results = count; + + return EOK; +} + +struct cache_req_result * +cache_req_create_result(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_result *ldb_result, + const char *lookup_name, + const char *well_known_domain) +{ + struct cache_req_result *result; + + result = talloc_zero(mem_ctx, struct cache_req_result); + if (result == NULL) { + return NULL; + } + + result->domain = domain; + result->ldb_result = talloc_steal(result, ldb_result); + result->count = ldb_result != NULL ? ldb_result->count : 0; + result->msgs = ldb_result != NULL ? ldb_result->msgs : NULL; + + if (lookup_name != NULL) { + result->lookup_name = talloc_strdup(result, lookup_name); + if (result->lookup_name == NULL) { + talloc_free(result); + return NULL; + } + } + + if (well_known_domain != NULL) { + result->well_known_domain = talloc_strdup(result, well_known_domain); + if (result->well_known_domain == NULL) { + talloc_free(result); + return NULL; + } + } + + return result; +} + +errno_t +cache_req_create_and_add_result(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct sss_domain_info *domain, + struct ldb_result *ldb_result, + const char *name, + struct cache_req_result ***_results, + size_t *_num_results) +{ + struct cache_req_result *item; + errno_t ret; + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Found %u entries in domain %s\n", + ldb_result->count, domain->name); + + item = cache_req_create_result(mem_ctx, domain, ldb_result, name, NULL); + if (item == NULL) { + return ENOMEM; + } + + ret = cache_req_add_result(mem_ctx, item, _results, _num_results); + if (ret != EOK) { + talloc_free(item); + } + + return ret; +} + +struct ldb_result * +cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx, + struct ldb_message **ldb_msgs, + size_t ldb_msg_count) +{ + struct ldb_result *ldb_result; + + if (ldb_msgs == NULL || ldb_msgs[0] == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n"); + return NULL; + } + + ldb_result = talloc_zero(NULL, struct ldb_result); + if (ldb_result == NULL) { + return NULL; + } + + ldb_result->extended = NULL; + ldb_result->controls = NULL; + ldb_result->refs = NULL; + ldb_result->count = ldb_msg_count; + ldb_result->msgs = talloc_zero_array(ldb_result, struct ldb_message *, + ldb_msg_count + 1); + if (ldb_result->msgs == NULL) { + talloc_free(ldb_result); + return NULL; + } + + for (size_t i = 0; i < ldb_msg_count; i++) { + ldb_result->msgs[i] = talloc_steal(ldb_result->msgs, ldb_msgs[i]); + } + + return ldb_result; +} + +struct ldb_result * +cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx, + struct ldb_message *ldb_msg) +{ + struct ldb_result *ldb_result; + + if (ldb_msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n"); + return NULL; + } + + ldb_result = talloc_zero(NULL, struct ldb_result); + if (ldb_result == NULL) { + return NULL; + } + + ldb_result->extended = NULL; + ldb_result->controls = NULL; + ldb_result->refs = NULL; + ldb_result->count = 1; + ldb_result->msgs = talloc_zero_array(ldb_result, struct ldb_message *, 2); + if (ldb_result->msgs == NULL) { + talloc_free(ldb_result); + return NULL; + } + + ldb_result->msgs[0] = talloc_steal(ldb_result->msgs, ldb_msg); + + return ldb_result; +} + +struct cache_req_result * +cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_message *ldb_msg, + const char *lookup_name, + const char *well_known_domain) +{ + struct cache_req_result *result; + struct ldb_result *ldb_result; + + if (ldb_msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n"); + return NULL; + } + + ldb_result = cache_req_create_ldb_result_from_msg(mem_ctx, ldb_msg); + if (ldb_result == NULL) { + return NULL; + } + + result = cache_req_create_result(mem_ctx, domain, ldb_result, + lookup_name, well_known_domain); + if (result == NULL) { + talloc_free(ldb_result); + return NULL; + } + + return result; +} + +struct cache_req_result * +cache_req_copy_limited_result(TALLOC_CTX *mem_ctx, + struct cache_req_result *result, + uint32_t start, + uint32_t limit) +{ + struct cache_req_result *out = NULL; + struct ldb_result *ldb_result; + unsigned int left; + errno_t ret; + + if (start >= result->count) { + ret = ERANGE; + goto done; + } + + out = talloc_zero(mem_ctx, struct cache_req_result); + if (out == NULL) { + ret = ENOMEM; + goto done; + } + + ldb_result = talloc_zero(out, struct ldb_result); + if (ldb_result == NULL) { + ret = ENOMEM; + goto done; + } + + left = result->count - start; + + ldb_result->extended = result->ldb_result->extended; + ldb_result->controls = result->ldb_result->controls; + ldb_result->refs = result->ldb_result->refs; + ldb_result->msgs = &(result->ldb_result->msgs[start]); + ldb_result->count = left < limit ? left : limit; + + out->domain = result->domain; + out->ldb_result = ldb_result; + out->lookup_name = result->lookup_name; + out->count = ldb_result->count; + out->msgs = ldb_result->msgs; + + ret = EOK; + +done: + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create cache request result " + "[%d]: %s\n", ret, sss_strerror(ret)); + + talloc_free(out); + return NULL; + } + + return out; +} diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c new file mode 100644 index 0000000..d800f47 --- /dev/null +++ b/src/responder/common/cache_req/cache_req_search.c @@ -0,0 +1,643 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <ldb.h> +#include <talloc.h> +#include <tevent.h> + +#include "util/util.h" +#include "responder/common/cache_req/cache_req_private.h" +#include "responder/common/cache_req/cache_req_plugin.h" +#include "db/sysdb.h" + +static errno_t cache_req_search_ncache(struct cache_req *cr) +{ + errno_t ret; + + if (cr->plugin->ncache_check_fn == NULL) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, + "This request type does not support negative cache\n"); + return EOK; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Checking negative cache for [%s]\n", + cr->debugobj); + + ret = cr->plugin->ncache_check_fn(cr->ncache, cr->domain, cr->data); + if (ret == EEXIST) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "[%s] does not exist (negative cache)\n", + cr->debugobj); + return ENOENT; + } else if (ret != EOK && ret != ENOENT) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Unable to check negative cache [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "[%s] is not present in negative cache\n", + cr->debugobj); + + return EOK; +} + +void cache_req_search_ncache_add_to_domain(struct cache_req *cr, + struct sss_domain_info *domain) +{ + errno_t ret; + + if (cr->plugin->ncache_add_fn == NULL) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, + "This request type does not support negative cache\n"); + return; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Adding [%s] to negative cache\n", + cr->debugobj); + + ret = cr->plugin->ncache_add_fn(cr->ncache, domain, cr->data); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr, + "Cannot set negative cache for [%s] [%d]: %s\n", + cr->debugobj, ret, sss_strerror(ret)); + /* not fatal */ + } + + return; +} + +static void cache_req_search_ncache_add(struct cache_req *cr) +{ + return cache_req_search_ncache_add_to_domain(cr, cr->domain); +} + +static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result **_result) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_result *filtered_result; + struct ldb_message **msgs; + size_t msg_count; + const char *name; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + if (cr->plugin->ncache_filter_fn == NULL) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "This request type does not support filtering " + "result by negative cache\n"); + + ret = EOK; + goto done; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Filtering out results by negative cache\n"); + + msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, (*_result)->count); + msg_count = 0; + + for (size_t i = 0; i < (*_result)->count; i++) { + name = sss_get_name_from_msg(cr->domain, (*_result)->msgs[i]); + if (name == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "sss_get_name_from_msg() returned NULL, which should never " + "happen in this scenario!\n"); + ret = ERR_INTERNAL; + goto done; + } + + ret = cr->plugin->ncache_filter_fn(cr->ncache, cr->domain, name); + if (ret == EEXIST) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "[%s] filtered out! (negative cache)\n", + name); + continue; + } else if (ret != EOK && ret != ENOENT) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Unable to check negative cache [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + msgs[msg_count] = talloc_steal(msgs, (*_result)->msgs[i]); + msg_count++; + } + + if (msg_count == 0) { + ret = ENOENT; + goto done; + } + + filtered_result = cache_req_create_ldb_result_from_msg_list(tmp_ctx, msgs, + msg_count); + if (filtered_result == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(*_result); + *_result = talloc_steal(mem_ctx, filtered_result); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static int +cache_req_should_be_in_cache(struct cache_req *cr, + struct ldb_result *result) +{ + id_t id = 0; + + if (result == NULL || result->count != 1) { + /* can't decide so keep it */ + return EOK; + } + + id = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_UIDNUM, 0); + if (id && OUT_OF_ID_RANGE(id, cr->domain->id_min, cr->domain->id_max)) { + return ERR_ID_OUTSIDE_RANGE; + } + + id = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_GIDNUM, 0); + if (id && OUT_OF_ID_RANGE(id, cr->domain->id_min, cr->domain->id_max)) { + return ERR_ID_OUTSIDE_RANGE; + } + + return EOK; +} + +static errno_t cache_req_search_cache(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result **_result) +{ + struct ldb_result *result = NULL; + errno_t ret; + + if (cr->plugin->lookup_fn == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Bug: No cache lookup function specified\n"); + return ERR_INTERNAL; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Looking up [%s] in cache\n", + cr->debugobj); + + ret = cr->plugin->lookup_fn(mem_ctx, cr, cr->data, cr->domain, &result); + if (ret == EOK && (result == NULL || result->count == 0)) { + ret = ENOENT; + } + + if (ret == EOK) { + ret = cache_req_should_be_in_cache(cr, result); + } + + switch (ret) { + case EOK: + if (cr->plugin->only_one_result && result->count > 1) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Multiple objects were found when " + "only one was expected!\n"); + ret = ERR_MULTIPLE_ENTRIES; + goto done; + } + + *_result = result; + break; + case ERR_ID_OUTSIDE_RANGE: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "ID [%s] was filtered out\n", + cr->debugobj); + break; + case ENOENT: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Object [%s] was not found in cache\n", + cr->debugobj); + break; + default: + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Unable to lookup [%s] in cache [%d]: %s\n", + cr->debugobj, ret, sss_strerror(ret)); + break; + } + +done: + if (ret != EOK) { + talloc_free(result); + } + + return ret; +} + +static enum cache_object_status +cache_req_expiration_status(struct cache_req *cr, + struct ldb_result *result) +{ + time_t expire; + errno_t ret; + + if (result == NULL || result->count == 0 || cr->plugin->bypass_cache) { + return CACHE_OBJECT_MISSING; + } + + expire = ldb_msg_find_attr_as_uint64(result->msgs[0], + cr->plugin->attr_expiration, 0); + + ret = sss_cmd_check_cache(result->msgs[0], cr->midpoint, expire); + if (ret == EOK) { + return CACHE_OBJECT_VALID; + } else if (ret == EAGAIN) { + return CACHE_OBJECT_MIDPOINT; + } + + return CACHE_OBJECT_EXPIRED; +} + +struct cache_req_search_state { + /* input data */ + struct tevent_context *ev; + struct resp_ctx *rctx; + struct cache_req *cr; + + /* output data */ + struct ldb_result *result; + bool dp_success; +}; + +static errno_t cache_req_search_dp(struct tevent_req *req, + enum cache_object_status status); +static void cache_req_search_oob_done(struct tevent_req *subreq); +static void cache_req_search_done(struct tevent_req *subreq); + +struct tevent_req * +cache_req_search_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr, + bool first_iteration, + bool cache_only_override) +{ + struct cache_req_search_state *state; + enum cache_object_status status; + struct tevent_req *req; + bool bypass_cache = false; + bool bypass_dp = false; + bool skip_refresh = false; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct cache_req_search_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Looking up %s\n", cr->debugobj); + + state->ev = ev; + state->cr = cr; + + ret = cache_req_search_ncache(cr); + if (ret != EOK) { + goto done; + } + + if (cache_only_override) { + bypass_dp = true; + } else { + switch (cr->cache_behavior) { + case CACHE_REQ_CACHE_FIRST: + bypass_cache = first_iteration ? false : true; + bypass_dp = first_iteration ? true : false; + break; + case CACHE_REQ_BYPASS_CACHE: + bypass_cache = true; + break; + case CACHE_REQ_BYPASS_PROVIDER: + bypass_dp = true; + skip_refresh = true; + break; + default: + break; + } + } + + /* If bypass_cache is enabled we always contact data provider before + * searching the cache. Thus we set expiration status to missing, + * which will trigger data provider request later. + * + * If disabled, we want to search the cache here to see if the + * object is already cached and valid or if data provider needs + * to be contacted. + */ + state->result = NULL; + status = CACHE_OBJECT_MISSING; + if (!bypass_cache) { + ret = cache_req_search_cache(state, cr, &state->result); + if (ret != EOK && ret != ENOENT) { + goto done; + } + + status = cache_req_expiration_status(cr, state->result); + if (status == CACHE_OBJECT_VALID) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Returning [%s] from cache\n", cr->debugobj); + ret = EOK; + goto done; + } + + /* For the CACHE_REQ_CACHE_FIRST case, if bypass_dp is true but we + * found the object in this domain, we will contact the data provider + * anyway to refresh it so we can return it without searching the rest + * of the domains. + */ + if (status != CACHE_OBJECT_MISSING && !skip_refresh) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Object found, but needs to be refreshed.\n"); + bypass_dp = false; + } else { + ret = ENOENT; + } + } + + if (!bypass_dp) { + ret = cache_req_search_dp(req, status); + } + + if (ret != EAGAIN) { + goto done; + } + + return req; + +done: + if (ret == EOK) { + ret = cache_req_search_ncache_filter(state, cr, &state->result); + } + + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + + return req; +} + +static errno_t cache_req_search_dp(struct tevent_req *req, + enum cache_object_status status) +{ + struct cache_req_search_state *state; + struct tevent_req *subreq; + errno_t ret; + + state = tevent_req_data(req, struct cache_req_search_state); + + switch (status) { + case CACHE_OBJECT_MIDPOINT: + /* Out of band update. The calling function will return the cached + * entry immediately. We need to use rctx so the request is not + * removed when state is freed. */ + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Performing midpoint cache update of [%s]\n", + state->cr->debugobj); + + subreq = state->cr->plugin->dp_send_fn(state->rctx, state->cr, + state->cr->data, + state->cr->domain, + state->result); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory sending out-of-band " + "data provider request\n"); + /* This is non-fatal, so we'll continue here */ + } else { + tevent_req_set_callback(subreq, cache_req_search_oob_done, req); + } + + ret = EOK; + break; + case CACHE_OBJECT_EXPIRED: + case CACHE_OBJECT_MISSING: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Looking up [%s] in data provider\n", + state->cr->debugobj); + + subreq = state->cr->plugin->dp_send_fn(state->cr, state->cr, + state->cr->data, + state->cr->domain, + state->result); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Out of memory sending data provider request\n"); + ret = ENOMEM; + break; + } + + tevent_req_set_callback(subreq, cache_req_search_done, req); + ret = EAGAIN; + break; + default: + /* error */ + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Unexpected status [%d]\n", status); + ret = ERR_INTERNAL; + break; + } + + return ret; +} + +static void cache_req_search_oob_done(struct tevent_req *subreq) +{ + DEBUG(SSSDBG_TRACE_INTERNAL, "Out of band request finished\n"); + talloc_zfree(subreq); + + return; +} + +static void cache_req_search_done(struct tevent_req *subreq) +{ + struct cache_req_search_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_search_state); + + state->dp_success = state->cr->plugin->dp_recv_fn(subreq, state->cr); + talloc_zfree(subreq); + + /* Do not try to read from cache if the domain is inconsistent */ + if (sss_domain_get_state(state->cr->domain) == DOM_INCONSISTENT) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Domain inconsistent, " + "we will not return cached data\n"); + + ret = ENOENT; + goto done; + } + + /* Get result from cache again. */ + ret = cache_req_search_cache(state, state->cr, &state->result); + if (ret != EOK) { + if (ret == ENOENT) { + /* Only store entry in negative cache if DP request succeeded + * because only then we know that the entry does not exist. */ + if (state->dp_success) { + cache_req_search_ncache_add(state->cr); + } + } + goto done; + } + + /* ret == EOK */ + ret = cache_req_search_ncache_filter(state, state->cr, &state->result); + if (ret != EOK) { + goto done; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Returning updated object [%s]\n", state->cr->debugobj); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ldb_result **_result, + bool *_dp_success) +{ + struct cache_req_search_state *state = NULL; + state = tevent_req_data(req, struct cache_req_search_state); + + *_dp_success = state->dp_success; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_result = talloc_steal(mem_ctx, state->result); + + return EOK; +} + +struct cache_req_locate_domain_state { + struct cache_req *cr; + + char *found_domain; +}; + +static void cache_req_locate_domain_done(struct tevent_req *subreq); + +struct tevent_req *cache_req_locate_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr) +{ + struct cache_req_locate_domain_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + errno_t ret; + bool should_run; + + req = tevent_req_create(mem_ctx, &state, struct cache_req_locate_domain_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + state->cr = cr; + + should_run = cr->plugin->dp_get_domain_check_fn(cr->rctx, + get_domains_head(cr->domain), + cr->data); + if (should_run == false) { + /* The request was tried too recently, don't issue a new one + * as its results are still valid + */ + ret = ERR_GET_ACCT_DOM_CACHED; + goto immediate; + } + + subreq = cr->plugin->dp_get_domain_send_fn(state, + cr->rctx, + get_domains_head(cr->domain), + cr->data); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, cache_req_locate_domain_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void cache_req_locate_domain_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct cache_req_locate_domain_state *state; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_locate_domain_state); + + ret = state->cr->plugin->dp_get_domain_recv_fn(state, + subreq, + state->cr, + &state->found_domain); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t cache_req_locate_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **_found_domain) +{ + struct cache_req_locate_domain_state *state = NULL; + + state = tevent_req_data(req, struct cache_req_locate_domain_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_found_domain = talloc_steal(mem_ctx, state->found_domain); + return EOK; +} diff --git a/src/responder/common/cache_req/cache_req_sr_overlay.c b/src/responder/common/cache_req/cache_req_sr_overlay.c new file mode 100644 index 0000000..8deb06a --- /dev/null +++ b/src/responder/common/cache_req/cache_req_sr_overlay.c @@ -0,0 +1,347 @@ +/* + Authors: + Nikolai Kondrashov <Nikolai.Kondrashov@redhat.com> + + Copyright (C) 2017 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 "db/sysdb.h" +#include "responder/common/cache_req/cache_req_private.h" + +struct cache_req_sr_overlay_state { + /* Input data */ + struct tevent_context *ev; + struct cache_req *cr; + struct cache_req_result **results; + size_t num_results; + /* Work data */ + size_t res_idx; + size_t msg_idx; +}; + +static errno_t cache_req_sr_overlay_match_users( + struct cache_req_sr_overlay_state *state); + +static errno_t cache_req_sr_overlay_match_users( + struct cache_req_sr_overlay_state *state); + +static struct tevent_req *cache_req_sr_overlay_match_all_step_send( + struct cache_req_sr_overlay_state *state); + +static void cache_req_sr_overlay_match_all_step_done( + struct tevent_req *subreq); + +struct tevent_req *cache_req_sr_overlay_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cache_req *cr, + struct cache_req_result **results, + size_t num_results) +{ + errno_t ret = EOK; + struct tevent_req *req; + struct tevent_req *subreq; + struct cache_req_sr_overlay_state *state; + struct resp_ctx *rctx = cr->rctx; + + req = tevent_req_create(mem_ctx, &state, + struct cache_req_sr_overlay_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->cr = cr; + state->results = results; + state->num_results = num_results; + + /* If session recording is selective */ + if (rctx->sr_conf.scope != SESSION_RECORDING_SCOPE_NONE) { + /* If it's a request for a user/users */ + switch (cr->data->type) { + case CACHE_REQ_USER_BY_NAME: + case CACHE_REQ_USER_BY_UPN: + case CACHE_REQ_USER_BY_ID: + case CACHE_REQ_ENUM_USERS: + /* If we have group names to match against */ + if ((rctx->sr_conf.groups != NULL && + rctx->sr_conf.groups[0] != NULL) || + (rctx->sr_conf.exclude_groups != NULL && + rctx->sr_conf.exclude_groups[0] != NULL)) { + /* Pull and match group and user names for each user entry */ + subreq = cache_req_sr_overlay_match_all_step_send(state); + if (subreq == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Failed allocating a session recording " + "user overlay request\n"); + ret = ENOMEM; + goto done; + } + tevent_req_set_callback( + subreq, cache_req_sr_overlay_match_all_step_done, req); + ret = EAGAIN; + } else { + /* Only match user names for each user entry */ + ret = cache_req_sr_overlay_match_users(state); + } + break; + default: + break; + } + } + +done: + if (ret != EAGAIN) { + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + } + + return req; +} + +static errno_t cache_req_sr_overlay_match_users( + struct cache_req_sr_overlay_state *state) +{ + struct cache_req *cr; + struct resp_ctx *rctx; + errno_t ret; + int lret; + TALLOC_CTX *tmp_ctx = NULL; + struct cache_req_result *result; + struct ldb_message *msg; + const char *name; + char *output_name; + char **conf_user; + char **conf_exclude_user; + bool enabled; + char *enabled_str; + + cr = state->cr; + rctx = cr->rctx; + + /* Create per-message talloc context */ + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Failed creating temporary talloc context\n"); + ret = ENOMEM; + goto done; + } + + /* For each result */ + for (state->res_idx = 0; + state->res_idx < state->num_results; + state->res_idx++) { + result = state->results[state->res_idx]; + + /* For each message */ + for (state->msg_idx = 0; + state->msg_idx < result->count; + state->msg_idx++) { + msg = result->msgs[state->msg_idx]; + + /* Format output username */ + name = sss_get_name_from_msg(result->domain, msg); + ret = sss_output_fqname(tmp_ctx, result->domain, name, + rctx->override_space, + &output_name); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Failed formatting output username from %s: %s\n", + name, sss_strerror(ret)); + goto done; + } + + /* For each user name in session recording config */ + enabled = false; + conf_user = rctx->sr_conf.users; + if (rctx->sr_conf.scope == SESSION_RECORDING_SCOPE_SOME) { + if (conf_user != NULL) { + for (; *conf_user != NULL; conf_user++) { + /* If it matches the requested user name */ + if (strcmp(*conf_user, output_name) == 0) { + enabled = true; + break; + } + } + } + /* For each exclude user name in session recording config */ + } else if (rctx->sr_conf.scope == SESSION_RECORDING_SCOPE_ALL) { + enabled = true; + conf_exclude_user = rctx->sr_conf.exclude_users; + if (conf_exclude_user != NULL) { + for (; *conf_exclude_user != NULL; conf_exclude_user++) { + /* If it matches the requested user name */ + if (strcmp(*conf_exclude_user, output_name) == 0) { + enabled = false; + break; + } + } + } + } + + /* Set sessionRecording attribute to enabled value */ + ldb_msg_remove_attr(msg, SYSDB_SESSION_RECORDING); + enabled_str = talloc_strdup(tmp_ctx, enabled ? "TRUE" : "FALSE"); + if (enabled_str == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Failed to allocate a %s attribute value\n", + SYSDB_SESSION_RECORDING); + ret = ENOMEM; + goto done; + } + lret = ldb_msg_add_string(msg, SYSDB_SESSION_RECORDING, enabled_str); + if (lret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(lret); + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Failed adding %s attribute: %s\n", + SYSDB_SESSION_RECORDING, sss_strerror(ret)); + goto done; + } + talloc_steal(msg, enabled_str); + + /* Free per-message allocations */ + talloc_free_children(tmp_ctx); + } + } + + ret = EOK; + +done: + talloc_zfree(tmp_ctx); + return ret; +} + +static struct tevent_req *cache_req_sr_overlay_match_all_step_send( + struct cache_req_sr_overlay_state *state) +{ + struct cache_req *cr = state->cr; + struct cache_req_result *result = + state->results[state->res_idx]; + const char *name; + + name = ldb_msg_find_attr_as_string(result->msgs[state->msg_idx], + SYSDB_NAME, NULL); + return cache_req_initgr_by_name_send(state, state->ev, cr->rctx, cr->ncache, + cr->midpoint, CACHE_REQ_ANY_DOM, + NULL, name); +} + +static void cache_req_sr_overlay_match_all_step_done( + struct tevent_req *subreq) +{ + int lret; + errno_t ret; + TALLOC_CTX *tmp_ctx = NULL; + struct tevent_req *req; + struct cache_req_sr_overlay_state *state; + struct cache_req_result *result; + struct ldb_message *msg; + const char *enabled; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_sr_overlay_state); + msg = state->results[state->res_idx]-> + msgs[state->msg_idx]; + + /* Create temporary allocation context */ + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Failed creating temporary talloc context\n"); + ret = ENOMEM; + goto done; + } + + /* Get initgroups result */ + ret = cache_req_initgr_by_name_recv(tmp_ctx, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Failed retrieving initgr request results: %s\n", + sss_strerror(ret)); + goto done; + } + + /* Overwrite sessionRecording attribute */ + ldb_msg_remove_attr(msg, SYSDB_SESSION_RECORDING); + enabled = ldb_msg_find_attr_as_string(result->msgs[0], + SYSDB_SESSION_RECORDING, NULL); + if (enabled != NULL) { + char *enabled_copy; + enabled_copy = talloc_strdup(tmp_ctx, enabled); + if (enabled_copy == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Failed to allocate a copy of %s attribute\n", + SYSDB_SESSION_RECORDING); + ret = ENOMEM; + goto done; + } + lret = ldb_msg_add_string(msg, SYSDB_SESSION_RECORDING, enabled_copy); + if (lret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(lret); + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Failed adding %s attribute: %s\n", + SYSDB_SESSION_RECORDING, sss_strerror(ret)); + goto done; + } + talloc_steal(msg, enabled_copy); + } + + /* Move onto next entry, if any */ + state->msg_idx++; + if (state->msg_idx >= + state->results[state->res_idx]->count) { + state->res_idx++; + if (state->res_idx >= state->num_results) { + ret = EOK; + goto done; + } + state->msg_idx = 0; + } + + /* Schedule next entry overlay */ + subreq = cache_req_sr_overlay_match_all_step_send(state); + if (subreq == NULL) { + ret = ENOMEM; + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Failed allocating a session recording " + "user overlay request\n"); + goto done; + } + tevent_req_set_callback(subreq, + cache_req_sr_overlay_match_all_step_done, req); + ret = EAGAIN; + +done: + if (ret == EOK) { + tevent_req_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } + talloc_free(tmp_ctx); +} + +errno_t cache_req_sr_overlay_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} diff --git a/src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c b/src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c new file mode 100644 index 0000000..b2b0a06 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c @@ -0,0 +1,161 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2019 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_autofs.h" +#include "util/util.h" +#include "util/sss_chain_id.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_autofs_entry_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_asprintf(mem_ctx, "%s:%s", + data->name.name, + data->autofs_entry_name); +} + +static errno_t +cache_req_autofs_entry_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + struct ldb_message *entry; + struct ldb_result *result; + errno_t ret; + + ret = sysdb_get_autofsentry(mem_ctx, domain, data->name.name, + data->autofs_entry_name, &entry); + if (ret != EOK) { + return ret; + } + + result = cache_req_create_ldb_result_from_msg(mem_ctx, entry); + if (result == NULL) { + talloc_free(entry); + return ENOMEM; + } + + *_result = talloc_steal(mem_ctx, result); + return EOK; +} + +static struct tevent_req * +cache_req_autofs_entry_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + struct be_conn *be_conn; + errno_t ret; + + ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + domain->name); + return NULL; + } + + return sbus_call_dp_autofs_GetEntry_send(mem_ctx, be_conn->conn, + be_conn->bus_name, SSS_BUS_PATH, + 0, data->name.name, + data->autofs_entry_name, + sss_chain_id_get()); +} + +bool +cache_req_autofs_entry_by_name_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + errno_t ret; + + ret = sbus_call_dp_autofs_GetEntry_recv(subreq); + + if (ret == ERR_MISSING_DP_TARGET || ret == ENOENT) { + ret = EOK; + } + + return ret == EOK; +} + +const struct cache_req_plugin cache_req_autofs_entry_by_name = { + .name = "Get autofs entry", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_autofs_entry_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_autofs_entry_by_name_lookup, + .dp_send_fn = cache_req_autofs_entry_by_name_dp_send, + .dp_recv_fn = cache_req_autofs_entry_by_name_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_autofs_entry_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *mapname, + const char *entryname) +{ + struct cache_req_data *data; + + data = cache_req_data_autofs_entry(mem_ctx, CACHE_REQ_AUTOFS_ENTRY_BY_NAME, + mapname, entryname); + if (data == NULL) { + return NULL; + } + + cache_req_data_set_propogate_offline_status(data, true); + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c b/src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c new file mode 100644 index 0000000..23b11b1 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c @@ -0,0 +1,155 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2019 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_autofs.h" +#include "util/util.h" +#include "util/sss_chain_id.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_autofs_map_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.name); +} + +static errno_t +cache_req_autofs_map_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + struct ldb_message *map; + struct ldb_result *result; + errno_t ret; + + ret = sysdb_get_map_byname(mem_ctx, domain, data->name.name, &map); + if (ret != EOK) { + return ret; + } + + result = cache_req_create_ldb_result_from_msg(mem_ctx, map); + if (result == NULL) { + talloc_free(map); + return ENOMEM; + } + + *_result = talloc_steal(mem_ctx, result); + return EOK; +} + +static struct tevent_req * +cache_req_autofs_map_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + struct be_conn *be_conn; + errno_t ret; + + ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + domain->name); + return NULL; + } + + return sbus_call_dp_autofs_GetMap_send(mem_ctx, be_conn->conn, + be_conn->bus_name, SSS_BUS_PATH, + 0, data->name.name, + sss_chain_id_get()); +} + +bool +cache_req_autofs_map_by_name_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + errno_t ret; + + ret = sbus_call_dp_autofs_GetMap_recv(subreq); + + if (ret == ERR_MISSING_DP_TARGET || ret == ENOENT) { + ret = EOK; + } + + return ret == EOK; +} + +const struct cache_req_plugin cache_req_autofs_map_by_name = { + .name = "Get autofs map", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_autofs_map_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_autofs_map_by_name_lookup, + .dp_send_fn = cache_req_autofs_map_by_name_dp_send, + .dp_recv_fn = cache_req_autofs_map_by_name_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_autofs_map_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_AUTOFS_MAP_BY_NAME, name); + if (data == NULL) { + return NULL; + } + + cache_req_data_set_propogate_offline_status(data, true); + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c b/src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c new file mode 100644 index 0000000..18c08ca --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c @@ -0,0 +1,187 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2019 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_autofs.h" +#include "util/util.h" +#include "util/sss_chain_id.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_autofs_map_entries_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.name); +} + +static errno_t +cache_req_autofs_map_entries_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_message *map; + struct ldb_message **mounts; + struct ldb_message **msgs; + struct ldb_result *result; + size_t count; + size_t i; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_get_map_byname(tmp_ctx, domain, data->name.name, &map); + if (ret != EOK) { + goto done; + } + + ret = sysdb_autofs_entries_by_map(tmp_ctx, domain, data->name.name, + &count, &mounts); + if (ret != EOK && ret != ENOENT) { + goto done; + } + + msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, count + 1); + if (msgs == NULL) { + ret = ENOMEM; + goto done; + } + + msgs[0] = talloc_steal(msgs, map); + for (i = 0; i < count; i++) { + msgs[i + 1] = talloc_steal(msgs, mounts[i]); + } + + result = cache_req_create_ldb_result_from_msg_list(tmp_ctx, msgs, count + 1); + if (result == NULL) { + ret = ENOMEM; + goto done; + } + + *_result = talloc_steal(mem_ctx, result); + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static struct tevent_req * +cache_req_autofs_map_entries_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + struct be_conn *be_conn; + errno_t ret; + + ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + domain->name); + return NULL; + } + + return sbus_call_dp_autofs_Enumerate_send(mem_ctx, be_conn->conn, + be_conn->bus_name, SSS_BUS_PATH, + 0, data->name.name, + sss_chain_id_get()); +} + +bool +cache_req_autofs_map_entries_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + errno_t ret; + + ret = sbus_call_dp_autofs_Enumerate_recv(subreq); + + if (ret == ERR_MISSING_DP_TARGET || ret == ENOENT) { + ret = EOK; + } + + return ret == EOK; +} + +const struct cache_req_plugin cache_req_autofs_map_entries = { + .name = "Get autofs entries", + .attr_expiration = SYSDB_ENUM_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_autofs_map_entries_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_autofs_map_entries_lookup, + .dp_send_fn = cache_req_autofs_map_entries_dp_send, + .dp_recv_fn = cache_req_autofs_map_entries_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_autofs_map_entries_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_AUTOFS_MAP_ENTRIES, name); + if (data == NULL) { + return NULL; + } + + cache_req_data_set_propogate_offline_status(data, true); + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c new file mode 100644 index 0000000..7eb0921 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_common.c @@ -0,0 +1,192 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +errno_t cache_req_idminmax_check(struct cache_req_data *data, + struct sss_domain_info *domain) +{ + if (((domain->id_min != 0) && (data->id < domain->id_min)) || + ((domain->id_max != 0) && (data->id > domain->id_max))) { + DEBUG(SSSDBG_FUNC_DATA, "id exceeds min/max boundaries\n"); + return ERR_ID_OUTSIDE_RANGE; + } + return EOK; +} + +static struct ldb_message * +cache_req_well_known_sid_msg(TALLOC_CTX *mem_ctx, + const char *sid, + const char *name) +{ + struct ldb_message *msg; + const char *dup_sid; + const char *dup_name; + int ldberr; + + msg = ldb_msg_new(NULL); + if (msg == NULL) { + return NULL; + } + + dup_sid = talloc_strdup(msg, sid); + if (dup_sid == NULL) { + ldberr = LDB_ERR_OTHER; + goto done; + } + + dup_name = talloc_strdup(msg, name); + if (name == NULL) { + ldberr = LDB_ERR_OTHER; + goto done; + } + + ldberr = ldb_msg_add_string(msg, SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS); + if (ldberr != LDB_SUCCESS) { + goto done; + } + + ldberr = ldb_msg_add_string(msg, SYSDB_NAME, dup_name); + if (ldberr != LDB_SUCCESS) { + goto done; + } + + ldberr = ldb_msg_add_string(msg, SYSDB_SID_STR, dup_sid); + if (ldberr != LDB_SUCCESS) { + goto done; + } + +done: + if (ldberr != LDB_SUCCESS) { + talloc_free(msg); + return NULL; + } + + return msg; +} + +struct cache_req_result * +cache_req_well_known_sid_result(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + const char *domname, + const char *sid, + const char *name) +{ + struct cache_req_result *result; + struct sss_domain_info *domain; + struct ldb_message *msg; + + msg = cache_req_well_known_sid_msg(NULL, sid, name); + if (msg == NULL) { + return NULL; + } + + if (domname != NULL) { + domain = find_domain_by_name(cr->rctx->domains, domname, true); + } else { + domain = NULL; + } + + result = cache_req_create_result_from_msg(mem_ctx, domain, msg, + name, domname); + if (result == NULL) { + talloc_free(msg); + } + + return result; +} + +bool +cache_req_common_process_dp_reply(struct cache_req *cr, + errno_t ret, + uint16_t err_maj, + uint32_t err_min, + const char *err_msg) +{ + bool bret; + + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_IMPORTANT_INFO, cr, + "Could not get account info [%d]: %s\n", + ret, sss_strerror(ret)); + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Due to an error we will return cached data\n"); + + bret = false; + goto done; + } + + if (err_maj) { + CACHE_REQ_DEBUG(SSSDBG_IMPORTANT_INFO, cr, + "Data Provider Error: %u, %u, %s\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg); + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Due to an error we will return cached data\n"); + + bret = false; + goto done; + } + + bret = true; + +done: + return bret; +} + +bool +cache_req_common_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + const char *err_msg; + uint16_t err_maj; + uint32_t err_min; + errno_t ret; + bool bret; + + /* Use subreq as memory context so err_msg is freed with it. */ + ret = sss_dp_get_account_recv(subreq, subreq, &err_maj, &err_min, &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +errno_t +cache_req_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *subreq, + struct cache_req *cr, + char **_domain) +{ + errno_t ret; + + ret = sss_dp_get_account_domain_recv(mem_ctx, subreq, _domain); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr, + "Could not get account domain [%d]: %s\n", + ret, sss_strerror(ret)); + } + return ret; +} diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c new file mode 100644 index 0000000..a0d1028 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c @@ -0,0 +1,93 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_enum_groups_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, "Groups enumeration"); +} + +static errno_t +cache_req_enum_groups_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_enumgrent_with_views(mem_ctx, domain, _result); +} + +static struct tevent_req * +cache_req_enum_groups_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_GROUP, NULL, 0, NULL); +} + +static errno_t +cache_req_enum_groups_ncache_filter(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + const char *name) +{ + return sss_ncache_check_group(ncache, domain, name); +} + +const struct cache_req_plugin cache_req_enum_groups = { + .name = "Enumerate groups", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, + .require_enumeration = true, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_enum_groups_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = cache_req_enum_groups_ncache_filter, + .lookup_fn = cache_req_enum_groups_lookup, + .dp_send_fn = cache_req_enum_groups_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c b/src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c new file mode 100644 index 0000000..ae468b3 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c @@ -0,0 +1,126 @@ +/* + SSSD + + Authors: + Samuel Cabrero <scabrero@suse.com> + + Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany. + + 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_iphosts.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_enum_host_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, "IP hosts enumeration"); +} + +static errno_t +cache_req_enum_host_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_enumhostent(mem_ctx, domain, _result); +} + +static struct tevent_req * +cache_req_enum_host_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true, + BE_REQ_HOST, BE_FILTER_ENUM, NULL); +} + +static bool +cache_req_enum_host_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + bool bret; + uint16_t err_maj; + uint32_t err_min; + errno_t ret; + const char *err_msg; + + ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min, + &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +const struct cache_req_plugin cache_req_enum_ip_hosts = { + .name = "Enumerate IP hosts", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, + .require_enumeration = true, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_enum_host_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_enum_host_lookup, + .dp_send_fn = cache_req_enum_host_dp_send, + .dp_recv_fn = cache_req_enum_host_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_enum_ip_hosts_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain) +{ + struct cache_req_data *data; + + data = cache_req_data_enum(mem_ctx, CACHE_REQ_ENUM_HOST); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c b/src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c new file mode 100644 index 0000000..e03bf6a --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c @@ -0,0 +1,126 @@ +/* + SSSD + + Authors: + Samuel Cabrero <scabrero@suse.com> + + Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany. + + 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_ipnetworks.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_enum_ip_networks_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, "IP networks enumeration"); +} + +static errno_t +cache_req_enum_ip_networks_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_enumnetent(mem_ctx, domain, _result); +} + +static struct tevent_req * +cache_req_enum_ip_networks_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true, + BE_REQ_IP_NETWORK, BE_FILTER_ENUM, NULL); +} + +static bool +cache_req_enum_ip_networks_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + bool bret; + uint16_t err_maj; + uint32_t err_min; + errno_t ret; + const char *err_msg; + + ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min, + &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +const struct cache_req_plugin cache_req_enum_ip_networks = { + .name = "Enumerate IP networks", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, + .require_enumeration = true, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_enum_ip_networks_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_enum_ip_networks_lookup, + .dp_send_fn = cache_req_enum_ip_networks_dp_send, + .dp_recv_fn = cache_req_enum_ip_networks_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_enum_ip_networks_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain) +{ + struct cache_req_data *data; + + data = cache_req_data_enum(mem_ctx, CACHE_REQ_ENUM_IP_NETWORK); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c new file mode 100644 index 0000000..282dc1c --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c @@ -0,0 +1,106 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_services.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_enum_svc_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, "Services enumeration"); +} + +static errno_t +cache_req_enum_svc_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_enumservent(mem_ctx, domain, _result); +} + +static struct tevent_req * +cache_req_enum_svc_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_SERVICES, NULL, 0, NULL); +} + +const struct cache_req_plugin cache_req_enum_svc = { + .name = "Enumerate services", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, + .require_enumeration = true, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_enum_svc_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_enum_svc_lookup, + .dp_send_fn = cache_req_enum_svc_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_enum_svc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain) +{ + struct cache_req_data *data; + + data = cache_req_data_enum(mem_ctx, CACHE_REQ_ENUM_SVC); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c new file mode 100644 index 0000000..87da79f --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c @@ -0,0 +1,93 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_enum_users_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, "Users enumeration"); +} + +static errno_t +cache_req_enum_users_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_enumpwent_with_views(mem_ctx, domain, _result); +} + +static struct tevent_req * +cache_req_enum_users_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_USER, NULL, 0, NULL); +} + +static errno_t +cache_req_enum_users_ncache_filter(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + const char *name) +{ + return sss_ncache_check_user(ncache, domain, name); +} + +const struct cache_req_plugin cache_req_enum_users = { + .name = "Enumerate users", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, + .require_enumeration = true, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_enum_users_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = cache_req_enum_users_ncache_filter, + .lookup_fn = cache_req_enum_users_lookup, + .dp_send_fn = cache_req_enum_users_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c new file mode 100644 index 0000000..1292f18 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c @@ -0,0 +1,171 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_group_by_filter_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_group_by_filter_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_group_by_filter_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + char *recent_filter; + errno_t ret; + + /* The "files" provider updates the record if /etc/passwd or /etc/group + * is touched. It does not perform any per-request update. + * Therefore the last update flag is not updated if no file was touched + * and we cannot use this optimization. + */ + if (is_files_provider(domain)) { + recent_filter = NULL; + } else { + recent_filter = talloc_asprintf(mem_ctx, "(%s>=%lu)", SYSDB_LAST_UPDATE, + cr->req_start); + if (recent_filter == NULL) { + return ENOMEM; + } + } + + ret = sysdb_enumgrent_filter_with_views(mem_ctx, domain, data->name.lookup, + recent_filter, _result); + talloc_free(recent_filter); + + return ret; +} + +static struct tevent_req * +cache_req_group_by_filter_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_WILDCARD_GROUP, + cr->data->name.lookup, cr->data->id, NULL); +} + +const struct cache_req_plugin cache_req_group_by_filter = { + .name = "Group by filter", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_group_by_filter_prepare_domain_data, + .create_debug_name_fn = cache_req_group_by_filter_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_group_by_filter_lookup, + .dp_send_fn = cache_req_group_by_filter_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *filter) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_GROUP_BY_FILTER, filter); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL, + 0, + req_dom_type, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c new file mode 100644 index 0000000..bb72ad9 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c @@ -0,0 +1,246 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_group_by_id_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_asprintf(mem_ctx, "GID:%"PRIu32"@%s", data->id, domain->name); +} + +static errno_t +cache_req_group_by_id_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + errno_t ret; + + if (domain != NULL) { + ret = sss_ncache_check_gid(ncache, domain, data->id); + if (ret == EEXIST) { + return ret; + } + } + + return sss_ncache_check_gid(ncache, NULL, data->id); +} + +static errno_t +cache_req_group_by_id_ncache_filter(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + const char *name) +{ + return sss_ncache_check_group(ncache, domain, name); +} + +static errno_t +cache_req_group_by_id_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) +{ + return sss_ncache_set_gid(ncache, false, NULL, data->id); +} + +static errno_t +cache_req_group_by_id_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_gid(ncache, false, domain, data->id); +} + +static errno_t +cache_req_group_by_id_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + errno_t ret; + + ret = cache_req_idminmax_check(data, domain); + if (ret != EOK) { + return ret; + } + return sysdb_getgrgid_with_views(mem_ctx, domain, data->id, _result); +} + +static errno_t +cache_req_group_by_id_dpreq_params(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result *result, + const char **_string, + uint32_t *_id, + const char **_flag) +{ + uint32_t id; + + *_id = cr->data->id; + *_string = NULL; + *_flag = NULL; + + if (!DOM_HAS_VIEWS(cr->domain)) { + return EOK; + } + + /* We must search with views. */ + if (result == NULL || result->count == 0) { + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* If domain has views we will try to use original values instead of the + * overridden ones. This is a must for the LOCAL view since we can't look + * it up otherwise. But it is also a shortcut for non-local views where + * we will not fail over to the overridden value. */ + + id = ldb_msg_find_attr_as_uint64(result->msgs[0], SYSDB_GIDNUM, 0); + if (id == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0\n"); + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* Now we have the original name and id. We don't have to search with + * views unless some error occurred. */ + *_id = id; + + return EOK; +} + +static struct tevent_req * +cache_req_group_by_id_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + const char *string; + const char *flag; + uint32_t id; + errno_t ret; + + ret = cache_req_group_by_id_dpreq_params(mem_ctx, cr, result, + &string, &id, &flag); + if (ret != EOK) { + return NULL; + } + + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_GROUP, string, id, flag); +} + +static bool +cache_req_group_by_id_get_domain_check(struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id); + if (nret == EEXIST) { + return false; + } + + return true; +} + +static struct tevent_req * +cache_req_group_by_id_get_domain_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id); + if (nret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot set negative cache, this might result in performance degradation\n"); + /* Not fatal */ + } + + return sss_dp_get_account_domain_send(mem_ctx, + rctx, + domain, + true, /* fast_reply */ + SSS_DP_GROUP, + data->id, + NULL); +} + +const struct cache_req_plugin cache_req_group_by_id = { + .name = "Group by ID", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_group_by_id_create_debug_name, + .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add, + .ncache_check_fn = cache_req_group_by_id_ncache_check, + .ncache_add_fn = cache_req_group_by_id_ncache_add, + .ncache_filter_fn = cache_req_group_by_id_ncache_filter, + .lookup_fn = cache_req_group_by_id_lookup, + .dp_send_fn = cache_req_group_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = cache_req_group_by_id_get_domain_check, + .dp_get_domain_send_fn = cache_req_group_by_id_get_domain_send, + .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv, +}; + +struct tevent_req * +cache_req_group_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + gid_t gid) +{ + struct cache_req_data *data; + + data = cache_req_data_id(mem_ctx, CACHE_REQ_GROUP_BY_ID, gid); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c new file mode 100644 index 0000000..3be0d5e --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c @@ -0,0 +1,227 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_group_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_create_internal_fqname(tmp_ctx, name, domain->name); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_group_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_group_by_name_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_group(ncache, domain, data->name.lookup); +} + +static errno_t +cache_req_group_by_name_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_group(ncache, false, domain, data->name.lookup); +} + +static errno_t +cache_req_group_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_getgrnam_with_views(mem_ctx, domain, data->name.lookup, + _result); +} + +static errno_t +cache_req_group_by_name_dpreq_params(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result *result, + const char **_string, + uint32_t *_id, + const char **_flag) +{ + const char *name; + + *_id = 0; + *_string = cr->data->name.lookup; + *_flag = NULL; + + if (!DOM_HAS_VIEWS(cr->domain)) { + return EOK; + } + + /* We must search with views. */ + if (result == NULL || result->count == 0) { + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* If domain has views we will try to use original values instead of the + * overridden ones. This is a must for the LOCAL view since we can't look + * it up otherwise. But it is also a shortcut for non-local views where + * we will not fail over to the overridden value. */ + + name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n"); + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* Now we have the original name and id. We don't have to search with + * views unless some error occurred. */ + *_string = talloc_steal(mem_ctx, name); + + return EOK; +} + +static struct tevent_req * +cache_req_group_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + const char *string; + const char *flag; + uint32_t id; + errno_t ret; + + ret = cache_req_group_by_name_dpreq_params(mem_ctx, cr, result, + &string, &id, &flag); + if (ret != EOK) { + return NULL; + } + + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_GROUP, string, id, flag); +} + +const struct cache_req_plugin cache_req_group_by_name = { + .name = "Group by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_group_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_group_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_group_by_name_ncache_check, + .ncache_add_fn = cache_req_group_by_name_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_group_by_name_lookup, + .dp_send_fn = cache_req_group_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_group_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_GROUP_BY_NAME, name); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + req_dom_type, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c new file mode 100644 index 0000000..c5bea9d --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c @@ -0,0 +1,242 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_initgroups_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_create_internal_fqname(tmp_ctx, name, domain->name); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_initgroups_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_initgroups_by_name_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_user(ncache, domain, data->name.lookup); +} + +static errno_t +cache_req_initgroups_by_name_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_user(ncache, false, domain, data->name.lookup); +} + +static errno_t +cache_req_initgroups_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_initgroups_with_views(mem_ctx, domain, data->name.lookup, + _result); +} + +static errno_t +cache_req_initgroups_by_name_dpreq_params(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result *result, + const char **_string, + uint32_t *_id, + const char **_flag) +{ + struct ldb_result *user; + const char *name; + errno_t ret; + + *_id = 0; + *_string = cr->data->name.lookup; + *_flag = NULL; + + if (!DOM_HAS_VIEWS(cr->domain)) { + return EOK; + } + + /* We must search with views. */ + if (result == NULL || result->count == 0) { + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* If domain has views we will try to use original values instead of the + * overridden ones. This is a must for the LOCAL view since we can't look + * it up otherwise. But it is also a shortcut for non-local views where + * we will not fail over to the overridden value. */ + + ret = sysdb_getpwnam_with_views(NULL, cr->domain, + cr->data->name.lookup, &user); + if (ret != EOK || user == NULL || user->count != 1) { + /* Case where the user is not found has been already handled. If + * this is not OK, it is an error. */ + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "Unable to match initgroups user [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + name = ldb_msg_find_attr_as_string(user->msgs[0], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n"); + talloc_free(user); + return ERR_INTERNAL; + } + + /* Now we have the original name. We don't have to search with + * views unless some error occurred. */ + *_string = talloc_steal(mem_ctx, name); + + talloc_free(user); + + return EOK; +} + +static struct tevent_req * +cache_req_initgroups_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + const char *string; + const char *flag; + uint32_t id; + errno_t ret; + + ret = cache_req_initgroups_by_name_dpreq_params(mem_ctx, cr, result, + &string, &id, &flag); + if (ret != EOK) { + return NULL; + } + + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_INITGROUPS, string, id, flag); +} + +const struct cache_req_plugin cache_req_initgroups_by_name = { + .name = "Initgroups by name", + .attr_expiration = SYSDB_INITGR_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = true, + .upn_equivalent = CACHE_REQ_INITGROUPS_BY_UPN, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_initgroups_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_initgroups_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_initgroups_by_name_ncache_check, + .ncache_add_fn = cache_req_initgroups_by_name_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_initgroups_by_name_lookup, + .dp_send_fn = cache_req_initgroups_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_INITGROUPS, name); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + req_dom_type, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c new file mode 100644 index 0000000..9bd00f3 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c @@ -0,0 +1,130 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_initgroups_by_upn_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *name; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed UPN is NULL?\n"); + return ERR_INTERNAL; + } + + /* When looking up UPNs we don't want to reverse-replace spaces, + * just search whatever the user passed in. strdup the name so we + * can safely steal it later. + */ + name = talloc_strdup(data, cr->data->name.name); + if (name == NULL) { + return ENOMEM; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + return EOK; +} + +static const char * +cache_req_initgroups_by_upn_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_initgroups_by_upn_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_upn(ncache, domain, data->name.lookup); +} + +static errno_t +cache_req_initgroups_by_upn_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_upn(ncache, false, domain, data->name.lookup); +} + +static errno_t +cache_req_initgroups_by_upn_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_initgroups_by_upn(mem_ctx, domain, data->name.lookup, + _result); +} + +static struct tevent_req * +cache_req_initgroups_by_upn_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_INITGROUPS, cr->data->name.lookup, + 0, EXTRA_NAME_IS_UPN); +} + +const struct cache_req_plugin cache_req_initgroups_by_upn = { + .name = "Initgroups by UPN", + .attr_expiration = SYSDB_INITGR_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_initgroups_by_upn_prepare_domain_data, + .create_debug_name_fn = cache_req_initgroups_by_upn_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_initgroups_by_upn_ncache_check, + .ncache_add_fn = cache_req_initgroups_by_upn_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_initgroups_by_upn_lookup, + .dp_send_fn = cache_req_initgroups_by_upn_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c new file mode 100644 index 0000000..324d20e --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c @@ -0,0 +1,174 @@ +/* + SSSD + + Authors: + Samuel Cabrero <scabrero@suse.com> + + Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany. + + 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 <talloc.h> +#include <ldb.h> +#include <arpa/inet.h> + +#include "db/sysdb.h" +#include "db/sysdb_iphosts.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_ip_host_by_addr_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + char buf[INET6_ADDRSTRLEN]; + const char *addr; + + if (data->addr.len == 0 || data->addr.data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: address is NULL?\n"); + return ERR_INTERNAL; + } + + if (data->addr.af != AF_INET && data->addr.af != AF_INET6) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bad address family [%d]\n", data->addr.af); + return EAFNOSUPPORT; + } + + addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN); + if (addr == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse address: %s\n", + strerror(errno)); + return ERR_INTERNAL; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_strdup(data, addr); + + return EOK; +} + +static const char * +cache_req_ip_host_by_addr_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *addr = NULL; + char buf[INET6_ADDRSTRLEN]; + + addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN); + if (addr == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse network address: %s\n", + strerror(errno)); + return NULL; + } + + return talloc_strdup(mem_ctx, addr); +} + +static errno_t +cache_req_ip_host_by_addr_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_gethostbyaddr(mem_ctx, domain, data->name.lookup, + _result); +} + +static struct tevent_req * +cache_req_ip_host_by_addr_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true, + BE_REQ_HOST, BE_FILTER_ADDR, + data->name.lookup); +} + +static bool +cache_req_ip_host_by_addr_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + bool bret; + uint16_t err_maj; + uint32_t err_min; + errno_t ret; + const char *err_msg; + + ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min, + &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +const struct cache_req_plugin cache_req_ip_host_by_addr = { + .name = "IP host by address", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_ip_host_by_addr_prepare_domain_data, + .create_debug_name_fn = cache_req_ip_host_by_addr_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_ip_host_by_addr_lookup, + .dp_send_fn = cache_req_ip_host_by_addr_dp_send, + .dp_recv_fn = cache_req_ip_host_by_addr_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_ip_host_by_addr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_HOST_BY_NAME, + name, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c new file mode 100644 index 0000000..b5f33ee --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c @@ -0,0 +1,170 @@ +/* + SSSD + + Authors: + Samuel Cabrero <scabrero@suse.com> + + Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany. + + 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_iphosts.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_ip_host_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + /* Host names always case insensitive */ + name = sss_get_cased_name(tmp_ctx, data->name.name, false); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_ip_host_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *name = data->name.lookup; + + return talloc_asprintf(mem_ctx, "%s@%s", name, domain->name); +} + +static errno_t +cache_req_ip_host_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_gethostbyname(mem_ctx, domain, data->name.lookup, + _result); +} + +static struct tevent_req * +cache_req_ip_host_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true, + BE_REQ_HOST, BE_FILTER_NAME, + data->name.lookup); +} + +static bool +cache_req_ip_host_by_name_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + bool bret; + uint16_t err_maj; + uint32_t err_min; + errno_t ret; + const char *err_msg; + + ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min, + &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +const struct cache_req_plugin cache_req_ip_host_by_name = { + .name = "IP host by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_ip_host_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_ip_host_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_ip_host_by_name_lookup, + .dp_send_fn = cache_req_ip_host_by_name_dp_send, + .dp_recv_fn = cache_req_ip_host_by_name_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_ip_host_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_HOST_BY_NAME, + name, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c new file mode 100644 index 0000000..0ad7f61 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c @@ -0,0 +1,174 @@ +/* + SSSD + + Authors: + Samuel Cabrero <scabrero@suse.com> + + Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany. + + 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 <talloc.h> +#include <ldb.h> +#include <arpa/inet.h> + +#include "db/sysdb.h" +#include "db/sysdb_ipnetworks.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_ip_network_by_addr_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + char buf[INET6_ADDRSTRLEN]; + const char *addr; + + if (data->addr.len == 0 || data->addr.data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: address is NULL?\n"); + return ERR_INTERNAL; + } + + if (data->addr.af != AF_INET && data->addr.af != AF_INET6) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bad address family [%d]\n", data->addr.af); + return EAFNOSUPPORT; + } + + addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN); + if (addr == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse address: %s\n", + strerror(errno)); + return ERR_INTERNAL; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_strdup(data, addr); + + return EOK; +} + +static const char * +cache_req_ip_network_by_addr_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *addr = NULL; + char buf[INET6_ADDRSTRLEN]; + + addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN); + if (addr == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse network address: %s\n", + strerror(errno)); + return NULL; + } + + return talloc_strdup(mem_ctx, addr); +} + +static errno_t +cache_req_ip_network_by_addr_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_getipnetworkbyaddr(mem_ctx, domain, data->name.lookup, + _result); +} + +static struct tevent_req * +cache_req_ip_network_by_addr_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true, + BE_REQ_IP_NETWORK, BE_FILTER_ADDR, + data->name.lookup); +} + +static bool +cache_req_ip_network_by_addr_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + bool bret; + uint16_t err_maj; + uint32_t err_min; + errno_t ret; + const char *err_msg; + + ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min, + &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +const struct cache_req_plugin cache_req_ip_network_by_addr = { + .name = "IP network by address", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_ip_network_by_addr_prepare_domain_data, + .create_debug_name_fn = cache_req_ip_network_by_addr_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_ip_network_by_addr_lookup, + .dp_send_fn = cache_req_ip_network_by_addr_dp_send, + .dp_recv_fn = cache_req_ip_network_by_addr_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_ip_network_by_addr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_NETWORK_BY_NAME, + name, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c new file mode 100644 index 0000000..c02bc06 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c @@ -0,0 +1,170 @@ +/* + SSSD + + Authors: + Samuel Cabrero <scabrero@suse.com> + + Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany. + + 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_ipnetworks.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_ip_network_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + /* Network names always case insensitive */ + name = sss_get_cased_name(tmp_ctx, data->name.name, false); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_ip_network_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *name = data->name.lookup; + + return talloc_asprintf(mem_ctx, "%s@%s", name, domain->name); +} + +static errno_t +cache_req_ip_network_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_getipnetworkbyname(mem_ctx, domain, data->name.lookup, + _result); +} + +static struct tevent_req * +cache_req_ip_network_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true, + BE_REQ_IP_NETWORK, BE_FILTER_NAME, + data->name.lookup); +} + +static bool +cache_req_ip_network_by_name_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + bool bret; + uint16_t err_maj; + uint32_t err_min; + errno_t ret; + const char *err_msg; + + ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min, + &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +const struct cache_req_plugin cache_req_ip_network_by_name = { + .name = "IP network by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_ip_network_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_ip_network_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_ip_network_by_name_lookup, + .dp_send_fn = cache_req_ip_network_by_name_dp_send, + .dp_recv_fn = cache_req_ip_network_by_name_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_ip_network_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_NETWORK_BY_NAME, + name, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c new file mode 100644 index 0000000..d370d34 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c @@ -0,0 +1,160 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_netgroup_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_netgroup_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_asprintf(mem_ctx, "%s@%s", data->name.lookup, domain->name); +} + +static errno_t +cache_req_netgroup_by_name_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_netgr(ncache, domain, data->name.lookup); +} + +static errno_t +cache_req_netgroup_by_name_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_netgr(ncache, false, domain, data->name.lookup); +} + +static errno_t +cache_req_netgroup_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_getnetgr(mem_ctx, domain, data->name.lookup, _result); +} + +static struct tevent_req * +cache_req_netgroup_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_NETGR, cr->data->name.lookup, + 0, NULL); +} + +const struct cache_req_plugin cache_req_netgroup_by_name = { + .name = "Netgroup by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_netgroup_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_netgroup_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_netgroup_by_name_ncache_check, + .ncache_add_fn = cache_req_netgroup_by_name_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_netgroup_by_name_lookup, + .dp_send_fn = cache_req_netgroup_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_netgroup_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_NETGROUP_BY_NAME, name); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c new file mode 100644 index 0000000..2bff990 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c @@ -0,0 +1,236 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_object_by_id_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_asprintf(mem_ctx, "ID:%"PRIu32"@%s", data->id, domain->name); +} + +static errno_t +cache_req_object_by_id_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + errno_t ret; + + ret = sss_ncache_check_uid(ncache, domain, data->id); + if (ret == EEXIST) { + ret = sss_ncache_check_gid(ncache, domain, data->id); + } + + return ret; +} + +static errno_t +cache_req_object_by_id_ncache_filter(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + const char *name) +{ + errno_t ret; + + ret = sss_ncache_check_user(ncache, domain, name); + if (ret == EEXIST) { + ret = sss_ncache_check_group(ncache, domain, name); + } + + return ret; +} + +static errno_t +cache_req_object_by_id_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) +{ + errno_t ret; + + ret = sss_ncache_set_uid(ncache, false, NULL, data->id); + if (ret != EOK) { + return ret; + } + + ret = sss_ncache_set_gid(ncache, false, NULL, data->id); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +static errno_t +cache_req_object_by_id_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + errno_t ret; + + ret = sss_ncache_set_uid(ncache, false, domain, data->id); + if (ret != EOK) { + return ret; + } + + ret = sss_ncache_set_gid(ncache, false, domain, data->id); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +static errno_t +cache_req_object_by_id_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + errno_t ret; + + ret = cache_req_idminmax_check(data, domain); + if (ret != EOK) { + return ret; + } + return sysdb_search_object_by_id(mem_ctx, domain, data->id, + data->attrs, _result); +} + +static struct tevent_req * +cache_req_object_by_id_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_USER_AND_GROUP, NULL, + cr->data->id, NULL); +} + +static bool +cache_req_object_by_id_get_domain_check(struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id); + if (nret == EEXIST) { + nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id); + if (nret == EEXIST) { + return false; + } + } + + return true; +} + +static struct tevent_req * +cache_req_object_by_id_get_domain_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id); + if (nret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot set negative cache, this might result in " + "performance degradation\n"); + /* Not fatal */ + } + + nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id); + if (nret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot set negative cache, this might result in " + "performance degradation\n"); + /* Not fatal */ + } + + return sss_dp_get_account_domain_send(mem_ctx, + rctx, + domain, + true, /* fast_reply */ + SSS_DP_USER_AND_GROUP, + data->id, + NULL); +} + +const struct cache_req_plugin cache_req_object_by_id = { + .name = "Object by ID", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_object_by_id_create_debug_name, + .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add, + .ncache_check_fn = cache_req_object_by_id_ncache_check, + .ncache_add_fn = cache_req_object_by_id_ncache_add, + .ncache_filter_fn = cache_req_object_by_id_ncache_filter, + .lookup_fn = cache_req_object_by_id_lookup, + .dp_send_fn = cache_req_object_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = cache_req_object_by_id_get_domain_check, + .dp_get_domain_send_fn = cache_req_object_by_id_get_domain_send, + .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv, +}; + +struct tevent_req * +cache_req_object_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + uint32_t id, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_id_attrs(mem_ctx, CACHE_REQ_OBJECT_BY_ID, id, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c new file mode 100644 index 0000000..907f1f3 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c @@ -0,0 +1,238 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_object_by_name_well_known(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct cache_req_result **_result) +{ + struct cache_req_result *result; + const char *sid; + char *domname; + char *name; + errno_t ret; + + ret = sss_parse_name(mem_ctx, cr->rctx->global_names, + data->name.input, &domname, &name); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr, "Unable to parse name " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + if (domname == NULL || name == NULL) { + CACHE_REQ_DEBUG(SSSDBG_FUNC_DATA, cr, "Unable to split [%s] in " + "name and domain part. Skipping detection of " + "well-known name.\n", data->name.input); + return ENOENT; + } + + ret = name_to_well_known_sid(domname, name, &sid); + if (ret != EOK) { + return ret; + } + + result = cache_req_well_known_sid_result(mem_ctx, cr, domname, sid, name); + talloc_free(domname); + talloc_free(name); + if (result == NULL) { + return ENOMEM; + } + + *_result = result; + + return EOK; +} + +static errno_t +cache_req_object_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_create_internal_fqname(tmp_ctx, name, domain->name); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_object_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_object_by_name_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + errno_t ret; + + ret = sss_ncache_check_user(ncache, domain, data->name.lookup); + if (ret == EEXIST) { + ret = sss_ncache_check_group(ncache, domain, data->name.lookup); + } + + return ret; +} + +static errno_t +cache_req_object_by_name_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + errno_t ret; + + ret = sss_ncache_set_user(ncache, false, domain, data->name.lookup); + if (ret != EOK) { + return ret; + } + + ret = sss_ncache_set_group(ncache, false, domain, data->name.lookup); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +static errno_t +cache_req_object_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_search_object_by_name(mem_ctx, domain, data->name.lookup, + data->attrs, _result); +} + +static struct tevent_req * +cache_req_object_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_USER_AND_GROUP, + cr->data->name.lookup, 0, NULL); +} + +const struct cache_req_plugin cache_req_object_by_name = { + .name = "Object by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = true, + .upn_equivalent = CACHE_REQ_USER_BY_UPN, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = cache_req_object_by_name_well_known, + .prepare_domain_data_fn = cache_req_object_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_object_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_object_by_name_ncache_check, + .ncache_add_fn = cache_req_object_by_name_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_object_by_name_lookup, + .dp_send_fn = cache_req_object_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_object_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_OBJECT_BY_NAME, + name, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c new file mode 100644 index 0000000..db731b1 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c @@ -0,0 +1,202 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_object_by_sid_well_known(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct cache_req_result **_result) +{ + struct cache_req_result *result; + const char *domname; + const char *name; + errno_t ret; + + ret = well_known_sid_to_name(data->sid, &domname, &name); + if (ret != EOK) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_ALL, cr, + "SID [%s] is not a Well-Known SID.\n", data->sid); + return ret; + } + + result = cache_req_well_known_sid_result(mem_ctx, cr, domname, + data->sid, name); + if (result == NULL) { + return ENOMEM; + } + + *_result = result; + + return EOK; +} + +static const char * +cache_req_object_by_sid_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_asprintf(mem_ctx, "SID:%s@%s", data->sid, domain->name); +} + +static errno_t +cache_req_object_by_sid_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_sid(ncache, domain, data->sid); +} + +static errno_t +cache_req_object_by_sid_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) +{ + return sss_ncache_set_sid(ncache, false, NULL, data->sid); +} + +static errno_t +cache_req_object_by_sid_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_sid(ncache, false, domain, data->sid); +} + +static errno_t +cache_req_object_by_sid_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_search_object_by_sid(mem_ctx, domain, data->sid, data->attrs, + _result); +} + +static struct tevent_req * +cache_req_object_by_sid_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_SECID, cr->data->sid, 0, NULL); +} + +static bool +cache_req_object_by_sid_get_domain_check(struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_check_locate_sid(rctx->ncache, domain, data->sid); + if (nret == EEXIST) { + return false; + } + + return true; +} + +static struct tevent_req * +cache_req_object_by_sid_get_domain_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_set_locate_sid(rctx->ncache, domain, data->sid); + if (nret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot set negative cache, this might result in " + "performance degradation\n"); + /* Not fatal */ + } + + return sss_dp_get_account_domain_send(mem_ctx, + rctx, + domain, + true, /* fast_reply */ + SSS_DP_SECID, + 0, + data->sid); +} + + +const struct cache_req_plugin cache_req_object_by_sid = { + .name = "Object by SID", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = cache_req_object_by_sid_well_known, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_object_by_sid_create_debug_name, + .global_ncache_add_fn = cache_req_object_by_sid_global_ncache_add, + .ncache_check_fn = cache_req_object_by_sid_ncache_check, + .ncache_add_fn = cache_req_object_by_sid_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_object_by_sid_lookup, + .dp_send_fn = cache_req_object_by_sid_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = cache_req_object_by_sid_get_domain_check, + .dp_get_domain_send_fn = cache_req_object_by_sid_get_domain_send, + .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv, +}; + +struct tevent_req * +cache_req_object_by_sid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *sid, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_sid(mem_ctx, CACHE_REQ_OBJECT_BY_SID, sid, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c b/src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c new file mode 100644 index 0000000..29f52f1 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c @@ -0,0 +1,164 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb_ssh.h" +#include "util/util.h" +#include "util/sss_chain_id.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_host_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.name); +} + +static errno_t +cache_req_host_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ +#ifdef BUILD_SSH + struct ldb_result *result; + struct ldb_message *msg; + errno_t ret; + + ret = sysdb_get_ssh_host(mem_ctx, domain, data->name.name, + data->attrs, &msg); + if (ret != EOK) { + return ret; + } + + result = cache_req_create_ldb_result_from_msg(mem_ctx, msg); + if (result == NULL) { + return ENOMEM; + } + + *_result = result; + + return EOK; +#else + return ERR_INTERNAL; +#endif /* BUILD_SSH */ +} + +static struct tevent_req * +cache_req_host_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + struct be_conn *be_conn; + errno_t ret; + + ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + domain->name); + return NULL; + } + + return sbus_call_dp_dp_hostHandler_send(mem_ctx, be_conn->conn, + be_conn->bus_name, SSS_BUS_PATH, + 0, data->name.name, data->alias, + sss_chain_id_get()); +} + +static bool +cache_req_host_by_name_dp_recv(struct tevent_req *subreq, + struct cache_req *cr) +{ + const char *err_msg; + dbus_uint16_t err_maj; + dbus_uint32_t err_min; + errno_t ret; + bool bret; + + /* Use subreq as memory context so err_msg is freed with it. */ + ret = sbus_call_dp_dp_hostHandler_recv(subreq, subreq, &err_maj, + &err_min, &err_msg); + bret = cache_req_common_process_dp_reply(cr, ret, err_maj, + err_min, err_msg); + + return bret; +} + +const struct cache_req_plugin cache_req_ssh_host_id_by_name = { + .name = "SSH Host ID by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, + .bypass_cache = true, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = 0, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_host_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_host_by_name_lookup, + .dp_send_fn = cache_req_host_by_name_dp_send, + .dp_recv_fn = cache_req_host_by_name_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_ssh_host_id_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char *alias, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_ssh_host_id(mem_ctx, CACHE_REQ_SSH_HOST_ID_BY_NAME, + name, alias, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c b/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c new file mode 100644 index 0000000..5485271 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c @@ -0,0 +1,143 @@ +/* + Copyright (C) 2021 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_subid.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_subid_ranges_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_subid_ranges_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_subid_ranges_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + struct ldb_result *result; + struct ldb_message *msg; + errno_t ret; + + ret = sysdb_get_subid_ranges(mem_ctx, domain, data->name.name, + data->attrs, &msg); + if (ret != EOK) { + return ret; + } + + result = cache_req_create_ldb_result_from_msg(mem_ctx, msg); + if (result == NULL) { + return ENOMEM; + } + + *_result = result; + + return EOK; +} + +static struct tevent_req * +cache_req_subid_ranges_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + /* Views aren't yet supported */ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_SUBID_RANGES, cr->data->name.lookup, 0, NULL); +} + +const struct cache_req_plugin cache_req_subid_ranges_by_name = { + .name = "SubID ranges by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_subid_ranges_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_subid_ranges_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_subid_ranges_by_name_lookup, + .dp_send_fn = cache_req_subid_ranges_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c new file mode 100644 index 0000000..5b17051 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c @@ -0,0 +1,185 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_services.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_svc_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + const char *protocol; + errno_t ret; + + if (data->svc.name->name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, data->svc.name->name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + if (data->svc.protocol.name == NULL) { + protocol = NULL; + } else { + protocol = sss_get_cased_name(tmp_ctx, data->svc.protocol.name, + domain->case_sensitive); + if (protocol == NULL) { + ret = ENOMEM; + goto done; + } + } + + talloc_zfree(data->svc.name->lookup); + talloc_zfree(data->svc.protocol.lookup); + data->svc.name->lookup = talloc_steal(data, name); + data->svc.protocol.lookup = talloc_steal(data, protocol); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_svc_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *protocol = data->svc.protocol.lookup; + const char *name = data->svc.name->lookup; + + protocol = protocol == NULL ? "<ANY>" : protocol; + + return talloc_asprintf(mem_ctx, "%s %s@%s", protocol, name, domain->name); +} + +static errno_t +cache_req_svc_by_name_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_service(ncache, domain, data->svc.name->lookup, + data->svc.protocol.lookup); +} + +static errno_t +cache_req_svc_by_name_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_service_name(ncache, false, domain, + data->svc.name->lookup, + data->svc.protocol.lookup); +} + +static errno_t +cache_req_svc_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_getservbyname(mem_ctx, domain, data->svc.name->lookup, + data->svc.protocol.lookup, _result); +} + +static struct tevent_req * +cache_req_svc_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_SERVICES, cr->data->svc.name->lookup, + 0, cr->data->svc.protocol.lookup); +} + +const struct cache_req_plugin cache_req_svc_by_name = { + .name = "Service by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_svc_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_svc_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_svc_by_name_ncache_check, + .ncache_add_fn = cache_req_svc_by_name_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_svc_by_name_lookup, + .dp_send_fn = cache_req_svc_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_svc_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char *protocol) +{ + struct cache_req_data *data; + + data = cache_req_data_svc(mem_ctx, CACHE_REQ_SVC_BY_NAME, name, protocol, 0); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c new file mode 100644 index 0000000..4c005df --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c @@ -0,0 +1,159 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "db/sysdb_services.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_svc_by_port_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *protocol; + + if (data->svc.protocol.name == NULL) { + return EOK; + } + + protocol = sss_get_cased_name(NULL, data->svc.protocol.name, + domain->case_sensitive); + if (protocol == NULL) { + return ENOMEM; + } + + talloc_zfree(data->svc.protocol.lookup); + data->svc.protocol.lookup = talloc_steal(data, protocol); + + return EOK; +} + +static const char * +cache_req_svc_by_port_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *protocol = data->svc.protocol.lookup; + + protocol = protocol == NULL ? "<ANY>" : protocol; + + return talloc_asprintf(mem_ctx, "%s %u@%s", protocol, + data->svc.port, domain->name); +} + +static errno_t +cache_req_svc_by_port_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_service_port(ncache, domain, data->svc.port, + data->svc.protocol.lookup); +} + +static errno_t +cache_req_svc_by_port_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_service_port(ncache, false, domain, + data->svc.port, + data->svc.protocol.lookup); +} + +static errno_t +cache_req_svc_by_port_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_getservbyport(mem_ctx, domain, data->svc.port, + data->svc.protocol.lookup, _result); +} + +static struct tevent_req * +cache_req_svc_by_port_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_SERVICES, NULL, cr->data->svc.port, + cr->data->svc.protocol.lookup); +} + +const struct cache_req_plugin cache_req_svc_by_port = { + .name = "Service by port", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_svc_by_port_prepare_domain_data, + .create_debug_name_fn = cache_req_svc_by_port_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_svc_by_port_ncache_check, + .ncache_add_fn = cache_req_svc_by_port_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_svc_by_port_lookup, + .dp_send_fn = cache_req_svc_by_port_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_svc_by_port_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + uint16_t port, + const char *protocol) +{ + struct cache_req_data *data; + + data = cache_req_data_svc(mem_ctx, CACHE_REQ_SVC_BY_PORT, + NULL, protocol, port); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c new file mode 100644 index 0000000..a2dc1fa --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c @@ -0,0 +1,127 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_user_by_cert_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + /* Certificates might be quite long, thus we only use + * the last 10 characters for logging. */ + return talloc_asprintf(mem_ctx, "CERT:%s@%s", + get_last_x_chars(data->cert, 10), domain->name); +} + +static errno_t +cache_req_user_by_cert_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_cert(ncache, data->cert); +} + +static errno_t +cache_req_user_by_cert_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) +{ + return sss_ncache_set_cert(ncache, false, data->cert); +} + +static errno_t +cache_req_user_by_cert_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + return sysdb_search_user_by_cert_with_views(mem_ctx, domain, data->cert, + _result); +} + +static struct tevent_req * +cache_req_user_by_cert_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_CERT, cr->data->cert, 0, NULL); +} + +const struct cache_req_plugin cache_req_user_by_cert = { + .name = "User by certificate", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = true, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_user_by_cert_create_debug_name, + .global_ncache_add_fn = cache_req_user_by_cert_global_ncache_add, + .ncache_check_fn = cache_req_user_by_cert_ncache_check, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_cert_lookup, + .dp_send_fn = cache_req_user_by_cert_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *pem_cert) +{ + struct cache_req_data *data; + + data = cache_req_data_cert(mem_ctx, CACHE_REQ_USER_BY_CERT, pem_cert); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + req_dom_type, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c new file mode 100644 index 0000000..11ea9ec --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c @@ -0,0 +1,176 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_user_by_filter_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_user_by_filter_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_user_by_filter_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + char *recent_filter; + const char *attr = (data->name.attr == NULL ? SYSDB_NAME : data->name.attr); + errno_t ret; + + /* The "files" provider updates the record if /etc/passwd or /etc/group + * is touched. It does not perform any per-request update. + * Therefore the last update flag is not updated if no file was touched + * and we cannot use this optimization. + * Neither it is possible to use it when asking for a non-"name" attribute + * as it could not be present in the timestamp cache. + */ + if (is_files_provider(domain) || data->name.attr != NULL) { + recent_filter = NULL; + } else { + recent_filter = talloc_asprintf(mem_ctx, "(%s>=%lu)", SYSDB_LAST_UPDATE, + cr->req_start); + if (recent_filter == NULL) { + return ENOMEM; + } + } + + ret = sysdb_enumpwent_filter_with_views(mem_ctx, domain, + attr, data->name.lookup, + recent_filter, _result); + talloc_free(recent_filter); + + return ret; +} + +static struct tevent_req * +cache_req_user_by_filter_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_WILDCARD_USER, cr->data->name.lookup, + cr->data->id, NULL); +} + +const struct cache_req_plugin cache_req_user_by_filter = { + .name = "User by filter", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_user_by_filter_prepare_domain_data, + .create_debug_name_fn = cache_req_user_by_filter_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_filter_lookup, + .dp_send_fn = cache_req_user_by_filter_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *attr, + const char *filter) +{ + struct cache_req_data *data; + + data = cache_req_data_attr(mem_ctx, CACHE_REQ_USER_BY_FILTER, attr, filter); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL, + 0, + req_dom_type, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c new file mode 100644 index 0000000..fee5736 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c @@ -0,0 +1,246 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static const char * +cache_req_user_by_id_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_asprintf(mem_ctx, "UID:%"PRIu32"@%s", data->id, domain->name); +} + +static errno_t +cache_req_user_by_id_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + errno_t ret; + + if (domain != NULL) { + ret = sss_ncache_check_uid(ncache, domain, data->id); + if (ret == EEXIST) { + return ret; + } + } + + return sss_ncache_check_uid(ncache, NULL, data->id); +} + +static errno_t +cache_req_user_by_id_ncache_filter(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + const char *name) +{ + return sss_ncache_check_user(ncache, domain, name); +} + +static errno_t +cache_req_user_by_id_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) +{ + return sss_ncache_set_uid(ncache, false, NULL, data->id); +} + +static errno_t +cache_req_user_by_id_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_uid(ncache, false, domain, data->id); +} + +static errno_t +cache_req_user_by_id_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + errno_t ret; + ret = cache_req_idminmax_check(data, domain); + if (ret != EOK) { + return ret; + } + return sysdb_getpwuid_with_views(mem_ctx, domain, data->id, _result); +} + +static errno_t +cache_req_user_by_id_dpreq_params(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result *result, + const char **_string, + uint32_t *_id, + const char **_flag) +{ + uint32_t id; + + *_id = cr->data->id; + *_string = NULL; + *_flag = NULL; + + if (!DOM_HAS_VIEWS(cr->domain)) { + return EOK; + } + + /* We must search with views. */ + if (result == NULL || result->count == 0) { + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* If domain has views we will try to use original values instead of the + * overridden ones. This is a must for the LOCAL view since we can't look + * it up otherwise. But it is also a shortcut for non-local views where + * we will not fail over to the overridden value. */ + + id = ldb_msg_find_attr_as_uint64(result->msgs[0], SYSDB_UIDNUM, 0); + if (id == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0\n"); + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* Now we have the original name and id. We don't have to search with + * views unless some error occurred. */ + *_id = id; + + return EOK; +} + +static struct tevent_req * +cache_req_user_by_id_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + const char *string; + const char *flag; + uint32_t id; + errno_t ret; + + ret = cache_req_user_by_id_dpreq_params(mem_ctx, cr, result, + &string, &id, &flag); + if (ret != EOK) { + return NULL; + } + + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_USER, string, id, flag); +} + +static bool +cache_req_user_by_id_get_domain_check(struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id); + if (nret == EEXIST) { + return false; + } + + return true; +} + +static struct tevent_req * +cache_req_user_by_id_get_domain_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + int nret; + + nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id); + if (nret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot set negative cache, this might result in " + "performance degradation\n"); + /* Not fatal */ + } + + return sss_dp_get_account_domain_send(mem_ctx, + rctx, + domain, + true, /* fast_reply */ + SSS_DP_USER, + data->id, + NULL); +} + +const struct cache_req_plugin cache_req_user_by_id = { + .name = "User by ID", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, + .create_debug_name_fn = cache_req_user_by_id_create_debug_name, + .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add, + .ncache_check_fn = cache_req_user_by_id_ncache_check, + .ncache_add_fn = cache_req_user_by_id_ncache_add, + .ncache_filter_fn = cache_req_user_by_id_ncache_filter, + .lookup_fn = cache_req_user_by_id_lookup, + .dp_send_fn = cache_req_user_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = cache_req_user_by_id_get_domain_check, + .dp_get_domain_send_fn = cache_req_user_by_id_get_domain_send, + .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv, +}; + +struct tevent_req * +cache_req_user_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + uid_t uid) +{ + struct cache_req_data *data; + + data = cache_req_data_id(mem_ctx, CACHE_REQ_USER_BY_ID, uid); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c new file mode 100644 index 0000000..d24a222 --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c @@ -0,0 +1,256 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_user_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_create_internal_fqname(tmp_ctx, name, domain->name); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_user_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_user_by_name_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_user(ncache, domain, data->name.lookup); +} + +static errno_t +cache_req_user_by_name_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_user(ncache, false, domain, data->name.lookup); +} + +static errno_t +cache_req_user_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + if (data->attrs == NULL) { + return sysdb_getpwnam_with_views(mem_ctx, domain, data->name.lookup, + _result); + } + + return sysdb_get_user_attr_with_views(mem_ctx, domain, data->name.lookup, + data->attrs, _result); +} + +static errno_t +cache_req_user_by_name_dpreq_params(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result *result, + const char **_string, + uint32_t *_id, + const char **_flag) +{ + const char *name; + + *_id = 0; + *_string = cr->data->name.lookup; + *_flag = NULL; + + if (!DOM_HAS_VIEWS(cr->domain)) { + return EOK; + } + + /* We must search with views. */ + if (result == NULL || result->count == 0) { + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* If domain has views we will try to use original values instead of the + * overridden ones. This is a must for the LOCAL view since we can't look + * it up otherwise. But it is also a shortcut for non-local views where + * we will not fail over to the overridden value. */ + + name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n"); + *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW; + return EOK; + } + + /* Now we have the original name and id. We don't have to search with + * views unless some error occurred. */ + *_string = talloc_steal(mem_ctx, name); + + return EOK; +} + +static struct tevent_req * +cache_req_user_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + const char *string; + const char *flag; + uint32_t id; + errno_t ret; + + ret = cache_req_user_by_name_dpreq_params(mem_ctx, cr, result, + &string, &id, &flag); + if (ret != EOK) { + return NULL; + } + + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_USER, string, id, flag); +} + +const struct cache_req_plugin cache_req_user_by_name = { + .name = "User by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = true, + .upn_equivalent = CACHE_REQ_USER_BY_UPN, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_user_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_user_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_user_by_name_ncache_check, + .ncache_add_fn = cache_req_user_by_name_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_name_lookup, + .dp_send_fn = cache_req_user_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_user_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_USER_BY_NAME, name); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + req_dom_type, domain, + data); +} + +struct tevent_req * +cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + const char *domain, + const char *name, + const char **attrs) +{ + struct cache_req_data *data; + + data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_USER_BY_NAME, + name, attrs); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, domain, + data); +} diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c new file mode 100644 index 0000000..037994c --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c @@ -0,0 +1,158 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <talloc.h> +#include <ldb.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_user_by_upn_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + const char *name; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed UPN is NULL?\n"); + return ERR_INTERNAL; + } + + /* When looking up UPNs we don't want to reverse-replace spaces, + * just search whatever the user passed in. strdup the name so we + * can safely steal it later. + */ + name = talloc_strdup(data, cr->data->name.name); + if (name == NULL) { + return ENOMEM; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + return EOK; +} + +static const char * +cache_req_user_by_upn_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_user_by_upn_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_check_upn(ncache, domain, data->name.lookup); +} + +static errno_t +cache_req_user_by_upn_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) +{ + return sss_ncache_set_upn(ncache, false, domain, data->name.lookup); +} + +static errno_t +cache_req_user_by_upn_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + if (data->attrs == NULL) { + return sysdb_getpwupn(mem_ctx, domain, true, data->name.lookup, _result); + } + + return sysdb_search_user_by_upn_res(mem_ctx, domain, true, + data->name.lookup, data->attrs, + _result); +} + +static struct tevent_req * +cache_req_user_by_upn_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_USER, cr->data->name.lookup, + 0, EXTRA_NAME_IS_UPN); +} + +const struct cache_req_plugin cache_req_user_by_upn = { + .name = "User by UPN", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_user_by_upn_prepare_domain_data, + .create_debug_name_fn = cache_req_user_by_upn_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_user_by_upn_ncache_check, + .ncache_add_fn = cache_req_user_by_upn_ncache_add, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_upn_lookup, + .dp_send_fn = cache_req_user_by_upn_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; + +struct tevent_req * +cache_req_user_by_upn_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, + enum cache_req_dom_type req_dom_type, + const char *domain, + const char *upn) +{ + struct cache_req_data *data; + + data = cache_req_data_name(mem_ctx, CACHE_REQ_USER_BY_UPN, upn); + if (data == NULL) { + return NULL; + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, + req_dom_type, domain, + data); +} diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c new file mode 100644 index 0000000..9ed0196 --- /dev/null +++ b/src/responder/common/negcache.c @@ -0,0 +1,1417 @@ +/* + SSSD + + NSS Responder + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + + 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 <fcntl.h> +#include <time.h> +#include "tdb.h" +#include "util/util.h" +#include "util/nss_dl_load.h" +#include "confdb/confdb.h" +#include "responder/common/negcache_files.h" +#include "responder/common/responder.h" +#include "responder/common/negcache.h" + + +#define NC_ENTRY_PREFIX "NCE/" +#define NC_USER_PREFIX NC_ENTRY_PREFIX"USER" +#define NC_GROUP_PREFIX NC_ENTRY_PREFIX"GROUP" +#define NC_NETGROUP_PREFIX NC_ENTRY_PREFIX"NETGR" +#define NC_SERVICE_PREFIX NC_ENTRY_PREFIX"SERVICE" +#define NC_UID_PREFIX NC_ENTRY_PREFIX"UID" +#define NC_GID_PREFIX NC_ENTRY_PREFIX"GID" +#define NC_SID_PREFIX NC_ENTRY_PREFIX"SID" +#define NC_CERT_PREFIX NC_ENTRY_PREFIX"CERT" +#define NC_DOMAIN_ACCT_LOCATE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE" +#define NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE_TYPE" + +struct sss_nc_ctx { + struct tdb_context *tdb; + uint32_t timeout; + uint32_t local_timeout; + struct sss_nss_ops ops; +}; + +typedef int (*ncache_set_byname_fn_t)(struct sss_nc_ctx *, bool, + const char *, const char *); + +static int sss_ncache_set_ent(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name, + ncache_set_byname_fn_t setter); + +static int string_to_tdb_data(char *str, TDB_DATA *ret) +{ + if (!str || !ret) return EINVAL; + + ret->dptr = (uint8_t *)str; + ret->dsize = strlen(str)+1; + + return EOK; +} + +static errno_t ncache_load_nss_symbols(struct sss_nss_ops *ops) +{ + errno_t ret; + struct sss_nss_symbols syms[] = { + {(void*)&ops->getpwnam_r, true, "getpwnam_r" }, + {(void*)&ops->getpwuid_r, true, "getpwuid_r" }, + {(void*)&ops->getgrnam_r, true, "getgrnam_r" }, + {(void*)&ops->getgrgid_r, true, "getgrgid_r" } + }; + size_t nsyms = sizeof(syms) / sizeof(struct sss_nss_symbols); + + ret = sss_load_nss_symbols(ops, "files", syms, nsyms); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +int sss_ncache_init(TALLOC_CTX *memctx, uint32_t timeout, + uint32_t local_timeout, struct sss_nc_ctx **_ctx) +{ + errno_t ret; + struct sss_nc_ctx *ctx; + + ctx = talloc_zero(memctx, struct sss_nc_ctx); + if (!ctx) return ENOMEM; + + ret = ncache_load_nss_symbols(&ctx->ops); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load NSS symbols [%d]: %s\n", + ret, sss_strerror(ret)); + talloc_free(ctx); + return ret; + } + + errno = 0; + /* open a memory only tdb with default hash size */ + ctx->tdb = tdb_open("memcache", 0, TDB_INTERNAL, O_RDWR|O_CREAT, 0); + if (!ctx->tdb) return errno; + + ctx->timeout = timeout; + ctx->local_timeout = local_timeout; + + *_ctx = ctx; + return EOK; +}; + +uint32_t sss_ncache_get_timeout(struct sss_nc_ctx *ctx) +{ + return ctx->timeout; +} + +static int sss_ncache_check_str(struct sss_nc_ctx *ctx, char *str) +{ + TDB_DATA key; + TDB_DATA data; + unsigned long long int timestamp; + bool expired = false; + char *ep; + int ret; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Checking negative cache for [%s]\n", str); + + data.dptr = NULL; + + ret = string_to_tdb_data(str, &key); + if (ret != EOK) goto done; + + data = tdb_fetch(ctx->tdb, key); + + if (!data.dptr) { + ret = ENOENT; + goto done; + } + + errno = 0; + timestamp = strtoull((const char *)data.dptr, &ep, 10); + if (errno != 0 || *ep != '\0') { + /* Malformed entry, remove it and return no entry */ + expired = true; + goto done; + } + + if (timestamp == 0) { + /* a 0 timestamp means this is a permanent entry */ + ret = EEXIST; + goto done; + } + + if (timestamp >= time(NULL)) { + /* still valid */ + ret = EEXIST; + goto done; + } + + expired = true; + +done: + if (expired) { + /* expired, remove and return no entry */ + tdb_delete(ctx->tdb, key); + ret = ENOENT; + } + + free(data.dptr); + return ret; +} + +static int sss_ncache_set_str(struct sss_nc_ctx *ctx, char *str, + bool permanent, bool use_local_negative) +{ + TDB_DATA key; + TDB_DATA data; + char *timest; + unsigned long long int timell; + int ret; + + ret = string_to_tdb_data(str, &key); + if (ret != EOK) return ret; + + if (permanent) { + timest = talloc_strdup(ctx, "0"); + } else { + if (use_local_negative == true && ctx->local_timeout > ctx->timeout) { + timell = ctx->local_timeout; + } else { + /* EOK is tested in cwrap based unit test */ + if (ctx->timeout == 0) { + return EOK; + } + timell = ctx->timeout; + } + timell += (unsigned long long int)time(NULL); + timest = talloc_asprintf(ctx, "%llu", timell); + } + if (!timest) return ENOMEM; + + ret = string_to_tdb_data(timest, &data); + if (ret != EOK) goto done; + + DEBUG(SSSDBG_TRACE_FUNC, "Adding [%s] to negative cache%s\n", + str, permanent?" permanently":""); + + ret = tdb_store(ctx->tdb, key, data, TDB_REPLACE); + if (ret != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Negative cache failed to set entry: [%s]\n", + tdb_errorstr(ctx->tdb)); + ret = EFAULT; + } + +done: + talloc_free(timest); + return ret; +} + +static int sss_ncache_check_user_int(struct sss_nc_ctx *ctx, const char *domain, + const char *name) +{ + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name); + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + +static int sss_ncache_check_group_int(struct sss_nc_ctx *ctx, + const char *domain, const char *name) +{ + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name); + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + +static int sss_ncache_check_netgr_int(struct sss_nc_ctx *ctx, + const char *domain, const char *name) +{ + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name); + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + +static int sss_ncache_check_service_int(struct sss_nc_ctx *ctx, + const char *domain, + const char *name) +{ + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", + NC_SERVICE_PREFIX, + domain, + name); + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + +typedef int (*ncache_check_byname_fn_t)(struct sss_nc_ctx *, const char *, + const char *); + +static int sss_cache_check_ent(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, const char *name, + ncache_check_byname_fn_t checker) +{ + char *lower; + errno_t ret; + + if (dom->case_sensitive == false) { + lower = sss_tc_utf8_str_tolower(ctx, name); + if (!lower) return ENOMEM; + ret = checker(ctx, dom->name, lower); + talloc_free(lower); + } else { + ret = checker(ctx, dom->name, name); + } + + return ret; +} + +int sss_ncache_check_user(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name) +{ + return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_user_int); +} + +int sss_ncache_check_upn(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name) +{ + char *neg_cache_name = NULL; + errno_t ret; + + neg_cache_name = talloc_asprintf(ctx, "@%s", name); + if (neg_cache_name == NULL) { + return ENOMEM; + } + + ret = sss_cache_check_ent(ctx, dom, neg_cache_name, + sss_ncache_check_user_int); + talloc_free(neg_cache_name); + + return ret; +} + +int sss_ncache_check_group(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name) +{ + return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_group_int); +} + +int sss_ncache_check_netgr(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name) +{ + return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_netgr_int); +} + +static int sss_ncache_set_service_int(struct sss_nc_ctx *ctx, bool permanent, + const char *domain, const char *name) +{ + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", NC_SERVICE_PREFIX, domain, name); + if (!str) return ENOMEM; + + ret = sss_ncache_set_str(ctx, str, permanent, false); + + talloc_free(str); + return ret; +} + +int sss_ncache_set_service_name(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, + const char *name, const char *proto) +{ + int ret; + char *service_and_protocol = talloc_asprintf(ctx, "%s:%s", + name, + proto ? proto : "<ANY>"); + if (!service_and_protocol) return ENOMEM; + + ret = sss_ncache_set_ent(ctx, permanent, dom, + service_and_protocol, + sss_ncache_set_service_int); + talloc_free(service_and_protocol); + return ret; +} + +int sss_ncache_check_service(struct sss_nc_ctx *ctx,struct sss_domain_info *dom, + const char *name, const char *proto) +{ + int ret; + char *service_and_protocol = talloc_asprintf(ctx, "%s:%s", + name, + proto ? proto : "<ANY>"); + if (!service_and_protocol) return ENOMEM; + + ret = sss_cache_check_ent(ctx, dom, service_and_protocol, + sss_ncache_check_service_int); + talloc_free(service_and_protocol); + return ret; +} + +int sss_ncache_set_service_port(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, + uint16_t port, const char *proto) +{ + int ret; + char *service_and_protocol = talloc_asprintf(ctx, "%ul:%s", + port, + proto ? proto : "<ANY>"); + if (!service_and_protocol) return ENOMEM; + + ret = sss_ncache_set_ent(ctx, permanent, dom, + service_and_protocol, + sss_ncache_set_service_int); + talloc_free(service_and_protocol); + return ret; +} + +int sss_ncache_check_service_port(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + uint16_t port, + const char *proto) +{ + int ret; + char *service_and_protocol = talloc_asprintf(ctx, "%ul:%s", + port, + proto ? proto : "<ANY>"); + if (!service_and_protocol) return ENOMEM; + + ret = sss_cache_check_ent(ctx, dom, service_and_protocol, + sss_ncache_check_service_int); + talloc_free(service_and_protocol); + return ret; +} + + + +int sss_ncache_check_uid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + uid_t uid) +{ + char *str; + int ret; + + if (dom != NULL) { + str = talloc_asprintf(ctx, "%s/%s/%"SPRIuid, NC_UID_PREFIX, dom->name, + uid); + } else { + str = talloc_asprintf(ctx, "%s/%"SPRIuid, NC_UID_PREFIX, uid); + } + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + +int sss_ncache_check_gid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + gid_t gid) +{ + char *str; + int ret; + + if (dom != NULL) { + str = talloc_asprintf(ctx, "%s/%s/%"SPRIgid, NC_GID_PREFIX, dom->name, + gid); + } else { + str = talloc_asprintf(ctx, "%s/%"SPRIgid, NC_GID_PREFIX, gid); + } + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + +int sss_ncache_check_sid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *sid) +{ + char *str; + int ret; + + if (dom != NULL) { + str = talloc_asprintf(ctx, "%s/%s/%s", NC_SID_PREFIX, dom->name, sid); + } else { + str = talloc_asprintf(ctx, "%s/%s", NC_SID_PREFIX, sid); + } + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + +int sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert) +{ + char *str; + int ret; + + str = talloc_asprintf(ctx, "%s/%s", NC_CERT_PREFIX, cert); + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + + talloc_free(str); + return ret; +} + + +static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent, + const char *domain, const char *name) +{ + bool use_local_negative = false; + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name); + if (!str) return ENOMEM; + + if ((!permanent) && (ctx->local_timeout > 0)) { + use_local_negative = is_user_local_by_name(&ctx->ops, name); + } + ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative); + + talloc_free(str); + return ret; +} + +static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent, + const char *domain, const char *name) +{ + bool use_local_negative = false; + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name); + if (!str) return ENOMEM; + + if ((!permanent) && (ctx->local_timeout > 0)) { + use_local_negative = is_group_local_by_name(&ctx->ops, name); + } + ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative); + + talloc_free(str); + return ret; +} + +static int sss_ncache_set_netgr_int(struct sss_nc_ctx *ctx, bool permanent, + const char *domain, const char *name) +{ + char *str; + int ret; + + if (!name || !*name) return EINVAL; + + str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name); + if (!str) return ENOMEM; + + ret = sss_ncache_set_str(ctx, str, permanent, false); + + talloc_free(str); + return ret; +} + +static int sss_ncache_set_ent(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name, + ncache_set_byname_fn_t setter) +{ + char *lower; + errno_t ret; + + if (dom->case_sensitive == false) { + lower = sss_tc_utf8_str_tolower(ctx, name); + if (!lower) return ENOMEM; + ret = setter(ctx, permanent, dom->name, lower); + talloc_free(lower); + } else { + ret = setter(ctx, permanent, dom->name, name); + } + + return ret; +} + + +int sss_ncache_set_user(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name) +{ + return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_user_int); +} + +int sss_ncache_set_upn(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name) +{ + char *neg_cache_name = NULL; + errno_t ret; + + neg_cache_name = talloc_asprintf(ctx, "@%s", name); + if (neg_cache_name == NULL) { + return ENOMEM; + } + + ret = sss_ncache_set_ent(ctx, permanent, dom, neg_cache_name, + sss_ncache_set_user_int); + talloc_free(neg_cache_name); + + return ret; +} + +int sss_ncache_set_group(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name) +{ + return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_group_int); +} + +int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name) +{ + return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_netgr_int); +} + +int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, uid_t uid) +{ + bool use_local_negative = false; + char *str; + int ret; + + if (dom != NULL) { + str = talloc_asprintf(ctx, "%s/%s/%"SPRIuid, NC_UID_PREFIX, dom->name, + uid); + } else { + str = talloc_asprintf(ctx, "%s/%"SPRIuid, NC_UID_PREFIX, uid); + } + if (!str) return ENOMEM; + + if ((!permanent) && (ctx->local_timeout > 0)) { + use_local_negative = is_user_local_by_uid(&ctx->ops, uid); + } + ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative); + + talloc_free(str); + return ret; +} + +int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, gid_t gid) +{ + bool use_local_negative = false; + char *str; + int ret; + + if (dom != NULL) { + str = talloc_asprintf(ctx, "%s/%s/%"SPRIgid, NC_GID_PREFIX, dom->name, + gid); + } else { + str = talloc_asprintf(ctx, "%s/%"SPRIgid, NC_GID_PREFIX, gid); + } + if (!str) return ENOMEM; + + if ((!permanent) && (ctx->local_timeout > 0)) { + use_local_negative = is_group_local_by_gid(&ctx->ops, gid); + } + ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative); + + talloc_free(str); + return ret; +} + +int sss_ncache_set_sid(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *sid) +{ + char *str; + int ret; + + if (dom != NULL) { + str = talloc_asprintf(ctx, "%s/%s/%s", NC_SID_PREFIX, dom->name, sid); + } else { + str = talloc_asprintf(ctx, "%s/%s", NC_SID_PREFIX, sid); + } + if (!str) return ENOMEM; + + ret = sss_ncache_set_str(ctx, str, permanent, false); + + talloc_free(str); + return ret; +} + +int sss_ncache_set_cert(struct sss_nc_ctx *ctx, bool permanent, + const char *cert) +{ + char *str; + int ret; + + str = talloc_asprintf(ctx, "%s/%s", NC_CERT_PREFIX, cert); + if (!str) return ENOMEM; + + ret = sss_ncache_set_str(ctx, str, permanent, false); + + talloc_free(str); + return ret; +} + +static char *domain_lookup_type_str(TALLOC_CTX *mem_ctx, + struct sss_domain_info *dom, + const char *lookup_type) +{ + return talloc_asprintf(mem_ctx, + "%s/%s/%s", + NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX, + dom->name, + lookup_type); +} + +int sss_ncache_set_domain_locate_type(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *lookup_type) +{ + char *str; + int ret; + + str = domain_lookup_type_str(ctx, dom, lookup_type); + if (!str) return ENOMEM; + + /* Permanent cache is always used here, because the lookup + * type's (getgrgid, getpwuid, ..) support locating an entry's domain + * doesn't change + */ + ret = sss_ncache_set_str(ctx, str, true, false); + talloc_free(str); + return ret; +} + +int sss_ncache_check_domain_locate_type(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *lookup_type) +{ + char *str; + int ret; + + str = domain_lookup_type_str(ctx, dom, lookup_type); + if (!str) return ENOMEM; + + ret = sss_ncache_check_str(ctx, str); + talloc_free(str); + return ret; +} + +static char *locate_gid_str(TALLOC_CTX *mem_ctx, + struct sss_domain_info *dom, + gid_t gid) +{ + return talloc_asprintf(mem_ctx, + "%s/%s/%s/%"SPRIgid, + NC_DOMAIN_ACCT_LOCATE_PREFIX, + NC_GID_PREFIX, + dom->name, + gid); +} + +int sss_ncache_set_locate_gid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + gid_t gid) +{ + char *str; + int ret; + + if (dom == NULL) { + return EINVAL; + } + + str = locate_gid_str(ctx, dom, gid); + if (str == NULL) { + return ENOMEM; + } + + ret = sss_ncache_set_str(ctx, str, false, false); + talloc_free(str); + return ret; +} + +int sss_ncache_check_locate_gid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + gid_t gid) +{ + char *str; + int ret; + + if (dom == NULL) { + return EINVAL; + } + + str = locate_gid_str(ctx, dom, gid); + if (str == NULL) { + return ENOMEM; + } + + ret = sss_ncache_check_str(ctx, str); + talloc_free(str); + return ret; +} + +static char *locate_uid_str(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + uid_t uid) +{ + return talloc_asprintf(ctx, + "%s/%s/%s/%"SPRIuid, + NC_DOMAIN_ACCT_LOCATE_PREFIX, + NC_UID_PREFIX, + dom->name, + uid); +} + +int sss_ncache_set_locate_uid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + uid_t uid) +{ + char *str; + int ret; + + if (dom == NULL) { + return EINVAL; + } + + str = locate_uid_str(ctx, dom, uid); + if (str == NULL) { + return ENOMEM; + } + + ret = sss_ncache_set_str(ctx, str, false, false); + talloc_free(str); + return ret; +} + +int sss_ncache_check_locate_uid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + uid_t uid) +{ + char *str; + int ret; + + if (dom == NULL) { + return EINVAL; + } + + str = locate_uid_str(ctx, dom, uid); + if (str == NULL) { + return ENOMEM; + } + + ret = sss_ncache_check_str(ctx, str); + talloc_free(str); + return ret; +} + +static char *locate_sid_str(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *sid) +{ + return talloc_asprintf(ctx, + "%s/%s/%s/%s", + NC_DOMAIN_ACCT_LOCATE_PREFIX, + NC_SID_PREFIX, + dom->name, + sid); +} + +int sss_ncache_check_locate_sid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *sid) +{ + char *str; + int ret; + + if (dom == NULL) { + return EINVAL; + } + + str = locate_sid_str(ctx, dom, sid); + if (str == NULL) { + return ENOMEM; + } + + ret = sss_ncache_check_str(ctx, str); + talloc_free(str); + return ret; +} + +int sss_ncache_set_locate_sid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *sid) +{ + char *str; + int ret; + + if (dom == NULL) { + return EINVAL; + } + + str = locate_sid_str(ctx, dom, sid); + if (str == NULL) { + return ENOMEM; + } + + ret = sss_ncache_set_str(ctx, str, false, false); + talloc_free(str); + return ret; +} + +static int delete_permanent(struct tdb_context *tdb, + TDB_DATA key, TDB_DATA data, void *state) +{ + unsigned long long int timestamp; + bool remove_key = false; + char *ep; + + if (strncmp((char *)key.dptr, + NC_ENTRY_PREFIX, sizeof(NC_ENTRY_PREFIX) - 1) != 0) { + /* not interested in this key */ + return 0; + } + + errno = 0; + timestamp = strtoull((const char *)data.dptr, &ep, 10); + if (errno != 0 || *ep != '\0') { + /* Malformed entry, remove it */ + remove_key = true; + goto done; + } + + if (timestamp == 0) { + /* a 0 timestamp means this is a permanent entry */ + remove_key = true; + } + +done: + if (remove_key) { + return tdb_delete(tdb, key); + } + + return 0; +} + +int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx) +{ + int ret; + + ret = tdb_traverse(ctx->tdb, delete_permanent, NULL); + if (ret < 0) + return EIO; + + return EOK; +} + +static int delete_prefix(struct tdb_context *tdb, + TDB_DATA key, TDB_DATA data, void *state) +{ + const char *prefix = (const char *) state; + unsigned long long int timestamp; + char *ep = NULL; + + if (strncmp((char *)key.dptr, prefix, strlen(prefix) - 1) != 0) { + /* not interested in this key */ + return 0; + } + + errno = 0; + timestamp = strtoull((const char *)data.dptr, &ep, 10); + if ((errno == 0) && (*ep == '\0') && (timestamp == 0)) { + /* skip permanent entries */ + return 0; + } + + return tdb_delete(tdb, key); +} + +static int sss_ncache_reset_pfx(struct sss_nc_ctx *ctx, + const char **prefixes) +{ + int ret; + + if (prefixes == NULL) { + return EOK; + } + + for (int i = 0; prefixes[i] != NULL; i++) { + ret = tdb_traverse(ctx->tdb, + delete_prefix, + discard_const(prefixes[i])); + if (ret < 0) { + return EIO; + } + } + + return EOK; +} + +int sss_ncache_reset_users(struct sss_nc_ctx *ctx) +{ + const char *prefixes[] = { + NC_USER_PREFIX, + NC_UID_PREFIX, + NULL, + }; + + return sss_ncache_reset_pfx(ctx, prefixes); +} + +int sss_ncache_reset_groups(struct sss_nc_ctx *ctx) +{ + const char *prefixes[] = { + NC_GROUP_PREFIX, + NC_GID_PREFIX, + NULL, + }; + + return sss_ncache_reset_pfx(ctx, prefixes); +} + +errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + struct confdb_ctx *cdb, + struct resp_ctx *rctx) +{ + errno_t ret; + char **filter_list = NULL; + char **default_list = NULL; + char *name = NULL; + struct sss_domain_info *dom = NULL; + struct sss_domain_info *domain_list = rctx->domains; + struct sss_domain_info *ddom; + char *domainname = NULL; + char *conf_path = NULL; + TALLOC_CTX *tmpctx = talloc_new(NULL); + int i; + char *fqname = NULL; + + if (tmpctx == NULL) { + return ENOMEM; + } + + /* Populate domain-specific negative cache user entries */ + for (dom = domain_list; dom; dom = get_next_domain(dom, 0)) { + conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, + dom->name); + if (!conf_path) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(filter_list); + ret = confdb_get_string_as_list(cdb, tmpctx, conf_path, + CONFDB_NSS_FILTER_USERS, + &filter_list); + if (ret == ENOENT) continue; + if (ret != EOK) goto done; + + for (i = 0; (filter_list && filter_list[i]); i++) { + ret = sss_parse_name_for_domains(tmpctx, domain_list, + NULL, + filter_list[i], + &domainname, &name); + if (ret == EAGAIN) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Can add [%s] only as UPN to negcache because the " + "required domain is not known yet\n", filter_list[i]); + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterUsers list: [%s] (%d)\n", + filter_list[i], ret); + continue; + } + + /* Check domain and its sub-domains */ + for (ddom = dom; ddom != NULL; + ddom = get_next_domain(ddom, SSS_GND_ALL_SUBDOMAINS)) { + + if (domainname && strcmp(domainname, ddom->name)) { + DEBUG(SSSDBG_TRACE_FUNC, + "Mismatch between domain name (%s) and name " + "set in FQN (%s), assuming %s is UPN\n", + ddom->name, domainname, filter_list[i]); + ret = sss_ncache_set_upn(ncache, true, ddom, filter_list[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_ncache_set_upn failed (%d [%s]), ignored\n", + ret, sss_strerror(ret)); + } + continue; + } + + fqname = sss_create_internal_fqname(tmpctx, name, ddom->name); + if (fqname == NULL) { + continue; + } + + ret = sss_ncache_set_upn(ncache, true, ddom, fqname); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_ncache_set_upn failed (%d [%s]), ignored\n", + ret, sss_strerror(ret)); + } + ret = sss_ncache_set_user(ncache, true, ddom, fqname); + talloc_zfree(fqname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to store permanent user filter for [%s]" + " (%d [%s])\n", filter_list[i], + ret, sss_strerror(ret)); + continue; + } + } + } + } + + talloc_zfree(filter_list); + /* Populate non domain-specific negative cache user entries */ + ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_FILTER_USERS, &filter_list); + if (ret != EOK && ret != ENOENT) { + goto done; + } + + for (i = 0; (filter_list && filter_list[i]); i++) { + ret = sss_parse_name_for_domains(tmpctx, domain_list, + NULL, filter_list[i], + &domainname, &name); + if (ret == EAGAIN) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Can add [%s] only as UPN to negcache because the " + "required domain is not known yet\n", filter_list[i]); + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterUsers list: [%s] (%d)\n", + filter_list[i], ret); + continue; + } + if (domainname) { + DEBUG(SSSDBG_TRACE_ALL, + "Adding [%s] to UPN negative cache of all domains.\n", + filter_list[i]); + for (dom = domain_list; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { + ret = sss_ncache_set_upn(ncache, true, dom, filter_list[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_ncache_set_upn failed (%d [%s]), ignored\n", + ret, sss_strerror(ret)); + } + } + + /* Add name to domain specific cache for known domain names */ + dom = responder_get_domain(rctx, domainname); + if (dom != NULL) { + fqname = sss_create_internal_fqname(tmpctx, name, dom->name); + if (fqname == NULL) { + continue; + } + + ret = sss_ncache_set_user(ncache, true, dom, fqname); + talloc_zfree(fqname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to store permanent user filter for [%s]" + " (%d [%s])\n", filter_list[i], + ret, strerror(ret)); + continue; + } + } + } else { + for (dom = domain_list; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { + fqname = sss_create_internal_fqname(tmpctx, name, dom->name); + if (fqname == NULL) { + continue; + } + + ret = sss_ncache_set_upn(ncache, true, dom, fqname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to store permanent upn filter for" + " [%s:%s] (%d [%s])\n", + dom->name, filter_list[i], + ret, strerror(ret)); + } + + ret = sss_ncache_set_user(ncache, true, dom, fqname); + talloc_zfree(fqname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to store permanent user filter for" + " [%s:%s] (%d [%s])\n", + dom->name, filter_list[i], + ret, strerror(ret)); + continue; + } + } + } + } + + /* Populate domain-specific negative cache group entries */ + for (dom = domain_list; dom; dom = get_next_domain(dom, 0)) { + conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, dom->name); + if (!conf_path) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(filter_list); + ret = confdb_get_string_as_list(cdb, tmpctx, conf_path, + CONFDB_NSS_FILTER_GROUPS, &filter_list); + if (ret == ENOENT) continue; + if (ret != EOK) goto done; + + for (i = 0; (filter_list && filter_list[i]); i++) { + ret = sss_parse_name_for_domains(tmpctx, domain_list, + NULL, filter_list[i], + &domainname, &name); + if (ret != EOK) { + /* Groups do not have UPNs, so domain names, if present, + * must be known */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterGroups list: [%s] (%d)\n", + filter_list[i], ret); + continue; + } + + /* Check domain and its sub-domains */ + for (ddom = dom; + ddom != NULL && (ddom == dom || ddom->parent != NULL); + ddom = get_next_domain(ddom, SSS_GND_ALL_DOMAINS)) { + if (domainname && strcmp(domainname, ddom->name)) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Mismatch between domain name (%s) and name " + "set in FQN (%s), skipping group %s\n", + ddom->name, domainname, name); + continue; + } + + fqname = sss_create_internal_fqname(tmpctx, name, ddom->name); + if (fqname == NULL) { + continue; + } + + ret = sss_ncache_set_group(ncache, true, ddom, fqname); + talloc_zfree(fqname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to store permanent group filter for [%s]" + " (%d [%s])\n", filter_list[i], + ret, strerror(ret)); + continue; + } + } + } + } + + talloc_zfree(filter_list); + /* Populate non domain-specific negative cache group entries */ + ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_FILTER_GROUPS, &filter_list); + if (ret != EOK && ret != ENOENT) { + goto done; + } + + for (i = 0; (filter_list && filter_list[i]); i++) { + ret = sss_parse_name_for_domains(tmpctx, domain_list, + NULL, filter_list[i], + &domainname, &name); + if (ret != EOK) { + /* Groups do not have UPNs, so domain names, if present, + * must be known */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterGroups list: [%s] (%d)\n", + filter_list[i], ret); + continue; + } + if (domainname) { + dom = responder_get_domain(rctx, domainname); + if (!dom) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid domain name [%s]\n", domainname); + continue; + } + + fqname = sss_create_internal_fqname(tmpctx, name, dom->name); + if (fqname == NULL) { + continue; + } + + ret = sss_ncache_set_group(ncache, true, dom, fqname); + talloc_zfree(fqname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to store permanent group filter for" + " [%s] (%d [%s])\n", filter_list[i], + ret, strerror(ret)); + continue; + } + } else { + for (dom = domain_list; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { + fqname = sss_create_internal_fqname(tmpctx, name, dom->name); + if (fqname == NULL) { + continue; + } + + ret = sss_ncache_set_group(ncache, true, dom, fqname); + talloc_zfree(fqname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to store permanent group filter for" + " [%s:%s] (%d [%s])\n", + dom->name, filter_list[i], + ret, strerror(ret)); + continue; + } + } + } + } + + /* SSSD doesn't handle "root", thus it'll be added to the negative cache + * nonetheless what's already added there. */ + default_list = talloc_array(tmpctx, char *, 2); + if (default_list == NULL) { + ret= ENOMEM; + goto done; + } + default_list[0] = talloc_strdup(tmpctx, "root"); + if (default_list[0] == NULL) { + ret = ENOMEM; + goto done; + } + default_list[1] = NULL; + + /* Populate negative cache users and groups entries for the + * "default_list" */ + for (i = 0; (default_list != NULL && default_list[i] != NULL); i++) { + for (dom = domain_list; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { + fqname = sss_create_internal_fqname(tmpctx, + default_list[i], + dom->name); + if (fqname == NULL) { + continue; + } + + ret = sss_ncache_set_user(ncache, true, dom, fqname); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to store permanent user filter for" + " [%s:%s] (%d [%s])\n", + dom->name, default_list[i], + ret, strerror(ret)); + continue; + } + + ret = sss_ncache_set_group(ncache, true, dom, fqname); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to store permanent group filter for" + " [%s:%s] (%d [%s])\n", + dom->name, default_list[i], + ret, strerror(ret)); + continue; + } + } + } + + /* Also add "root" uid and gid to the negative cache */ + ret = sss_ncache_set_uid(ncache, true, NULL, 0); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to store permanent uid filter for root (0) " + "(%d [%s])\n", + ret, strerror(ret)); + } + + ret = sss_ncache_set_gid(ncache, true, NULL, 0); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to store permanent gid filter for root (0) " + "(%d [%s])\n", + ret, strerror(ret)); + } + + ret = EOK; + +done: + talloc_free(tmpctx); + return ret; +} + +/* Reset permanent negcache after checking the domains */ +errno_t sss_ncache_reset_repopulate_permanent(struct resp_ctx *rctx, + struct sss_nc_ctx *ncache) +{ + int ret; + + ret = sss_ncache_reset_permanent(ncache); + if (ret == EOK) { + ret = sss_ncache_prepopulate(ncache, rctx->cdb, rctx); + } + + return ret; +} diff --git a/src/responder/common/negcache.h b/src/responder/common/negcache.h new file mode 100644 index 0000000..29c0e6b --- /dev/null +++ b/src/responder/common/negcache.h @@ -0,0 +1,174 @@ +/* + SSSD + + NSS Responder + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + + 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/>. +*/ + +#ifndef _NSS_NEG_CACHE_H_ +#define _NSS_NEG_CACHE_H_ + +struct sss_nc_ctx; + +/* init the in memory negative cache */ +int sss_ncache_init(TALLOC_CTX *memctx, uint32_t timeout, + uint32_t local_timeout, struct sss_nc_ctx **_ctx); + +uint32_t sss_ncache_get_timeout(struct sss_nc_ctx *ctx); + +/* check if the user is expired according to the passed in time to live */ +int sss_ncache_check_user(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name); +int sss_ncache_check_upn(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name); +int sss_ncache_check_group(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name); +int sss_ncache_check_netgr(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *name); +int sss_ncache_check_uid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + uid_t uid); +int sss_ncache_check_gid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + gid_t gid); +int sss_ncache_check_sid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom, + const char *sid); +int sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert); + +int sss_ncache_check_service(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *name, + const char *proto); +int sss_ncache_check_service_port(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + uint16_t port, + const char *proto); + +/* add a new neg-cache entry setting the timestamp to "now" unless + * "permanent" is set to true, in which case the timestamps is set to 0 + * and the negative cache never expires (used to permanently filter out + * users and groups) */ +int sss_ncache_set_user(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name); +int sss_ncache_set_upn(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name); +int sss_ncache_set_group(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name); +int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name); +int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, uid_t uid); +int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, gid_t gid); +int sss_ncache_set_sid(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *sid); +int sss_ncache_set_cert(struct sss_nc_ctx *ctx, bool permanent, + const char *cert); +int sss_ncache_set_service_name(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, + const char *name, const char *proto); +int sss_ncache_set_service_port(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, + uint16_t port, const char *proto); +/* + * Mark the lookup_type as not supporting the negative cache. This + * would be used by the corresponding checker to avoid needless + * subsequent calls to the locator for configurations that do not + * support the locator plugin. + * + * @param ctx The negative cache. + * @param dom The top-level domain. It is expected that the caller + * would use the top-level domain head here, because + * this negative cache is "per-request-type" which is the + * same for all subdomains of a domain. + * @param lookup_type Lookup type, e.g. getpwuid, getgrnam. + * + * @return EOK on success, errno on failure. + */ +int sss_ncache_set_domain_locate_type(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *lookup_type); +/* + * Check if the lookup_type supports the domain locator request. + * + * @param ctx The negative cache. + * @param dom The top-level domain. It is expected that the caller + * would use the top-level domain head here, because + * this negative cache is "per-request-type" which is the + * same for all subdomains of a domain. + * @param lookup_type Lookup type, e.g. getpwuid, getgrnam. + * + * @return ENOENT if the request supports the locator (or we + * haven't checked yet), EEXIST if the request does + * not support the domain locator request. + */ +int sss_ncache_check_domain_locate_type(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *key); + +/* + * Call these two functions to mark a GID as checked until the negative + * cache expires. This function is used to avoid a situation where + * GID would be found in a subsequent domain, so any request that + * searches for this GID again (even if it was cached) would first + * run the locator again. + * + * While this negative cache entry is valid, it is expected that + * the negatively cached entries in the domain's GID negative + * cache (if any) are valid. + * + * The sss_ncache_set_locate_gid() is called by the locator request + * when it finishes, the sss_ncache_check_locate_gid() is called + * by the caller of the locator request to find if the locator + * should be called at all. + */ +int sss_ncache_set_locate_gid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + gid_t gid); +int sss_ncache_check_locate_gid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + gid_t gid); +int sss_ncache_check_locate_uid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + uid_t uid); +int sss_ncache_set_locate_uid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + uid_t uid); +int sss_ncache_check_locate_sid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *sid); +int sss_ncache_set_locate_sid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *sid); + +int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx); +/* sss_ncache_reset_[users/groups] skips permanent entries */ +int sss_ncache_reset_users(struct sss_nc_ctx *ctx); +int sss_ncache_reset_groups(struct sss_nc_ctx *ctx); + +struct resp_ctx; + +/* Set up the negative cache with values from filter_users and + * filter_groups in the sssd.conf + */ +errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + struct confdb_ctx *cdb, + struct resp_ctx *rctx); + +/* Flush the negcache permament entries and then repopulate them */ +errno_t sss_ncache_reset_repopulate_permanent(struct resp_ctx *rctx, + struct sss_nc_ctx *ncache); + +#endif /* _NSS_NEG_CACHE_H_ */ diff --git a/src/responder/common/negcache_files.c b/src/responder/common/negcache_files.c new file mode 100644 index 0000000..f22796a --- /dev/null +++ b/src/responder/common/negcache_files.c @@ -0,0 +1,105 @@ +/* + SSSD + + NSS Responder + + Copyright (C) Petr Čech <pcech@redhat.com> 2016 + + 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 "util/nss_dl_load.h" +#include "responder/common/negcache_files.h" + +#define BUFFER_SIZE 16384 +static char s_nss_buffer[BUFFER_SIZE]; + +bool is_user_local_by_name(const struct sss_nss_ops *ops, const char *name) +{ + struct passwd pwd = { 0 }; + int errnop; + enum nss_status ret; + char *shortname = NULL; + int parse_ret; + + parse_ret = sss_parse_internal_fqname(NULL, name, &shortname, NULL); + if (parse_ret != EOK) { + return false; + } + + ret = ops->getpwnam_r(shortname, &pwd, s_nss_buffer, BUFFER_SIZE, &errnop); + talloc_free(shortname); + if (ret == NSS_STATUS_SUCCESS) { + DEBUG(SSSDBG_TRACE_FUNC, "User %s is a local user\n", name); + return true; + } + + return false; +} + +bool is_user_local_by_uid(const struct sss_nss_ops *ops, uid_t uid) +{ + struct passwd pwd = { 0 }; + int errnop; + enum nss_status ret; + + ret = ops->getpwuid_r(uid, &pwd, s_nss_buffer, BUFFER_SIZE, &errnop); + if (ret == NSS_STATUS_SUCCESS) { + DEBUG(SSSDBG_TRACE_FUNC, + "User with UID %"SPRIuid" is a local user\n", uid); + return true; + } + + return false; +} + +bool is_group_local_by_name(const struct sss_nss_ops *ops, const char *name) +{ + struct group grp = { 0 }; + int errnop; + enum nss_status ret; + char *shortname = NULL; + int parse_ret; + + parse_ret = sss_parse_internal_fqname(NULL, name, &shortname, NULL); + if (parse_ret != EOK) { + return false; + } + + ret = ops->getgrnam_r(shortname, &grp, s_nss_buffer, BUFFER_SIZE, &errnop); + talloc_free(shortname); + if (ret == NSS_STATUS_SUCCESS) { + DEBUG(SSSDBG_TRACE_FUNC, "Group %s is a local group\n", name); + return true; + } + + return false; +} + +bool is_group_local_by_gid(const struct sss_nss_ops *ops, uid_t gid) +{ + struct group grp = { 0 }; + int errnop; + enum nss_status ret; + + ret = ops->getgrgid_r(gid, &grp, s_nss_buffer, BUFFER_SIZE, &errnop); + if (ret == NSS_STATUS_SUCCESS) { + DEBUG(SSSDBG_TRACE_FUNC, + "Group with GID %"SPRIgid" is a local group\n", gid); + return true; + } + + return false; +} diff --git a/src/responder/common/negcache_files.h b/src/responder/common/negcache_files.h new file mode 100644 index 0000000..a3e18de --- /dev/null +++ b/src/responder/common/negcache_files.h @@ -0,0 +1,35 @@ +/* + SSSD + + NSS Responder + + Copyright (C) Petr Čech <pcech@redhat.com> 2016 + + 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/>. +*/ + +#ifndef _NEGCACHE_FILES_H_ +#define _NEGCACHE_FILES_H_ + +#include <stdbool.h> + +struct sss_nss_ops; + +bool is_user_local_by_name(const struct sss_nss_ops *ops, const char *name); +bool is_user_local_by_uid(const struct sss_nss_ops *ops, uid_t uid); + +bool is_group_local_by_name(const struct sss_nss_ops *ops, const char *name); +bool is_group_local_by_gid(const struct sss_nss_ops *ops, uid_t gid); + +#endif /* _NEGCACHE_FILES_H_ */ diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h new file mode 100644 index 0000000..5f04d25 --- /dev/null +++ b/src/responder/common/responder.h @@ -0,0 +1,450 @@ +/* + SSSD + + SSS Client Responder, header file + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + + 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/>. +*/ + +#ifndef __SSS_RESPONDER_H__ +#define __SSS_RESPONDER_H__ + +#include "config.h" + +#include <stdint.h> +#include <sys/un.h> +#include <sys/resource.h> +#include <talloc.h> +#include <tevent.h> +#include <ldb.h> +#include <dhash.h> + +#include "util/sss_regexp.h" +#include "sss_iface/sss_iface_async.h" +#include "responder/common/negcache.h" +#include "sss_client/sss_cli.h" +#include "responder/common/cache_req/cache_req_domain.h" +#include "util/session_recording.h" + +extern hash_table_t *dp_requests; + +/* we want default permissions on created files to be very strict, + * so set our umask to 0177 */ +#define DFL_RSP_UMASK SSS_DFL_UMASK + +/* Public sockets must be readable and writable by anybody on the system. + * So we set umask to 0111. */ +#define SCKT_RSP_UMASK 0111 + +/* needed until nsssrv.h is updated */ +struct cli_request { + + /* original request from the wire */ + struct sss_packet *in; + + /* reply data */ + struct sss_packet *out; +}; + +struct cli_protocol_version { + uint32_t version; + const char *date; + const char *description; +}; + +struct cli_protocol { + struct cli_request *creq; + struct cli_protocol_version *cli_protocol_version; +}; + +struct resp_ctx; + +struct be_conn { + struct be_conn *next; + struct be_conn *prev; + + struct resp_ctx *rctx; + + const char *cli_name; + struct sss_domain_info *domain; + + char *bus_name; + char *sbus_address; + struct sbus_connection *conn; +}; + +struct resp_ctx { + struct tevent_context *ev; + struct tevent_fd *lfde; + int lfd; + struct tevent_fd *priv_lfde; + int priv_lfd; + struct confdb_ctx *cdb; + const char *sock_name; + const char *priv_sock_name; + + struct sss_nc_ctx *ncache; + struct sss_names_ctx *global_names; + + struct sbus_connection *mon_conn; + struct be_conn *be_conns; + + struct sss_domain_info *domains; + int domains_timeout; + int client_idle_timeout; + + struct cache_req_domain *cr_domains; + const char *domain_resolution_order; + + time_t last_request_time; + int idle_timeout; + struct tevent_timer *idle; + + struct sss_cmd_table *sss_cmds; + const char *sss_pipe_name; + const char *confdb_service_path; + + struct timeval get_domains_last_call; + + size_t allowed_uids_count; + uid_t *allowed_uids; + + char *default_domain; + char override_space; + + char **allowed_shells; + char *override_shell; + char **vetoed_shells; + char **etc_shells; + char *shell_fallback; + char *default_shell; + + struct session_recording_conf sr_conf; + + uint32_t cache_req_num; + uint32_t client_id_num; + + void *pvt_ctx; + + bool shutting_down; + bool socket_activated; + bool dbus_activated; + bool cache_first; + bool enumeration_warn_logged; +}; + +struct cli_creds; + +struct cli_ctx { + struct tevent_context *ev; + struct resp_ctx *rctx; + int cfd; + struct tevent_fd *cfde; + tevent_fd_handler_t cfd_handler; + struct sockaddr_un addr; + int priv; + + struct cli_creds *creds; + char *cmd_line; + + void *protocol_ctx; + void *state_ctx; + + struct tevent_timer *idle; + time_t last_request_time; + uint32_t client_id_num; +}; + +struct sss_cmd_table { + enum sss_cli_command cmd; + int (*fn)(struct cli_ctx *cctx); +}; + +/* from generated code */ +struct mon_cli_iface; + +/* + * responder_common.c + * + */ + +typedef int (*connection_setup_t)(struct cli_ctx *cctx); + +int sss_connection_setup(struct cli_ctx *cctx); + +void sss_client_fd_handler(void *ptr, + void (*recv_fn) (struct cli_ctx *cctx), + void (*send_fn) (struct cli_ctx *cctx), + uint16_t flags); + +int sss_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb, + struct sss_cmd_table sss_cmds[], + const char *sss_pipe_name, + int pipe_fd, + const char *sss_priv_pipe_name, + int priv_pipe_fd, + const char *confdb_service_path, + const char *conn_name, + const char *svc_name, + connection_setup_t conn_setup, + struct resp_ctx **responder_ctx); + +int sss_dp_get_domain_conn(struct resp_ctx *rctx, const char *domain, + struct be_conn **_conn); +struct sss_domain_info * +responder_get_domain(struct resp_ctx *rctx, const char *domain); + +errno_t responder_get_domain_by_id(struct resp_ctx *rctx, const char *id, + struct sss_domain_info **_ret_dom); + +int create_pipe_fd(const char *sock_name, int *_fd, mode_t umaskval); +int activate_unix_sockets(struct resp_ctx *rctx, + connection_setup_t conn_setup); + +/* responder_cmd.c */ +int sss_cmd_empty_packet(struct sss_packet *packet); +int sss_cmd_send_empty(struct cli_ctx *cctx); +int sss_cmd_send_error(struct cli_ctx *cctx, int err); +void sss_cmd_done(struct cli_ctx *cctx, void *freectx); +int sss_cmd_get_version(struct cli_ctx *cctx); +int sss_cmd_execute(struct cli_ctx *cctx, + enum sss_cli_command cmd, + struct sss_cmd_table *sss_cmds); +struct cli_protocol_version *register_cli_protocol_version(void); + +struct setent_req_list; + +/* A facility for notifying setent requests */ +struct tevent_req *setent_get_req(struct setent_req_list *sl); +errno_t setent_add_ref(TALLOC_CTX *memctx, + struct setent_req_list **list, + struct tevent_req *req); +void setent_notify(struct setent_req_list **list, errno_t err); +void setent_notify_done(struct setent_req_list **list); + +errno_t +sss_cmd_check_cache(struct ldb_message *msg, + int cache_refresh_percent, + uint64_t cache_expire); + +void handle_requests_after_reconnect(struct resp_ctx *rctx); + +errno_t +responder_logrotate(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct resp_ctx *rctx); + +/* Send a request to the data provider + * Once this function is called, the communication + * with the data provider will always run to + * completion. Freeing the returned tevent_req will + * cancel the notification of completion, but not + * the data provider action. + */ + +enum sss_dp_acct_type { + SSS_DP_USER = 1, + SSS_DP_GROUP, + SSS_DP_INITGROUPS, + SSS_DP_SUBID_RANGES, + SSS_DP_NETGR, + SSS_DP_SERVICES, + SSS_DP_SECID, + SSS_DP_USER_AND_GROUP, + SSS_DP_CERT, + SSS_DP_WILDCARD_USER, + SSS_DP_WILDCARD_GROUP, +}; + +struct tevent_req * +sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + enum sss_dp_acct_type type, + const char *opt_name, + uint32_t opt_id, + const char *extra); +errno_t +sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + uint16_t *_dp_error, + uint32_t *_error, + const char **_error_message); + +struct tevent_req * +sss_dp_resolver_get_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + uint32_t entry_type, + uint32_t filter_type, + const char *filter_value); + +errno_t +sss_dp_resolver_get_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + uint16_t *_dp_error, + uint32_t *_error, + const char **_error_message); + +bool sss_utf8_check(const uint8_t *s, size_t n); + +void responder_set_fd_limit(rlim_t fd_limit); + +errno_t reset_client_idle_timer(struct cli_ctx *cctx); + +errno_t responder_setup_idle_timeout_config(struct resp_ctx *rctx); + +#define GET_DOMAINS_DEFAULT_TIMEOUT 60 + +struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + bool force, + const char *hint); + +errno_t sss_dp_get_domains_recv(struct tevent_req *req); + +/* + * Call a getAccountDomain request + * + * Only requests by ID are supported. + * + * @param mem_ctx Parent memory context + * @param rctx Responder context + * @param domain The SSSD domain we're querying. The response can + * be either NULL or come from any of domain's subdomains + * or domain itself + * @param type Either SSS_DP_USER, SSS_DP_GROUP or SSS_DP_SECID, other + * types are not supported at the moment + * @param opt_id The ID number we're trying to locate + * @param opt_str The SID number we're trying to locate + * + * @return A tevent request or NULL if allocating the request fails. + */ +struct tevent_req *sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *domain, + bool fast_reply, + enum sss_dp_acct_type type, + uint32_t opt_id, + const char *opt_str); + +/* Receive a getAccountDomain request result + * + * @param mem_ctx The memory context that will own the contents of _domain + * @param req The request that had finished + * @para _domain Either NULL (the request did not match any domain) or + * a string that corresponds to either the input domain + * or any of its subdomains + * + * @return EOK on success, errno otherwise + */ +errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **_domain); + +typedef void (get_domains_callback_fn_t)(void *); +errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *optional_ncache, + get_domains_callback_fn_t *callback, + void *callback_pvt); + +errno_t csv_string_to_uid_array(TALLOC_CTX *mem_ctx, const char *csv_string, + size_t *_uid_count, uid_t **_uids); + +uid_t client_euid(struct cli_creds *creds); +errno_t check_allowed_uids(uid_t uid, size_t allowed_uids_count, + const uid_t *allowed_uids); + +struct tevent_req * +sss_parse_inp_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + const char *default_domain, + const char *rawinp); + +errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char **_name, char **_domname); + +const char **parse_attr_list_ex(TALLOC_CTX *mem_ctx, const char *conf_str, + const char **defaults); + +char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool name_is_upn, + const char *orig_name); + +errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx); + +const char * +sss_resp_get_shell_override(struct ldb_message *msg, + struct resp_ctx *rctx, + struct sss_domain_info *domain); + +/** + * Helper functions to format output names + */ + +/* Format orig_name into a sized_string in output format as prescribed + * by the name_dom domain + */ +int sized_output_name(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + const char *orig_name, + struct sss_domain_info *name_dom, + struct sized_string **_name); + +/* Format orig_name into a sized_string in output format as prescribed + * by the domain read from the fully qualified name. + */ +int sized_domain_name(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + const char *member_name, + struct sized_string **_name); + +/* Given a ldb_result structure that contains a result of sysdb_initgroups + * where some groups might be just 'stubs' that don't have a name, but only + * a SID and a GID, resolve those incomplete groups into full group objects + */ +struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + struct ldb_result *initgr_res); + +int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ldb_result **_initgr_named_res); + +/** + * Register common responder sbus interface on connection. + */ +errno_t +sss_resp_register_sbus_iface(struct sbus_connection *conn, + struct resp_ctx *rctx); + +/** + * Register common service sbus interface on monitor connection. + */ +errno_t +sss_resp_register_service_iface(struct resp_ctx *rctx); + +#endif /* __SSS_RESPONDER_H__ */ diff --git a/src/responder/common/responder_cmd.c b/src/responder/common/responder_cmd.c new file mode 100644 index 0000000..bd05ca2 --- /dev/null +++ b/src/responder/common/responder_cmd.c @@ -0,0 +1,302 @@ +/* + SSSD + + SSS Client Responder, command parser + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + + 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 <errno.h> +#include "db/sysdb.h" +#include "util/util.h" +#include "responder/common/responder.h" +#include "responder/common/responder_packet.h" + + +int sss_cmd_send_error(struct cli_ctx *cctx, int err) +{ + struct cli_protocol *pctx; + int ret; + + pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + if (!pctx) return EINVAL; + + /* create response packet */ + ret = sss_packet_new(pctx->creq, 0, + sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create new packet: %d\n", ret); + return ret; + } + + sss_packet_set_error(pctx->creq->out, err); + return EOK; +} + +int sss_cmd_empty_packet(struct sss_packet *packet) +{ + uint8_t *body; + size_t blen; + int ret; + + ret = sss_packet_grow(packet, 2*sizeof(uint32_t)); + if (ret != EOK) return ret; + + sss_packet_get_body(packet, &body, &blen); + + /* num results */ + SAFEALIGN_SETMEM_UINT32(body, 0, NULL); + + /* reserved */ + SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); + + return EOK; +} + +int sss_cmd_send_empty(struct cli_ctx *cctx) +{ + struct cli_protocol *pctx; + int ret; + + pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + if (!pctx) return EINVAL; + + /* create response packet */ + ret = sss_packet_new(pctx->creq, 0, + sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + + ret = sss_cmd_empty_packet(pctx->creq->out); + if (ret != EOK) { + return ret; + } + + sss_packet_set_error(pctx->creq->out, EOK); + return EOK; +} + +void sss_cmd_done(struct cli_ctx *cctx, void *freectx) +{ + /* now that the packet is in place, unlock queue + * making the event writable */ + TEVENT_FD_WRITEABLE(cctx->cfde); + + /* free all request related data through the talloc hierarchy */ + talloc_free(freectx); +} + +int sss_cmd_get_version(struct cli_ctx *cctx) +{ + struct cli_protocol *pctx; + uint8_t *req_body; + size_t req_blen; + uint8_t *body; + size_t blen; + int ret; + uint32_t client_version; + uint32_t protocol_version; + int i; + static struct cli_protocol_version *cli_protocol_version = NULL; + + pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + if (!pctx) return EINVAL; + + pctx->cli_protocol_version = NULL; + + if (cli_protocol_version == NULL) { + cli_protocol_version = register_cli_protocol_version(); + } + + if (cli_protocol_version != NULL) { + pctx->cli_protocol_version = &cli_protocol_version[0]; + + sss_packet_get_body(pctx->creq->in, &req_body, &req_blen); + if (req_blen == sizeof(uint32_t)) { + memcpy(&client_version, req_body, sizeof(uint32_t)); + DEBUG(SSSDBG_FUNC_DATA, + "Received client version [%d].\n", client_version); + + i=0; + while(cli_protocol_version[i].version>0) { + if (cli_protocol_version[i].version == client_version) { + pctx->cli_protocol_version = &cli_protocol_version[i]; + break; + } + i++; + } + } + } + + /* create response packet */ + ret = sss_packet_new(pctx->creq, sizeof(uint32_t), + sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + sss_packet_get_body(pctx->creq->out, &body, &blen); + + protocol_version = (pctx->cli_protocol_version != NULL) + ? pctx->cli_protocol_version->version : 0; + + SAFEALIGN_COPY_UINT32(body, &protocol_version, NULL); + DEBUG(SSSDBG_FUNC_DATA, "Offered version [%d].\n", protocol_version); + + sss_cmd_done(cctx, NULL); + return EOK; +} + +int sss_cmd_execute(struct cli_ctx *cctx, + enum sss_cli_command cmd, + struct sss_cmd_table *sss_cmds) +{ + int i; + + for (i = 0; sss_cmds[i].cmd != SSS_CLI_NULL; i++) { + if (cmd == sss_cmds[i].cmd) { + return sss_cmds[i].fn(cctx); + } + } + + return EINVAL; +} +struct setent_req_list { + struct setent_req_list *prev; + struct setent_req_list *next; + /* Need to modify the list from a talloc destructor */ + struct setent_req_list **head; + + struct tevent_req *req; +}; + +struct tevent_req * +setent_get_req(struct setent_req_list *sl) +{ + return sl->req; +} + +int setent_remove_ref(TALLOC_CTX *ctx) +{ + struct setent_req_list *entry = + talloc_get_type(ctx, struct setent_req_list); + DLIST_REMOVE(*(entry->head), entry); + return 0; +} + +errno_t setent_add_ref(TALLOC_CTX *memctx, + struct setent_req_list **list, + struct tevent_req *req) +{ + struct setent_req_list *entry; + + entry = talloc_zero(memctx, struct setent_req_list); + if (!entry) { + return ENOMEM; + } + + entry->req = req; + DLIST_ADD_END(*list, entry, struct setent_req_list *); + entry->head = list; + + talloc_set_destructor((TALLOC_CTX *)entry, setent_remove_ref); + return EOK; +} + +void setent_notify(struct setent_req_list **list, errno_t err) +{ + struct setent_req_list *reql; + + /* Notify the waiting clients */ + while ((reql = *list) != NULL) { + /* Each tevent_req_done() call will free + * the request, removing it from the list. + */ + if (err == EOK) { + tevent_req_done(reql->req); + } else { + tevent_req_error(reql->req, err); + } + + if (reql == *list) { + /* The consumer failed to free the + * request. Log a bug and continue. + */ + DEBUG(SSSDBG_FATAL_FAILURE, + "BUG: a callback did not free its request. " + "May leak memory\n"); + /* Skip to the next since a memory leak is non-fatal */ + *list = (*list)->next; + } + } +} + +void setent_notify_done(struct setent_req_list **list) +{ + return setent_notify(list, EOK); +} + +/* + * Return values: + * EOK - cache hit + * EAGAIN - cache hit, but schedule off band update + * ENOENT - cache miss + */ +errno_t +sss_cmd_check_cache(struct ldb_message *msg, + int cache_refresh_percent, + uint64_t cache_expire) +{ + uint64_t lastUpdate; + uint64_t midpoint_refresh = 0; + time_t now; + + now = time(NULL); + lastUpdate = ldb_msg_find_attr_as_uint64(msg, SYSDB_LAST_UPDATE, 0); + midpoint_refresh = 0; + + if(cache_refresh_percent) { + midpoint_refresh = lastUpdate + + (cache_expire - lastUpdate)*cache_refresh_percent/100.0; + if (midpoint_refresh - lastUpdate < 10) { + /* If the percentage results in an expiration + * less than ten seconds after the lastUpdate time, + * that's too often we will simply set it to 10s + */ + midpoint_refresh = lastUpdate+10; + } + } + + if (cache_expire > now) { + /* cache still valid */ + + if (midpoint_refresh && midpoint_refresh < now) { + /* We're past the cache refresh timeout + * We'll return the value from the cache, but we'll also + * queue the cache entry for update out-of-band. + */ + return EAGAIN; + } else { + /* Cache is still valid. */ + return EOK; + } + } + + /* Cache needs to be updated */ + return ENOENT; +} diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c new file mode 100644 index 0000000..ac0e727 --- /dev/null +++ b/src/responder/common/responder_common.c @@ -0,0 +1,2001 @@ +/* + SSSD + + Common Responder methods + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + + 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 "config.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <string.h> +#include <sys/time.h> +#include <errno.h> +#include <fcntl.h> +#include <popt.h> +#include <dbus/dbus.h> + +#include "util/util.h" +#include "util/strtonum.h" +#include "db/sysdb.h" +#include "confdb/confdb.h" +#include "responder/common/responder.h" +#include "responder/common/responder_packet.h" +#include "providers/data_provider.h" +#include "util/util_creds.h" +#include "sss_iface/sss_iface_async.h" +#include "util/sss_chain_id_tevent.h" +#include "util/sss_chain_id.h" + +#ifdef HAVE_SYSTEMD +#include <systemd/sd-daemon.h> +#endif + +#define SHELL_REALLOC_INCREMENT 5 +#define SHELL_REALLOC_MAX 50 + +static errno_t set_close_on_exec(int fd) +{ + int v; + int ferr; + errno_t error; + + /* Get the current flags for this file descriptor */ + v = fcntl(fd, F_GETFD, 0); + + errno = 0; + /* Set the close-on-exec flags on this fd */ + ferr = fcntl(fd, F_SETFD, v | FD_CLOEXEC); + if (ferr < 0) { + error = errno; + DEBUG(SSSDBG_FATAL_FAILURE, + "Unable to set fd close-on-exec: [%d][%s]\n", + error, strerror(error)); + return error; + } + return EOK; +} + +static void client_close_fn(struct tevent_context *ev, + struct tevent_fd *fde, int fd, + void *ptr) +{ + errno_t ret; + struct cli_ctx *ctx = talloc_get_type(ptr, struct cli_ctx); + + if ((ctx->cfd > 0) && close(ctx->cfd) < 0) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to close fd [%d]: [%s]\n", + ctx->cfd, strerror(ret)); + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Terminated client [%p][%d]\n", + ctx, ctx->cfd); + + ctx->cfd = -1; +} + +static errno_t get_client_cred(struct cli_ctx *cctx) +{ + SEC_CTX secctx; + int ret; + + cctx->creds = talloc_zero(cctx, struct cli_creds); + if (!cctx->creds) return ENOMEM; + +#ifdef HAVE_UCRED + socklen_t client_cred_len = sizeof(struct ucred); + char proc_path[32]; + char cmd_line[255] = { 0 }; + int proc_fd; + + cctx->creds->ucred.uid = -1; + cctx->creds->ucred.gid = -1; + cctx->creds->ucred.pid = -1; + + ret = getsockopt(cctx->cfd, SOL_SOCKET, SO_PEERCRED, &cctx->creds->ucred, + &client_cred_len); + if (ret != EOK) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "getsockopt failed [%d][%s].\n", ret, strerror(ret)); + return ret; + } + if (client_cred_len != sizeof(struct ucred)) { + DEBUG(SSSDBG_CRIT_FAILURE, + "getsockopt returned unexpected message size.\n"); + return ENOMSG; + } + + if (cctx->creds->ucred.pid > -1) { + snprintf(proc_path, sizeof(proc_path), "/proc/%d/cmdline", + (int)cctx->creds->ucred.pid); + proc_fd = open(proc_path, O_RDONLY); + if (proc_fd != -1) { + if (sss_fd_nonblocking(proc_fd) == EOK) { + ret = read(proc_fd, cmd_line, sizeof(cmd_line)-1); + if (ret > 0) { + cmd_line[ret] = 0; + cctx->cmd_line = talloc_strdup(cctx, cmd_line); + } + } + close(proc_fd); + } + } + + DEBUG(SSSDBG_TRACE_ALL, + "Client [%p][%d] creds: euid[%d] egid[%d] pid[%d] cmd_line['%s'].\n", + cctx, cctx->cfd, + cctx->creds->ucred.uid, cctx->creds->ucred.gid, + cctx->creds->ucred.pid, cmd_line); +#endif + + ret = SELINUX_getpeercon(cctx->cfd, &secctx); + if (ret != 0) { + ret = errno; + DEBUG(SSSDBG_MINOR_FAILURE, + "The following failure is expected to happen in case SELinux is disabled:\n" + "SELINUX_getpeercon failed [%d][%s].\n" + "Please, consider enabling SELinux in your system.\n", ret, strerror(ret)); + /* This is not fatal, as SELinux may simply be disabled */ + ret = EOK; + } else { + cctx->creds->selinux_ctx = SELINUX_context_new(secctx); + SELINUX_freecon(secctx); + } + + return ret; +} + +uid_t client_euid(struct cli_creds *creds) +{ + if (!creds) return -1; + return cli_creds_get_uid(creds); +} + +errno_t check_allowed_uids(uid_t uid, size_t allowed_uids_count, + const uid_t *allowed_uids) +{ + size_t c; + + if (allowed_uids == NULL) { + return EINVAL; + } + + for (c = 0; c < allowed_uids_count; c++) { + if (uid == allowed_uids[c]) { + return EOK; + } + } + + return EACCES; +} + +errno_t csv_string_to_uid_array(TALLOC_CTX *mem_ctx, const char *csv_string, + size_t *_uid_count, uid_t **_uids) +{ + int ret; + size_t c; + char **list = NULL; + int list_size; + uid_t *uids = NULL; + char *endptr; + const char *envvar; + bool loops_were_allowed; + + envvar = getenv("_SSS_LOOPS"); + loops_were_allowed = (envvar == NULL || strcmp(envvar, "NO") != 0); + + if (!loops_were_allowed) { + ret = unsetenv("_SSS_LOOPS"); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to unset _SSS_LOOPS.\n"); + goto done; + } + } + + ret = split_on_separator(mem_ctx, csv_string, ',', true, false, + &list, &list_size); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed [%d][%s].\n", + ret, strerror(ret)); + goto done; + } + + uids = talloc_array(mem_ctx, uint32_t, list_size); + if (uids == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n"); + ret = ENOMEM; + goto done; + } + + for (c = 0; c < list_size; c++) { + if (*list[c] == '\0') { + DEBUG(SSSDBG_OP_FAILURE, "Empty list item.\n"); + ret = EINVAL; + goto done; + } + + uids[c] = strtouint32(list[c], &endptr, 10); + if ((errno != 0) || (*endptr != '\0') || (list[c] == endptr)) { + ret = errno; + if (ret == ERANGE) { + DEBUG(SSSDBG_OP_FAILURE, "List item [%s] is out of range.\n", + list[c]); + goto done; + } + + ret = sss_user_by_name_or_uid(list[c], &uids[c], NULL); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "List item [%s] is neither a valid " + "UID nor a user name which could be " + "resolved by getpwnam().\n", list[c]); + sss_log(SSS_LOG_WARNING, "List item [%s] is neither a valid " + "UID nor a user name which could be " + "resolved by getpwnam().\n", list[c]); + goto done; + } + } + } + + *_uid_count = list_size; + *_uids = uids; + + ret = EOK; + +done: + if (!loops_were_allowed) { + if (setenv("_SSS_LOOPS", "NO" , 0) != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to restore _SSS_LOOPS.\n"); + } + } + talloc_free(list); + if (ret != EOK) { + talloc_free(uids); + } + + return ret; +} + +static void client_send(struct cli_ctx *cctx) +{ + struct cli_protocol *pctx; + int ret; + + pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + + ret = sss_packet_send(pctx->creq->out, cctx->cfd); + if (ret == EAGAIN) { + /* not all data was sent, loop again */ + return; + } + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n"); + talloc_free(cctx); + return; + } + + /* ok all sent */ + TEVENT_FD_NOT_WRITEABLE(cctx->cfde); + TEVENT_FD_READABLE(cctx->cfde); + talloc_zfree(pctx->creq); + return; +} + +static int client_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds) +{ + struct cli_protocol *pctx; + enum sss_cli_command cmd; + + pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + cmd = sss_packet_get_cmd(pctx->creq->in); + return sss_cmd_execute(cctx, cmd, sss_cmds); +} + +static void client_recv(struct cli_ctx *cctx) +{ + struct cli_protocol *pctx; + int ret; + + pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + + if (!pctx->creq) { + pctx->creq = talloc_zero(cctx, struct cli_request); + if (!pctx->creq) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to alloc request, aborting client!\n"); + talloc_free(cctx); + return; + } + } + + if (!pctx->creq->in) { + ret = sss_packet_new(pctx->creq, SSS_PACKET_MAX_RECV_SIZE, + 0, &pctx->creq->in); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to alloc request, aborting client!\n"); + talloc_free(cctx); + return; + } + } + + ret = sss_packet_recv(pctx->creq->in, cctx->cfd); + switch (ret) { + case EOK: + /* do not read anymore */ + TEVENT_FD_NOT_READABLE(cctx->cfde); + /* execute command */ + ret = client_cmd_execute(cctx, cctx->rctx->sss_cmds); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to execute request, aborting client!\n"); + talloc_free(cctx); + } + /* past this point cctx can be freed at any time by callbacks + * in case of error, do not use it */ + return; + + case EAGAIN: + /* need to read still some data, loop again */ + break; + + case EINVAL: + DEBUG(SSSDBG_TRACE_FUNC, + "Invalid data from client, closing connection!\n"); + talloc_free(cctx); + break; + + case ENODATA: + DEBUG(SSSDBG_FUNC_DATA, "Client disconnected!\n"); + talloc_free(cctx); + break; + + default: + DEBUG(SSSDBG_TRACE_FUNC, "Failed to read request, aborting client!\n"); + talloc_free(cctx); + } + + return; +} + +static errno_t schedule_responder_idle_timer(struct resp_ctx *rctx); + +static void responder_idle_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *data) +{ + struct resp_ctx *rctx; + time_t now; + + rctx = talloc_get_type(data, struct resp_ctx); + + now = time(NULL); + if (rctx->last_request_time > now) { + DEBUG(SSSDBG_IMPORTANT_INFO, + "Time shift detected, re-scheduling the responder timeout\n"); + goto end; + } + + if ((now - rctx->last_request_time) >= rctx->idle_timeout) { + /* This responder is idle. Terminate it */ + DEBUG(SSSDBG_TRACE_INTERNAL, + "Terminating idle responder [%p]\n", rctx); + + talloc_free(rctx); + + orderly_shutdown(0); + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Re-scheduling the idle timeout [%s] for the responder [%p]\n", + CONFDB_RESPONDER_IDLE_TIMEOUT, rctx); + +end: + schedule_responder_idle_timer(rctx); +} + +static errno_t schedule_responder_idle_timer(struct resp_ctx *rctx) +{ + struct timeval tv; + + tv = tevent_timeval_current_ofs(rctx->idle_timeout / 2, 0); + + talloc_zfree(rctx->idle); + rctx->idle = tevent_add_timer(rctx->ev, + rctx, + tv, + responder_idle_handler, + rctx); + if (rctx->idle == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to allocate time event: responder [%p] shutdown timeout\n", + rctx); + return ENOMEM; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Re-scheduling the idle timeout [%s] for the responder [%p]\n", + CONFDB_RESPONDER_IDLE_TIMEOUT, rctx); + + return EOK; +} + +static errno_t setup_responder_idle_timer(struct resp_ctx *rctx) +{ + errno_t ret; + + rctx->last_request_time = time(NULL); + + ret = schedule_responder_idle_timer(rctx); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Error scheduling the idle timeout [%s] for the responder [%p]: " + "%d [%s]\n", + CONFDB_RESPONDER_IDLE_TIMEOUT, rctx, ret, sss_strerror(ret)); + return ret; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Setting up the idle timeout [%s] for the responder [%p]\n", + CONFDB_RESPONDER_IDLE_TIMEOUT, rctx); + + return EOK; +} + +static void client_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *ptr) +{ + sss_client_fd_handler(ptr, client_recv, client_send, flags); +} + +static errno_t setup_client_idle_timer(struct cli_ctx *cctx); + +static int cli_ctx_destructor(struct cli_ctx *cctx) +{ + if (cctx->creds == NULL) { + return 0; + } + + if (cctx->creds->selinux_ctx == NULL) { + return 0; + } + + SELINUX_context_free(cctx->creds->selinux_ctx); + cctx->creds->selinux_ctx = NULL; + + return 0; +} + +struct accept_fd_ctx { + struct resp_ctx *rctx; + bool is_private; + connection_setup_t connection_setup; +}; + +/* + * Use this function only before the client context is established + */ +static void accept_and_terminate_cli(int fd) +{ + struct sockaddr_un addr; + int client_fd; + socklen_t len; + + /* accept and close to signal the client we have a problem */ + memset(&addr, 0, sizeof(addr)); + len = sizeof(addr); + client_fd = accept(fd, (struct sockaddr *)&addr, &len); + if (client_fd == -1) { + return; + } + close(client_fd); + return; +} + +static void accept_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *ptr) +{ + static uid_t last_violator_uid = (uid_t)-1; + /* accept and attach new event handler */ + struct accept_fd_ctx *accept_ctx = + talloc_get_type(ptr, struct accept_fd_ctx); + struct resp_ctx *rctx = accept_ctx->rctx; + struct cli_ctx *cctx; + socklen_t len; + struct stat stat_buf; + int ret; + int fd = accept_ctx->is_private ? rctx->priv_lfd : rctx->lfd; + + rctx->client_id_num++; + if (accept_ctx->is_private) { + ret = stat(rctx->priv_sock_name, &stat_buf); + if (ret == -1) { + DEBUG(SSSDBG_CRIT_FAILURE, + "stat on privileged pipe failed: [%d][%s].\n", + errno, strerror(errno)); + accept_and_terminate_cli(fd); + return; + } + + if ( ! (stat_buf.st_uid == 0 && stat_buf.st_gid == 0 && + (stat_buf.st_mode&(S_IFSOCK|S_IRUSR|S_IWUSR)) == stat_buf.st_mode)) { + DEBUG(SSSDBG_CRIT_FAILURE, + "privileged pipe has an illegal status.\n"); + accept_and_terminate_cli(fd); + return; + } + } + + cctx = talloc_zero(rctx, struct cli_ctx); + if (!cctx) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Out of memory trying to setup client context%s!\n", + accept_ctx->is_private ? " on privileged pipe": ""); + accept_and_terminate_cli(fd); + return; + } + + talloc_set_destructor(cctx, cli_ctx_destructor); + + cctx->client_id_num = rctx->client_id_num; + + len = sizeof(cctx->addr); + cctx->cfd = accept(fd, (struct sockaddr *)&cctx->addr, &len); + if (cctx->cfd == -1) { + DEBUG(SSSDBG_CRIT_FAILURE, "Accept failed [%s]\n", strerror(errno)); + talloc_free(cctx); + return; + } + + cctx->priv = accept_ctx->is_private; + + ret = get_client_cred(cctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "get_client_cred failed, " + "client cred may not be available.\n"); + } + + if (rctx->allowed_uids_count != 0) { + if (client_euid(cctx->creds) == -1) { + DEBUG(SSSDBG_CRIT_FAILURE, "allowed_uids configured, " \ + "but platform does not support " \ + "reading peer credential from the " \ + "socket. Access denied.\n"); + close(cctx->cfd); + talloc_free(cctx); + return; + } + + ret = check_allowed_uids(client_euid(cctx->creds), rctx->allowed_uids_count, + rctx->allowed_uids); + if (ret != EOK) { + if (ret == EACCES) { + if (client_euid(cctx->creds) != last_violator_uid) { + last_violator_uid = client_euid(cctx->creds); + DEBUG(SSSDBG_IMPORTANT_INFO, + "Access denied for uid [%"SPRIuid"].\n", + last_violator_uid); + } + } else { + DEBUG(SSSDBG_OP_FAILURE, "check_allowed_uids failed.\n"); + } + close(cctx->cfd); + talloc_free(cctx); + return; + } + } + + ret = accept_ctx->connection_setup(cctx); + if (ret != EOK) { + close(cctx->cfd); + talloc_free(cctx); + DEBUG(SSSDBG_OP_FAILURE, + "Failed to setup client handler%s\n", + accept_ctx->is_private ? " on privileged pipe" : ""); + return; + } + + cctx->cfde = tevent_add_fd(ev, cctx, cctx->cfd, + TEVENT_FD_READ, cctx->cfd_handler, + cctx); + if (!cctx->cfde) { + close(cctx->cfd); + talloc_free(cctx); + DEBUG(SSSDBG_OP_FAILURE, + "Failed to queue client handler%s\n", + accept_ctx->is_private ? " on privileged pipe" : ""); + return; + } + tevent_fd_set_close_fn(cctx->cfde, client_close_fn); + + cctx->ev = ev; + cctx->rctx = rctx; + + /* Record the new time and set up the idle timer */ + ret = reset_client_idle_timer(cctx); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Could not create idle timer for client. " + "This connection may not auto-terminate\n"); + /* Non-fatal, continue */ + } + + ret = setup_client_idle_timer(cctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not create idle timer for client. " + "This connection may not auto-terminate\n"); + /* Non-fatal, continue */ + } + + DEBUG(SSSDBG_TRACE_FUNC, + "[CID#%u] Client [cmd %s][uid %u][%p][%d] connected%s!\n", + cctx->client_id_num, cctx->cmd_line, client_euid(cctx->creds), + cctx, cctx->cfd, accept_ctx->is_private ? " to privileged pipe" : ""); + + return; +} + +static void client_idle_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *data) +{ + time_t now = time(NULL); + struct cli_ctx *cctx = talloc_get_type(data, struct cli_ctx); + + if (cctx->last_request_time > now) { + DEBUG(SSSDBG_IMPORTANT_INFO, + "Time shift detected, re-scheduling the client timeout [%s].\n", + CONFDB_RESPONDER_CLI_IDLE_TIMEOUT); + goto done; + } + + if ((now - cctx->last_request_time) > cctx->rctx->client_idle_timeout) { + /* This connection is idle. Terminate it */ + DEBUG(SSSDBG_TRACE_INTERNAL, + "Terminating idle client [%p][%d]\n", + cctx, cctx->cfd); + + /* The cli_ctx destructor will handle the rest */ + talloc_free(cctx); + return; + } + +done: + setup_client_idle_timer(cctx); +} + +errno_t reset_client_idle_timer(struct cli_ctx *cctx) +{ + cctx->last_request_time = time(NULL); + + return EOK; +} + +static errno_t setup_client_idle_timer(struct cli_ctx *cctx) +{ + struct timeval tv = + tevent_timeval_current_ofs(cctx->rctx->client_idle_timeout/2, 0); + + talloc_zfree(cctx->idle); + + cctx->idle = tevent_add_timer(cctx->ev, cctx, tv, client_idle_handler, cctx); + if (!cctx->idle) return ENOMEM; + + DEBUG(SSSDBG_TRACE_ALL, + "Idle timer re-set for client [%p][%d]\n", + cctx, cctx->cfd); + + return EOK; +} + +static void +sss_dp_on_reconnect(struct sbus_connection *conn, + enum sbus_reconnect_status status, + struct be_conn *be_conn); + +static void +sss_dp_init_done(struct tevent_req *req); + +static errno_t +sss_dp_init(struct resp_ctx *rctx, + const char *conn_name, + const char *cli_name, + struct sss_domain_info *domain) +{ + struct tevent_req *req; + struct be_conn *be_conn; + int max_retries; + errno_t ret; + + ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path, + CONFDB_SERVICE_RECON_RETRIES, 3, &max_retries); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read confdb [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + be_conn = talloc_zero(rctx, struct be_conn); + if (!be_conn) return ENOMEM; + + be_conn->cli_name = cli_name; + be_conn->domain = domain; + be_conn->rctx = rctx; + + be_conn->sbus_address = sss_iface_domain_address(be_conn, domain); + if (be_conn->sbus_address == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not locate DP address.\n"); + ret = ENOMEM; + goto done; + } + + be_conn->bus_name = sss_iface_domain_bus(be_conn, domain); + if (be_conn->bus_name == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not locate DP address.\n"); + ret = ENOMEM; + goto done; + } + + ret = sss_iface_connect_address(be_conn, rctx->ev, conn_name, + be_conn->sbus_address, NULL, + &be_conn->conn); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to connect to backend server.\n"); + goto done; + } + + ret = sss_resp_register_sbus_iface(be_conn->conn, rctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Cannot register generic responder " + "interface [%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + sbus_reconnect_enable(be_conn->conn, max_retries, sss_dp_on_reconnect, + be_conn); + + DLIST_ADD_END(rctx->be_conns, be_conn, struct be_conn *); + + /* Identify ourselves to the DP */ + req = sbus_call_dp_client_Register_send(be_conn, be_conn->conn, + be_conn->bus_name, + SSS_BUS_PATH, cli_name); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sss_dp_init_done, be_conn); + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(be_conn); + } + + return ret; +} + +static void +sss_dp_on_reconnect(struct sbus_connection *conn, + enum sbus_reconnect_status status, + struct be_conn *be_conn) +{ + struct tevent_req *req; + + if (status != SBUS_RECONNECT_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not reconnect to %s provider.\n", + be_conn->domain->name); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Reconnected to the Data Provider.\n"); + + /* Identify ourselves to the DP */ + req = sbus_call_dp_client_Register_send(be_conn, be_conn->conn, + be_conn->bus_name, + SSS_BUS_PATH, + be_conn->cli_name); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sbus_call_dp_client_Register_send() failed\n"); + return; + } + + tevent_req_set_callback(req, sss_dp_init_done, be_conn); +} + +static void +sss_dp_init_done(struct tevent_req *req) +{ + errno_t ret; + + ret = sbus_call_dp_client_Register_recv(req); + talloc_zfree(req); + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register client with DP\n"); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Client is registered with DP\n"); +} + +int create_pipe_fd(const char *sock_name, int *_fd, mode_t umaskval) +{ + struct sockaddr_un addr; + mode_t orig_umaskval; + errno_t ret; + int fd; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + return EIO; + } + + orig_umaskval = umask(umaskval); + + ret = sss_fd_nonblocking(fd); + if (ret != EOK) { + goto done; + } + + ret = set_close_on_exec(fd); + if (ret != EOK) { + goto done; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path) - 1); + addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; + + /* make sure we have no old sockets around */ + ret = unlink(sock_name); + if (ret != 0 && errno != ENOENT) { + ret = errno; + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot remove old socket (errno=%d [%s]), bind might fail!\n", + ret, sss_strerror(ret)); + } + + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + ret = errno; + DEBUG(SSSDBG_FATAL_FAILURE, + "Unable to bind on socket '%s' [%d]: %s\n", + sock_name, ret, sss_strerror(ret)); + goto done; + } + + if (listen(fd, 128) == -1) { + ret = errno; + DEBUG(SSSDBG_FATAL_FAILURE, + "Unable to listen on socket '%s' [%d]: %s\n", + sock_name, ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; + +done: + /* restore previous umask value */ + umask(orig_umaskval); + if (ret == EOK) { + *_fd = fd; + } else { + close(fd); + } + return ret; +} + +/* create a unix socket and listen to it */ +static int set_unix_socket(struct resp_ctx *rctx, + connection_setup_t conn_setup) +{ + errno_t ret; + struct accept_fd_ctx *accept_ctx = NULL; + +/* for future use */ +#if 0 + char *default_pipe; + int ret; + + default_pipe = talloc_asprintf(rctx, "%s/%s", PIPE_PATH, + rctx->sss_pipe_name); + if (!default_pipe) { + return ENOMEM; + } + + ret = confdb_get_string(rctx->cdb, rctx, + rctx->confdb_socket_path, "unixSocket", + default_pipe, &rctx->sock_name); + if (ret != EOK) { + talloc_free(default_pipe); + return ret; + } + talloc_free(default_pipe); + + default_pipe = talloc_asprintf(rctx, "%s/private/%s", PIPE_PATH, + rctx->sss_pipe_name); + if (!default_pipe) { + return ENOMEM; + } + + ret = confdb_get_string(rctx->cdb, rctx, + rctx->confdb_socket_path, "privUnixSocket", + default_pipe, &rctx->priv_sock_name); + if (ret != EOK) { + talloc_free(default_pipe); + return ret; + } + talloc_free(default_pipe); +#endif + + if (rctx->sock_name != NULL ) { + /* Set the umask so that permissions are set right on the socket. + * It must be readable and writable by anybody on the system. */ + if (rctx->lfd == -1) { + ret = create_pipe_fd(rctx->sock_name, &rctx->lfd, SCKT_RSP_UMASK); + if (ret != EOK) { + return ret; + } + } + + accept_ctx = talloc_zero(rctx, struct accept_fd_ctx); + if(!accept_ctx) goto failed; + accept_ctx->rctx = rctx; + accept_ctx->is_private = false; + accept_ctx->connection_setup = conn_setup; + + rctx->lfde = tevent_add_fd(rctx->ev, rctx, rctx->lfd, + TEVENT_FD_READ, accept_fd_handler, + accept_ctx); + if (!rctx->lfde) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to queue handler on pipe\n"); + goto failed; + } + } + + if (rctx->priv_sock_name != NULL ) { + /* create privileged pipe */ + if (rctx->priv_lfd == -1) { + ret = create_pipe_fd(rctx->priv_sock_name, &rctx->priv_lfd, + DFL_RSP_UMASK); + if (ret != EOK) { + goto failed; + } + } + + accept_ctx = talloc_zero(rctx, struct accept_fd_ctx); + if(!accept_ctx) goto failed; + accept_ctx->rctx = rctx; + accept_ctx->is_private = true; + accept_ctx->connection_setup = conn_setup; + + rctx->priv_lfde = tevent_add_fd(rctx->ev, rctx, rctx->priv_lfd, + TEVENT_FD_READ, accept_fd_handler, + accept_ctx); + if (!rctx->priv_lfde) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to queue handler on privileged pipe\n"); + goto failed; + } + } + + return EOK; + +failed: + if (rctx->lfd >= 0) close(rctx->lfd); + if (rctx->priv_lfd >= 0) close(rctx->priv_lfd); + return EIO; +} + +int activate_unix_sockets(struct resp_ctx *rctx, + connection_setup_t conn_setup) +{ + int ret; + +#ifdef HAVE_SYSTEMD + struct sockaddr_un sockaddr; + socklen_t sockaddr_len = sizeof(sockaddr); + + if (rctx->lfd == -1 && rctx->priv_lfd == -1) { + int numfds = (rctx->sock_name ? 1 : 0) + + (rctx->priv_sock_name ? 1 : 0); + /* but if systemd support is available, check if the sockets + * have been opened for us, via socket activation */ + ret = sd_listen_fds(1); + if (ret < 0) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Unexpected error probing for active sockets. " + "Will proceed with no sockets. [Error %d (%s)]\n", + -ret, sss_strerror(-ret)); + } else if (ret > numfds) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Too many activated sockets have been found, " + "expected %d, found %d\n", numfds, ret); + ret = E2BIG; + goto done; + } + + if (ret == numfds) { + rctx->lfd = SD_LISTEN_FDS_START; + ret = sd_is_socket_unix(rctx->lfd, SOCK_STREAM, 1, NULL, 0); + if (ret < 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Activated socket is not a UNIX listening socket\n"); + ret = EIO; + goto done; + } + + ret = getsockname(rctx->lfd, (struct sockaddr *) &sockaddr, &sockaddr_len); + if (ret == EOK) { + if (rctx->sock_name && + memcmp(rctx->sock_name, sockaddr.sun_path, strlen(rctx->sock_name)) != 0) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Warning: socket path defined in systemd unit (%s) and sssd.conf (%s) don't match\n", + sockaddr.sun_path, rctx->sock_name); + } + } + + ret = sss_fd_nonblocking(rctx->lfd); + if (ret != EOK) goto done; + if (numfds == 2) { + rctx->priv_lfd = SD_LISTEN_FDS_START + 1; + ret = sd_is_socket_unix(rctx->priv_lfd, SOCK_STREAM, 1, NULL, 0); + if (ret < 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Activated priv socket is not a UNIX listening socket\n"); + ret = EIO; + goto done; + } + + ret = sss_fd_nonblocking(rctx->priv_lfd); + if (ret != EOK) goto done; + } + } + } +#endif + + ret = set_unix_socket(rctx, conn_setup); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error initializing sockets\n"); + goto done; + } + +done: + return ret; +} + +void sss_client_fd_handler(void *ptr, + void (*recv_fn) (struct cli_ctx *cctx), + void (*send_fn) (struct cli_ctx *cctx), + uint16_t flags) +{ + errno_t ret; + uint64_t old_chain_id; + struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx); + + /* Always reset the responder idle timer on any activity */ + cctx->rctx->last_request_time = time(NULL); + + /* Always reset the client idle timer on any activity */ + ret = reset_client_idle_timer(cctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not create idle timer for the client. " + "This connection may not auto-terminate.\n"); + /* Non-fatal, continue */ + } + + /* Set the chain id */ + old_chain_id = sss_chain_id_set(cctx->client_id_num); + + if (flags & TEVENT_FD_READ) { + recv_fn(cctx); + return; + } + + if (flags & TEVENT_FD_WRITE) { + send_fn(cctx); + return; + } + /* Restore the original chain id */ + sss_chain_id_set(old_chain_id); +} + +int sss_connection_setup(struct cli_ctx *cctx) +{ + cctx->protocol_ctx = talloc_zero(cctx, struct cli_protocol); + if (!cctx->protocol_ctx) { + return ENOMEM; + } + + cctx->cfd_handler = client_fd_handler; + + return EOK; +} + +static int sss_responder_ctx_destructor(void *ptr) +{ + struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx); + + /* mark that we are shutting down the responder, so it is propagated + * into underlying contexts that are freed right before rctx */ + DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n"); + rctx->shutting_down = true; + + return 0; +} + +static errno_t responder_init_ncache(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + struct sss_nc_ctx **ncache) +{ + uint32_t neg_timeout; + uint32_t locals_timeout; + int tmp_value; + int ret; + + /* neg_timeout */ + ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_ENTRY_NEG_TIMEOUT, + 15, &tmp_value); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Fatal failure of setup negative cache timeout [%s].\n", + CONFDB_NSS_ENTRY_NEG_TIMEOUT); + ret = ENOENT; + goto done; + } + + if (tmp_value < 0) { + ret = EINVAL; + goto done; + } + + neg_timeout = tmp_value; + + /* local_timeout */ + ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY, + CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT, + CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT_DEFAULT, + &tmp_value); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Fatal failure of setup negative cache timeout [%s].\n", + CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT); + ret = ENOENT; + goto done; + } + + if (tmp_value < 0) { + ret = EINVAL; + goto done; + } + + locals_timeout = tmp_value; + + /* negative cache init */ + ret = sss_ncache_init(mem_ctx, neg_timeout, locals_timeout, ncache); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Fatal failure of initializing negative cache.\n"); + goto done; + } + + ret = EOK; + +done: + return ret; +} + +static errno_t sss_get_etc_shells(TALLOC_CTX *mem_ctx, char ***_shells) +{ + int i = 0; + char *sh; + char **shells = NULL; + TALLOC_CTX *tmp_ctx; + errno_t ret; + int size; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + shells = talloc_array(tmp_ctx, char *, SHELL_REALLOC_INCREMENT); + if (!shells) { + ret = ENOMEM; + goto done; + } + size = SHELL_REALLOC_INCREMENT; + + setusershell(); + while ((sh = getusershell())) { + shells[i] = talloc_strdup(shells, sh); + if (!shells[i]) { + endusershell(); + ret = ENOMEM; + goto done; + } + DEBUG(SSSDBG_TRACE_FUNC, "Found shell %s in /etc/shells\n", shells[i]); + i++; + + if (i == size) { + size += SHELL_REALLOC_INCREMENT; + if (size > SHELL_REALLOC_MAX) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Reached maximum number of shells [%d]. " + "Users may be denied access. " + "Please check /etc/shells for sanity\n", + SHELL_REALLOC_MAX); + break; + } + shells = talloc_realloc(NULL, shells, char *, + size); + if (!shells) { + ret = ENOMEM; + goto done; + } + } + } + endusershell(); + + if (i + 1 < size) { + shells = talloc_realloc(NULL, shells, char *, i + 1); + if (!shells) { + ret = ENOMEM; + goto done; + } + } + shells[i] = NULL; + + *_shells = talloc_move(mem_ctx, &shells); + ret = EOK; +done: + talloc_zfree(tmp_ctx); + return ret; +} + +int sss_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb, + struct sss_cmd_table sss_cmds[], + const char *sss_pipe_name, + int pipe_fd, + const char *sss_priv_pipe_name, + int priv_pipe_fd, + const char *confdb_service_path, + const char *conn_name, + const char *svc_name, + connection_setup_t conn_setup, + struct resp_ctx **responder_ctx) +{ + struct resp_ctx *rctx; + struct sss_domain_info *dom; + int ret; + char *tmp = NULL; + + rctx = talloc_zero(mem_ctx, struct resp_ctx); + if (!rctx) { + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n"); + return ENOMEM; + } + rctx->ev = ev; + rctx->cdb = cdb; + rctx->sss_cmds = sss_cmds; + rctx->sock_name = sss_pipe_name; + rctx->priv_sock_name = sss_priv_pipe_name; + rctx->lfd = pipe_fd; + rctx->priv_lfd = priv_pipe_fd; + rctx->confdb_service_path = confdb_service_path; + rctx->shutting_down = false; + rctx->socket_activated = is_socket_activated(); + rctx->dbus_activated = is_dbus_activated(); + + talloc_set_destructor((TALLOC_CTX*)rctx, sss_responder_ctx_destructor); + + ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path, + CONFDB_RESPONDER_CLI_IDLE_TIMEOUT, + CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT, + &rctx->client_idle_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get the client idle timeout [%s] [%d]: %s\n", + CONFDB_RESPONDER_CLI_IDLE_TIMEOUT, ret, strerror(ret)); + goto fail; + } + + /* Ensure that the client timeout is at least ten seconds */ + if (rctx->client_idle_timeout < 10) { + rctx->client_idle_timeout = 10; + } + + if (rctx->socket_activated || rctx->dbus_activated) { + ret = responder_setup_idle_timeout_config(rctx); + if (ret != EOK) { + goto fail; + } + } + + ret = confdb_get_bool(rctx->cdb, rctx->confdb_service_path, + CONFDB_RESPONDER_CACHE_FIRST, + CONFDB_RESPONDER_CACHE_FIRST_DEFAILT, + &rctx->cache_first); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get \"cache_first_option\".\n" + "Querying the caches first before querying the " + "Data Providers will not be enforced [%d]: %s.\n", + ret, sss_strerror(ret)); + } + + ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path, + CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT, + GET_DOMAINS_DEFAULT_TIMEOUT, &rctx->domains_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get the default domain timeout [%s] [%d]: %s\n", + CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT, ret, strerror(ret)); + goto fail; + } + + if (rctx->domains_timeout < 0) { + DEBUG(SSSDBG_CONF_SETTINGS, + "timeout [%s] can't be set to negative value, " + "setting default [%d] seconds.\n", + CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT, + GET_DOMAINS_DEFAULT_TIMEOUT); + rctx->domains_timeout = GET_DOMAINS_DEFAULT_TIMEOUT; + } + + ret = confdb_get_domains(rctx->cdb, &rctx->domains); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up domain map\n"); + goto fail; + } + + ret = confdb_get_string(rctx->cdb, rctx, CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_DEFAULT_DOMAIN, NULL, + &rctx->default_domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get the default domain [%d]: %s\n", + ret, strerror(ret)); + goto fail; + } + + ret = confdb_get_string(rctx->cdb, rctx, CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_OVERRIDE_SPACE, NULL, + &tmp); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get the space substitution character [%d]: %s\n", + ret, strerror(ret)); + goto fail; + } + + if (tmp != NULL) { + if (strlen(tmp) > 1) { + DEBUG(SSSDBG_MINOR_FAILURE, "Option %s is longer than 1 character " + "only the first character %c will be used\n", + CONFDB_MONITOR_OVERRIDE_SPACE, tmp[0]); + } + + rctx->override_space = tmp[0]; + } + + ret = confdb_get_string(rctx->cdb, rctx, + CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_DOMAIN_RESOLUTION_ORDER, NULL, + &tmp); + if (ret == EOK) { + rctx->domain_resolution_order = sss_replace_char(rctx, tmp, ',', ':'); + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot get the \"domain_resolution_order\" option.\n" + "The set up lookup_order won't be followed [%d]: %s.\n", + ret, sss_strerror(ret)); + } + + /* Read shell settings */ + ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_OVERRIDE_SHELL, NULL, + &rctx->override_shell); + if (ret != EOK && ret != ENOENT) goto fail; + + ret = confdb_get_string_as_list(cdb, rctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_ALLOWED_SHELL, + &rctx->allowed_shells); + if (ret != EOK && ret != ENOENT) goto fail; + + ret = confdb_get_string_as_list(cdb, rctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_VETOED_SHELL, + &rctx->vetoed_shells); + if (ret != EOK && ret != ENOENT) goto fail; + + ret = sss_get_etc_shells(rctx, &rctx->etc_shells); + if (ret != EOK) goto fail; + + ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_SHELL_FALLBACK, + CONFDB_DEFAULT_SHELL_FALLBACK, + &rctx->shell_fallback); + if (ret != EOK) goto fail; + + ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_DEFAULT_SHELL, + NULL, + &rctx->default_shell); + if (ret != EOK) goto fail; + + /* Read session_recording section */ + ret = session_recording_conf_load(rctx, rctx->cdb, &rctx->sr_conf); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed loading session recording configuration: %s\n", + strerror(ret)); + goto fail; + } + + for (dom = rctx->domains; dom; dom = get_next_domain(dom, 0)) { + ret = sss_names_init(rctx->cdb, rctx->cdb, dom->name, &dom->names); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "fatal error initializing regex data for domain: %s\n", + dom->name); + goto fail; + } + + ret = sss_dp_init(rctx, conn_name, svc_name, dom); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "fatal error setting up backend connector\n"); + goto fail; + } + } + + ret = sysdb_init(rctx, rctx->domains); + if (ret != EOK) { + SYSDB_VERSION_ERROR_DAEMON(ret); + DEBUG(SSSDBG_FATAL_FAILURE, + "fatal error initializing sysdb connection\n"); + goto fail; + } + + /* after all initializations we are ready to listen on our socket */ + ret = activate_unix_sockets(rctx, conn_setup); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing socket\n"); + goto fail; + } + + ret = responder_init_ncache(rctx, rctx->cdb, &rctx->ncache); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "fatal error initializing negcache\n"); + goto fail; + } + + ret = sss_ad_default_names_ctx(rctx, &rctx->global_names); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_ad_default_names_ctx failed.\n"); + goto fail; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Responder initialization complete (%s)\n", + rctx->socket_activated ? "socket-activated" : + rctx->dbus_activated ? "dbus-activated" : + "explicitly configured"); + + *responder_ctx = rctx; + return EOK; + +fail: + talloc_free(rctx); + return ret; +} + +int sss_dp_get_domain_conn(struct resp_ctx *rctx, const char *domain, + struct be_conn **_conn) +{ + struct be_conn *iter; + + if (!rctx->be_conns) return ENOENT; + + for (iter = rctx->be_conns; iter; iter = iter->next) { + if (strcasecmp(domain, iter->domain->name) == 0) break; + } + + if (!iter) return ENOENT; + + *_conn = iter; + + return EOK; +} + +struct sss_domain_info * +responder_get_domain(struct resp_ctx *rctx, const char *name) +{ + struct sss_domain_info *dom; + struct sss_domain_info *ret_dom = NULL; + + for (dom = rctx->domains; dom; + dom = get_next_domain(dom, SSS_GND_DESCEND)) { + if (sss_domain_get_state(dom) == DOM_DISABLED) { + continue; + } + + if (strcasecmp(dom->name, name) == 0 || + (dom->flat_name != NULL && + strcasecmp(dom->flat_name, name) == 0)) { + ret_dom = dom; + break; + } + } + + if (!ret_dom) { + DEBUG(SSSDBG_OP_FAILURE, "Unknown domain [%s]\n", name); + } + + return ret_dom; +} + +errno_t responder_get_domain_by_id(struct resp_ctx *rctx, const char *id, + struct sss_domain_info **_ret_dom) +{ + struct sss_domain_info *dom; + struct sss_domain_info *ret_dom = NULL; + size_t id_len; + size_t dom_id_len; + int ret; + + if (id == NULL || _ret_dom == NULL) { + return EINVAL; + } + + id_len = strlen(id); + + for (dom = rctx->domains; dom; + dom = get_next_domain(dom, SSS_GND_DESCEND)) { + if (sss_domain_get_state(dom) == DOM_DISABLED || + dom->domain_id == NULL) { + continue; + } + + dom_id_len = strlen(dom->domain_id); + if ((id_len >= dom_id_len) && + strncasecmp(dom->domain_id, id, dom_id_len) == 0) { + if (IS_SUBDOMAIN(dom) && + ((time(NULL) - dom->parent->subdomains_last_checked.tv_sec) > + rctx->domains_timeout)) { + DEBUG(SSSDBG_TRACE_FUNC, "Domain entry with id [%s] " \ + "is expired.\n", id); + ret = EAGAIN; + goto done; + } + ret_dom = dom; + break; + } + } + + if (ret_dom == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Unknown domain id [%s], checking for " + "possible subdomains!\n", id); + ret = ENOENT; + } else { + *_ret_dom = ret_dom; + ret = EOK; + } + +done: + return ret; +} + +errno_t +responder_logrotate(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct resp_ctx *rctx) +{ + return server_common_rotate_logs(rctx->cdb, rctx->confdb_service_path); + + return EOK; +} + +void responder_set_fd_limit(rlim_t fd_limit) +{ + struct rlimit current_limit, new_limit; + int limret; + + /* First, let's see if we have permission to just set + * the value as-is. + */ + new_limit.rlim_cur = fd_limit; + new_limit.rlim_max = fd_limit; + limret = setrlimit(RLIMIT_NOFILE, &new_limit); + if (limret == 0) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Maximum file descriptors set to [%"SPRIrlim"]\n", + new_limit.rlim_cur); + return; + } + + /* We couldn't set the soft and hard limits to this + * value. Let's see how high we CAN set it. + */ + + /* Determine the maximum hard limit */ + limret = getrlimit(RLIMIT_NOFILE, ¤t_limit); + if (limret == 0) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Current fd limit: [%"SPRIrlim"]\n", + current_limit.rlim_cur); + /* Choose the lesser of the requested and the hard limit */ + if (current_limit.rlim_max < fd_limit) { + new_limit.rlim_cur = current_limit.rlim_max; + } else { + new_limit.rlim_cur = fd_limit; + } + new_limit.rlim_max = current_limit.rlim_max; + + limret = setrlimit(RLIMIT_NOFILE, &new_limit); + if (limret == 0) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Maximum file descriptors set to [%"SPRIrlim"]\n", + new_limit.rlim_cur); + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not set new fd limits. Proceeding with " + "[%"SPRIrlim"]\n", current_limit.rlim_cur); + } + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not determine fd limits. " + "Proceeding with system values\n"); + } +} + +errno_t responder_setup_idle_timeout_config(struct resp_ctx *rctx) +{ + errno_t ret; + + ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path, + CONFDB_RESPONDER_IDLE_TIMEOUT, + CONFDB_RESPONDER_IDLE_DEFAULT_TIMEOUT, + &rctx->idle_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get the responder idle timeout [%s] [%d]: %s\n", + CONFDB_RESPONDER_IDLE_TIMEOUT, ret, sss_strerror(ret)); + goto fail; + } + + /* Idle timeout set to 0 means that no timeout will be set up to + * the responder */ + if (rctx->idle_timeout == 0) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Responder idle timeout won't be set up as the " + "responder_idle_timeout is set to 0\n"); + } else { + /* Ensure that the responder timeout is at least sixty seconds */ + if (rctx->idle_timeout < 60) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "responder_idle_timeout is set to a value lower than " + "the minimum allowed (60s). " + "The minimum allowed value will be used.\n"); + + rctx->idle_timeout = 60; + } + + ret = setup_responder_idle_timer(rctx); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "An error occurred when setting up the responder's idle " + "timeout [%s] for the responder [%p]: %s [%d].\n" + "The responder won't be automatically shutdown after %d " + "seconds inactive.\n", + CONFDB_RESPONDER_IDLE_TIMEOUT, + rctx, sss_strerror(ret), ret, + rctx->idle_timeout); + } + } + + ret = EOK; + +fail: + return ret; + +} + +/* ====== Helper functions for the domain resolution order ======= */ +static errno_t +sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, + struct sysdb_ctx *sysdb, + struct cache_req_domain **_cr_domains) +{ + TALLOC_CTX *tmp_ctx; + struct cache_req_domain *cr_domains = NULL; + const char *domain_resolution_order = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_get_view_domain_resolution_order(tmp_ctx, sysdb, + &domain_resolution_order); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + "sysdb_get_view_cache_req_domain() failed [%d]: [%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + if (ret == ENOENT) { + goto done; + } + + ret = cache_req_domain_new_list_from_domain_resolution_order( + mem_ctx, domains, domain_resolution_order, &cr_domains); + if (ret != EOK) { + DEBUG(SSSDBG_DEFAULT, + "cache_req_domain_new_list_from_domain_resolution_order() " + "failed [%d]: [%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + *_cr_domains = cr_domains; + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, + struct sysdb_ctx *sysdb, + const char *domain, + struct cache_req_domain **_cr_domains) +{ + TALLOC_CTX *tmp_ctx; + const char *domain_resolution_order = NULL; + errno_t ret; + + *_cr_domains = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_domain_get_domain_resolution_order(tmp_ctx, sysdb, domain, + &domain_resolution_order); + + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + "sysdb_domain_get_cache_req_domain() failed [%d]: [%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + if (ret == ENOENT) { + goto done; + } + + ret = cache_req_domain_new_list_from_domain_resolution_order( + mem_ctx, domains, domain_resolution_order, _cr_domains); + if (ret != EOK) { + DEBUG(SSSDBG_DEFAULT, + "cache_req_domain_new_list_from_domain_resolution_order() " + "failed [%d]: [%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) +{ + struct cache_req_domain *cr_domains = NULL; + struct sss_domain_info *dom; + errno_t ret; + + if (rctx->domain_resolution_order != NULL) { + ret = cache_req_domain_new_list_from_domain_resolution_order( + rctx, rctx->domains, + rctx->domain_resolution_order, &cr_domains); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + "Using domain_resolution_order from sssd.conf\n"); + goto done; + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to use domain_resolution_order set in the config file.\n" + "Trying to fallback to use ipaDomainOrderResolution setup by " + "IPA.\n"); + } + } + + for (dom = rctx->domains; dom != NULL; dom = dom->next) { + if (dom->provider != NULL && strcmp(dom->provider, "ipa") == 0) { + break; + } + } + + if (dom == NULL) { + ret = cache_req_domain_new_list_from_domain_resolution_order( + rctx, rctx->domains, NULL, &cr_domains); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to flatten the list of domains.\n"); + } + goto done; + } + + if (dom->has_views) { + ret = sss_resp_new_cr_domains_from_ipa_id_view(rctx, rctx->domains, + dom->sysdb, + &cr_domains); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + "Using domain_resolution_order from IPA ID View\n"); + goto done; + } + + if (ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to use ipaDomainResolutionOrder set for the " + "view \"%s\".\n" + "Trying to fallback to use ipaDomainOrderResolution " + "set in ipaConfig for the domain: %s.\n", + dom->view_name, dom->name); + } + } + + ret = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains, + dom->sysdb, dom->name, + &cr_domains); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + "Using domain_resolution_order from IPA Config\n"); + goto done; + } + + if (ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to use ipaDomainResolutionOrder set in ipaConfig " + "for the domain: \"%s\".\n" + "No ipaDomainResolutionOrder will be followed.\n", + dom->name); + } + + ret = cache_req_domain_new_list_from_domain_resolution_order( + rctx, rctx->domains, NULL, &cr_domains); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to flatten the list of domains.\n"); + goto done; + } + + ret = EOK; + +done: + cache_req_domain_list_zfree(&rctx->cr_domains); + rctx->cr_domains = cr_domains; + + return ret; +} + +/** + * Helper functions to format output names + */ +int sized_output_name(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + const char *orig_name, + struct sss_domain_info *name_dom, + struct sized_string **_name) +{ + TALLOC_CTX *tmp_ctx = NULL; + errno_t ret; + char *name_str; + struct sized_string *name; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = talloc_zero(tmp_ctx, struct sized_string); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sss_output_fqname(name, name_dom, orig_name, + rctx->override_space, &name_str); + if (ret != EOK) { + goto done; + } + + to_sized_string(name, name_str); + *_name = talloc_steal(mem_ctx, name); + ret = EOK; +done: + talloc_zfree(tmp_ctx); + return ret; +} + +int sized_domain_name(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + const char *member_name, + struct sized_string **_name) +{ + TALLOC_CTX *tmp_ctx = NULL; + errno_t ret; + char *domname; + struct sss_domain_info *member_dom; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sss_parse_internal_fqname(tmp_ctx, member_name, NULL, &domname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_internal_fqname failed\n"); + goto done; + } + + if (domname == NULL) { + ret = ERR_WRONG_NAME_FORMAT; + goto done; + } + + member_dom = find_domain_by_name(get_domains_head(rctx->domains), + domname, true); + if (member_dom == NULL) { + ret = ERR_DOMAIN_NOT_FOUND; + goto done; + } + + ret = sized_output_name(mem_ctx, rctx, member_name, + member_dom, _name); +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c new file mode 100644 index 0000000..227a229 --- /dev/null +++ b/src/responder/common/responder_dp.c @@ -0,0 +1,460 @@ +/* + Authors: + Simo Sorce <ssorce@redhat.com> + Stephen Gallagher <sgallagh@redhat.com> + + Copyright (C) 2009 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 <sys/time.h> +#include <time.h> +#include "util/util.h" +#include "util/sss_chain_id.h" +#include "responder/common/responder_packet.h" +#include "responder/common/responder.h" +#include "providers/data_provider.h" + +#ifdef BUILD_FILES_PROVIDER +static errno_t +sss_dp_account_files_params(struct sss_domain_info *dom, + enum sss_dp_acct_type type_in, + const char *opt_name_in, + enum sss_dp_acct_type *_type_out, + const char **_opt_name_out) +{ + if (type_in != SSS_DP_CERT) { + if (sss_domain_get_state(dom) != DOM_INCONSISTENT) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "The entries in the files domain are up-to-date\n"); + return EOK; + } + + if (sss_domain_fallback_to_nss(dom)) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Domain files is not consistent, falling back to nss.\n"); + return ENOENT; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Domain files is not consistent, issuing update\n"); + } + + switch(type_in) { + case SSS_DP_USER: + case SSS_DP_GROUP: + *_type_out = type_in; + *_opt_name_out = NULL; + return EAGAIN; + case SSS_DP_INITGROUPS: + /* There is no initgroups enumeration so let's use a dummy + * name to let the DP chain the requests + */ + *_type_out = type_in; + *_opt_name_out = DP_REQ_OPT_FILES_INITGR; + return EAGAIN; + case SSS_DP_CERT: + /* Let the backend handle certificate mapping for local users */ + *_type_out = type_in; + *_opt_name_out = opt_name_in; + return EAGAIN; + /* These are not handled by the files provider, just fall back */ + case SSS_DP_SUBID_RANGES: + case SSS_DP_NETGR: + case SSS_DP_SERVICES: + case SSS_DP_SECID: + case SSS_DP_USER_AND_GROUP: + case SSS_DP_WILDCARD_USER: + case SSS_DP_WILDCARD_GROUP: + return EOK; + } + + DEBUG(SSSDBG_CRIT_FAILURE, "Unhandled type %d\n", type_in); + return EINVAL; +} +#endif + +static errno_t +sss_dp_get_account_filter(TALLOC_CTX *mem_ctx, + enum sss_dp_acct_type type, + bool fast_reply, + const char *opt_name, + uint32_t opt_id, + uint32_t *_dp_flags, + uint32_t *_entry_type, + char **_filter) +{ + uint32_t entry_type = 0; + uint32_t dp_flags; + char *filter; + + switch (type) { + case SSS_DP_USER: + case SSS_DP_WILDCARD_USER: + entry_type = BE_REQ_USER; + break; + case SSS_DP_GROUP: + case SSS_DP_WILDCARD_GROUP: + entry_type = BE_REQ_GROUP; + break; + case SSS_DP_INITGROUPS: + entry_type = BE_REQ_INITGROUPS; + break; + case SSS_DP_SUBID_RANGES: + entry_type = BE_REQ_SUBID_RANGES; + break; + case SSS_DP_NETGR: + entry_type = BE_REQ_NETGROUP; + break; + case SSS_DP_SERVICES: + entry_type = BE_REQ_SERVICES; + break; + case SSS_DP_SECID: + entry_type = BE_REQ_BY_SECID; + break; + case SSS_DP_USER_AND_GROUP: + entry_type = BE_REQ_USER_AND_GROUP; + break; + case SSS_DP_CERT: + entry_type = BE_REQ_BY_CERT; + break; + } + + dp_flags = fast_reply ? DP_FAST_REPLY : 0; + + if (opt_name != NULL) { + switch(type) { + case SSS_DP_SECID: + filter = talloc_asprintf(mem_ctx, "%s=%s", DP_SEC_ID, + opt_name); + break; + case SSS_DP_CERT: + filter = talloc_asprintf(mem_ctx, "%s=%s", DP_CERT, + opt_name); + break; + case SSS_DP_WILDCARD_USER: + case SSS_DP_WILDCARD_GROUP: + filter = talloc_asprintf(mem_ctx, "%s=%s", DP_WILDCARD, + opt_name); + break; + default: + filter = talloc_asprintf(mem_ctx, "name=%s", opt_name); + break; + } + } else if (opt_id != 0) { + filter = talloc_asprintf(mem_ctx, "idnumber=%u", opt_id); + } else { + filter = talloc_strdup(mem_ctx, ENUM_INDICATOR); + } + + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); + return ENOMEM; + } + + *_dp_flags = dp_flags; + *_entry_type = entry_type; + *_filter = filter; + + return EOK; +} + +struct sss_dp_get_account_state { + uint16_t dp_error; + uint32_t error; + const char *error_message; +}; + +static void sss_dp_get_account_done(struct tevent_req *subreq); + +struct tevent_req * +sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + enum sss_dp_acct_type type, + const char *opt_name, + uint32_t opt_id, + const char *extra) +{ + struct sss_dp_get_account_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + struct be_conn *be_conn; + uint32_t entry_type; + uint32_t dp_flags; + char *filter; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_account_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + /* either, or, not both */ + if (opt_name != NULL && opt_id != 0) { + ret = EINVAL; + goto done; + } + + if (dom == NULL) { + ret = EINVAL; + goto done; + } + +#ifdef BUILD_FILES_PROVIDER + if (is_files_provider(dom)) { + /* This is a special case. If the files provider is just being updated, + * we issue an enumeration request. We always use the same request type + * (user enumeration) to make sure concurrent requests are just chained + * in the Data Provider */ + ret = sss_dp_account_files_params(dom, type, opt_name, + &type, &opt_name); + if (ret == EOK) { + state->dp_error = DP_ERR_OK; + state->error = EOK; + state->error_message = talloc_strdup(state, "Success"); + if (state->error_message == NULL) { + ret = ENOMEM; + goto done; + } + goto done; + } else if (ret != EAGAIN) { + DEBUG((ret == ENOENT) ? SSSDBG_MINOR_FAILURE : SSSDBG_OP_FAILURE, + "Failed to set files provider update [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + /* EAGAIN, fall through to issuing the request */ + } +#endif + + ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + dom->name); + ret = EIO; + goto done; + } + + /* Build filter. */ + ret = sss_dp_get_account_filter(state, type, fast_reply, opt_name, opt_id, + &dp_flags, &entry_type, &filter); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Creating request for [%s][%#x][%s][%s:%s]\n", + dom->name, entry_type, be_req2str(entry_type), + filter, extra == NULL ? "-" : extra); + + subreq = sbus_call_dp_dp_getAccountInfo_send(state, be_conn->conn, + be_conn->bus_name, SSS_BUS_PATH, dp_flags, + entry_type, filter, dom->name, extra, + sss_chain_id_get()); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sss_dp_get_account_done, req); + + ret = EAGAIN; + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, rctx->ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + } + + return req; +} + +static void sss_dp_get_account_done(struct tevent_req *subreq) +{ + struct sss_dp_get_account_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sss_dp_get_account_state); + + ret = sbus_call_dp_dp_getAccountInfo_recv(state, subreq, &state->dp_error, + &state->error, + &state->error_message); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + uint16_t *_dp_error, + uint32_t *_error, + const char **_error_message) +{ + struct sss_dp_get_account_state *state; + state = tevent_req_data(req, struct sss_dp_get_account_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_dp_error = state->dp_error; + *_error = state->error; + *_error_message = talloc_steal(mem_ctx, state->error_message); + + return EOK; +} + +struct sss_dp_resolver_get_state { + uint16_t dp_error; + uint32_t error; + const char *error_message; +}; + +static void sss_dp_resolver_get_done(struct tevent_req *subreq); + +struct tevent_req * +sss_dp_resolver_get_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + uint32_t entry_type, + uint32_t filter_type, + const char *filter_value) +{ + struct sss_dp_resolver_get_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + struct be_conn *be_conn; + uint32_t dp_flags; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sss_dp_resolver_get_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + /* Validate filter_type */ + switch (filter_type) { + case BE_FILTER_NAME: + case BE_FILTER_ADDR: + case BE_FILTER_ENUM: + break; + default: + ret = EINVAL; + goto done; + } + + if (dom == NULL) { + ret = EINVAL; + goto done; + } + + ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + dom->name); + ret = EIO; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Creating request for [%s][%#x][%s][%#x:%s]\n", + dom->name, entry_type, be_req2str(entry_type), + filter_type, filter_value ? filter_value : "-"); + + dp_flags = fast_reply ? DP_FAST_REPLY : 0; + subreq = sbus_call_dp_dp_resolverHandler_send(state, be_conn->conn, + be_conn->bus_name, + SSS_BUS_PATH, + dp_flags, entry_type, + filter_type, filter_value, + sss_chain_id_get()); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sss_dp_resolver_get_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + } + + return req; +} + +static void sss_dp_resolver_get_done(struct tevent_req *subreq) +{ + struct sss_dp_resolver_get_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sss_dp_resolver_get_state); + + ret = sbus_call_dp_dp_resolverHandler_recv(state, subreq, + &state->dp_error, + &state->error, + &state->error_message); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +sss_dp_resolver_get_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + uint16_t *_dp_error, + uint32_t *_error, + const char **_error_message) +{ + struct sss_dp_resolver_get_state *state; + state = tevent_req_data(req, struct sss_dp_resolver_get_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_dp_error = state->dp_error; + *_error = state->error; + *_error_message = talloc_steal(mem_ctx, state->error_message); + + return EOK; +} diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c new file mode 100644 index 0000000..1a388e7 --- /dev/null +++ b/src/responder/common/responder_get_domains.c @@ -0,0 +1,841 @@ +/* + Authors: + Jan Zeleny <jzeleny@redhat.com> + + Copyright (C) 2011 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 "util/sss_chain_id.h" +#include "responder/common/responder.h" +#include "providers/data_provider.h" +#include "db/sysdb.h" +#include "sss_iface/sss_iface_async.h" + +/* ========== Get subdomains for a domain ================= */ +struct get_subdomains_state { + uint16_t dp_error; + uint32_t error; + const char *error_message; +}; + +static void get_subdomains_done(struct tevent_req *subreq); + +struct tevent_req * +get_subdomains_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + const char *hint) +{ + struct get_subdomains_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + struct be_conn *be_conn; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct get_subdomains_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + if (is_files_provider(dom)) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Domain %s does not check DP\n", + dom->name); + state->dp_error = DP_ERR_OK; + state->error = EOK; + state->error_message = talloc_strdup(state, "Success"); + if (state->error_message == NULL) { + ret = ENOMEM; + goto done; + } + ret = EOK; + goto done; + } + + ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + dom->name); + ret = EIO; + goto done; + } + + subreq = sbus_call_dp_dp_getDomains_send(state, be_conn->conn, + be_conn->bus_name, + SSS_BUS_PATH, hint); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, get_subdomains_done, req); + + ret = EAGAIN; + +done: +#ifdef BUILD_FILES_PROVIDER + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, rctx->ev); + } else +#endif + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + } + + return req; +} + +static void get_subdomains_done(struct tevent_req *subreq) +{ + struct get_subdomains_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct get_subdomains_state); + + ret = sbus_call_dp_dp_getDomains_recv(state, subreq, &state->dp_error, + &state->error, &state->error_message); + talloc_zfree(subreq); + if (ret != EOK) { + state->dp_error = DP_ERR_FATAL; + state->error = ret; + } + + tevent_req_done(req); + return; +} + +static errno_t +get_subdomains_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + uint16_t *_dp_error, + uint32_t *_error, + const char **_error_message) +{ + struct get_subdomains_state *state; + state = tevent_req_data(req, struct get_subdomains_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_dp_error = state->dp_error; + *_error = state->error; + *_error_message = talloc_steal(mem_ctx, state->error_message); + + return EOK; +} + +/* ====== Iterate over all domains, searching for their subdomains ======= */ +static errno_t process_subdomains(struct sss_domain_info *dom, + struct confdb_ctx *confdb); +static void set_time_of_last_request(struct resp_ctx *rctx); +static errno_t check_last_request(struct resp_ctx *rctx, const char *hint); + +struct sss_dp_get_domains_state { + struct resp_ctx *rctx; + struct sss_domain_info *dom; + const char *hint; +}; + +static void +sss_dp_get_domains_process(struct tevent_req *subreq); + +struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + bool force, + const char *hint) +{ + errno_t ret; + struct tevent_req *req; + struct tevent_req *subreq; + struct sss_dp_get_domains_state *state; + bool refresh_timeout = false; + + req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_domains_state); + if (req == NULL) { + return NULL; + } + + if (rctx->domains == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No domains configured.\n"); + ret = EINVAL; + goto immediately; + } + + if (!force) { + ret = check_last_request(rctx, hint); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + "Last call was too recent, nothing to do!\n"); + goto immediately; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_TRACE_FUNC, "check_domain_request failed with [%d][%s]\n", + ret, strerror(ret)); + goto immediately; + } + } + refresh_timeout = true; + + state->rctx = rctx; + if (hint != NULL) { + state->hint = hint; + } else { + state->hint = talloc_strdup(state, ""); + if (state->hint == NULL) { + ret = ENOMEM; + goto immediately; + } + } + + state->dom = rctx->domains; + while(is_files_provider(state->dom)) { + state->dom = get_next_domain(state->dom, 0); + } + + if (state->dom == NULL) { + /* All domains were local */ + ret = sss_resp_populate_cr_domains(state->rctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "sss_resp_populate_cr_domains() failed [%d]: [%s]\n", + ret, sss_strerror(ret)); + goto immediately; + } + ret = EOK; + goto immediately; + } + + subreq = get_subdomains_send(req, rctx, state->dom, state->hint); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + tevent_req_set_callback(subreq, sss_dp_get_domains_process, req); + + return req; + +immediately: + if (ret == EOK) { + if (refresh_timeout) { + set_time_of_last_request(rctx); + } + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, rctx->ev); + + return req; +} + +static void sss_resp_update_certmaps(struct resp_ctx *rctx) +{ + int ret; + struct certmap_info **certmaps; + bool user_name_hint; + struct sss_domain_info *dom; + + for (dom = rctx->domains; dom != NULL; dom = dom->next) { + ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint); + if (ret == EOK) { + dom->user_name_hint = user_name_hint; + talloc_free(dom->certmaps); + dom->certmaps = certmaps; + } else { + DEBUG(SSSDBG_OP_FAILURE, + "sysdb_get_certmap failed for domain [%s].\n", dom->name); + } + } +} + +static void +sss_dp_get_domains_process(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sss_dp_get_domains_state *state = tevent_req_data(req, + struct sss_dp_get_domains_state); + uint16_t dp_err; + uint32_t dp_ret; + const char *err_msg; + + ret = get_subdomains_recv(subreq, subreq, &dp_err, &dp_ret, &err_msg); + talloc_zfree(subreq); + if (ret != EOK) { + goto fail; + } + + ret = process_subdomains(state->dom, state->rctx->cdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "process_subdomains failed, " + "trying next domain.\n"); + goto fail; + } + + /* Advance to the next domain */ + state->dom = get_next_domain(state->dom, 0); + + /* Skip "files provider" */ + while(is_files_provider(state->dom)) { + state->dom = get_next_domain(state->dom, 0); + } + + if (state->dom == NULL) { + /* No more domains to check, refreshing the active configuration */ + set_time_of_last_request(state->rctx); + ret = sss_resp_populate_cr_domains(state->rctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "sss_resp_populate_cr_domains() failed [%d]: [%s]\n", + ret, sss_strerror(ret)); + goto fail; + } + + sss_resp_update_certmaps(state->rctx); + + ret = sss_ncache_reset_repopulate_permanent(state->rctx, + state->rctx->ncache); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_ncache_reset_repopulate_permanent failed, ignored.\n"); + } + + tevent_req_done(req); + return; + } + + subreq = get_subdomains_send(req, state->rctx, state->dom, state->hint); + if (subreq == NULL) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sss_dp_get_domains_process, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static errno_t +process_subdomains(struct sss_domain_info *domain, struct confdb_ctx *confdb) +{ + int ret; + + if (domain->realm == NULL || + domain->flat_name == NULL || + domain->domain_id == NULL) { + ret = sysdb_master_domain_update(domain); + if (ret != EOK) { + DEBUG(SSSDBG_FUNC_DATA, "sysdb_master_domain_get_info " \ + "failed.\n"); + goto done; + } + } + + /* Retrieve all subdomains of this domain from sysdb + * and create their struct sss_domain_info representations + */ + ret = sysdb_update_subdomains(domain, confdb); + if (ret != EOK) { + DEBUG(SSSDBG_FUNC_DATA, "sysdb_update_subdomains failed.\n"); + goto done; + } + + errno = 0; + ret = gettimeofday(&domain->subdomains_last_checked, NULL); + if (ret == -1) { + ret = errno; + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to update sub-domains " + "of domain [%s].\n", domain->name); + } + + return ret; +} + +errno_t sss_dp_get_domains_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static void set_time_of_last_request(struct resp_ctx *rctx) +{ + int ret; + + errno = 0; + ret = gettimeofday(&rctx->get_domains_last_call, NULL); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, "gettimeofday failed [%d][%s].\n", + ret, strerror(ret)); + } +} + +static errno_t check_last_request(struct resp_ctx *rctx, const char *hint) +{ + struct sss_domain_info *dom; + time_t now = time(NULL); + time_t diff; + + diff = now - rctx->get_domains_last_call.tv_sec; + if (diff >= rctx->domains_timeout) { + /* Timeout, expired, fetch domains again */ + return EAGAIN; + } + + if (hint != NULL) { + for (dom = rctx->domains; dom; + dom = get_next_domain(dom, SSS_GND_DESCEND)) { + if (!IS_SUBDOMAIN(dom)) { + diff = now - dom->subdomains_last_checked.tv_sec; + /* not a subdomain */ + continue; + } + if (strcasecmp(dom->name, hint) == 0) { + if (diff >= rctx->domains_timeout) { + /* Timeout, expired, fetch domains again */ + return EAGAIN; + } + } + } + } + + return EOK; +} + +struct get_domains_state { + struct resp_ctx *rctx; + struct sss_nc_ctx *optional_ncache; + get_domains_callback_fn_t *callback; + void *callback_pvt; +}; + +static void get_domains_at_startup_done(struct tevent_req *req) +{ + int ret; + struct get_domains_state *state; + + state = tevent_req_callback_data(req, struct get_domains_state); + + ret = sss_dp_get_domains_recv(req); + talloc_free(req); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "sss_dp_get_domains request failed.\n"); + } + + if (state->optional_ncache != NULL) { + ret = sss_ncache_reset_repopulate_permanent(state->rctx, + state->optional_ncache); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "sss_ncache_reset_repopulate_permanent failed.\n"); + } + } + + if (is_files_provider(state->rctx->domains)) { + ret = sysdb_master_domain_update(state->rctx->domains); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_master_domain_update failed, " + "ignored.\n"); + } + } + + if (state->callback != NULL) { + state->callback(state->callback_pvt); + } + + talloc_free(state); + return; +} + +static void get_domains_at_startup(struct tevent_context *ev, + struct tevent_immediate *imm, + void *pvt) +{ + struct tevent_req *req; + struct get_domains_state *state; + + state = talloc_get_type(pvt, struct get_domains_state); + + req = sss_dp_get_domains_send(state, state->rctx, true, NULL); + if (req == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "sss_dp_get_domains_send failed.\n"); + talloc_free(state); + return; + } + + tevent_req_set_callback(req, get_domains_at_startup_done, state); + return; +} + +errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_nc_ctx *optional_ncache, + get_domains_callback_fn_t *callback, + void *callback_pvt) +{ + struct tevent_immediate *imm; + struct get_domains_state *state; + + state = talloc(mem_ctx, struct get_domains_state); + if (state == NULL) { + return ENOMEM; + } + state->rctx = rctx; + state->optional_ncache = optional_ncache; + state->callback = callback; + state->callback_pvt = callback_pvt; + + imm = tevent_create_immediate(mem_ctx); + if (imm == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n"); + talloc_free(state); + return ENOMEM; + } + + tevent_schedule_immediate(imm, ev, get_domains_at_startup, state); + + return EOK; +} + +struct sss_parse_inp_state { + struct resp_ctx *rctx; + const char *default_domain; + const char *rawinp; + + char *name; + char *domname; + errno_t error; +}; + +static void sss_parse_inp_done(struct tevent_req *subreq); + +struct tevent_req * +sss_parse_inp_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + const char *default_domain, + const char *rawinp) +{ + errno_t ret; + struct tevent_req *req; + struct tevent_req *subreq; + struct sss_parse_inp_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sss_parse_inp_state); + if (req == NULL) { + return NULL; + } + + if (rawinp == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Empty input!\n"); + ret = EINVAL; + goto done; + } + + state->rctx = rctx; + + state->rawinp = talloc_strdup(state, rawinp); + if (state->rawinp == NULL) { + ret = ENOMEM; + goto done; + } + + + state->default_domain = talloc_strdup(state, default_domain); + if (default_domain != NULL && state->default_domain == NULL) { + ret = ENOMEM; + goto done; + } + + /* If the subdomains haven't been checked yet, we need to always + * attach to the post-startup subdomain request and only then parse + * the input. Otherwise, we might not be able to parse input with a + * flat domain name specifier */ + if (rctx->get_domains_last_call.tv_sec > 0) { + ret = sss_parse_name_for_domains(state, rctx->domains, + default_domain, rawinp, + &state->domname, &state->name); + if (ret == EOK) { + /* Was able to use cached domains */ + goto done; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawinp); + ret = ERR_INPUT_PARSE; + goto done; + } + } + + /* EAGAIN - check the DP for subdomains */ + + DEBUG(SSSDBG_FUNC_DATA, "Requesting info for [%s] from [%s]\n", + state->name, state->domname ? state->domname : "<ALL>"); + + /* We explicitly use force=false here. This request should decide itself + * if it's time to re-use the cached subdomain list or refresh. If the + * caller needs to specify the 'force' parameter, they should use the + * sss_dp_get_domains_send() request itself + */ + subreq = sss_dp_get_domains_send(state, rctx, false, state->domname); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + tevent_req_set_callback(subreq, sss_parse_inp_done, req); + return req; + +done: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, rctx->ev); + return req; +} + +static void sss_parse_inp_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sss_parse_inp_state *state = tevent_req_data(req, + struct sss_parse_inp_state); + + ret = sss_dp_get_domains_recv(subreq); + talloc_free(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + state->error = ERR_OK; + + ret = sss_parse_name_for_domains(state, state->rctx->domains, + state->default_domain, + state->rawinp, + &state->domname, &state->name); + if (ret == EAGAIN && state->domname != NULL && state->name == NULL) { + DEBUG(SSSDBG_FUNC_DATA, + "Unknown domain in [%s]\n", state->rawinp); + state->error = ERR_DOMAIN_NOT_FOUND; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Invalid name received [%s]\n", state->rawinp); + state->error = ERR_INPUT_PARSE; + } + + if (state->error != ERR_OK) { + tevent_req_error(req, state->error); + return; + } + + /* Was able to parse the name now */ + tevent_req_done(req); +} + +errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char **_name, char **_domname) +{ + struct sss_parse_inp_state *state = tevent_req_data(req, + struct sss_parse_inp_state); + + if (state->error != ERR_DOMAIN_NOT_FOUND) { + TEVENT_REQ_RETURN_ON_ERROR(req); + } + + if (_name) { + *_name = talloc_steal(mem_ctx, state->name); + } + + if (_domname) { + *_domname = talloc_steal(mem_ctx, state->domname); + } + + return state->error; +} + +/* ========== Get domain of an account ================= */ + + +struct sss_dp_get_account_domain_state { + uint16_t dp_error; + uint32_t error; + const char *domain_name; +}; + +static void sss_dp_get_account_domain_done(struct tevent_req *subreq); + +struct tevent_req * +sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + enum sss_dp_acct_type type, + uint32_t opt_id, + const char *opt_str) +{ + struct sss_dp_get_account_domain_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + struct be_conn *be_conn; + uint32_t entry_type; + char *filter; + uint32_t dp_flags; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_account_domain_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: The Data Provider connection for %s is not available!\n", + dom->name); + ret = EIO; + goto done; + } + + switch (type) { + case SSS_DP_USER: + entry_type = BE_REQ_USER; + break; + case SSS_DP_GROUP: + entry_type = BE_REQ_GROUP; + break; + case SSS_DP_USER_AND_GROUP: + entry_type = BE_REQ_USER_AND_GROUP; + break; + case SSS_DP_SECID: + entry_type = BE_REQ_BY_SECID; + break; + default: + DEBUG(SSSDBG_OP_FAILURE, + "Unsupported lookup type %X for this request\n", type); + return NULL; + } + + if (type == SSS_DP_SECID) { + if (opt_str == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "BUG: SID search called without SID parameter!\n"); + ret = EINVAL; + goto done; + } + filter = talloc_asprintf(state, DP_SEC_ID"=%s", opt_str); + } else { + filter = talloc_asprintf(state, "idnumber=%u", opt_id); + } + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); + ret = ENOMEM; + goto done; + } + + dp_flags = fast_reply ? DP_FAST_REPLY : 0; + + subreq = sbus_call_dp_dp_getAccountDomain_send(state, be_conn->conn, + be_conn->bus_name, + SSS_BUS_PATH, dp_flags, + entry_type, filter, + sss_chain_id_get()); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sss_dp_get_account_domain_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + } + + return req; +} + +static void sss_dp_get_account_domain_done(struct tevent_req *subreq) +{ + struct sss_dp_get_account_domain_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sss_dp_get_account_domain_state); + + ret = sbus_call_dp_dp_getAccountDomain_recv(state, subreq, &state->dp_error, + &state->error, + &state->domain_name); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not get account info [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (state->dp_error != DP_ERR_OK) { + DEBUG(state->error == ERR_GET_ACCT_DOM_NOT_SUPPORTED ? SSSDBG_TRACE_INTERNAL + : SSSDBG_IMPORTANT_INFO, + "Data Provider Error: %u, %u [%s]\n", + (unsigned int)state->dp_error, (unsigned int)state->error, + sss_strerror(state->error)); + tevent_req_error(req, state->error ? state->error : EIO); + return; + } + + tevent_req_done(req); + return; +} + +errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **_domain) +{ + struct sss_dp_get_account_domain_state *state; + state = tevent_req_data(req, struct sss_dp_get_account_domain_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_domain = talloc_strdup(mem_ctx, state->domain_name); + if (*_domain == NULL) { + return ENOMEM; + } + + return EOK; +} diff --git a/src/responder/common/responder_iface.c b/src/responder/common/responder_iface.c new file mode 100644 index 0000000..cf32fe0 --- /dev/null +++ b/src/responder/common/responder_iface.c @@ -0,0 +1,161 @@ +/* + Copyright (C) 2016 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 "sss_iface/sss_iface_async.h" +#include "responder/common/negcache.h" +#include "responder/common/responder.h" + +static void set_domain_state_by_name(struct resp_ctx *rctx, + const char *domain_name, + enum sss_domain_state state) +{ + struct sss_domain_info *dom; + + if (domain_name == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "BUG: NULL domain name\n"); + return; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Setting state of domain %s\n", domain_name); + + for (dom = rctx->domains; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { + + if (strcasecmp(dom->name, domain_name) == 0) { + break; + } + } + + if (dom != NULL) { + sss_domain_set_state(dom, state); + } +} + +static errno_t +sss_resp_domain_active(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct resp_ctx *rctx, + const char *domain_name) +{ + DEBUG(SSSDBG_TRACE_LIBS, "Enabling domain %s\n", domain_name); + + set_domain_state_by_name(rctx, domain_name, DOM_ACTIVE); + + return EOK; +} + +static errno_t +sss_resp_domain_inconsistent(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct resp_ctx *rctx, + const char *domain_name) +{ + DEBUG(SSSDBG_TRACE_LIBS, "Disabling domain %s\n", domain_name); + + set_domain_state_by_name(rctx, domain_name, DOM_INCONSISTENT); + + return EOK; +} + +static errno_t +sss_resp_reset_ncache_users(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct resp_ctx *rctx) +{ + sss_ncache_reset_users(rctx->ncache); + + return EOK; +} + +static errno_t +sss_resp_reset_ncache_groups(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct resp_ctx *rctx) +{ + sss_ncache_reset_groups(rctx->ncache); + + return EOK; +} + +errno_t +sss_resp_register_sbus_iface(struct sbus_connection *conn, + struct resp_ctx *rctx) +{ + errno_t ret; + + SBUS_INTERFACE(iface_resp_domain, + sssd_Responder_Domain, + SBUS_METHODS( + SBUS_SYNC(METHOD, sssd_Responder_Domain, SetActive, sss_resp_domain_active, rctx), + SBUS_SYNC(METHOD, sssd_Responder_Domain, SetInconsistent, sss_resp_domain_inconsistent, rctx) + ), + SBUS_SIGNALS(SBUS_NO_SIGNALS), + SBUS_PROPERTIES(SBUS_NO_PROPERTIES) + ); + + SBUS_INTERFACE(iface_resp_negcache, + sssd_Responder_NegativeCache, + SBUS_METHODS( + SBUS_SYNC(METHOD, sssd_Responder_NegativeCache, ResetUsers, sss_resp_reset_ncache_users, rctx), + SBUS_SYNC(METHOD, sssd_Responder_NegativeCache, ResetGroups, sss_resp_reset_ncache_groups, rctx) + ), + SBUS_SIGNALS(SBUS_NO_SIGNALS), + SBUS_PROPERTIES(SBUS_NO_PROPERTIES) + ); + + struct sbus_path paths[] = { + {SSS_BUS_PATH, &iface_resp_domain}, + {SSS_BUS_PATH, &iface_resp_negcache}, + {NULL, NULL} + }; + + ret = sbus_connection_add_path_map(conn, paths); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n", + ret, sss_strerror(ret)); + } + + return ret; +} + +errno_t +sss_resp_register_service_iface(struct resp_ctx *rctx) +{ + errno_t ret; + + SBUS_INTERFACE(iface_svc, + sssd_service, + SBUS_METHODS( + SBUS_SYNC(METHOD, sssd_service, rotateLogs, responder_logrotate, rctx) + ), + SBUS_SIGNALS(SBUS_NO_SIGNALS), + SBUS_PROPERTIES( + SBUS_SYNC(GETTER, sssd_service, debug_level, generic_get_debug_level, NULL), + SBUS_SYNC(SETTER, sssd_service, debug_level, generic_set_debug_level, NULL) + ) + ); + + ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface" + "[%d]: %s\n", ret, sss_strerror(ret)); + } + + return ret; +} diff --git a/src/responder/common/responder_packet.c b/src/responder/common/responder_packet.c new file mode 100644 index 0000000..db74cc1 --- /dev/null +++ b/src/responder/common/responder_packet.c @@ -0,0 +1,364 @@ +/* + SSSD + + SSS Client Responder, command parser + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + + 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 <sys/types.h> +#include <sys/socket.h> +#include <string.h> +#include <errno.h> +#include <talloc.h> + +#include "util/util.h" +#include "responder/common/responder_packet.h" + +#define SSSSRV_PACKET_MEM_SIZE 512 + +struct sss_packet { + size_t memsize; + + /* Structure of the buffer: + * Bytes Content + * --------------------------------- + * 0-15 packet header + * 0-3 packet length (uint32_t) + * 4-7 command type (uint32_t) + * 8-11 status (uint32_t) + * 12-15 reserved + * 16+ packet body */ + uint8_t *buffer; + + /* io pointer */ + size_t iop; +}; + +/* Offsets to data in sss_packet's buffer */ +#define SSS_PACKET_LEN_OFFSET 0 +#define SSS_PACKET_CMD_OFFSET sizeof(uint32_t) +#define SSS_PACKET_ERR_OFFSET (2*(sizeof(uint32_t))) +#define SSS_PACKET_BODY_OFFSET (4*(sizeof(uint32_t))) + +static void sss_packet_set_len(struct sss_packet *packet, uint32_t len); +static void sss_packet_set_cmd(struct sss_packet *packet, + enum sss_cli_command cmd); +static uint32_t sss_packet_get_len(struct sss_packet *packet); + +/* + * Allocate a new packet structure + * + * - if size is defined use it otherwise the default packet will be + * SSSSRV_PACKET_MEM_SIZE bytes. + */ +int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size, + enum sss_cli_command cmd, + struct sss_packet **rpacket) +{ + struct sss_packet *packet; + + packet = talloc(mem_ctx, struct sss_packet); + if (!packet) return ENOMEM; + + if (size) { + int n = (size + SSS_NSS_HEADER_SIZE) / SSSSRV_PACKET_MEM_SIZE; + packet->memsize = (n + 1) * SSSSRV_PACKET_MEM_SIZE; + } else { + packet->memsize = SSSSRV_PACKET_MEM_SIZE; + } + + packet->buffer = talloc_size(packet, packet->memsize); + if (!packet->buffer) { + talloc_free(packet); + return ENOMEM; + } + memset(packet->buffer, 0, SSS_NSS_HEADER_SIZE); + + sss_packet_set_len(packet, size + SSS_NSS_HEADER_SIZE); + sss_packet_set_cmd(packet, cmd); + + packet->iop = 0; + + *rpacket = packet; + + return EOK; +} + +/* grows a packet size only in SSSSRV_PACKET_MEM_SIZE chunks */ +int sss_packet_grow(struct sss_packet *packet, size_t size) +{ + size_t totlen, len; + uint8_t *newmem; + uint32_t packet_len; + + if (size == 0) { + return EOK; + } + + totlen = packet->memsize; + packet_len = sss_packet_get_len(packet); + + len = packet_len + size; + + /* make sure we do not overflow */ + if (totlen < len) { + int n = len / SSSSRV_PACKET_MEM_SIZE + 1; + totlen += n * SSSSRV_PACKET_MEM_SIZE; + if (totlen < len) { + return EINVAL; + } + } + + if (totlen > packet->memsize) { + newmem = talloc_realloc_size(packet, packet->buffer, totlen); + if (!newmem) { + return ENOMEM; + } + + packet->memsize = totlen; + + /* re-set pointers if realloc had to move memory */ + if (newmem != packet->buffer) { + packet->buffer = newmem; + } + } + + packet_len += size; + sss_packet_set_len(packet, packet_len); + + + return 0; +} + +/* reclaim back previously reserved space in the packet + * usually done in function recovering from not fatal errors */ +int sss_packet_shrink(struct sss_packet *packet, size_t size) +{ + size_t newlen; + size_t oldlen = sss_packet_get_len(packet); + + if (size > oldlen) return EINVAL; + + newlen = oldlen - size; + if (newlen < SSS_NSS_HEADER_SIZE) return EINVAL; + + sss_packet_set_len(packet, newlen); + return 0; +} + +int sss_packet_set_size(struct sss_packet *packet, size_t size) +{ + size_t newlen; + + newlen = SSS_NSS_HEADER_SIZE + size; + + /* make sure we do not overflow */ + if (packet->memsize < newlen) return EINVAL; + + sss_packet_set_len(packet, newlen); + + return 0; +} + +int sss_packet_recv(struct sss_packet *packet, int fd) +{ + size_t rb; + size_t len; + void *buf; + size_t new_len; + int ret; + + buf = (uint8_t *)packet->buffer + packet->iop; + if (packet->iop >= SSS_PACKET_CMD_OFFSET) { + len = sss_packet_get_len(packet) - packet->iop; + } else { + len = packet->memsize - packet->iop; + } + + /* check for wrapping */ + if (len > (packet->memsize - packet->iop)) { + return EINVAL; + } + + errno = 0; + rb = recv(fd, buf, len, 0); + + if (rb == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + return EAGAIN; + } else { + return errno; + } + } + + if (rb == 0) { + return ENODATA; + } + + packet->iop += rb; + if (packet->iop < SSS_PACKET_CMD_OFFSET) { + return EAGAIN; + } + + new_len = sss_packet_get_len(packet); + if (new_len > packet->memsize) { + enum sss_cli_command cmd = sss_packet_get_cmd(packet); + size_t max_recv_size; + + /* Allow certain packet types to use a larger buffer. */ + switch (cmd) { + case SSS_NSS_GETNAMEBYCERT: + case SSS_NSS_GETLISTBYCERT: + max_recv_size = SSS_CERT_PACKET_MAX_RECV_SIZE; + break; + + case SSS_GSSAPI_SEC_CTX: + case SSS_PAC_ADD_PAC_USER: + max_recv_size = SSS_GSSAPI_PACKET_MAX_RECV_SIZE; + break; + + default: + max_recv_size = 0; + } + + /* Due to the way sss_packet_grow() works, the packet len must be set + * to 0 first, and then grown to the expected size. */ + if (new_len <= max_recv_size) { + sss_packet_set_len(packet, 0); + ret = sss_packet_grow(packet, new_len); + if (ret != EOK) { + return ret; + } + } else { + DEBUG(SSSDBG_OP_FAILURE, + "Refusing to read overlarge packet from fd %d (length %zu bytes, cmd %#04x)", + fd, new_len, cmd); + return EINVAL; + } + } + + if (packet->iop < new_len) { + return EAGAIN; + } + + return EOK; +} + +int sss_packet_send(struct sss_packet *packet, int fd) +{ + size_t rb; + size_t len; + void *buf; + + if (!packet) { + /* No packet object to write to? */ + return EINVAL; + } + + buf = packet->buffer + packet->iop; + len = sss_packet_get_len(packet) - packet->iop; + + errno = 0; + rb = send(fd, buf, len, 0); + + if (rb == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + return EAGAIN; + } else { + return errno; + } + } + + if (rb == 0) { + return EIO; + } + + packet->iop += rb; + + if (packet->iop < sss_packet_get_len(packet)) { + return EAGAIN; + } + + return EOK; +} + +enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet) +{ + uint32_t cmd; + + SAFEALIGN_COPY_UINT32(&cmd, packet->buffer + SSS_PACKET_CMD_OFFSET, NULL); + return (enum sss_cli_command)cmd; +} + +uint32_t sss_packet_get_status(struct sss_packet *packet) +{ + uint32_t status; + + SAFEALIGN_COPY_UINT32(&status, packet->buffer + SSS_PACKET_ERR_OFFSET, + NULL); + return status; +} + +void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen) +{ + *body = packet->buffer + SSS_PACKET_BODY_OFFSET; + *blen = sss_packet_get_len(packet) - SSS_NSS_HEADER_SIZE; +} + +errno_t sss_packet_set_body(struct sss_packet *packet, + uint8_t *body, + size_t blen) +{ + uint8_t *pbody; + size_t plen; + errno_t ret; + + ret = sss_packet_grow(packet, blen); + if (ret != EOK) { + return ret; + } + + sss_packet_get_body(packet, &pbody, &plen); + memcpy(pbody, body, blen); + + return EOK; +} + +void sss_packet_set_error(struct sss_packet *packet, int error) +{ + SAFEALIGN_SETMEM_UINT32(packet->buffer + SSS_PACKET_ERR_OFFSET, error, + NULL); +} + +static void sss_packet_set_len(struct sss_packet *packet, uint32_t len) +{ + SAFEALIGN_SETMEM_UINT32(packet->buffer + SSS_PACKET_LEN_OFFSET, len, NULL); +} + +static void sss_packet_set_cmd(struct sss_packet *packet, + enum sss_cli_command cmd) +{ + SAFEALIGN_SETMEM_UINT32(packet->buffer + SSS_PACKET_CMD_OFFSET, cmd, NULL); +} + +static uint32_t sss_packet_get_len(struct sss_packet *packet) +{ + uint32_t len; + + SAFEALIGN_COPY_UINT32(&len, packet->buffer + SSS_PACKET_LEN_OFFSET, NULL); + return len; +} diff --git a/src/responder/common/responder_packet.h b/src/responder/common/responder_packet.h new file mode 100644 index 0000000..fd99196 --- /dev/null +++ b/src/responder/common/responder_packet.h @@ -0,0 +1,51 @@ +/* + SSSD + + SSS Client Responder, header file + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + + 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/>. +*/ + +#ifndef __SSSSRV_PACKET_H__ +#define __SSSSRV_PACKET_H__ + +#include "sss_client/sss_cli.h" + +#define SSS_PACKET_MAX_RECV_SIZE 1024 +#define SSS_CERT_PACKET_MAX_RECV_SIZE ( 10 * SSS_PACKET_MAX_RECV_SIZE ) +#define SSS_GSSAPI_PACKET_MAX_RECV_SIZE ( 128 * 1024 ) + +struct sss_packet; + +int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size, + enum sss_cli_command cmd, + struct sss_packet **rpacket); +int sss_packet_grow(struct sss_packet *packet, size_t size); +int sss_packet_shrink(struct sss_packet *packet, size_t size); +int sss_packet_set_size(struct sss_packet *packet, size_t size); +int sss_packet_recv(struct sss_packet *packet, int fd); +int sss_packet_send(struct sss_packet *packet, int fd); +enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet); +uint32_t sss_packet_get_status(struct sss_packet *packet); +void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen); +void sss_packet_set_error(struct sss_packet *packet, int error); + +/* Grow packet and set its body. */ +errno_t sss_packet_set_body(struct sss_packet *packet, + uint8_t *body, + size_t blen); + +#endif /* __SSSSRV_PACKET_H__ */ diff --git a/src/responder/common/responder_utils.c b/src/responder/common/responder_utils.c new file mode 100644 index 0000000..47aeace --- /dev/null +++ b/src/responder/common/responder_utils.c @@ -0,0 +1,527 @@ + +/* + SSSD + + Common Responder utility functions + + Copyright (C) Sumit Bose <sbose@redhat.com> 2014 + + 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 <talloc.h> + +#include "db/sysdb.h" +#include "responder/common/responder.h" +#include "responder/common/cache_req/cache_req.h" +#include "util/util.h" + +static inline bool +attr_in_list(const char **list, size_t nlist, const char *str) +{ + return string_in_list_size(str, list, nlist, false); +} + +const char **parse_attr_list_ex(TALLOC_CTX *mem_ctx, const char *conf_str, + const char **defaults) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + const char **list = NULL; + const char **res = NULL; + int list_size; + char **conf_list = NULL; + int conf_list_size = 0; + const char **allow = NULL; + const char **deny = NULL; + int ai = 0, di = 0, li = 0; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + if (conf_str) { + ret = split_on_separator(tmp_ctx, conf_str, ',', true, true, + &conf_list, &conf_list_size); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot parse attribute ACL list %s: %d\n", conf_str, ret); + goto done; + } + + allow = talloc_zero_array(tmp_ctx, const char *, conf_list_size); + deny = talloc_zero_array(tmp_ctx, const char *, conf_list_size); + if (allow == NULL || deny == NULL) { + goto done; + } + } + + for (i = 0; i < conf_list_size; i++) { + switch (conf_list[i][0]) { + case '+': + allow[ai] = conf_list[i] + 1; + ai++; + continue; + case '-': + deny[di] = conf_list[i] + 1; + di++; + continue; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "ACL values must start with " + "either '+' (allow) or '-' (deny), got '%s'\n", + conf_list[i]); + goto done; + } + } + + /* Assume the output will have to hold defaults and all the configured, + * values, resize later + */ + list_size = 0; + if (defaults != NULL) { + while (defaults[list_size]) { + list_size++; + } + } + list_size += conf_list_size; + + list = talloc_zero_array(tmp_ctx, const char *, list_size + 1); + if (list == NULL) { + goto done; + } + + /* Start by copying explicitly allowed attributes */ + for (i = 0; i < ai; i++) { + /* if the attribute is explicitly denied, skip it */ + if (attr_in_list(deny, di, allow[i])) { + continue; + } + + /* If the attribute is already in the list, skip it */ + if (attr_in_list(list, li, allow[i])) { + continue; + } + + list[li] = talloc_strdup(list, allow[i]); + if (list[li] == NULL) { + goto done; + } + li++; + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Added allowed attr %s to whitelist\n", allow[i]); + } + + /* Add defaults */ + if (defaults != NULL) { + for (i = 0; defaults[i]; i++) { + /* if the attribute is explicitly denied, skip it */ + if (attr_in_list(deny, di, defaults[i])) { + continue; + } + + /* If the attribute is already in the list, skip it */ + if (attr_in_list(list, li, defaults[i])) { + continue; + } + + list[li] = talloc_strdup(list, defaults[i]); + if (list[li] == NULL) { + goto done; + } + li++; + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Added default attr %s to whitelist\n", defaults[i]); + } + } + + res = talloc_steal(mem_ctx, list); +done: + talloc_free(tmp_ctx); + return res; +} + +char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool name_is_upn, + const char *orig_name) +{ + TALLOC_CTX *tmp_ctx; + char *name; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + name = sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_get_cased_name failed\n"); + talloc_free(tmp_ctx); + return NULL; + } + + name = sss_reverse_replace_space(tmp_ctx, name, rctx->override_space); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_reverse_replace_space failed\n"); + talloc_free(tmp_ctx); + return NULL; + } + + + if (name_is_upn == false) { + name = sss_create_internal_fqname(tmp_ctx, name, dom->name); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_create_internal_fqname failed\n"); + talloc_free(tmp_ctx); + return NULL; + } + } + + name = talloc_steal(mem_ctx, name); + talloc_free(tmp_ctx); + return name; +} + +struct resp_resolve_group_names_state { + struct tevent_context *ev; + struct resp_ctx *rctx; + struct sss_domain_info *dom; + struct ldb_result *initgr_res; + + bool needs_refresh; + unsigned int group_iter; + bool is_original_primary_group_request; + + struct ldb_result *initgr_named_res; +}; + +static void resp_resolve_group_done(struct tevent_req *subreq); +static errno_t resp_resolve_group_next(struct tevent_req *req); +static errno_t resp_resolve_group_trigger_request(struct tevent_req *req, const char *attr_name); +static errno_t resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state); + +struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + struct ldb_result *initgr_res) +{ + struct resp_resolve_group_names_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct resp_resolve_group_names_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + state->ev = ev; + state->rctx = rctx; + state->dom = dom; + state->initgr_res = initgr_res; + state->is_original_primary_group_request = true; + + ret = resp_resolve_group_next(req); + if (ret == EOK) { + goto immediate; + } else if (ret != EAGAIN) { + goto immediate; + } + + return req; + +immediate: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + return req; +} + +static bool +resp_resolve_group_needs_refresh(struct resp_resolve_group_names_state *state) +{ + /* Refresh groups that have a non-zero GID, + * but are marked as non-POSIX + */ + bool is_posix; + uint64_t gid; + struct ldb_message *group_msg; + + group_msg = state->initgr_res->msgs[state->group_iter]; + + is_posix = ldb_msg_find_attr_as_bool(group_msg, SYSDB_POSIX, false); + gid = ldb_msg_find_attr_as_uint64(group_msg, SYSDB_GIDNUM, 0); + + if (is_posix == false && gid != 0) { + return true; + } + + return false; +} + +static errno_t resp_resolve_group_next(struct tevent_req *req) +{ + struct resp_resolve_group_names_state *state; + errno_t ret; + + state = tevent_req_data(req, struct resp_resolve_group_names_state); + + while (state->group_iter < state->initgr_res->count + && !resp_resolve_group_needs_refresh(state)) { + state->group_iter++; + } + + if (state->group_iter >= state->initgr_res->count) { + /* All groups were refreshed */ + return EOK; + } + + if(state->group_iter == 0 && + state->is_original_primary_group_request == true) { + ret = resp_resolve_group_trigger_request(req, + SYSDB_PRIMARY_GROUP_GIDNUM); + + /* If auto_private_groups is disabled then + * resp_resolve_group_trigger_request will return EINVAL, but this + * doesn't mean a failure. Thus, the search should continue with the + * next element. + */ + if(ret == EINVAL) { + state->is_original_primary_group_request = false; + return resp_resolve_group_trigger_request(req, SYSDB_GIDNUM); + } else { + return ret; + } + } else { + return resp_resolve_group_trigger_request(req, SYSDB_GIDNUM); + } +} + +static errno_t resp_resolve_group_trigger_request(struct tevent_req *req, + const char *attr_name) +{ + struct cache_req_data *data; + uint64_t gid; + struct tevent_req *subreq; + struct resp_resolve_group_names_state *state; + + state = tevent_req_data(req, struct resp_resolve_group_names_state); + + gid = ldb_msg_find_attr_as_uint64(state->initgr_res->msgs[state->group_iter], + attr_name, 0); + if (gid == 0) { + return EINVAL; + } + + data = cache_req_data_id_attrs(state, CACHE_REQ_GROUP_BY_ID, gid, NULL); + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n"); + return ENOMEM; + } + + subreq = cache_req_send(state, + state->ev, + state->rctx, + state->rctx->ncache, + 0, + CACHE_REQ_ANY_DOM, + NULL, + data); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n"); + return ENOMEM; + } + + tevent_req_set_callback(subreq, resp_resolve_group_done, req); + return EAGAIN; +} + +static void resp_resolve_group_done(struct tevent_req *subreq) +{ + struct resp_resolve_group_names_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct resp_resolve_group_names_state); + + ret = cache_req_single_domain_recv(state, subreq, NULL); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh group\n"); + /* Try to refresh the others on error */ + } + + if(state->group_iter == 0 && + state->is_original_primary_group_request == true) { + state->is_original_primary_group_request = false; + } else { + state->group_iter++; + } + state->needs_refresh = true; + + ret = resp_resolve_group_next(req); + if (ret == EOK) { + ret = resp_resolve_group_reread_names(state); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + DEBUG(SSSDBG_TRACE_FUNC, "All groups are refreshed, done\n"); + tevent_req_done(req); + return; + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } + + /* Continue refreshing.. */ +} + +static errno_t +resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state) +{ + errno_t ret; + const char *username; + + /* re-read reply in case any groups were renamed */ + /* msgs[0] is the user entry */ + username = sss_view_ldb_msg_find_attr_as_string(state->dom, + state->initgr_res->msgs[0], + SYSDB_NAME, + NULL); + if (username == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n"); + return EINVAL; + } + + ret = sysdb_initgroups_with_views(state, + state->dom, + username, + &state->initgr_named_res); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot re-read the group names\n"); + return ret; + } + + return EOK; +} + +int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ldb_result **_initgr_named_res) +{ + struct resp_resolve_group_names_state *state = NULL; + state = tevent_req_data(req, struct resp_resolve_group_names_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_initgr_named_res = talloc_steal(mem_ctx, state->initgr_named_res); + return EOK; +} + +const char * +sss_resp_get_shell_override(struct ldb_message *msg, + struct resp_ctx *rctx, + struct sss_domain_info *domain) +{ + const char *shell; + int i; + + /* Here we skip the files provider as it should always return *only* + * what's in the files and nothing else. */ + if (!is_files_provider(domain)) { + /* Check whether we are unconditionally overriding + * the server for the login shell. */ + if (domain->override_shell) { + return domain->override_shell; + } else if (rctx->override_shell) { + return rctx->override_shell; + } + } + + shell = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_SHELL, + NULL); + if (shell == NULL) { + /* Check whether there is a default shell specified */ + if (domain->default_shell) { + return domain->default_shell; + } else if (rctx->default_shell) { + return rctx->default_shell; + } + + return ""; + } + + if (rctx->allowed_shells == NULL && rctx->vetoed_shells == NULL) { + return shell; + } + + if (rctx->vetoed_shells) { + for (i = 0; rctx->vetoed_shells[i]; i++) { + if (strcmp(rctx->vetoed_shells[i], shell) == 0) { + DEBUG(SSSDBG_FUNC_DATA, + "The shell '%s' is vetoed. Using fallback.\n", + shell); + return rctx->shell_fallback; + } + } + } + + if (rctx->etc_shells) { + for (i = 0; rctx->etc_shells[i]; i++) { + if (strcmp(shell, rctx->etc_shells[i]) == 0) { + DEBUG(SSSDBG_TRACE_ALL, + "Shell %s found in /etc/shells\n", shell); + break; + } + } + + if (rctx->etc_shells[i]) { + DEBUG(SSSDBG_TRACE_ALL, "Using original shell '%s'\n", shell); + return shell; + } + } + + if (rctx->allowed_shells) { + if (strcmp(rctx->allowed_shells[0], "*") == 0) { + DEBUG(SSSDBG_FUNC_DATA, + "The shell '%s' is allowed but does not exist. " + "Using fallback\n", shell); + return rctx->shell_fallback; + } else { + for (i = 0; rctx->allowed_shells[i]; i++) { + if (strcmp(rctx->allowed_shells[i], shell) == 0) { + DEBUG(SSSDBG_FUNC_DATA, + "The shell '%s' is allowed but does not exist. " + "Using fallback\n", shell); + return rctx->shell_fallback; + } + } + } + } + + DEBUG(SSSDBG_FUNC_DATA, + "The shell '%s' is not allowed and does not exist.\n", shell); + + return NOLOGIN_SHELL; +} |