summaryrefslogtreecommitdiffstats
path: root/src/responder
diff options
context:
space:
mode:
authorJan Zeleny <jzeleny@redhat.com>2012-03-29 04:30:34 -0400
committerStephen Gallagher <sgallagh@redhat.com>2012-04-24 09:19:42 -0400
commitc0f9698cd951b7223f251ff2511c4b22a6e4ba60 (patch)
tree4c9928cca58793ac1cbad612e9a4d3c3c8210a6e /src/responder
parentd3f2fd9cb21cc10dce663a2f7d0deda07074e44e (diff)
downloadsssd-c0f9698cd951b7223f251ff2511c4b22a6e4ba60.tar.gz
sssd-c0f9698cd951b7223f251ff2511c4b22a6e4ba60.tar.xz
sssd-c0f9698cd951b7223f251ff2511c4b22a6e4ba60.zip
Responder part of the subdomain retrieval work
Diffstat (limited to 'src/responder')
-rw-r--r--src/responder/common/responder.h11
-rw-r--r--src/responder/common/responder_common.c8
-rw-r--r--src/responder/common/responder_get_domains.c340
3 files changed, 359 insertions, 0 deletions
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 1309c14da..f331fee33 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 52b271b95..661483872 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 000000000..702593f67
--- /dev/null
+++ b/src/responder/common/responder_get_domains.c
@@ -0,0 +1,340 @@
+/*
+ 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 "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;
+}