From ea48f8b5c252755168e9ae83d4b7ebb97152ea3c Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Fri, 27 Nov 2015 04:15:00 -0500
Subject: [PATCH 2/2] IDMAP: introduce secondary slices

Resolves:
https://fedorahosted.org/sssd/ticket/2188
---
 src/config/SSSDConfig/__init__.py.in     |   1 +
 src/config/etc/sssd.api.d/sssd-ad.conf   |   1 +
 src/config/etc/sssd.api.d/sssd-ipa.conf  |   1 +
 src/config/etc/sssd.api.d/sssd-ldap.conf |   1 +
 src/lib/idmap/sss_idmap.c                | 430 +++++++++++++++++++++++++++----
 src/lib/idmap/sss_idmap.exports          |   3 +
 src/lib/idmap/sss_idmap.h                |  58 +++++
 src/lib/idmap/sss_idmap_private.h        |   4 +
 src/man/include/ldap_id_mapping.xml      |  11 +
 src/providers/ad/ad_opts.c               |   1 +
 src/providers/ipa/ipa_opts.c             |   1 +
 src/providers/ldap/ldap_opts.c           |   1 +
 src/providers/ldap/sdap.h                |   1 +
 src/providers/ldap/sdap_idmap.c          |  15 +-
 src/tests/cmocka/test_sss_idmap.c        |  86 ++++++-
 src/tests/sss_idmap-tests.c              | 114 ++++++++
 16 files changed, 667 insertions(+), 62 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index fe2971d993ccf2975bbddbe6c74572ab9f5d4e51..7f9ced668b4b57c90ed4da4f49f56bf42b172304 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -354,6 +354,7 @@ option_strings = {
     'ldap_idmap_autorid_compat' : _('Use autorid-compatible algorithm for ID-mapping'),
     'ldap_idmap_default_domain' : _('Name of the default domain for ID-mapping'),
     'ldap_idmap_default_domain_sid' : _('SID of the default domain for ID-mapping'),
+    'ldap_idmap_extra_slice_max' : _('Number of secondary slices'),
 
     'ldap_groups_use_matching_rule_in_chain' : _('Use LDAP_MATCHING_RULE_IN_CHAIN for group lookups'),
     'ldap_initgroups_use_matching_rule_in_chain' : _('Use LDAP_MATCHING_RULE_IN_CHAIN for initgroup lookups'),
diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
index 5eb546caac913b839112a70bd81dbde2c7ff2d9f..3e5ec1923e055ee6d7de0e0e43421fb9801a0986 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -123,6 +123,7 @@ ldap_idmap_range_size = int, None, false
 ldap_idmap_autorid_compat = bool, None, false
 ldap_idmap_default_domain = str, None, false
 ldap_idmap_default_domain_sid = str, None, false
+ldap_idmap_extra_slice_max = int, None, false
 ldap_groups_use_matching_rule_in_chain = bool, None, false
 ldap_initgroups_use_matching_rule_in_chain = bool, None, false
 ldap_use_tokengroups = bool, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index ab712fe55cdac6d247a085aeca5cc82d65966623..1a7430db407ef519b60ffb2f35982bb188fb1225 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -131,6 +131,7 @@ ldap_idmap_range_size = int, None, false
 ldap_idmap_autorid_compat = bool, None, false
 ldap_idmap_default_domain = str, None, false
 ldap_idmap_default_domain_sid = str, None, false
+ldap_idmap_extra_slice_max = int, None, false
 ldap_groups_use_matching_rule_in_chain = bool, None, false
 ldap_initgroups_use_matching_rule_in_chain = bool, None, false
 ldap_use_tokengroups = bool, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index 8fd45fd4093714f458161eb352157c845d926f06..e8d652cd535a5ee32a13510a337b4b6815114555 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -118,6 +118,7 @@ ldap_idmap_range_size = int, None, false
 ldap_idmap_autorid_compat = bool, None, false
 ldap_idmap_default_domain = str, None, false
 ldap_idmap_default_domain_sid = str, None, false
+ldap_idmap_extra_slice_max = int, None, false
 ldap_groups_use_matching_rule_in_chain = bool, None, false
 ldap_initgroups_use_matching_rule_in_chain = bool, None, false
 ldap_use_tokengroups = bool, None, false
diff --git a/src/lib/idmap/sss_idmap.c b/src/lib/idmap/sss_idmap.c
index 7a91e419983d59bed6a8ae06f7cfd448e8e18f63..3b26b9ad53c684150ba751dbd84dd8b76c56cfde 100644
--- a/src/lib/idmap/sss_idmap.c
+++ b/src/lib/idmap/sss_idmap.c
@@ -41,6 +41,7 @@ struct idmap_range_params {
     char *range_id;
 
     uint32_t first_rid;
+    struct idmap_range_params *next;
 };
 
 struct idmap_domain_info {
@@ -49,6 +50,9 @@ struct idmap_domain_info {
     struct idmap_range_params range_params;
     struct idmap_domain_info *next;
     bool external_mapping;
+
+    struct idmap_range_params *sec_ranges;
+    bool use_sec_ranges;
 };
 
 static void *default_alloc(size_t size, void *pvt)
@@ -195,6 +199,7 @@ enum idmap_error_code sss_idmap_init(idmap_alloc_func *alloc_func,
     ctx->idmap_opts.idmap_lower = SSS_IDMAP_DEFAULT_LOWER;
     ctx->idmap_opts.idmap_upper = SSS_IDMAP_DEFAULT_UPPER;
     ctx->idmap_opts.rangesize = SSS_IDMAP_DEFAULT_RANGESIZE;
+    ctx->idmap_opts.extra_slice_max = SSS_IDMAP_DEFAULT_EXTRA_SLICE_MAX;
 
     *_ctx = ctx;
 
@@ -204,11 +209,24 @@ enum idmap_error_code sss_idmap_init(idmap_alloc_func *alloc_func,
 static void sss_idmap_free_domain(struct sss_idmap_ctx *ctx,
                                   struct idmap_domain_info *dom)
 {
+    struct idmap_range_params *it;
+
     if (ctx == NULL || dom == NULL) {
         return;
     }
 
     ctx->free_func(dom->range_params.range_id, ctx->alloc_pvt);
+
+    /* Free list of secondary ranges */
+    it = dom->sec_ranges;
+    while (it != NULL) {
+        struct idmap_range_params *tmp = it->next;
+
+        ctx->free_func(it->range_id, ctx->alloc_pvt);
+        ctx->free_func(it, ctx->alloc_pvt);
+        it = tmp;
+    }
+
     ctx->free_func(dom->name, ctx->alloc_pvt);
     ctx->free_func(dom->sid, ctx->alloc_pvt);
     ctx->free_func(dom, ctx->alloc_pvt);
@@ -269,6 +287,27 @@ enum idmap_error_code sss_idmap_free_bin_sid(struct sss_idmap_ctx *ctx,
     return sss_idmap_free_ptr(ctx, bin_sid);
 }
 
+static bool check_overlap(struct idmap_range_params *range,
+                          id_t min, id_t max)
+{
+    return ((range->min_id <= min && range->max_id >= max)
+                || (range->min_id >= min && range->min_id <= max)
+                || (range->max_id >= min && range->max_id <= max));
+}
+
+static bool check_collision_sec_slices(struct idmap_range_params *sec_ranges,
+                                       id_t min, id_t max)
+{
+    struct idmap_range_params *it;
+
+    for (it = sec_ranges; it != NULL; it = it->next) {
+        if (check_overlap(it, min, max)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx,
                                                 const char *dom_sid,
                                                 id_t *slice_num,
@@ -327,12 +366,9 @@ enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx,
         /* Verify that this slice is not already in use */
         do {
             for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) {
-                uint32_t dmin = dom->range_params.min_id;
-                uint32_t dmax = dom->range_params.max_id;
 
-                if ((dmin <= min && dmax >= max) ||
-                    (dmin >= min && dmin <= max) ||
-                    (dmax >= min && dmax <= max)) {
+                if (check_overlap(&dom->range_params, min, max) ||
+                    check_collision_sec_slices(dom->sec_ranges, min, max)) {
                     /* This range overlaps one already registered
                      * We'll try the next available slot
                      */
@@ -486,6 +522,106 @@ static enum idmap_error_code dom_check_collision(
     return IDMAP_SUCCESS;
 }
 
+static char*
+generate_sec_slice_name(struct sss_idmap_ctx *ctx,
+                        const char *domain_name, int slice_index)
+{
+    const char * SEC_SLICE_NAME_FMT = "%s#%d";
+    char *slice_name;
+    int len, len2;
+
+    len = snprintf(NULL, 0, SEC_SLICE_NAME_FMT, domain_name, slice_index + 1);
+    if (len <= 0) {
+        return NULL;
+    }
+
+    slice_name = ctx->alloc_func(len + 1, ctx->alloc_pvt);
+    if (slice_name == NULL) {
+        return NULL;
+    }
+
+    len2 = snprintf(slice_name, len + 1, SEC_SLICE_NAME_FMT, domain_name,
+                    slice_index + 1);
+    if (len != len2) {
+        ctx->free_func(slice_name, ctx->alloc_pvt);
+        return NULL;
+    }
+
+    return slice_name;
+}
+
+static enum idmap_error_code
+generate_slice(struct sss_idmap_ctx *ctx, char *slice_name, uint32_t first_rid,
+               struct idmap_range_params **_slice)
+{
+    struct idmap_range_params *slice;
+    struct sss_idmap_range tmp_range;
+    enum idmap_error_code err;
+
+    slice = ctx->alloc_func(sizeof(struct idmap_range_params), ctx->alloc_pvt);
+    if (slice == NULL) {
+        return IDMAP_OUT_OF_MEMORY;
+    }
+
+    slice->next = NULL;
+
+    err = sss_idmap_calculate_range(ctx, slice_name, NULL, &tmp_range);
+    if (err != IDMAP_SUCCESS) {
+        ctx->free_func(slice, ctx->alloc_pvt);
+        return err;
+    }
+
+    slice->min_id = tmp_range.min;
+    slice->max_id = tmp_range.max;
+    slice->range_id = slice_name;
+    slice->first_rid = first_rid;
+
+    *_slice = slice;
+    return IDMAP_SUCCESS;
+}
+
+static enum idmap_error_code
+get_sec_slices(struct sss_idmap_ctx *ctx,
+               const char *domain_name,
+               uint32_t first_rid,
+               struct idmap_range_params **_sec_slices)
+{
+    struct idmap_range_params *prev = NULL;
+    struct idmap_range_params *sec_slices = NULL;
+
+    for (int i = 0; i < ctx->idmap_opts.extra_slice_max; i++) {
+        static enum idmap_error_code err;
+        struct idmap_range_params *slice;
+        char *secondary_name;
+
+        secondary_name = generate_sec_slice_name(ctx, domain_name, i);
+        if (secondary_name == NULL) {
+            return IDMAP_OUT_OF_MEMORY;
+        }
+
+        err = generate_slice(ctx, secondary_name, first_rid, &slice);
+        if (err != IDMAP_SUCCESS) {
+            ctx->free_func(secondary_name, ctx->alloc_pvt);
+            return err;
+        }
+
+        first_rid += ctx->idmap_opts.rangesize;
+
+        if (prev != NULL) {
+            prev->next = slice;
+        }
+
+        if (sec_slices == NULL) {
+            sec_slices = slice;
+        }
+
+        prev = slice;
+    }
+
+    *_sec_slices = sec_slices;
+    return IDMAP_SUCCESS;
+}
+
 enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx,
                                               const char *domain_name,
                                               const char *domain_sid,
@@ -566,6 +702,48 @@ fail:
     return err;
 }
 
+enum idmap_error_code
+sss_idmap_add_auto_domain_ex(struct sss_idmap_ctx *ctx,
+                             const char *domain_name,
+                             const char *domain_sid,
+                             struct sss_idmap_range *range,
+                             const char *range_id,
+                             uint32_t rid,
+                             bool external_mapping)
+{
+    enum idmap_error_code err;
+
+    err = sss_idmap_add_domain_ex(ctx, domain_name, domain_sid, range,
+                                  range_id, rid, external_mapping);
+    if (err != IDMAP_SUCCESS) {
+        return err;
+    }
+
+    if (external_mapping) {
+        /* There's no point in generating secondary ranges if external_mapping
+           is enabled. */
+        ctx->idmap_domain_info->use_sec_ranges = false;
+        return IDMAP_SUCCESS;
+    }
+
+    /* Add size of primary slice for first_rid of secondary slices. */
+    rid += ctx->idmap_opts.rangesize;
+    err = get_sec_slices(ctx, domain_name, rid,
+                         &ctx->idmap_domain_info->sec_ranges);
+    if (err == IDMAP_SUCCESS) {
+        ctx->idmap_domain_info->use_sec_ranges = true;
+    } else {
+        /* Running out of slices for secondary mapping is a non-fatal
+         * problem. */
+        if (err == IDMAP_OUT_OF_SLICES) {
+            err = IDMAP_SUCCESS;
+        }
+        ctx->idmap_domain_info->use_sec_ranges = false;
+    }
+
+    return err;
+}
+
 enum idmap_error_code sss_idmap_add_domain(struct sss_idmap_ctx *ctx,
                                            const char *domain_name,
                                            const char *domain_sid,
@@ -584,16 +762,93 @@ static bool sss_idmap_sid_is_builtin(const char *sid)
     return false;
 }
 
+static bool parse_rid(const char *sid, size_t dom_prefix_len, long long *_rid)
+{
+    long long rid;
+    char *endptr;
+
+    errno = 0;
+    /* Use suffix of sid - part after domain and following '-' */
+    rid = strtoull(sid + dom_prefix_len + 1, &endptr, 10);
+    if (errno != 0 || rid > UINT32_MAX || *endptr != '\0') {
+        return false;
+    }
+
+    *_rid = rid;
+    return true;
+}
+
+static bool is_sid_from_dom(const char *dom_sid, const char *sid,
+                            size_t *_dom_sid_len)
+{
+    size_t dom_sid_len;
+
+    if (dom_sid == NULL) {
+        return false;
+    }
+
+    dom_sid_len = strlen(dom_sid);
+    *_dom_sid_len = dom_sid_len;
+
+    if (strlen(sid) < dom_sid_len || sid[dom_sid_len] != '-') {
+        return false;
+    }
+
+    return strncmp(sid, dom_sid, dom_sid_len) == 0;
+}
+
+static bool comp_id(struct idmap_range_params *range_params, long long rid,
+                    uint32_t *_id)
+{
+    uint32_t id;
+
+    if (rid >= range_params->first_rid) {
+        id = range_params->min_id + (rid - range_params->first_rid);
+        if (id <= range_params->max_id) {
+            *_id = id;
+            return true;
+        }
+    }
+    return false;
+}
+
+static enum idmap_error_code
+get_new_sec_slide(struct sss_idmap_ctx *ctx,
+                  const char *dom_name,
+                  long long rid,
+                  struct idmap_range_params **_slice)
+{
+    char *secondary_name;
+    enum idmap_error_code err;
+    int index;
+    int first_rid;
+    struct idmap_range_params *slice;
+
+    index = rid / ctx->idmap_opts.rangesize;
+    first_rid = index * ctx->idmap_opts.rangesize;
+
+    secondary_name = generate_sec_slice_name(ctx, dom_name, index);
+    if (secondary_name == NULL) {
+        return IDMAP_OUT_OF_MEMORY;
+    }
+
+    err = generate_slice(ctx, secondary_name, first_rid, &slice);
+    if (err == IDMAP_OUT_OF_SLICES) {
+        return err;
+    }
+
+    *_slice = slice;
+    return IDMAP_SUCCESS;
+}
+
 enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx,
                                             const char *sid,
                                             uint32_t *_id)
 {
     struct idmap_domain_info *idmap_domain_info;
+    struct idmap_domain_info *matched_dom = NULL;
     size_t dom_len;
     long long rid;
-    char *endptr;
-    uint32_t id;
-    bool no_range = false;
 
     if (sid == NULL || _id == NULL) {
         return IDMAP_ERROR;
@@ -607,39 +862,68 @@ enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx,
         return IDMAP_BUILTIN_SID;
     }
 
+    /* Try primary slices */
     while (idmap_domain_info != NULL) {
-        if (idmap_domain_info->sid != NULL) {
-            dom_len = strlen(idmap_domain_info->sid);
-            if (strlen(sid) > dom_len && sid[dom_len] == '-'
-                    && strncmp(sid, idmap_domain_info->sid, dom_len) == 0) {
 
-                if (idmap_domain_info->external_mapping == true) {
-                    return IDMAP_EXTERNAL;
-                }
+        if (is_sid_from_dom(idmap_domain_info->sid, sid, &dom_len)) {
 
-                errno = 0;
-                rid = strtoull(sid + dom_len + 1, &endptr, 10);
-                if (errno != 0 || rid > UINT32_MAX || *endptr != '\0') {
-                    return IDMAP_SID_INVALID;
-                }
+            if (idmap_domain_info->external_mapping == true) {
+                return IDMAP_EXTERNAL;
+            }
 
-                if (rid >= idmap_domain_info->range_params.first_rid) {
-                    id = idmap_domain_info->range_params.min_id
-                            + (rid - idmap_domain_info->range_params.first_rid);
-                    if (id <= idmap_domain_info->range_params.max_id) {
-                        *_id = id;
-                        return IDMAP_SUCCESS;
-                    }
-                }
+            if (parse_rid(sid, dom_len, &rid) == false) {
+                return IDMAP_SID_INVALID;
+            }
 
-                no_range = true;
+            if (comp_id(&idmap_domain_info->range_params, rid, _id)) {
+                return IDMAP_SUCCESS;
             }
+
+            matched_dom = idmap_domain_info;
         }
 
         idmap_domain_info = idmap_domain_info->next;
     }
 
-    return no_range ? IDMAP_NO_RANGE : IDMAP_NO_DOMAIN;
+    /* Try secondary slices */
+    if (matched_dom != NULL && matched_dom->use_sec_ranges) {
+        struct idmap_range_params *last_slide = NULL;
+        struct idmap_range_params *new_slice;
+        enum idmap_error_code err;
+
+        dom_len = strlen(matched_dom->sid);
+
+        if (parse_rid(sid, dom_len, &rid) == false) {
+            return IDMAP_SID_INVALID;
+        }
+
+        for (struct idmap_range_params *it = matched_dom->sec_ranges;
+             it != NULL; it = it->next) {
+            if (comp_id(it, rid, _id)) {
+                return IDMAP_SUCCESS;
+            }
+            last_slide = it;
+        }
+
+        /* If no secondary slide matched then new slide is added. */
+        err = get_new_sec_slide(ctx, matched_dom->name, rid, &new_slice);
+        if (err != IDMAP_SUCCESS) {
+            return err;
+        }
+
+        if (last_slide == NULL) {
+            matched_dom->sec_ranges = new_slice;
+        } else {
+            last_slide->next = new_slice;
+        }
+
+        if (comp_id(new_slice, rid, _id)) {
+            return IDMAP_SUCCESS;
+        }
+
+    }
+
+    return matched_dom ? IDMAP_NO_RANGE : IDMAP_NO_DOMAIN;
 }
 
 enum idmap_error_code sss_idmap_check_sid_unix(struct sss_idmap_ctx *ctx,
@@ -687,15 +971,41 @@ enum idmap_error_code sss_idmap_check_sid_unix(struct sss_idmap_ctx *ctx,
     return no_range ? IDMAP_NO_RANGE : IDMAP_SID_UNKNOWN;
 }
 
+static enum idmap_error_code generate_sid(struct sss_idmap_ctx *ctx,
+                                          const char *dom_sid,
+                                          uint32_t rid,
+                                          char **_sid)
+{
+    char *sid;
+    int len;
+    int ret;
+
+    len = snprintf(NULL, 0, SID_FMT, dom_sid, rid);
+    if (len <= 0 || len > SID_STR_MAX_LEN) {
+        return IDMAP_ERROR;
+    }
+
+    sid = ctx->alloc_func(len + 1, ctx->alloc_pvt);
+    if (sid == NULL) {
+        return IDMAP_OUT_OF_MEMORY;
+    }
+
+    ret = snprintf(sid, len + 1, SID_FMT, dom_sid, rid);
+    if (ret != len) {
+        ctx->free_func(sid, ctx->alloc_pvt);
+        return IDMAP_ERROR;
+    }
+
+    *_sid = sid;
+    return IDMAP_SUCCESS;
+}
+
 enum idmap_error_code sss_idmap_unix_to_sid(struct sss_idmap_ctx *ctx,
                                             uint32_t id,
                                             char **_sid)
 {
     struct idmap_domain_info *idmap_domain_info;
-    int len;
-    int ret;
     uint32_t rid;
-    char *sid = NULL;
 
     CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
 
@@ -709,24 +1019,30 @@ enum idmap_error_code sss_idmap_unix_to_sid(struct sss_idmap_ctx *ctx,
                 return IDMAP_EXTERNAL;
             }
 
-            len = snprintf(NULL, 0, SID_FMT, idmap_domain_info->sid, rid);
-            if (len <= 0 || len > SID_STR_MAX_LEN) {
-                return IDMAP_ERROR;
-            }
+            return generate_sid(ctx, idmap_domain_info->sid, rid, _sid);
+        }
 
-            sid = ctx->alloc_func(len + 1, ctx->alloc_pvt);
-            if (sid == NULL) {
-                return IDMAP_OUT_OF_MEMORY;
-            }
+        idmap_domain_info = idmap_domain_info->next;
+    }
 
-            ret = snprintf(sid, len + 1, SID_FMT, idmap_domain_info->sid, rid);
-            if (ret != len) {
-                ctx->free_func(sid, ctx->alloc_pvt);
-                return IDMAP_ERROR;
-            }
+    /* Check secondary ranges. */
+    idmap_domain_info = ctx->idmap_domain_info;
+    while (idmap_domain_info != NULL) {
+
+        for (struct idmap_range_params *it = idmap_domain_info->sec_ranges;
+             it != NULL;
+             it = it->next) {
 
-            *_sid = sid;
-            return IDMAP_SUCCESS;
+
+            if (id_is_in_range(id, it, &rid)) {
+
+                if (idmap_domain_info->external_mapping == true
+                    || idmap_domain_info->sid == NULL) {
+                    return IDMAP_EXTERNAL;
+                }
+
+                return generate_sid(ctx, idmap_domain_info->sid, rid, _sid);
+            }
         }
 
         idmap_domain_info = idmap_domain_info->next;
@@ -969,6 +1285,15 @@ sss_idmap_ctx_set_rangesize(struct sss_idmap_ctx *ctx, id_t rangesize)
 }
 
 enum idmap_error_code
+sss_idmap_ctx_set_extra_slice_max(struct sss_idmap_ctx *ctx,
+                                  int extra_slice_max)
+{
+    CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+    ctx->idmap_opts.extra_slice_max = extra_slice_max;
+    return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
 sss_idmap_ctx_get_autorid(struct sss_idmap_ctx *ctx, bool *_autorid)
 {
     CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
@@ -1001,6 +1326,15 @@ sss_idmap_ctx_get_rangesize(struct sss_idmap_ctx *ctx, id_t *_rangesize)
 }
 
 enum idmap_error_code
+sss_idmap_ctx_get_extra_slide_max(struct sss_idmap_ctx *ctx,
+                                  int *_extra_slide_max)
+{
+    CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+    *_extra_slide_max =  ctx->idmap_opts.extra_slice_max;
+    return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
 sss_idmap_domain_has_algorithmic_mapping(struct sss_idmap_ctx *ctx,
                                          const char *dom_sid,
                                          bool *has_algorithmic_mapping)
diff --git a/src/lib/idmap/sss_idmap.exports b/src/lib/idmap/sss_idmap.exports
index 52115636d5a6b936f18b4392e9d12adc26c85f53..d4d3d0cb50146ba62b1bbd068420be2e6ea83976 100644
--- a/src/lib/idmap/sss_idmap.exports
+++ b/src/lib/idmap/sss_idmap.exports
@@ -8,13 +8,16 @@ SSS_IDMAP_0.4 {
         sss_idmap_ctx_set_lower;
         sss_idmap_ctx_set_upper;
         sss_idmap_ctx_set_rangesize;
+        sss_idmap_ctx_set_extra_slice_max;
         sss_idmap_ctx_get_autorid;
         sss_idmap_ctx_get_lower;
         sss_idmap_ctx_get_upper;
         sss_idmap_ctx_get_rangesize;
+        sss_idmap_ctx_get_extra_slice_max;
         sss_idmap_calculate_range;
         sss_idmap_add_domain;
         sss_idmap_add_domain_ex;
+        sss_idmap_add_auto_domain_ex;
         sss_idmap_check_collision;
         sss_idmap_check_collision_ex;
         sss_idmap_sid_to_unix;
diff --git a/src/lib/idmap/sss_idmap.h b/src/lib/idmap/sss_idmap.h
index 0797083293f7e010962828ddcd72709b290859b9..b90f5067afffa931682229a2ebf6db23b1568606 100644
--- a/src/lib/idmap/sss_idmap.h
+++ b/src/lib/idmap/sss_idmap.h
@@ -175,6 +175,16 @@ enum idmap_error_code
 sss_idmap_ctx_set_rangesize(struct sss_idmap_ctx *ctx, id_t rangesize);
 
 /**
+ * @brief Set the maximal number of secondary slices available for domain
+ *
+ * @param[in] ctx                  idmap context
+ * @param[in] extra_slice_max      maximal number of secondary slices
+ */
+enum idmap_error_code
+sss_idmap_ctx_set_extra_slice_max(struct sss_idmap_ctx *ctx,
+                                  int extra_slice_max);
+
+/**
  * @brief Check if autorid compatibility mode is set
  *
  * @param[in] ctx           idmap context
@@ -211,6 +221,16 @@ enum idmap_error_code
 sss_idmap_ctx_get_rangesize(struct sss_idmap_ctx *ctx, id_t *rangesize);
 
 /**
+ * @brief Get the maximal number of secondary slices available for domain
+ *
+ * @param[in] ctx                  idmap context
+ * @param[out] _extra_slice_max    maximal number of secondary slices
+ */
+enum idmap_error_code
+sss_idmap_ctx_get_extra_slide_max(struct sss_idmap_ctx *ctx,
+                                  int *_extra_slide_max);
+
+/**
  * @brief Calculate new range of available POSIX IDs
  *
  * @param[in] ctx           Idmap context
@@ -291,6 +311,44 @@ enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx,
                                               bool external_mapping);
 
 /**
+ * @brief Add a domain with the first mappable RID to the idmap context and
+ * generate automatically secondary slices
+ *
+ * @param[in] ctx         Idmap context
+ * @param[in] domain_name Zero-terminated string with the domain name
+ * @param[in] domain_sid  Zero-terminated string representation of the domain
+ *                        SID (S-1-15-.....)
+ * @param[in] range       TBD Some information about the id ranges of this
+ *                        domain
+ * @param[in] range_id    optional unique identifier of a range, it is needed
+ *                        to allow updates at runtime
+ * @param[in] rid         The RID that should be mapped to the first ID of the
+ *                        given range.
+ * @param[in] external_mapping  If set to true the ID will not be mapped
+ *                        algorithmically, but the *_to_unix and *_unix_to_*
+ *                        calls will return IDMAP_EXTERNAL to instruct the
+ *                        caller to check external sources. For a single
+ *                        domain all ranges must be of the same type. It is
+ *                        not possible to mix algorithmic and external
+ *                        mapping.
+ *
+ * @return
+ *  - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap
+ *                          context
+ *  - #IDMAP_SID_INVALID:   Invalid SID provided
+ *  - #IDMAP_NO_DOMAIN:     No domain domain name given
+ *  - #IDMAP_COLLISION:     New domain collides with existing one
+ */
+enum idmap_error_code
+sss_idmap_add_auto_domain_ex(struct sss_idmap_ctx *ctx,
+                             const char *domain_name,
+                             const char *domain_sid,
+                             struct sss_idmap_range *range,
+                             const char *range_id,
+                             uint32_t rid,
+                             bool external_mapping);
+
+/**
  * @brief Check if a new range would collide with any existing one
  *
  * @param[in] ctx         Idmap context
diff --git a/src/lib/idmap/sss_idmap_private.h b/src/lib/idmap/sss_idmap_private.h
index 1d3a36901781ae51ab79015d0b789559325c8de5..f09f60e00450e4e65baced99d1cb77da4165fa2e 100644
--- a/src/lib/idmap/sss_idmap_private.h
+++ b/src/lib/idmap/sss_idmap_private.h
@@ -29,6 +29,7 @@
 #define SSS_IDMAP_DEFAULT_UPPER 2000200000
 #define SSS_IDMAP_DEFAULT_RANGESIZE 200000
 #define SSS_IDMAP_DEFAULT_AUTORID false
+#define SSS_IDMAP_DEFAULT_EXTRA_SLICE_MAX 10
 
 #define CHECK_IDMAP_CTX(ctx, ret) do { \
     if (ctx == NULL || ctx->alloc_func == NULL || ctx->free_func == NULL) { \
@@ -48,6 +49,9 @@ struct sss_idmap_opts {
 
     /* number of available UIDs (for single domain) */
     id_t rangesize;
+
+    /* maximal number of secondary slices */
+    int extra_slice_max;
 };
 
 struct sss_idmap_ctx {
diff --git a/src/man/include/ldap_id_mapping.xml b/src/man/include/ldap_id_mapping.xml
index 17ef803289d14fa52b725c90062ee4ba0379acd0..bd72bc01186762a15f94de88e4a3708d3dc0f7ad 100644
--- a/src/man/include/ldap_id_mapping.xml
+++ b/src/man/include/ldap_id_mapping.xml
@@ -243,6 +243,17 @@ ldap_schema = ad
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>ldap_idmap_extra_slice_max (integer)</term>
+                    <listitem>
+                        <para>
+                          Number of secondary slices to use for ID mapping.
+                        </para>
+                        <para>
+                            Default: 10
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </refsect3>
     </refsect2>
diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
index 4ea96637ca7264c76109ed8c2f7b5e8a94f73bfe..bfc845d1e15603483243e176d69d207dabd3f79f 100644
--- a/src/providers/ad/ad_opts.c
+++ b/src/providers/ad/ad_opts.c
@@ -135,6 +135,7 @@ struct dp_option ad_def_ldap_opts[] = {
     { "ldap_idmap_autorid_compat", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_idmap_default_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_idmap_default_domain_sid", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_idmap_extra_slice_max", DP_OPT_NUMBER, { .number = 10 }, NULL_NUMBER },
     { "ldap_groups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_use_tokengroups", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE},
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
index bc983ec32d63c37b6fdf06d6009df9084f82d4bf..db1c22e70088debe367bb4b3b2c83fec2d2acf82 100644
--- a/src/providers/ipa/ipa_opts.c
+++ b/src/providers/ipa/ipa_opts.c
@@ -147,6 +147,7 @@ struct dp_option ipa_def_ldap_opts[] = {
     { "ldap_idmap_autorid_compat", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_idmap_default_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_idmap_default_domain_sid", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_idmap_extra_slice_max", DP_OPT_NUMBER, { .number = 10 }, NULL_NUMBER },
     { "ldap_groups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_use_tokengroups", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE},
diff --git a/src/providers/ldap/ldap_opts.c b/src/providers/ldap/ldap_opts.c
index 54926c6c31b1b6edfc1f07b22e79225c5fdddf5b..5eb042b341ed6b13446cbd462950551a52c35ed1 100644
--- a/src/providers/ldap/ldap_opts.c
+++ b/src/providers/ldap/ldap_opts.c
@@ -111,6 +111,7 @@ struct dp_option default_basic_opts[] = {
     { "ldap_idmap_autorid_compat", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_idmap_default_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_idmap_default_domain_sid", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_idmap_extra_slice_max", DP_OPT_NUMBER, { .number = 10 }, NULL_NUMBER },
     { "ldap_groups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_use_tokengroups", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE},
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index edfbf229b4c4396592020de931eba5f83a8f06ed..4405d3ce554de2fda90073dcaee994a5a484a4d9 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -223,6 +223,7 @@ enum sdap_basic_opt {
     SDAP_IDMAP_AUTORID_COMPAT,
     SDAP_IDMAP_DEFAULT_DOMAIN,
     SDAP_IDMAP_DEFAULT_DOMAIN_SID,
+    SDAP_IDMAP_EXTRA_SLICE_MAX,
     SDAP_AD_MATCHING_RULE_GROUPS,
     SDAP_AD_MATCHING_RULE_INITGROUPS,
     SDAP_AD_USE_TOKENGROUPS,
diff --git a/src/providers/ldap/sdap_idmap.c b/src/providers/ldap/sdap_idmap.c
index 4d9de8a447d46dedc8df49dc5663037248eb6996..846969d54d5f2be9a892f5f71fa7de522f3d8a8d 100644
--- a/src/providers/ldap/sdap_idmap.c
+++ b/src/providers/ldap/sdap_idmap.c
@@ -94,9 +94,10 @@ sdap_idmap_add_configured_external_range(struct sdap_idmap_ctx *idmap_ctx)
 
     id_ctx = idmap_ctx->id_ctx;
 
-    err = sss_idmap_add_domain_ex(idmap_ctx->map, id_ctx->be->domain->name,
-                                  id_ctx->be->domain->domain_id, &range,
-                                  NULL, 0, true);
+    err = sss_idmap_add_auto_domain_ex(idmap_ctx->map,
+                                       id_ctx->be->domain->name,
+                                       id_ctx->be->domain->domain_id, &range,
+                                       NULL, 0, true);
     if (err != IDMAP_SUCCESS) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               "Could not add domain [%s] to the map: [%d]\n",
@@ -142,6 +143,7 @@ sdap_idmap_init(TALLOC_CTX *mem_ctx,
     id_t idmap_upper;
     id_t rangesize;
     bool autorid_mode;
+    int extra_slice_max;
     struct sdap_idmap_ctx *idmap_ctx = NULL;
 
     tmp_ctx = talloc_new(NULL);
@@ -163,6 +165,8 @@ sdap_idmap_init(TALLOC_CTX *mem_ctx,
                                SDAP_IDMAP_RANGESIZE);
     autorid_mode = dp_opt_get_bool(idmap_ctx->id_ctx->opts->basic,
                                    SDAP_IDMAP_AUTORID_COMPAT);
+    extra_slice_max = dp_opt_get_int(idmap_ctx->id_ctx->opts->basic,
+                                     SDAP_IDMAP_EXTRA_SLICE_MAX);
 
     /* Validate that the values make sense */
     if (rangesize <= 0
@@ -203,6 +207,7 @@ sdap_idmap_init(TALLOC_CTX *mem_ctx,
     err |= sss_idmap_ctx_set_lower(idmap_ctx->map, idmap_lower);
     err |= sss_idmap_ctx_set_upper(idmap_ctx->map, idmap_upper);
     err |= sss_idmap_ctx_set_rangesize(idmap_ctx->map, rangesize);
+    err |= sss_idmap_ctx_set_extra_slice_max(idmap_ctx->map, extra_slice_max);
     if (err != IDMAP_SUCCESS) {
         /* This should never happen */
         DEBUG(SSSDBG_CRIT_FAILURE, "sss_idmap_ctx corrupted\n");
@@ -377,8 +382,8 @@ sdap_idmap_add_domain(struct sdap_idmap_ctx *idmap_ctx,
     }
 
     /* Add this domain to the map */
-    err = sss_idmap_add_domain_ex(idmap_ctx->map, dom_name, dom_sid, &range,
-                                  NULL, 0, external_mapping);
+    err = sss_idmap_add_auto_domain_ex(idmap_ctx->map, dom_name, dom_sid,
+                                       &range, NULL, 0, external_mapping);
     if (err != IDMAP_SUCCESS) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               "Could not add domain [%s] to the map: [%d]\n",
diff --git a/src/tests/cmocka/test_sss_idmap.c b/src/tests/cmocka/test_sss_idmap.c
index 1e52c8507461ab3caa04eb2c0e63410c363ba723..91e38546436bb3c248e63dcba0bbc3ba425c3483 100644
--- a/src/tests/cmocka/test_sss_idmap.c
+++ b/src/tests/cmocka/test_sss_idmap.c
@@ -82,7 +82,7 @@ static int test_sss_idmap_setup(void **state)
 }
 
 static int setup_ranges(struct test_ctx *test_ctx, bool external_mapping,
-                        bool second_domain)
+                        bool second_domain, bool sec_slices)
 {
     struct sss_idmap_range range;
     enum idmap_error_code err;
@@ -103,15 +103,26 @@ static int setup_ranges(struct test_ctx *test_ctx, bool external_mapping,
         sid = TEST_DOM_SID;
     }
 
-    err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, name, sid, &range, NULL,
-                                  0, external_mapping);
+    if (sec_slices) {
+        err = sss_idmap_add_auto_domain_ex(test_ctx->idmap_ctx, name, sid,
+                                           &range, NULL, 0, external_mapping);
+    } else {
+        err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, name, sid, &range,
+                                      NULL, 0, external_mapping);
+    }
     assert_int_equal(err, IDMAP_SUCCESS);
 
     range.min += TEST_OFFSET;
     range.max += TEST_OFFSET;
 
-    err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, name, sid, &range, NULL,
-                                  TEST_OFFSET, external_mapping);
+    if (sec_slices) {
+        err = sss_idmap_add_auto_domain_ex(test_ctx->idmap_ctx, name, sid,
+                                           &range, NULL, TEST_OFFSET,
+                                           external_mapping);
+    } else {
+        err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, name, sid, &range,
+                                      NULL, TEST_OFFSET, external_mapping);
+    }
     assert_int_equal(err, IDMAP_SUCCESS);
     return 0;
 }
@@ -124,7 +135,19 @@ static int test_sss_idmap_setup_with_domains(void **state) {
     test_ctx = talloc_get_type(*state, struct test_ctx);
     assert_non_null(test_ctx);
 
-    setup_ranges(test_ctx, false, false);
+    setup_ranges(test_ctx, false, false, false);
+    return 0;
+}
+
+static int test_sss_idmap_setup_with_domains_sec_slices(void **state) {
+    struct test_ctx *test_ctx;
+
+    test_sss_idmap_setup(state);
+
+    test_ctx = talloc_get_type(*state, struct test_ctx);
+    assert_non_null(test_ctx);
+
+    setup_ranges(test_ctx, false, false, true);
     return 0;
 }
 
@@ -136,7 +159,7 @@ static int test_sss_idmap_setup_with_external_mappings(void **state) {
     test_ctx = talloc_get_type(*state, struct test_ctx);
     assert_non_null(test_ctx);
 
-    setup_ranges(test_ctx, true, false);
+    setup_ranges(test_ctx, true, false, false);
     return 0;
 }
 
@@ -148,8 +171,8 @@ static int test_sss_idmap_setup_with_both(void **state) {
     test_ctx = talloc_get_type(*state, struct test_ctx);
     assert_non_null(test_ctx);
 
-    setup_ranges(test_ctx, false, false);
-    setup_ranges(test_ctx, true, true);
+    setup_ranges(test_ctx, false, false, false);
+    setup_ranges(test_ctx, true, true, false);
     return 0;
 }
 
@@ -274,6 +297,48 @@ void test_map_id(void **state)
     sss_idmap_free_sid(test_ctx->idmap_ctx, sid);
 }
 
+void test_map_id_sec_slices(void **state)
+{
+    struct test_ctx *test_ctx;
+    enum idmap_error_code err;
+    uint32_t id;
+    char *sid = NULL;
+
+    test_ctx = talloc_get_type(*state, struct test_ctx);
+
+    assert_non_null(test_ctx);
+
+    err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"1-1", &id);
+    assert_int_equal(err, IDMAP_NO_DOMAIN);
+
+    err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-4000000",
+                                &id);
+    assert_int_equal(err, IDMAP_SUCCESS);
+    assert_int_equal(id, 351800000);
+
+    err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_OFFSET - 1, &sid);
+    assert_int_equal(err, IDMAP_NO_DOMAIN);
+
+    err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-0", &id);
+    assert_int_equal(err, IDMAP_SUCCESS);
+    assert_int_equal(id, TEST_RANGE_MIN);
+
+    err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid);
+    assert_int_equal(err, IDMAP_SUCCESS);
+    assert_string_equal(sid, TEST_DOM_SID"-0");
+    sss_idmap_free_sid(test_ctx->idmap_ctx, sid);
+
+    err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx,
+                                TEST_DOM_SID"-"TEST_OFFSET_STR, &id);
+    assert_int_equal(err, IDMAP_SUCCESS);
+    assert_int_equal(id, TEST_RANGE_MIN+TEST_OFFSET);
+
+    err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid);
+    assert_int_equal(err, IDMAP_SUCCESS);
+    assert_string_equal(sid, TEST_DOM_SID"-"TEST_OFFSET_STR);
+    sss_idmap_free_sid(test_ctx->idmap_ctx, sid);
+}
+
 void test_map_id_external(void **state)
 {
     struct test_ctx *test_ctx;
@@ -523,6 +588,9 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_map_id,
                                         test_sss_idmap_setup_with_domains,
                                         test_sss_idmap_teardown),
+        cmocka_unit_test_setup_teardown(test_map_id_sec_slices,
+                                        test_sss_idmap_setup_with_domains_sec_slices,
+                                        test_sss_idmap_teardown),
         cmocka_unit_test_setup_teardown(test_map_id_external,
                                         test_sss_idmap_setup_with_external_mappings,
                                         test_sss_idmap_teardown),
diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
index f5ec68383679bfc685467bd625c86b8d6f474d48..3d2fbde196364d2b078ef7090cbf40c69fa6e90c 100644
--- a/src/tests/sss_idmap-tests.c
+++ b/src/tests/sss_idmap-tests.c
@@ -85,6 +85,17 @@ void idmap_add_domain_setup(void)
     fail_unless(err == IDMAP_SUCCESS, "sss_idmap_add_domain failed.");
 }
 
+void idmap_add_domain_with_sec_slices_setup(void)
+{
+    enum idmap_error_code err;
+    struct sss_idmap_range range = {IDMAP_RANGE_MIN, IDMAP_RANGE_MAX};
+
+    err = sss_idmap_add_auto_domain_ex(idmap_ctx, "test.dom", "S-1-5-21-1-2-3",
+                                       &range, NULL, 0, false);
+
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_add_auto_domain_ex failed.");
+}
+
 START_TEST(idmap_test_is_domain_sid)
 {
     size_t c;
@@ -225,6 +236,63 @@ START_TEST(idmap_test_sid2uid)
 }
 END_TEST
 
+START_TEST(idmap_test_sid2uid_ss)
+{
+    enum idmap_error_code err;
+    uint32_t id;
+
+    err = sss_idmap_sid_to_unix(idmap_ctx, "S-1-5-21-1-2-3333-1000", &id);
+    fail_unless(err == IDMAP_NO_DOMAIN, "sss_idmap_sid_to_unix did not detect "
+                                        "unknown domain");
+
+    /* RID out of primary and secondary range */
+    err = sss_idmap_sid_to_unix(idmap_ctx, "S-1-5-21-1-2-3-4000000", &id);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_sid_to_unix failed.");
+    fail_unless(id == 351800000,
+                "sss_idmap_sid_to_unix returned wrong id, "
+                "got [%d], expected [%d].", id, 351800000);
+
+    err = sss_idmap_sid_to_unix(idmap_ctx, "S-1-5-21-1-2-3-1000", &id);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_sid_to_unix failed.");
+    fail_unless(id == (1000 + IDMAP_RANGE_MIN),
+                "sss_idmap_sid_to_unix returned wrong id, "
+                "got [%d], expected [%d].", id, 1000 + IDMAP_RANGE_MIN);
+
+    err = sss_idmap_sid_to_unix(idmap_ctx, "S-1-5-21-1-2-3-210000", &id);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_sid_to_unix failed.");
+    fail_unless(id == 263810000,
+                "sss_idmap_sid_to_unix returned wrong id, "
+                "got [%d], expected [%d].", id, 265210000);
+}
+END_TEST
+
+START_TEST(idmap_test_sid2uid_ext_sec_slices)
+{
+    enum idmap_error_code err;
+    uint32_t id;
+    char *sid;
+
+    err = sss_idmap_unix_to_sid(idmap_ctx, 351800000, &sid);
+    fail_unless(err == IDMAP_NO_DOMAIN, "sss_idmap_unix_to_sid did not detect "
+                                        "id out of range");
+
+    /* RID out of primary and secondary range */
+    err = sss_idmap_sid_to_unix(idmap_ctx, "S-1-5-21-1-2-3-4000000", &id);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_sid_to_unix failed.");
+    fail_unless(id == 351800000,
+                "sss_idmap_sid_to_unix returned wrong id, "
+                "got [%d], expected [%d].", id, 351800000);
+
+    /* Secondary ranges were expanded by sid_to_unix call */
+    err = sss_idmap_unix_to_sid(idmap_ctx, 351800000, &sid);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_unix_to_sid failed.");
+    fail_unless(strcmp(sid, "S-1-5-21-1-2-3-4000000") == 0,
+                "sss_idmap_unix_to_sid returned wrong SID, "
+                "expected [%s], got [%s].", "S-1-5-21-1-2-3-4000000", sid);
+    sss_idmap_free_sid(idmap_ctx, sid);
+}
+END_TEST
+
 START_TEST(idmap_test_bin_sid2uid)
 {
     enum idmap_error_code err;
@@ -284,6 +352,34 @@ START_TEST(idmap_test_uid2sid)
 }
 END_TEST
 
+START_TEST(idmap_test_uid2sid_ss)
+{
+    enum idmap_error_code err;
+    char *sid;
+
+    err = sss_idmap_unix_to_sid(idmap_ctx, 10000, &sid);
+    fail_unless(err == IDMAP_NO_DOMAIN, "sss_idmap_unix_to_sid did not detect "
+                                        "id out of range");
+
+    err = sss_idmap_unix_to_sid(idmap_ctx, 2234, &sid);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_unix_to_sid failed.");
+    fail_unless(strcmp(sid, "S-1-5-21-1-2-3-1000") == 0,
+                "sss_idmap_unix_to_sid returned wrong SID, "
+                "expected [%s], got [%s].", "S-1-5-21-1-2-3-1000", sid);
+
+    sss_idmap_free_sid(idmap_ctx, sid);
+
+    /* Secondary ranges */
+    err = sss_idmap_unix_to_sid(idmap_ctx, 263810000, &sid);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_unix_to_sid failed.");
+    fail_unless(strcmp(sid, "S-1-5-21-1-2-3-210000") == 0,
+                "sss_idmap_unix_to_sid returned wrong SID, "
+                "expected [%s], got [%s].", "S-1-5-21-1-2-3-210000", sid);
+
+    sss_idmap_free_sid(idmap_ctx, sid);
+}
+END_TEST
+
 START_TEST(idmap_test_uid2dom_sid)
 {
     enum idmap_error_code err;
@@ -618,6 +714,24 @@ Suite *idmap_test_suite (void)
 
     suite_add_tcase(s, tc_map);
 
+    /* Test secondary slices */
+    TCase *tc_map_ss = tcase_create("IDMAP mapping tests");
+    tcase_add_checked_fixture(tc_map_ss,
+                              ck_leak_check_setup,
+                              ck_leak_check_teardown);
+    tcase_add_checked_fixture(tc_map_ss,
+                              idmap_ctx_setup,
+                              idmap_ctx_teardown);
+    tcase_add_checked_fixture(tc_map_ss,
+                              idmap_add_domain_with_sec_slices_setup,
+                              NULL);
+
+    tcase_add_test(tc_map_ss, idmap_test_sid2uid_ss);
+    tcase_add_test(tc_map_ss, idmap_test_uid2sid_ss);
+    tcase_add_test(tc_map_ss, idmap_test_sid2uid_ext_sec_slices);
+
+    suite_add_tcase(s, tc_map_ss);
+
     return s;
 }
 int main(int argc, const char *argv[])
-- 
2.4.3

