From 0caaf35dc861e99c9861f7ef11395eba627f475e 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/lib/idmap/sss_idmap.c   | 141 ++++++++++++++++++++++++++++++++++++++++++++
 src/tests/sss_idmap-tests.c |  18 +++++-
 2 files changed, 158 insertions(+), 1 deletion(-)

diff --git a/src/lib/idmap/sss_idmap.c b/src/lib/idmap/sss_idmap.c
index 7a91e419983d59bed6a8ae06f7cfd448e8e18f63..37005fd4de9c186220f41374d0f0ee53742687c5 100644
--- a/src/lib/idmap/sss_idmap.c
+++ b/src/lib/idmap/sss_idmap.c
@@ -49,6 +49,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[10];
+    int sec_ranges_used;
 };
 
 static void *default_alloc(size_t size, void *pvt)
@@ -346,6 +349,30 @@ enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx,
                     max = min + rangesize;
                     break;
                 }
+
+                // Check collisions with secondary ranges.
+                for (int j=0; j < dom->sec_ranges_used; j++) {
+
+                    dmin = dom->sec_ranges[j].min_id;
+                    dmax = dom->sec_ranges[j].max_id;
+
+                    if ((dmin <= min && dmax >= max) ||
+                        (dmin >= min && dmin <= max) ||
+                        (dmax >= min && dmax <= max)) {
+                        /* This range overlaps one already registered
+                         * We'll try the next available slot
+                         */
+                        new_slice++;
+                        if (new_slice >= max_slices) {
+                            /* loop around to the beginning if necessary */
+                            new_slice = 0;
+                        }
+
+                        min = (rangesize * new_slice) + idmap_lower;
+                        max = min + rangesize;
+                        break;
+                    }
+                }
             }
 
             /* Keep trying until dom is NULL (meaning we got to the end
@@ -558,6 +585,55 @@ enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx,
     dom->next = ctx->idmap_domain_info;
     ctx->idmap_domain_info = dom;
 
+    /* Add secondary ranges */
+    /* Should be done after primary slices for all domains are generated. */
+    int tmp = 10;
+    for (int i = 0; i < tmp; i++) {
+
+#define FMT "%s#%d"
+
+        int len = snprintf(NULL, 0, FMT, domain_name, i+1);
+        if (len <= 0) {
+            err = IDMAP_ERROR;
+            goto fail;
+        }
+
+        char *secondary_name = ctx->alloc_func(len + 1, ctx->alloc_pvt);
+        if (secondary_name == NULL) {
+            err = IDMAP_OUT_OF_MEMORY;
+            goto fail;
+        }
+
+        int len2 = snprintf(secondary_name, len + 1, SID_FMT, domain_name, i+1);
+        if (len != len2) {
+            ctx->free_func(secondary_name, ctx->alloc_pvt);
+            err = IDMAP_ERROR;
+            goto fail;
+        }
+
+        struct sss_idmap_range tmp_range;
+        err = sss_idmap_calculate_range(ctx, secondary_name, NULL, &tmp_range);
+        if (err != IDMAP_SUCCESS) {
+            ctx->free_func(secondary_name, ctx->alloc_pvt);
+            if (err == IDMAP_OUT_OF_SLICES) {
+                break;
+            }
+            goto fail;
+        }
+
+        struct idmap_range_params *rp = &dom->sec_ranges[i];
+        rp->min_id = tmp_range.min;
+        rp->max_id = tmp_range.max;
+        // Todo free
+        //rp->range_id = secondary_name;
+        ctx->free_func(secondary_name, ctx->alloc_pvt);
+
+
+        rp->first_rid = rid;
+
+        dom->sec_ranges_used++;
+    }
+
     return IDMAP_SUCCESS;
 
 fail:
@@ -589,6 +665,7 @@ enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx,
                                             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;
@@ -617,6 +694,8 @@ enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx,
                     return IDMAP_EXTERNAL;
                 }
 
+                matched_dom = idmap_domain_info;
+
                 errno = 0;
                 rid = strtoull(sid + dom_len + 1, &endptr, 10);
                 if (errno != 0 || rid > UINT32_MAX || *endptr != '\0') {
@@ -639,6 +718,31 @@ enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx,
         idmap_domain_info = idmap_domain_info->next;
     }
 
+    // Let's try secondary slices
+    if (matched_dom != NULL) {
+
+        dom_len = strlen(matched_dom->sid);
+
+        errno = 0;
+        rid = strtoull(sid + dom_len + 1, &endptr, 10);
+        if (errno != 0 || rid > UINT32_MAX || *endptr != '\0') {
+            return IDMAP_SID_INVALID;
+        }
+
+        for (int j=0; j < matched_dom->sec_ranges_used; j++) {
+
+            struct idmap_range_params *rp = &matched_dom->sec_ranges[j];
+
+            if (rid >= rp->first_rid) {
+                id = rp->min_id + (rid - rp->first_rid);
+                if (id <= rp->max_id) {
+                    *_id = id;
+                    return IDMAP_SUCCESS;
+                }
+            }
+        }
+    }
+
     return no_range ? IDMAP_NO_RANGE : IDMAP_NO_DOMAIN;
 }
 
@@ -732,6 +836,43 @@ enum idmap_error_code sss_idmap_unix_to_sid(struct sss_idmap_ctx *ctx,
         idmap_domain_info = idmap_domain_info->next;
     }
 
+    // Secondary ranges
+    idmap_domain_info = ctx->idmap_domain_info;
+    while (idmap_domain_info != NULL) {
+
+        for (int j=0; j < idmap_domain_info->sec_ranges_used; j++) {
+
+            if (id_is_in_range(id, &idmap_domain_info->sec_ranges[j], &rid)) {
+
+                if (idmap_domain_info->external_mapping == true
+                    || idmap_domain_info->sid == NULL) {
+                    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;
+                }
+
+                sid = ctx->alloc_func(len + 1, ctx->alloc_pvt);
+                if (sid == NULL) {
+                    return IDMAP_OUT_OF_MEMORY;
+                }
+
+                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;
+                }
+
+                *_sid = sid;
+                return IDMAP_SUCCESS;
+            }
+        }
+
+        idmap_domain_info = idmap_domain_info->next;
+    }
+
     return IDMAP_NO_DOMAIN;
 }
 
diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
index f5ec68383679bfc685467bd625c86b8d6f474d48..071f1af19e5313670bd082816797b394423037a9 100644
--- a/src/tests/sss_idmap-tests.c
+++ b/src/tests/sss_idmap-tests.c
@@ -213,7 +213,8 @@ START_TEST(idmap_test_sid2uid)
     fail_unless(err == IDMAP_NO_DOMAIN, "sss_idmap_sid_to_unix did not detect "
                                         "unknown domain");
 
-    err = sss_idmap_sid_to_unix(idmap_ctx, "S-1-5-21-1-2-3-10000", &id);
+    /* RID out of primary and secondary range */
+    err = sss_idmap_sid_to_unix(idmap_ctx, "S-1-5-21-1-2-3-2000000", &id);
     fail_unless(err == IDMAP_NO_RANGE, "sss_idmap_sid_to_unix did not detect "
                                        "RID out of range");
 
@@ -222,6 +223,12 @@ START_TEST(idmap_test_sid2uid)
     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-10000", &id);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_sid_to_unix failed.");
+    fail_unless(id == 265210000,
+                "sss_idmap_sid_to_unix returned wrong id, "
+                "got [%d], expected [%d].", id, 265210000);
 }
 END_TEST
 
@@ -281,6 +288,15 @@ START_TEST(idmap_test_uid2sid)
                 "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, 265210000, &sid);
+    fail_unless(err == IDMAP_SUCCESS, "sss_idmap_unix_to_sid failed.");
+    fail_unless(strcmp(sid, "S-1-5-21-1-2-3-10000") == 0,
+                "sss_idmap_unix_to_sid returned wrong SID, "
+                "expected [%s], got [%s].", "S-1-5-21-1-2-3-10000", sid);
+
+    sss_idmap_free_sid(idmap_ctx, sid);
 }
 END_TEST
 
-- 
2.4.3

