summaryrefslogtreecommitdiffstats
path: root/src/providers/ipa/ipa_idmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/ipa/ipa_idmap.c')
-rw-r--r--src/providers/ipa/ipa_idmap.c521
1 files changed, 521 insertions, 0 deletions
diff --git a/src/providers/ipa/ipa_idmap.c b/src/providers/ipa/ipa_idmap.c
new file mode 100644
index 0000000..5d8d56b
--- /dev/null
+++ b/src/providers/ipa/ipa_idmap.c
@@ -0,0 +1,521 @@
+/*
+ SSSD
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2013 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 "providers/ldap/sdap_idmap.h"
+#include "providers/ipa/ipa_common.h"
+#include "util/util_sss_idmap.h"
+
+static errno_t ipa_idmap_check_posix_child(struct sdap_idmap_ctx *idmap_ctx,
+ const char *dom_name,
+ const char *dom_sid_str,
+ size_t range_count,
+ struct range_info **range_list)
+{
+ bool has_algorithmic_mapping;
+ enum idmap_error_code err;
+ struct sss_domain_info *dom;
+ struct sss_domain_info *forest_root;
+ size_t c;
+ struct sss_idmap_range range;
+ struct range_info *r;
+ char *range_id;
+ TALLOC_CTX *tmp_ctx;
+ bool found = false;
+ int ret;
+
+ err = sss_idmap_domain_has_algorithmic_mapping(idmap_ctx->map, dom_sid_str,
+ &has_algorithmic_mapping);
+ if (err == IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Idmap of domain [%s] already known, nothing to do.\n",
+ dom_sid_str);
+ return EOK;
+ } else {
+ err = sss_idmap_domain_by_name_has_algorithmic_mapping(idmap_ctx->map,
+ dom_name,
+ &has_algorithmic_mapping);
+ if (err == IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Idmap of domain [%s] already known, nothing to do.\n",
+ dom_sid_str);
+ return EOK;
+ }
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Trying to add idmap for domain [%s].\n",
+ dom_sid_str);
+
+ if (err != IDMAP_SID_UNKNOWN && err != IDMAP_NAME_UNKNOWN) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_idmap_domain_has_algorithmic_mapping failed.\n");
+ return EINVAL;
+ }
+
+ dom = find_domain_by_sid(idmap_ctx->id_ctx->be->domain, dom_sid_str);
+ if (dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "find_domain_by_sid failed with SID [%s].\n", dom_sid_str);
+ return EINVAL;
+ }
+
+ if (dom->forest == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No forest available for domain [%s].\n",
+ dom_sid_str);
+ return EINVAL;
+ }
+
+ forest_root = find_domain_by_name(idmap_ctx->id_ctx->be->domain,
+ dom->forest, true);
+ if (forest_root == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "find_domain_by_name failed to find forest root [%s].\n",
+ dom->forest);
+ return ENOENT;
+ }
+
+ if (forest_root->domain_id == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Forest root [%s] does not have a SID.\n",
+ dom->forest);
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ for (c = 0; c < range_count; c++) {
+ r = range_list[c];
+ if (r->trusted_dom_sid != NULL
+ && strcmp(r->trusted_dom_sid, forest_root->domain_id) == 0) {
+
+ if (r->range_type == NULL
+ || strcmp(r->range_type, IPA_RANGE_AD_TRUST_POSIX) != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Forest root does not have range type [%s].\n",
+ IPA_RANGE_AD_TRUST_POSIX);
+ ret = EINVAL;
+ goto done;
+ }
+
+ range.min = r->base_id;
+ range.max = r->base_id + r->id_range_size -1;
+ range_id = talloc_asprintf(tmp_ctx, "%s-%s", dom_sid_str, r->name);
+ if (range_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ err = sss_idmap_add_domain_ex(idmap_ctx->map, dom_name, dom_sid_str,
+ &range, range_id, 0, true);
+ if (err != IDMAP_SUCCESS && err != IDMAP_COLLISION) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not add range [%s] to ID map\n", range_id);
+ ret = EIO;
+ goto done;
+ }
+
+ found = true;
+ }
+ }
+
+ if (!found) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No idrange found for forest root [%s].\n",
+ forest_root->domain_id);
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t get_idmap_data_from_range(struct range_info *r, char *domain_name,
+ char **_name, char **_sid, uint32_t *_rid,
+ struct sss_idmap_range *_range,
+ bool *_external_mapping)
+{
+ if (r->range_type == NULL) {
+ /* Older IPA servers might not have the range_type attribute, but
+ * only support local ranges and trusts with algorithmic mapping. */
+
+ if (r->trusted_dom_sid == NULL && r->secondary_base_rid != 0) {
+ /* local IPA domain */
+ *_rid = 0;
+ *_external_mapping = true;
+ *_name = domain_name;
+ *_sid = NULL;
+ } else if (r->trusted_dom_sid != NULL
+ && r->secondary_base_rid == 0) {
+ /* trusted domain */
+ *_rid = r->base_rid;
+ *_external_mapping = false;
+ *_name = r->trusted_dom_sid;
+ *_sid = r->trusted_dom_sid;
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot determine range type, " \
+ "for id range [%s].\n",
+ r->name);
+ return EINVAL;
+ }
+ } else {
+ if (strcmp(r->range_type, IPA_RANGE_LOCAL) == 0) {
+ *_rid = 0;
+ *_external_mapping = true;
+ *_name = domain_name;
+ *_sid = NULL;
+ } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST_POSIX) == 0) {
+ *_rid = 0;
+ *_external_mapping = true;
+ *_name = r->trusted_dom_sid;
+ *_sid = r->trusted_dom_sid;
+ } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST) == 0) {
+ *_rid = r->base_rid;
+ *_external_mapping = false;
+ *_name = r->trusted_dom_sid;
+ *_sid = r->trusted_dom_sid;
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Range type [%s] of id range " \
+ "[%s] not supported.\n", \
+ r->range_type, r->name);
+ return ERR_UNSUPPORTED_RANGE_TYPE;
+ }
+ }
+
+ _range->min = r->base_id;
+ _range->max = r->base_id + r->id_range_size -1;
+
+ return EOK;
+}
+
+errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+ char *domain_name,
+ size_t count,
+ struct sysdb_attrs **reply,
+ struct range_info ***_range_list)
+{
+ struct range_info **range_list = NULL;
+ struct range_info *r;
+ const char *value;
+ size_t c;
+ size_t rc = 0;
+ size_t d;
+ int ret;
+ enum idmap_error_code err;
+ char *name1;
+ char *name2;
+ char *sid1;
+ char *sid2;
+ uint32_t rid1;
+ uint32_t rid2;
+ struct sss_idmap_range range1;
+ struct sss_idmap_range range2;
+ bool mapping1;
+ bool mapping2;
+
+ range_list = talloc_array(mem_ctx, struct range_info *, count + 1);
+ if (range_list == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ return ENOMEM;
+ }
+
+ for (c = 0; c < count; c++) {
+ r = talloc_zero(range_list, struct range_info);
+ if (r == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(reply[c], IPA_CN, &value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ r->name = talloc_strdup(r, value);
+ if (r->name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(reply[c], IPA_TRUSTED_DOMAIN_SID, &value);
+ if (ret == EOK) {
+ r->trusted_dom_sid = talloc_strdup(r, value);
+ if (r->trusted_dom_sid == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(reply[c], IPA_BASE_ID,
+ &r->base_id);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(reply[c], IPA_ID_RANGE_SIZE,
+ &r->id_range_size);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(reply[c], IPA_BASE_RID,
+ &r->base_rid);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(reply[c], IPA_SECONDARY_BASE_RID,
+ &r->secondary_base_rid);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(reply[c], IPA_RANGE_TYPE, &value);
+ if (ret == EOK) {
+ r->range_type = talloc_strdup(r, value);
+ if (r->range_type == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (ret == ENOENT) {
+ /* Older IPA servers might not have the range_type attribute, but
+ * only support local ranges and trusts with algorithmic mapping. */
+ if (r->trusted_dom_sid == NULL) {
+ r->range_type = talloc_strdup(r, IPA_RANGE_LOCAL);
+ } else {
+ r->range_type = talloc_strdup(r, IPA_RANGE_AD_TRUST);
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+ if (r->range_type == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(reply[c], IPA_ID_RANGE_MPG, &value);
+ if (ret == EOK) {
+ r->mpg_mode = str_to_domain_mpg_mode(value);
+ } else if (ret == ENOENT) {
+ r->mpg_mode = MPG_DEFAULT;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = get_idmap_data_from_range(r, domain_name, &name1, &sid1, &rid1,
+ &range1, &mapping1);
+ if (ret == ERR_UNSUPPORTED_RANGE_TYPE) {
+ talloc_free(r);
+ continue;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_idmap_data_from_range failed.\n");
+ goto done;
+ }
+ for (d = 0; d < rc; d++) {
+ ret = get_idmap_data_from_range(range_list[d], domain_name, &name2,
+ &sid2, &rid2, &range2, &mapping2);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "get_idmap_data_from_range failed.\n");
+ goto done;
+ }
+
+ err = sss_idmap_check_collision_ex(name1, sid1, &range1, rid1,
+ r->name, mapping1,
+ name2, sid2, &range2, rid2,
+ range_list[d]->name, mapping2);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Collision of ranges [%s] and [%s] detected.\n",
+ r->name, range_list[d]->name);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ range_list[rc++] = r;
+ }
+
+ range_list[rc] = NULL;
+
+ *_range_list = range_list;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(range_list);
+ }
+
+ return ret;
+}
+
+errno_t ipa_idmap_get_ranges_from_sysdb(struct sdap_idmap_ctx *idmap_ctx,
+ const char *dom_name,
+ const char *dom_sid_str,
+ bool allow_collisions)
+{
+ int ret;
+ size_t range_count;
+ struct range_info **range_list;
+ TALLOC_CTX *tmp_ctx;
+ size_t c;
+ enum idmap_error_code err;
+ struct sss_idmap_range range;
+ uint32_t rid;
+ bool external_mapping;
+ char *name;
+ char *sid;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_get_ranges(tmp_ctx, idmap_ctx->id_ctx->be->domain->sysdb,
+ &range_count, &range_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_ranges failed.\n");
+ goto done;
+ }
+
+ for (c = 0; c < range_count; c++) {
+ ret = get_idmap_data_from_range(range_list[c],
+ idmap_ctx->id_ctx->be->domain->name,
+ &name, &sid, &rid, &range,
+ &external_mapping);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_idmap_data_from_range failed for " \
+ "id range [%s], skipping.\n",
+ range_list[c]->name);
+ continue;
+ }
+
+ err = sss_idmap_add_domain_ex(idmap_ctx->map, name, sid, &range,
+ range_list[c]->name, rid,
+ external_mapping);
+ if (err != IDMAP_SUCCESS) {
+ if (!allow_collisions || err != IDMAP_COLLISION) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not add range [%s] to ID map\n",
+ range_list[c]->name);
+ ret = EIO;
+ goto done;
+ }
+ }
+ }
+
+ if (dom_name != NULL || dom_sid_str != NULL) {
+ ret = ipa_idmap_check_posix_child(idmap_ctx, dom_name, dom_sid_str,
+ range_count, range_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_idmap_check_posix_child failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t ipa_idmap_find_new_domain(struct sdap_idmap_ctx *idmap_ctx,
+ const char *dom_name,
+ const char *dom_sid_str)
+{
+ return ipa_idmap_get_ranges_from_sysdb(idmap_ctx, dom_name, dom_sid_str,
+ true);
+}
+
+errno_t ipa_idmap_init(TALLOC_CTX *mem_ctx,
+ struct sdap_id_ctx *id_ctx,
+ struct sdap_idmap_ctx **_idmap_ctx)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ enum idmap_error_code err;
+ struct sdap_idmap_ctx *idmap_ctx = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ idmap_ctx = talloc_zero(tmp_ctx, struct sdap_idmap_ctx);
+ if (!idmap_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+ idmap_ctx->id_ctx = id_ctx;
+ idmap_ctx->find_new_domain = ipa_idmap_find_new_domain;
+
+ /* Initialize the map */
+ err = sss_idmap_init(sss_idmap_talloc, idmap_ctx,
+ sss_idmap_talloc_free,
+ &idmap_ctx->map);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize the ID map: [%s]\n",
+ idmap_error_string(err));
+ if (err == IDMAP_OUT_OF_MEMORY) {
+ ret = ENOMEM;
+ } else {
+ ret = EINVAL;
+ }
+ goto done;
+ }
+
+ ret = ipa_idmap_get_ranges_from_sysdb(idmap_ctx, NULL, NULL, false);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_idmap_get_ranges_from_sysdb failed.\n");
+ goto done;
+ }
+
+ *_idmap_ctx = talloc_steal(mem_ctx, idmap_ctx);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}