summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am8
-rw-r--r--src/providers/ldap/sdap_idmap.c282
-rw-r--r--src/providers/ldap/sdap_idmap.h52
3 files changed, 340 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index d31515e9b..348ad2787 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1096,6 +1096,8 @@ libsss_ldap_common_la_SOURCES = \
src/providers/ldap/sdap_child_helpers.c \
src/providers/ldap/sdap_fd_events.c \
src/providers/ldap/sdap_id_op.c \
+ src/providers/ldap/sdap_idmap.c \
+ src/providers/ldap/sdap_idmap.h \
src/providers/ldap/sdap.c
if BUILD_SUDO
@@ -1140,7 +1142,8 @@ libsss_ldap_la_LIBADD = \
$(DHASH_LIBS) \
$(KRB5_LIBS) \
libsss_util.la \
- libsss_ldap_common.la
+ libsss_ldap_common.la \
+ libsss_idmap.la
libsss_ldap_la_LDFLAGS = \
-avoid-version \
-module
@@ -1231,7 +1234,8 @@ libsss_ipa_la_LIBADD = \
libsss_util.la \
libsss_ldap_common.la \
libsss_krb5_common.la \
- libipa_hbac.la
+ libipa_hbac.la \
+ libsss_idmap.la
libsss_ipa_la_LDFLAGS = \
-avoid-version \
-module
diff --git a/src/providers/ldap/sdap_idmap.c b/src/providers/ldap/sdap_idmap.c
new file mode 100644
index 000000000..ea65195ac
--- /dev/null
+++ b/src/providers/ldap/sdap_idmap.c
@@ -0,0 +1,282 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2012 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/dlinklist.h"
+#include "util/murmurhash3.h"
+#include "providers/ldap/sdap_idmap.h"
+
+static void *
+sdap_idmap_talloc(size_t size, void *pvt)
+{
+ return talloc_size(pvt, size);
+}
+
+static void
+sdap_idmap_talloc_free(void *ptr, void *pvt)
+{
+ talloc_free(ptr);
+}
+
+errno_t
+sdap_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;
+ size_t i;
+ struct ldb_result *res;
+ const char *dom_name;
+ const char *sid_str;
+ id_t slice_num;
+ struct sdap_idmap_ctx *idmap_ctx = NULL;
+ struct sysdb_ctx *sysdb = id_ctx->be->sysdb;
+
+ 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;
+
+ /* Initialize the map */
+ err = sss_idmap_init(sdap_idmap_talloc, idmap_ctx,
+ sdap_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;
+ }
+
+ /* Read in any existing mappings from the cache */
+ ret = sysdb_idmap_get_mappings(tmp_ctx, sysdb, &res);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("Could not read ID mappings from the cache: [%s]\n",
+ strerror(ret)));
+ goto done;
+ }
+
+ if (ret == EOK && res->count > 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ ("Initializing [%d] domains for ID-mapping\n", res->count));
+
+ for (i = 0; i < res->count; i++) {
+ dom_name = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_NAME,
+ NULL);
+ if (!dom_name) {
+ /* This should never happen */
+ ret = EINVAL;
+ goto done;
+ }
+
+ sid_str = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_IDMAP_SID_ATTR,
+ NULL);
+ if (!sid_str) {
+ /* This should never happen */
+ ret = EINVAL;
+ goto done;
+ }
+
+ slice_num = ldb_msg_find_attr_as_int(res->msgs[i],
+ SYSDB_IDMAP_SLICE_ATTR,
+ -1);
+ if (slice_num == -1) {
+ /* This should never happen */
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sdap_idmap_add_domain(idmap_ctx, dom_name,
+ sid_str, slice_num);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Could not add domain [%s][%s][%u] to ID map: [%s]\n",
+ dom_name, sid_str, slice_num, strerror(ret)));
+ goto done;
+ }
+ }
+ }
+
+ *_idmap_ctx = talloc_steal(mem_ctx, idmap_ctx);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sdap_idmap_add_domain(struct sdap_idmap_ctx *idmap_ctx,
+ const char *dom_name,
+ const char *dom_sid,
+ id_t slice)
+{
+ errno_t ret;
+ struct sdap_idmap_slice *new_slice;
+ id_t idmap_lower;
+ id_t idmap_upper;
+ id_t rangesize;
+ id_t max_slices;
+ id_t orig_slice;
+ uint32_t hash_val;
+ struct sdap_idmap_slice *s;
+ struct sss_idmap_range range;
+ enum idmap_error_code err;
+
+ idmap_lower = dp_opt_get_int(idmap_ctx->id_ctx->opts->basic,
+ SDAP_IDMAP_LOWER);
+ idmap_upper = dp_opt_get_int(idmap_ctx->id_ctx->opts->basic,
+ SDAP_IDMAP_UPPER);
+ rangesize = dp_opt_get_int(idmap_ctx->id_ctx->opts->basic,
+ SDAP_IDMAP_RANGESIZE);
+
+ /* Validate that the values make sense */
+ if (rangesize <= 0
+ || idmap_upper <= idmap_lower
+ || (idmap_upper-idmap_lower) < rangesize)
+ {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Invalid settings for range selection: [%d][%d][%d]\n",
+ idmap_lower, idmap_upper, rangesize));
+ return EINVAL;
+ }
+
+ max_slices = (idmap_upper - idmap_lower + 1) / rangesize;
+ if (((idmap_upper - idmap_lower + 1) % rangesize) != 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ ("Range size does not divide evenly. Uppermost range will "
+ "not be used\n"));
+ }
+
+ new_slice = talloc_zero(idmap_ctx, struct sdap_idmap_slice);
+ if (!new_slice) return ENOMEM;
+
+ if (slice != -1) {
+ /* The slice is being set explicitly.
+ * This may happen at system startup when we're loading
+ * previously-determined slices. In the future, we may also
+ * permit configuration to select the slice for a domain
+ * explicitly.
+ */
+ new_slice->slice_num = slice;
+ } else {
+ /* If slice is -1, we're being asked to pick a new slice */
+
+ /* Hash the domain sid string */
+ hash_val = murmurhash3(dom_sid, strlen(dom_sid), 0xdeadbeef);
+
+ /* Now get take the modulus of the hash val and the max_slices
+ * to determine its optimal position in the range.
+ */
+ new_slice->slice_num = hash_val % max_slices;
+ orig_slice = new_slice->slice_num;
+
+ /* Verify that this slice is not already in use */
+ do {
+ DLIST_FOR_EACH(s, idmap_ctx->slices) {
+ if (s->slice_num == new_slice->slice_num) {
+ /* This slice number matches one already registered
+ * We'll try the next available slot
+ */
+ new_slice->slice_num++;
+ if (new_slice->slice_num > max_slices) {
+ /* loop around to the beginning if necessary */
+ new_slice->slice_num = 0;
+ }
+ break;
+ }
+ }
+
+ /* Keep trying until s is NULL (meaning we got to the end
+ * without matching) or we have run out of slices and gotten
+ * back to the first one we tried.
+ */
+ } while (s && new_slice->slice_num != orig_slice);
+
+ if (s) {
+ /* We looped all the way through and found no empty slots */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Could not add domain [%s]: no free slices\n",
+ dom_name));
+ ret = ENOSPC;
+ goto done;
+ }
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ ("Adding domain [%s] as slice [%d]\n",
+ dom_name, new_slice->slice_num));
+
+ DLIST_ADD_END(idmap_ctx->slices, new_slice, struct sdap_idmap_slice *);
+ /* Not adding a destructor to remove from this list, because it
+ * should never be possible. Removal from this list can only
+ * destabilize the system.
+ */
+
+ /* Create a range object to add to the mapping */
+ range.min = (rangesize * new_slice->slice_num) + idmap_lower;
+ range.max = range.min + rangesize;
+
+ if (range.max > idmap_upper) {
+ /* This should never happen */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("BUG: Range maximum exceeds the global maximum: %d > %d\n",
+ range.max, idmap_upper));
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Add this domain to the map */
+ err = sss_idmap_add_domain(idmap_ctx->map, dom_name, dom_sid, &range);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Could not add domain [%s] to the map: [%d]\n",
+ dom_name, err));
+ ret = EIO;
+ goto done;
+ }
+
+ /* Add this domain to the SYSDB cache so it will survive reboot */
+ ret = sysdb_idmap_store_mapping(idmap_ctx->id_ctx->be->sysdb,
+ dom_name, dom_sid,
+ new_slice->slice_num);
+done:
+ if (ret != EOK) {
+ talloc_free(new_slice);
+ }
+ return ret;
+}
diff --git a/src/providers/ldap/sdap_idmap.h b/src/providers/ldap/sdap_idmap.h
new file mode 100644
index 000000000..9ac8be133
--- /dev/null
+++ b/src/providers/ldap/sdap_idmap.h
@@ -0,0 +1,52 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2012 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 SDAP_IDMAP_H_
+#define SDAP_IDMAP_H_
+
+#include "src/providers/ldap/sdap.h"
+#include "src/providers/ldap/ldap_common.h"
+
+struct sdap_idmap_slice {
+ struct sdap_idmap_slice *prev;
+ struct sdap_idmap_slice *next;
+
+ id_t slice_num;
+};
+
+struct sdap_idmap_ctx {
+ struct sss_idmap_ctx *map;
+ struct sdap_idmap_slice *slices;
+
+ struct sdap_id_ctx *id_ctx;
+};
+
+errno_t sdap_idmap_init(TALLOC_CTX *mem_ctx,
+ struct sdap_id_ctx *id_ctx,
+ struct sdap_idmap_ctx **_idmap_ctx);
+
+errno_t
+sdap_idmap_add_domain(struct sdap_idmap_ctx *idmap_ctx,
+ const char *dom_name,
+ const char *dom_sid,
+ id_t slice);
+#endif /* SDAP_IDMAP_H_ */