summaryrefslogtreecommitdiffstats
path: root/src/providers/ipa/ipa_hbac_hosts.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/ipa/ipa_hbac_hosts.c')
-rw-r--r--src/providers/ipa/ipa_hbac_hosts.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/src/providers/ipa/ipa_hbac_hosts.c b/src/providers/ipa/ipa_hbac_hosts.c
new file mode 100644
index 000000000..4e753f374
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_hosts.c
@@ -0,0 +1,524 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@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 "db/sysdb.h"
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ldap/sdap_async.h"
+
+struct ipa_hbac_host_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+ struct sdap_options *opts;
+ const char *search_base;
+ const char **attrs;
+
+ /* Return values */
+ size_t host_count;
+ struct sysdb_attrs **hosts;
+
+ size_t hostgroup_count;
+ struct sysdb_attrs **hostgroups;
+};
+
+static void
+ipa_hbac_host_info_done(struct tevent_req *subreq);
+
+static void
+ipa_hbac_hostgroup_info_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sdap_options *opts,
+ const char *search_base)
+{
+ errno_t ret;
+ struct ipa_hbac_host_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ char *host_filter;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_host_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->sh = sh;
+ state->opts = opts;
+ state->search_base = search_base;
+
+ host_filter = talloc_asprintf(state, "(objectClass=%s)", IPA_HOST);
+ if (host_filter == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ state->attrs = talloc_array(state, const char *, 8);
+ if (state->attrs == NULL) {
+ DEBUG(1, ("Failed to allocate host attribute list.\n"));
+ ret = ENOMEM;
+ goto immediate;
+ }
+ state->attrs[0] = "objectClass";
+ state->attrs[1] = IPA_HOST_SERVERHOSTNAME;
+ state->attrs[2] = IPA_HOST_FQDN;
+ state->attrs[3] = IPA_UNIQUE_ID;
+ state->attrs[4] = IPA_MEMBER;
+ state->attrs[5] = IPA_MEMBEROF;
+ state->attrs[6] = IPA_CN;
+ state->attrs[7] = NULL;
+
+ subreq = sdap_get_generic_send(state, ev, opts, sh, search_base,
+ LDAP_SCOPE_SUB, host_filter,
+ state->attrs, NULL, 0,
+ dp_opt_get_int(opts->basic,
+ SDAP_ENUM_SEARCH_TIMEOUT));
+ if (subreq == NULL) {
+ DEBUG(1, ("Error requesting host info\n"));
+ ret = EIO;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ipa_hbac_host_info_done, req);
+
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void
+ipa_hbac_host_info_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct ipa_hbac_host_state *state =
+ tevent_req_data(req, struct ipa_hbac_host_state);
+ char *hostgroup_filter;
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->host_count,
+ &state->hosts);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->host_count == 0) {
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+
+ ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
+ state->host_count,
+ state->hosts);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ hostgroup_filter = talloc_asprintf(state, "(objectClass=%s)",
+ IPA_HOSTGROUP);
+ if (hostgroup_filter == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /* Look up host groups */
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ state->search_base, LDAP_SCOPE_SUB,
+ hostgroup_filter, state->attrs, NULL, 0,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_ENUM_SEARCH_TIMEOUT));
+ if (subreq == NULL) {
+ DEBUG(1, ("Error requesting host info\n"));
+ tevent_req_error(req, EIO);
+ return;
+ }
+ tevent_req_set_callback(subreq, ipa_hbac_hostgroup_info_done, req);
+}
+
+static void
+ipa_hbac_hostgroup_info_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct ipa_hbac_host_state *state =
+ tevent_req_data(req, struct ipa_hbac_host_state);
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->hostgroup_count,
+ &state->hostgroups);
+ talloc_zfree(subreq);
+ if (ret != EOK) goto done;
+
+ ret = replace_attribute_name(IPA_MEMBER, SYSDB_ORIG_MEMBER,
+ state->hostgroup_count,
+ state->hostgroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ goto done;
+ }
+
+ ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
+ state->hostgroup_count,
+ state->hostgroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ DEBUG(3, ("Error [%d][%s]\n", ret, strerror(ret)));
+ tevent_req_error(req, ret);
+ }
+}
+
+errno_t
+ipa_hbac_host_info_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *host_count,
+ struct sysdb_attrs ***hosts,
+ size_t *hostgroup_count,
+ struct sysdb_attrs ***hostgroups)
+{
+ size_t c;
+ struct ipa_hbac_host_state *state =
+ tevent_req_data(req, struct ipa_hbac_host_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *host_count = state->host_count;
+ *hosts = talloc_steal(mem_ctx, state->hosts);
+ for (c = 0; c < state->host_count; c++) {
+ /* Guarantee the memory heirarchy of the list */
+ talloc_steal(state->hosts, state->hosts[c]);
+ }
+
+ *hostgroup_count = state->hostgroup_count;
+ *hostgroups = talloc_steal(mem_ctx, state->hostgroups);
+
+ return EOK;
+}
+
+/*
+ * Functions to convert sysdb_attrs to the hbac_rule format
+ */
+static errno_t hbac_host_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ const char *category_attr,
+ const char *member_attr,
+ size_t *host_count,
+ struct hbac_rule_element **hosts)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct hbac_rule_element *new_hosts;
+ const char *attrs[] = { IPA_HOST_FQDN, NULL };
+ struct ldb_message_element *el;
+ size_t num_hosts = 0;
+ size_t num_hostgroups = 0;
+ size_t i;
+ char *member_dn;
+ char *filter;
+ size_t count;
+ struct ldb_message **msgs;
+ const char *name;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ new_hosts = talloc_zero(tmp_ctx, struct hbac_rule_element);
+ if (new_hosts == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First check for host category */
+ ret = hbac_get_category(rule_attrs, category_attr, &new_hosts->category);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not identify host categories\n"));
+ goto done;
+ }
+ if (new_hosts->category & HBAC_CATEGORY_ALL) {
+ /* Short-cut to the exit */
+ ret = EOK;
+ goto done;
+ }
+
+ /* Get the list of DNs from the member_attr */
+ ret = sysdb_attrs_get_el(rule_attrs, member_attr, &el);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ goto done;
+ }
+ if (ret == ENOENT || el->num_values == 0) {
+ el->num_values = 0;
+ DEBUG(4, ("No host specified, rule will never apply.\n"));
+ }
+
+ /* Assume maximum size; We'll trim it later */
+ new_hosts->names = talloc_array(new_hosts,
+ const char *,
+ el->num_values +1);
+ if (new_hosts->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_hosts->groups = talloc_array(new_hosts,
+ const char *,
+ el->num_values + 1);
+ if (new_hosts->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ ret = sss_filter_sanitize(tmp_ctx,
+ (const char *)el->values[i].data,
+ &member_dn);
+ if (ret != EOK) goto done;
+
+ filter = talloc_asprintf(member_dn, "(%s=%s)",
+ SYSDB_ORIG_DN, member_dn);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First check if this is a specific host */
+ ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter,
+ HBAC_HOSTS_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple hosts. Skipping \n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single host. Get the hostname */
+ name = ldb_msg_find_attr_as_string(msgs[0],
+ IPA_HOST_FQDN,
+ NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_hosts->names[num_hosts] = talloc_strdup(new_hosts->names,
+ name);
+ if (new_hosts->names[num_hosts] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(8, ("Added host [%s] to rule [%s]\n",
+ name, rule_name));
+ num_hosts++;
+ } else { /* ret == ENOENT */
+ /* Check if this is a hostgroup */
+ ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter,
+ HBAC_HOSTGROUPS_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple hostgroups. "
+ "Skipping\n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single group. Get the groupname */
+ name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_hosts->groups[num_hostgroups] =
+ talloc_strdup(new_hosts->groups, name);
+ if (new_hosts->groups[num_hostgroups] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(8, ("Added hostgroup [%s] to rule [%s]\n",
+ name, rule_name));
+ num_hostgroups++;
+ } else { /* ret == ENOENT */
+ /* Neither a host nor a hostgroup? Skip it */
+ DEBUG(1, ("[%s] does not map to either a host or hostgroup. "
+ "Skipping\n", member_dn));
+ }
+ }
+ talloc_zfree(member_dn);
+ }
+ new_hosts->names[num_hosts] = NULL;
+ new_hosts->groups[num_hostgroups] = NULL;
+
+ /* Shrink the arrays down to their real sizes */
+ new_hosts->names = talloc_realloc(new_hosts, new_hosts->names,
+ const char *, num_hosts + 1);
+ if (new_hosts->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_hosts->groups = talloc_realloc(new_hosts, new_hosts->groups,
+ const char *, num_hostgroups + 1);
+ if (new_hosts->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *hosts = talloc_steal(mem_ctx, new_hosts);
+ if (host_count) *host_count = num_hosts;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+hbac_thost_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **thosts)
+{
+ DEBUG(7, ("Processing target hosts for rule [%s]\n", rule_name));
+
+ return hbac_host_attrs_to_rule(mem_ctx, sysdb, domain,
+ rule_name, rule_attrs,
+ IPA_HOST_CATEGORY, IPA_MEMBER_HOST,
+ NULL, thosts);
+}
+
+errno_t
+hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **source_hosts)
+{
+ errno_t ret;
+ size_t host_count;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ size_t idx;
+ struct ldb_message_element *el;
+ struct hbac_rule_element *shosts;
+
+ DEBUG(7, ("Processing source hosts for rule [%s]\n", rule_name));
+
+ ret = hbac_host_attrs_to_rule(tmp_ctx, sysdb, domain,
+ rule_name, rule_attrs,
+ IPA_SOURCE_HOST_CATEGORY, IPA_SOURCE_HOST,
+ &host_count, &shosts);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (shosts->category & HBAC_CATEGORY_ALL) {
+ /* All hosts (including external) are
+ * allowed.
+ */
+ goto done;
+ }
+
+ /* Include external (non-IPA-managed) source hosts */
+ ret = sysdb_attrs_get_el(rule_attrs, IPA_EXTERNAL_HOST, &el);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && el->num_values == 0) ret = ENOENT;
+
+ if (ret != ENOENT) {
+ shosts->names = talloc_realloc(shosts, shosts->names, const char *,
+ host_count + el->num_values + 1);
+ if (shosts->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (idx = host_count; idx <= host_count + el->num_values; idx++) {
+ shosts->names[idx] =
+ talloc_strdup(shosts->names,
+ (const char *)el->values[idx].data);
+ if (shosts->names[idx] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(8, ("Added external source host [%s] to rule [%s]\n",
+ shosts->names[idx], rule_name));
+ }
+ shosts->names[idx] = NULL;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *source_hosts = talloc_steal(mem_ctx, shosts);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}