From c0f9698cd951b7223f251ff2511c4b22a6e4ba60 Mon Sep 17 00:00:00 2001 From: Jan Zeleny Date: Thu, 29 Mar 2012 04:30:34 -0400 Subject: Responder part of the subdomain retrieval work --- src/responder/common/responder.h | 11 + src/responder/common/responder_common.c | 8 + src/responder/common/responder_get_domains.c | 340 +++++++++++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 src/responder/common/responder_get_domains.c (limited to 'src/responder/common') diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h index 1309c14d..f331fee3 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -86,6 +86,7 @@ struct resp_ctx { struct be_conn *be_conns; struct sss_domain_info *domains; + int domains_timeout; struct sysdb_ctx_list *db_list; struct sss_cmd_table *sss_cmds; @@ -96,6 +97,8 @@ struct resp_ctx { hash_table_t *dp_request_table; + struct timeval get_domains_last_call; + void *pvt_ctx; }; @@ -273,4 +276,12 @@ bool sss_utf8_check(const uint8_t *s, size_t n); void responder_set_fd_limit(rlim_t fd_limit); +#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); #endif /* __SSS_RESPONDER_H__ */ diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c index 52b271b9..66148387 100644 --- a/src/responder/common/responder_common.c +++ b/src/responder/common/responder_common.c @@ -545,6 +545,14 @@ int sss_process_init(TALLOC_CTX *mem_ctx, rctx->priv_sock_name = sss_priv_pipe_name; rctx->confdb_service_path = confdb_service_path; + ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path, + CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT, + GET_DOMAINS_DEFAULT_TIMEOUT, &rctx->domains_timeout); + if (rctx->domains_timeout < 0) { + DEBUG(SSSDBG_CONF_SETTINGS, ("timeout can't be set to negative value, setting default\n")); + rctx->domains_timeout = GET_DOMAINS_DEFAULT_TIMEOUT; + } + ret = confdb_get_domains(rctx->cdb, &rctx->domains); if (ret != EOK) { DEBUG(0, ("fatal error setting up domain map\n")); diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c new file mode 100644 index 00000000..702593f6 --- /dev/null +++ b/src/responder/common/responder_get_domains.c @@ -0,0 +1,340 @@ +/* + Authors: + Jan Zeleny + + 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 . +*/ + +#include "util/util.h" +#include "responder/common/responder.h" +#include "providers/data_provider.h" +#include "db/sysdb.h" + +struct sss_dp_domains_info { + struct resp_ctx *rctx; + struct sss_domain_info *dom; + const char *hint; + bool force; + + struct sss_dp_req_state *state; +}; + +static DBusMessage *sss_dp_get_domains_msg(void *pvt); +static errno_t get_domains_next(struct tevent_req *req); +static void sss_dp_get_domains_callback(struct tevent_req *subreq); + +static errno_t get_domains_done(struct tevent_req *req); +static errno_t check_last_request(struct resp_ctx *rctx, const char *hint); + +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 sss_dp_domains_info *info; + + req = tevent_req_create(mem_ctx, &info, struct sss_dp_domains_info); + if (req == NULL) { + return NULL; + } + + if (rctx->domains == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("No domains configured.\n")); + ret = EINVAL; + goto done; + } + + 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 done; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_TRACE_FUNC, ("check_domain_request failed with [%d][%s]\n", + ret, strerror(ret))); + goto done; + } + } + + info->rctx = rctx; + info->dom = rctx->domains; + info->force = force; + if (hint != NULL) { + info->hint = hint; + } else { + info->hint = talloc_strdup(info, ""); + if (info->hint == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = get_domains_next(req); + if (ret == EAGAIN) { + ret = EOK; + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + } + + return req; +} + +static errno_t get_domains_next(struct tevent_req *req) +{ + struct sss_dp_domains_info *info; + struct tevent_req *subreq; + struct sss_dp_req_state *state; + errno_t ret; + char *key; + + info = tevent_req_data(req, struct sss_dp_domains_info); + if (info->dom == NULL) { + /* Note that tevent_req_post() is not here. This will + * influence the program only in case that this will + * be the first call of the function (i.e. there is no + * problem when this is called from get_domains_done(), + * it is in fact required). In case no domains are in + * the state, it should be treated as an error one level + * above. + */ + tevent_req_done(req); + return EOK; + } + + subreq = tevent_req_create(info, &state, struct sss_dp_req_state); + if (subreq == NULL) { + return ENOMEM; + } + + key = talloc_asprintf(info, "domains@%s", info->dom->name); + if (key == NULL) { + talloc_free(subreq); + return ENOMEM; + } + + ret = sss_dp_issue_request(info, info->rctx, key, info->dom, + sss_dp_get_domains_msg, info, subreq); + talloc_free(key); + if (ret != EOK) { + talloc_free(subreq); + return ret; + } + + tevent_req_set_callback(subreq, sss_dp_get_domains_callback, req); + + return EAGAIN; +} + +static DBusMessage * +sss_dp_get_domains_msg(void *pvt) +{ + struct sss_dp_domains_info *info; + DBusMessage *msg = NULL; + dbus_bool_t dbret; + + info = talloc_get_type(pvt, struct sss_dp_domains_info); + + msg = dbus_message_new_method_call(NULL, + DP_PATH, + DP_INTERFACE, + DP_METHOD_GETDOMAINS); + if (msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory?!\n")); + return NULL; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Sending get domains request for [%s][%sforced][%s]\n", + info->dom->name, info->force ? "" : "not ", info->hint)); + + /* Send the hint argument to provider as well. This will + * be useful for some cases of transitional trust where + * the server might not know all trusted domains + */ + dbret = dbus_message_append_args(msg, + DBUS_TYPE_BOOLEAN, &info->force, + DBUS_TYPE_STRING, &info->hint, + DBUS_TYPE_INVALID); + if (!dbret) { + DEBUG(SSSDBG_OP_FAILURE ,("Failed to build message\n")); + dbus_message_unref(msg); + return NULL; + } + + return msg; +} + +static void sss_dp_get_domains_callback(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct sss_dp_domains_info *info = tevent_req_data(req, struct sss_dp_domains_info); + errno_t ret; + dbus_uint16_t dp_err; + dbus_uint32_t dp_ret; + char *err_msg; + + /* TODO: handle errors better */ + ret = sss_dp_req_recv(info, subreq, &dp_err, &dp_ret, &err_msg); + talloc_free(subreq); + if (ret != EOK) { + goto fail; + } + + ret = get_domains_done(req); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("get_domains_done failed, " + "trying next domain.\n")); + goto fail; + } + + info->dom = info->dom->next; + ret = get_domains_next(req); + if (ret != EOK && ret != EAGAIN) { + goto fail; + } + + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static errno_t get_domains_done(struct tevent_req *req) +{ + int ret; + size_t c; + struct sss_dp_domains_info *state; + struct sss_domain_info *domain; + struct sss_domain_info **new_sd_list = NULL; + size_t subdomain_count; + struct subdomain_info **subdomains; + + state = tevent_req_data(req, struct sss_dp_domains_info); + domain = state->dom; + + /* Retrieve all subdomains of this domain from sysdb + * and create their struct sss_domain_info representations + */ + ret = sysdb_get_subdomains(domain, domain->sysdb, + &subdomain_count, &subdomains); + if (ret != EOK) { + DEBUG(SSSDBG_FUNC_DATA, ("sysdb_get_subdomains failed.\n")); + goto done; + } + + new_sd_list = talloc_zero_array(domain, struct sss_domain_info *, + subdomain_count); + if (new_sd_list == NULL) { + ret = ENOMEM; + goto done; + } + for (c = 0; c < subdomain_count; c++) { + DEBUG(SSSDBG_FUNC_DATA, ("Adding subdomain [%s] to the domain [%s]!\n", + subdomains[c]->name, domain->name)); + new_sd_list[c] = new_subdomain(new_sd_list, domain, + subdomains[c]->name, + subdomains[c]->flat_name, + subdomains[c]->id); + if (new_sd_list[c] == NULL) { + ret = ENOMEM; + goto done; + } + } + + /* Link all subdomains into single-linked list + * (the list is used when processing all domains) + */ + while (c > 1) { + new_sd_list[c-1]->next = new_sd_list[c]; + --c; + } + + errno = 0; + ret = gettimeofday(&domain->subdomains_last_checked, NULL); + if (ret == -1) { + ret = errno; + goto done; + } + + domain->subdomain_count = subdomain_count; + talloc_zfree(domain->subdomains); + domain->subdomains = new_sd_list; + new_sd_list = NULL; + + ret = EOK; + +done: + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to update sub-domains " + "of domain [%s].\n", domain->name)); + talloc_free(new_sd_list); + } + + return ret; +} + +errno_t sss_dp_get_domains_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +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; + int i; + + diff = now-rctx->get_domains_last_call.tv_sec; + if (diff >= rctx->domains_timeout) { + /* Timeout, expired, fetch domains again */ + return EAGAIN; + } + + if (hint != NULL) { + dom = rctx->domains; + while (dom) { + for (i = 0; i< dom->subdomain_count; i++) { + if (strcasecmp(dom->subdomains[i]->name, hint) == 0) { + diff = now-dom->subdomains_last_checked.tv_sec; + if (diff >= rctx->domains_timeout) { + /* Timeout, expired, fetch domains again */ + return EAGAIN; + } + /* Skip the rest of this domain, but check other domains + * perhaps this subdomain will be also a part of another + * domain where it will need refreshing + */ + break; + } + } + dom = dom->next; + } + } + + return EOK; +} -- cgit