sdap_idmap.c revision 2fd5864ac8eb2c4cfa0fafe7c0431a74f2ebe1fb
1933N/A/*
1933N/A SSSD
1933N/A
1933N/A Authors:
1933N/A Stephen Gallagher <sgallagh@redhat.com>
1933N/A
1933N/A Copyright (C) 2012 Red Hat
1933N/A
1933N/A This program is free software; you can redistribute it and/or modify
1933N/A it under the terms of the GNU General Public License as published by
1933N/A the Free Software Foundation; either version 3 of the License, or
1933N/A (at your option) any later version.
1933N/A
1933N/A This program is distributed in the hope that it will be useful,
1933N/A but WITHOUT ANY WARRANTY; without even the implied warranty of
1933N/A MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1933N/A GNU General Public License for more details.
1933N/A
1933N/A You should have received a copy of the GNU General Public License
1933N/A along with this program. If not, see <http://www.gnu.org/licenses/>.
1933N/A*/
1933N/A
1933N/A#include "util/util.h"
1933N/A#include "util/dlinklist.h"
1933N/A#include "util/murmurhash3.h"
1933N/A#include "providers/ldap/sdap_idmap.h"
1933N/A
1933N/Astatic void *
1933N/Asdap_idmap_talloc(size_t size, void *pvt)
1933N/A{
1933N/A return talloc_size(pvt, size);
1933N/A}
1933N/A
1933N/Astatic void
1933N/Asdap_idmap_talloc_free(void *ptr, void *pvt)
1933N/A{
1933N/A talloc_free(ptr);
1933N/A}
1933N/A
1933N/Aerrno_t
1933N/Asdap_idmap_init(TALLOC_CTX *mem_ctx,
1933N/A struct sdap_id_ctx *id_ctx,
1933N/A struct sdap_idmap_ctx **_idmap_ctx)
1933N/A{
1933N/A errno_t ret;
1933N/A TALLOC_CTX *tmp_ctx;
1933N/A enum idmap_error_code err;
1933N/A size_t i;
1933N/A struct ldb_result *res;
1933N/A const char *dom_name;
1933N/A const char *sid_str;
1933N/A id_t slice_num;
1933N/A struct sdap_idmap_ctx *idmap_ctx = NULL;
1933N/A struct sysdb_ctx *sysdb = id_ctx->be->sysdb;
1933N/A
1933N/A tmp_ctx = talloc_new(NULL);
1933N/A if (!tmp_ctx) return ENOMEM;
1933N/A
1933N/A idmap_ctx = talloc_zero(tmp_ctx, struct sdap_idmap_ctx);
1933N/A if (!idmap_ctx) {
1933N/A ret = ENOMEM;
1933N/A goto done;
1933N/A }
1933N/A idmap_ctx->id_ctx = id_ctx;
1933N/A
1933N/A /* Initialize the map */
1933N/A err = sss_idmap_init(sdap_idmap_talloc, idmap_ctx,
1933N/A sdap_idmap_talloc_free,
1933N/A &idmap_ctx->map);
1933N/A if (err != IDMAP_SUCCESS) {
1933N/A DEBUG(SSSDBG_CRIT_FAILURE,
1933N/A ("Could not initialize the ID map: [%s]\n",
1933N/A idmap_error_string(err)));
1933N/A if (err == IDMAP_OUT_OF_MEMORY) {
1933N/A ret = ENOMEM;
1933N/A } else {
1933N/A ret = EINVAL;
1933N/A }
1933N/A goto done;
1933N/A }
1933N/A
1933N/A /* Read in any existing mappings from the cache */
1933N/A ret = sysdb_idmap_get_mappings(tmp_ctx, sysdb, &res);
1933N/A if (ret != EOK && ret != ENOENT) {
1933N/A DEBUG(SSSDBG_FATAL_FAILURE,
1933N/A ("Could not read ID mappings from the cache: [%s]\n",
1933N/A strerror(ret)));
1933N/A goto done;
1933N/A }
1933N/A
1933N/A if (ret == EOK && res->count > 0) {
1933N/A DEBUG(SSSDBG_CONF_SETTINGS,
1933N/A ("Initializing [%d] domains for ID-mapping\n", res->count));
1933N/A
1933N/A for (i = 0; i < res->count; i++) {
1933N/A dom_name = ldb_msg_find_attr_as_string(res->msgs[i],
1933N/A SYSDB_NAME,
1933N/A NULL);
1933N/A if (!dom_name) {
1933N/A /* This should never happen */
1933N/A ret = EINVAL;
1933N/A goto done;
1933N/A }
1933N/A
1933N/A sid_str = ldb_msg_find_attr_as_string(res->msgs[i],
1933N/A SYSDB_IDMAP_SID_ATTR,
1933N/A NULL);
1933N/A if (!sid_str) {
1933N/A /* This should never happen */
1933N/A ret = EINVAL;
1933N/A goto done;
1933N/A }
1933N/A
1933N/A slice_num = ldb_msg_find_attr_as_int(res->msgs[i],
1933N/A SYSDB_IDMAP_SLICE_ATTR,
1933N/A -1);
1933N/A if (slice_num == -1) {
1933N/A /* This should never happen */
1933N/A ret = EINVAL;
1933N/A goto done;
1933N/A }
1933N/A
1933N/A ret = sdap_idmap_add_domain(idmap_ctx, dom_name,
1933N/A sid_str, slice_num);
1933N/A if (ret != EOK) {
1933N/A DEBUG(SSSDBG_CRIT_FAILURE,
1933N/A ("Could not add domain [%s][%s][%u] to ID map: [%s]\n",
1933N/A dom_name, sid_str, slice_num, strerror(ret)));
1933N/A goto done;
1933N/A }
1933N/A }
1933N/A }
1933N/A
1933N/A *_idmap_ctx = talloc_steal(mem_ctx, idmap_ctx);
1933N/A ret = EOK;
1933N/A
1933N/Adone:
1933N/A talloc_free(tmp_ctx);
1933N/A return ret;
1933N/A}
1933N/A
1933N/Aerrno_t
1933N/Asdap_idmap_add_domain(struct sdap_idmap_ctx *idmap_ctx,
1933N/A const char *dom_name,
1933N/A const char *dom_sid,
1933N/A id_t slice)
1933N/A{
1933N/A errno_t ret;
1933N/A struct sdap_idmap_slice *new_slice;
1933N/A id_t idmap_lower;
1933N/A id_t idmap_upper;
1933N/A id_t rangesize;
1933N/A id_t max_slices;
1933N/A id_t orig_slice;
1933N/A uint32_t hash_val;
1933N/A struct sdap_idmap_slice *s;
1933N/A struct sss_idmap_range range;
1933N/A enum idmap_error_code err;
1933N/A
1933N/A idmap_lower = dp_opt_get_int(idmap_ctx->id_ctx->opts->basic,
1933N/A SDAP_IDMAP_LOWER);
1933N/A idmap_upper = dp_opt_get_int(idmap_ctx->id_ctx->opts->basic,
1933N/A SDAP_IDMAP_UPPER);
1933N/A rangesize = dp_opt_get_int(idmap_ctx->id_ctx->opts->basic,
1933N/A SDAP_IDMAP_RANGESIZE);
1933N/A
1933N/A /* Validate that the values make sense */
1933N/A if (rangesize <= 0
1933N/A || idmap_upper <= idmap_lower
1933N/A || (idmap_upper-idmap_lower) < rangesize)
1933N/A {
1933N/A DEBUG(SSSDBG_CRIT_FAILURE,
1933N/A ("Invalid settings for range selection: [%d][%d][%d]\n",
1933N/A idmap_lower, idmap_upper, rangesize));
1933N/A return EINVAL;
1933N/A }
1933N/A
1933N/A max_slices = (idmap_upper - idmap_lower + 1) / rangesize;
1933N/A if (((idmap_upper - idmap_lower + 1) % rangesize) != 0) {
1933N/A DEBUG(SSSDBG_CONF_SETTINGS,
1933N/A ("Range size does not divide evenly. Uppermost range will "
1933N/A "not be used\n"));
1933N/A }
1933N/A
1933N/A new_slice = talloc_zero(idmap_ctx, struct sdap_idmap_slice);
1933N/A if (!new_slice) return ENOMEM;
1933N/A
1933N/A if (slice != -1) {
1933N/A /* The slice is being set explicitly.
1933N/A * This may happen at system startup when we're loading
1933N/A * previously-determined slices. In the future, we may also
1933N/A * permit configuration to select the slice for a domain
1933N/A * explicitly.
1933N/A */
1933N/A new_slice->slice_num = slice;
1933N/A } else {
1933N/A /* If slice is -1, we're being asked to pick a new slice */
1933N/A
1933N/A if (dp_opt_get_bool(idmap_ctx->id_ctx->opts->basic, SDAP_IDMAP_AUTORID_COMPAT)) {
1933N/A /* In autorid compatibility mode, always start at 0 and find the first
1933N/A * free value.
1933N/A */
1933N/A orig_slice = 0;
1933N/A } else {
1933N/A /* Hash the domain sid string */
1933N/A hash_val = murmurhash3(dom_sid, strlen(dom_sid), 0xdeadbeef);
1933N/A
1933N/A /* Now get take the modulus of the hash val and the max_slices
1933N/A * to determine its optimal position in the range.
1933N/A */
1933N/A new_slice->slice_num = hash_val % max_slices;
1933N/A orig_slice = new_slice->slice_num;
1933N/A }
1933N/A /* Verify that this slice is not already in use */
1933N/A do {
1933N/A DLIST_FOR_EACH(s, idmap_ctx->slices) {
1933N/A if (s->slice_num == new_slice->slice_num) {
1933N/A /* This slice number matches one already registered
1933N/A * We'll try the next available slot
1933N/A */
1933N/A new_slice->slice_num++;
1933N/A if (new_slice->slice_num > max_slices) {
1933N/A /* loop around to the beginning if necessary */
1933N/A new_slice->slice_num = 0;
1933N/A }
1933N/A break;
1933N/A }
1933N/A }
1933N/A
1933N/A /* Keep trying until s is NULL (meaning we got to the end
1933N/A * without matching) or we have run out of slices and gotten
1933N/A * back to the first one we tried.
1933N/A */
1933N/A } while (s && new_slice->slice_num != orig_slice);
1933N/A
1933N/A if (s) {
1933N/A /* We looped all the way through and found no empty slots */
1933N/A DEBUG(SSSDBG_CRIT_FAILURE,
1933N/A ("Could not add domain [%s]: no free slices\n",
1933N/A dom_name));
1933N/A ret = ENOSPC;
1933N/A goto done;
1933N/A }
1933N/A }
1933N/A
1933N/A DEBUG(SSSDBG_CONF_SETTINGS,
1933N/A ("Adding domain [%s] as slice [%d]\n",
1933N/A dom_name, new_slice->slice_num));
1933N/A
1933N/A DLIST_ADD_END(idmap_ctx->slices, new_slice, struct sdap_idmap_slice *);
1933N/A /* Not adding a destructor to remove from this list, because it
1933N/A * should never be possible. Removal from this list can only
1933N/A * destabilize the system.
1933N/A */
1933N/A
1933N/A /* Create a range object to add to the mapping */
1933N/A range.min = (rangesize * new_slice->slice_num) + idmap_lower;
1933N/A range.max = range.min + rangesize;
1933N/A
1933N/A if (range.max > idmap_upper) {
1933N/A /* This should never happen */
1933N/A DEBUG(SSSDBG_CRIT_FAILURE,
1933N/A ("BUG: Range maximum exceeds the global maximum: %d > %d\n",
1933N/A range.max, idmap_upper));
1933N/A ret = EINVAL;
1933N/A goto done;
1933N/A }
1933N/A
1933N/A /* Add this domain to the map */
1933N/A err = sss_idmap_add_domain(idmap_ctx->map, dom_name, dom_sid, &range);
1933N/A if (err != IDMAP_SUCCESS) {
1933N/A DEBUG(SSSDBG_CRIT_FAILURE,
1933N/A ("Could not add domain [%s] to the map: [%d]\n",
1933N/A dom_name, err));
1933N/A ret = EIO;
1933N/A goto done;
1933N/A }
1933N/A
1933N/A /* Add this domain to the SYSDB cache so it will survive reboot */
1933N/A ret = sysdb_idmap_store_mapping(idmap_ctx->id_ctx->be->sysdb,
1933N/A dom_name, dom_sid,
1933N/A new_slice->slice_num);
1933N/Adone:
1933N/A if (ret != EOK) {
1933N/A talloc_free(new_slice);
1933N/A }
1933N/A return ret;
1933N/A}
1933N/A