From 3002c430fa2eb01541e78f5cf24b22d3d4936391 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Sun, 22 Apr 2012 13:52:51 -0400
Subject: [PATCH 04/20] LDAP: Add helper routines for ID-mapping

---
 Makefile.am                     |    8 +-
 src/providers/ldap/sdap_idmap.c |  271 +++++++++++++++++++++++++++++++++++++++
 src/providers/ldap/sdap_idmap.h |   52 ++++++++
 3 files changed, 329 insertions(+), 2 deletions(-)
 create mode 100644 src/providers/ldap/sdap_idmap.c
 create mode 100644 src/providers/ldap/sdap_idmap.h

diff --git a/Makefile.am b/Makefile.am
index 14ce773a8f22e6612d8a56d8fa161d23b78ca39c..3790509a26a703e11b83ca9a3a5bae12550f2393 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1095,6 +1095,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
@@ -1139,7 +1141,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
@@ -1230,7 +1233,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 0000000000000000000000000000000000000000..5da8c6c3d47b8e85ce47f14a187db634017a4c8e
--- /dev/null
+++ b/src/providers/ldap/sdap_idmap.c
@@ -0,0 +1,271 @@
+/*
+    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);
+
+    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 0000000000000000000000000000000000000000..9ac8be1334933b2ac61d8cf6b7148a22a0a632a3
--- /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_ */
-- 
1.7.10

