summaryrefslogtreecommitdiffstats
path: root/src/responder/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/responder/common/cache_req/cache_req.c1612
-rw-r--r--src/responder/common/cache_req/cache_req.h521
-rw-r--r--src/responder/common/cache_req/cache_req_data.c519
-rw-r--r--src/responder/common/cache_req/cache_req_domain.c317
-rw-r--r--src/responder/common/cache_req/cache_req_domain.h63
-rw-r--r--src/responder/common/cache_req/cache_req_plugin.h331
-rw-r--r--src/responder/common/cache_req/cache_req_private.h227
-rw-r--r--src/responder/common/cache_req/cache_req_result.c274
-rw-r--r--src/responder/common/cache_req/cache_req_search.c643
-rw-r--r--src/responder/common/cache_req/cache_req_sr_overlay.c347
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c161
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c155
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c187
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_common.c192
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_groups.c93
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c126
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c126
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_svc.c106
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_users.c93
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_group_by_filter.c171
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_group_by_id.c246
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_group_by_name.c227
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c242
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c130
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c174
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c170
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c174
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c170
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c160
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_object_by_id.c236
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_object_by_name.c238
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_object_by_sid.c202
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c164
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c143
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_svc_by_name.c185
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_svc_by_port.c159
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_cert.c127
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_filter.c176
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_id.c246
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_name.c256
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_upn.c158
-rw-r--r--src/responder/common/negcache.c1417
-rw-r--r--src/responder/common/negcache.h174
-rw-r--r--src/responder/common/negcache_files.c105
-rw-r--r--src/responder/common/negcache_files.h35
-rw-r--r--src/responder/common/responder.h450
-rw-r--r--src/responder/common/responder_cmd.c302
-rw-r--r--src/responder/common/responder_common.c2001
-rw-r--r--src/responder/common/responder_dp.c460
-rw-r--r--src/responder/common/responder_get_domains.c841
-rw-r--r--src/responder/common/responder_iface.c161
-rw-r--r--src/responder/common/responder_packet.c364
-rw-r--r--src/responder/common/responder_packet.h51
-rw-r--r--src/responder/common/responder_utils.c527
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, &current_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;
+}