autorid: add debug messages to idmap_autorid_get_domainrange()
[obnox/samba/samba-obnox.git] / source3 / winbindd / idmap_autorid_tdb.c
index 4220a9c78768a6451ed99c2799ee5306e1d4ab42..de6af7d123bb4f674935ef31ba5c3ab8679b601a 100644 (file)
@@ -42,6 +42,23 @@ static void idmap_autorid_build_keystr(const char *domsid,
        }
 }
 
+static char *idmap_autorid_build_keystr_talloc(TALLOC_CTX *mem_ctx,
+                                             const char *domsid,
+                                             uint32_t domain_range_index)
+{
+       char *keystr;
+
+       if (domain_range_index > 0) {
+               keystr = talloc_asprintf(mem_ctx, "%s#%"PRIu32, domsid,
+                                        domain_range_index);
+       } else {
+               keystr = talloc_strdup(mem_ctx, domsid);
+       }
+
+       return keystr;
+}
+
+
 static bool idmap_autorid_validate_sid(const char *sid)
 {
        struct dom_sid ignore;
@@ -74,6 +91,7 @@ static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
        struct autorid_global_config *globalcfg;
        fstring keystr;
        uint32_t increment;
+       TALLOC_CTX *mem_ctx = NULL;
 
        ctx = (struct idmap_autorid_addrange_ctx *)private_data;
        range = ctx->range;
@@ -129,11 +147,12 @@ static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
        if (!NT_STATUS_IS_OK(ret)) {
                DEBUG(1, ("Fatal error while fetching current "
                          "HWM value: %s\n", nt_errstr(ret)));
-               ret = NT_STATUS_INTERNAL_ERROR;
-               goto error;
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       ret = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
+       mem_ctx = talloc_stackframe();
+
+       ret = idmap_autorid_loadconfig(db, mem_ctx, &globalcfg);
        if (!NT_STATUS_IS_OK(ret)) {
                DEBUG(1, ("Fatal error while fetching configuration: %s\n",
                          nt_errstr(ret)));
@@ -145,18 +164,6 @@ static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
                 * automatically acquire the next range
                 */
                requested_rangenum = hwm;
-       } else {
-               /*
-                * set a specified range
-                */
-
-               if (requested_rangenum < hwm) {
-                       DEBUG(3, ("Invalid range %u requested: Range may not "
-                                 "be smaller than %u (current HWM)\n",
-                                 requested_rangenum, hwm));
-                       ret = NT_STATUS_INVALID_PARAMETER;
-                       goto error;
-               }
        }
 
        if (requested_rangenum >= globalcfg->maxranges) {
@@ -167,28 +174,66 @@ static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
                ret = NT_STATUS_NO_MEMORY;
                goto error;
        }
-       TALLOC_FREE(globalcfg);
 
-       /* HWM always contains current max range + 1 */
-       increment = requested_rangenum + 1 - hwm;
+       /*
+        * Check that it is not yet taken.
+        * If the range is requested and < HWM, we need
+        * to check anyways, and otherwise, we also better
+        * check in order to prevent further corruption
+        * in case the db has been externally modified.
+        */
+
+       numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
+       if (!numstr) {
+               DEBUG(1, ("Talloc failed!\n"));
+               ret = NT_STATUS_NO_MEMORY;
+               goto error;
+       }
+
+       if (dbwrap_exists(db, string_term_tdb_data(numstr))) {
+               DEBUG(1, ("Requested range '%s' is already in use.\n", numstr));
+
+               if (requested_rangenum < hwm) {
+                       ret = NT_STATUS_INVALID_PARAMETER;
+               } else {
+                       ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
 
-       /* increase the HWM */
-       ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm, increment);
-       if (!NT_STATUS_IS_OK(ret)) {
-               DEBUG(1, ("Fatal error while fetching a new "
-                         "domain range value!\n"));
                goto error;
        }
 
-       /* store away the new mapping in both directions */
+       if (requested_rangenum >= hwm) {
+               /*
+                * requested or automatic range >= HWM:
+                * increment the HWM.
+                */
+
+               /* HWM always contains current max range + 1 */
+               increment = requested_rangenum + 1 - hwm;
+
+               /* increase the HWM */
+               ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm,
+                                                          increment);
+               if (!NT_STATUS_IS_OK(ret)) {
+                       DEBUG(1, ("Fatal error while incrementing the HWM "
+                                 "value in the database: %s\n",
+                                 nt_errstr(ret)));
+                       goto error;
+               }
+       }
+
+       /*
+        * store away the new mapping in both directions
+        */
+
        ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
        if (!NT_STATUS_IS_OK(ret)) {
                DEBUG(1, ("Fatal error while storing new "
-                         "domain->range assignment!\n"));
+                         "domain->range assignment: %s\n", nt_errstr(ret)));
                goto error;
        }
 
-       numstr = talloc_asprintf(db, "%u", requested_rangenum);
+       numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
        if (!numstr) {
                ret = NT_STATUS_NO_MEMORY;
                goto error;
@@ -197,14 +242,16 @@ static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
        ret = dbwrap_store_bystring(db, numstr,
                        string_term_tdb_data(keystr), TDB_INSERT);
 
-       talloc_free(numstr);
        if (!NT_STATUS_IS_OK(ret)) {
-               DEBUG(1, ("Fatal error while storing "
-                         "new domain->range assignment!\n"));
+               DEBUG(1, ("Fatal error while storing new "
+                         "domain->range assignment: %s\n", nt_errstr(ret)));
                goto error;
        }
-       DEBUG(5, ("Acquired new range #%d for domain %s "
-                 "(domain_range_index=%"PRIu32")\n", requested_rangenum, keystr,
+
+       DEBUG(5, ("%s new range #%d for domain %s "
+                 "(domain_range_index=%"PRIu32")\n",
+                 (acquire?"Acquired":"Stored"),
+                 requested_rangenum, keystr,
                  range->domain_range_index));
 
        range->rangenum = requested_rangenum;
@@ -212,9 +259,10 @@ static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
        range->low_id = globalcfg->minvalue
                      + range->rangenum * globalcfg->rangesize;
 
-       return NT_STATUS_OK;
+       ret = NT_STATUS_OK;
 
 error:
+       talloc_free(mem_ctx);
        return ret;
 }
 
@@ -249,6 +297,12 @@ NTSTATUS idmap_autorid_setrange(struct db_context *db,
        return status;
 }
 
+static NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
+                                           struct autorid_range_config *range)
+{
+       return idmap_autorid_addrange(db, range, true);
+}
+
 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
                                           struct autorid_range_config *range)
 {
@@ -261,13 +315,19 @@ static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
                goto done;
        }
 
+       if (!idmap_autorid_validate_sid(range->domsid)) {
+               DEBUG(3, ("Invalid SID: '%s'\n", range->domsid));
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+
        idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
                                   keystr);
 
        DEBUG(10, ("reading domain range for key %s\n", keystr));
        status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to read database for key '%s': %s\n",
+               DEBUG(1, ("Failed to read database record for key '%s': %s\n",
                          keystr, nt_errstr(status)));
                goto done;
        }
@@ -324,11 +384,15 @@ NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
 
        ret = idmap_autorid_getrange_int(db, range);
        if (!NT_STATUS_IS_OK(ret)) {
+               DEBUG(10, ("Failed to read range config for '%s': %s\n",
+                          range->domsid, nt_errstr(ret)));
                if (read_only) {
+                       DEBUG(10, ("Not allocating new range for '%s' because "
+                                  "read-only is enabled.\n", range->domsid));
                        return NT_STATUS_NOT_FOUND;
                }
 
-               ret = idmap_autorid_addrange(db, range, true);
+               ret = idmap_autorid_acquire_range(db, range);
        }
 
        DEBUG(10, ("Using range #%d for domain %s "
@@ -340,33 +404,309 @@ NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
 }
 
 /* initialize the given HWM to 0 if it does not exist yet */
+static NTSTATUS idmap_autorid_init_hwm_action(struct db_context *db,
+                                             void *private_data)
+{
+       NTSTATUS status;
+       uint32_t hwmval;
+       const char *hwm;
+
+       hwm = (char *)private_data;
+
+       status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
+       if (NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("HWM (%s) already initialized in autorid database "
+                         "(value %"PRIu32").\n", hwm, hwmval));
+               return NT_STATUS_OK;
+       }
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               DEBUG(0, ("Error fetching HWM (%s) from autorid "
+                         "database: %s\n", hwm, nt_errstr(status)));
+               return status;
+       }
+
+       status = dbwrap_trans_store_uint32_bystring(db, hwm, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("Error storing HWM (%s) in autorid database: %s\n",
+                         hwm, nt_errstr(status)));
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
 {
        NTSTATUS status;
        uint32_t hwmval;
 
        status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND))  {
-               status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0,
-                             ("Unable to initialise HWM (%s) in autorid "
-                              "database: %s\n", hwm, nt_errstr(status)));
-                       return NT_STATUS_INTERNAL_DB_ERROR;
-               }
-       } else if (!NT_STATUS_IS_OK(status)) {
+       if (NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("HWM (%s) already initialized in autorid database "
+                         "(value %"PRIu32").\n", hwm, hwmval));
+               return NT_STATUS_OK;
+       }
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
                DEBUG(0, ("unable to fetch HWM (%s) from autorid "
                          "database: %s\n", hwm,  nt_errstr(status)));
                return status;
        }
 
+       status = dbwrap_trans_do(db, idmap_autorid_init_hwm_action,
+                                (void *)hwm);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("Error initializing HWM (%s) in autorid database: "
+                         "%s\n", hwm, nt_errstr(status)));
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       DEBUG(1, ("Initialized HWM (%s) in autorid database.\n", hwm));
+
        return NT_STATUS_OK;
 }
 
 /*
- * open and initialize the database which stores the ranges for the domains
+ * Delete a domain#index <-> range mapping from the database.
+ * The mapping is specified by the sid and index.
+ * If force == true, invalid mapping records are deleted as far
+ * as possible, otherwise they are left untouched.
  */
-NTSTATUS idmap_autorid_db_init(const char *path,
+
+struct idmap_autorid_delete_range_by_sid_ctx {
+       const char *domsid;
+       uint32_t domain_range_index;
+       bool force;
+};
+
+static NTSTATUS idmap_autorid_delete_range_by_sid_action(struct db_context *db,
+                                                        void *private_data)
+{
+       struct idmap_autorid_delete_range_by_sid_ctx *ctx =
+               (struct idmap_autorid_delete_range_by_sid_ctx *)private_data;
+       const char *domsid;
+       uint32_t domain_range_index;
+       uint32_t rangenum;
+       char *keystr;
+       char *range_keystr;
+       TDB_DATA data;
+       NTSTATUS status;
+       TALLOC_CTX *frame = talloc_stackframe();
+       bool is_valid_range_mapping = true;
+       bool force;
+
+       domsid = ctx->domsid;
+       domain_range_index = ctx->domain_range_index;
+       force = ctx->force;
+
+       keystr = idmap_autorid_build_keystr_talloc(frame, domsid,
+                                                  domain_range_index);
+       if (keystr == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       status = dbwrap_fetch_uint32_bystring(db, keystr, &rangenum);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
+       if (range_keystr == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       status = dbwrap_fetch_bystring(db, frame, range_keystr, &data);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               DEBUG(1, ("Incomplete mapping %s -> %s: no backward mapping\n",
+                         keystr, range_keystr));
+               is_valid_range_mapping = false;
+       } else if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Error fetching reverse mapping for %s -> %s:  %s\n",
+                         keystr, range_keystr, nt_errstr(status)));
+               goto done;
+       } else if (strncmp((const char *)data.dptr, keystr, strlen(keystr))
+                  != 0)
+       {
+               DEBUG(1, ("Invalid mapping: %s -> %s -> %s\n",
+                         keystr, range_keystr, (const char *)data.dptr));
+               is_valid_range_mapping = false;
+       }
+
+       if (!is_valid_range_mapping && !force) {
+               DEBUG(10, ("Not deleting invalid mapping, since not in force "
+                          "mode.\n"));
+               status = NT_STATUS_FILE_INVALID;
+               goto done;
+       }
+
+       status = dbwrap_delete_bystring(db, keystr);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Deletion of '%s' failed: %s\n",
+                         keystr, nt_errstr(status)));
+               goto done;
+       }
+
+       if (!is_valid_range_mapping) {
+               goto done;
+       }
+
+       status = dbwrap_delete_bystring(db, range_keystr);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Deletion of '%s' failed: %s\n",
+                         range_keystr, nt_errstr(status)));
+               goto done;
+       }
+
+       DEBUG(10, ("Deleted range mapping %s <--> %s\n", keystr,
+                  range_keystr));
+
+done:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+NTSTATUS idmap_autorid_delete_range_by_sid(struct db_context *db,
+                                          const char *domsid,
+                                          uint32_t domain_range_index,
+                                          bool force)
+{
+       NTSTATUS status;
+       struct idmap_autorid_delete_range_by_sid_ctx ctx;
+
+       ctx.domain_range_index = domain_range_index;
+       ctx.domsid = domsid;
+       ctx.force = force;
+
+       status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_sid_action,
+                                &ctx);
+       return status;
+}
+
+/*
+ * Delete a domain#index <-> range mapping from the database.
+ * The mapping is specified by the range number.
+ * If force == true, invalid mapping records are deleted as far
+ * as possible, otherwise they are left untouched.
+ */
+struct idmap_autorid_delete_range_by_num_ctx {
+       uint32_t rangenum;
+       bool force;
+};
+
+static NTSTATUS idmap_autorid_delete_range_by_num_action(struct db_context *db,
+                                                          void *private_data)
+{
+       struct idmap_autorid_delete_range_by_num_ctx *ctx =
+               (struct idmap_autorid_delete_range_by_num_ctx *)private_data;
+       uint32_t rangenum;
+       char *keystr;
+       char *range_keystr;
+       TDB_DATA val;
+       NTSTATUS status;
+       TALLOC_CTX *frame = talloc_stackframe();
+       bool is_valid_range_mapping = true;
+       bool force;
+
+       rangenum = ctx->rangenum;
+       force = ctx->force;
+
+       range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
+       if (range_keystr == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       ZERO_STRUCT(val);
+
+       status = dbwrap_fetch_bystring(db, frame, range_keystr, &val);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               DEBUG(10, ("Did not find range '%s' in database.\n",
+                          range_keystr));
+               goto done;
+       } else if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(5, ("Error fetching rang key: %s\n", nt_errstr(status)));
+               goto done;
+       }
+
+       if (val.dptr == NULL) {
+               DEBUG(1, ("Invalid mapping: %s -> empty value\n",
+                         range_keystr));
+               is_valid_range_mapping = false;
+       } else {
+               uint32_t reverse_rangenum = 0;
+
+               keystr = (char *)val.dptr;
+
+               status = dbwrap_fetch_uint32_bystring(db, keystr,
+                                                     &reverse_rangenum);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+                       DEBUG(1, ("Incomplete mapping %s -> %s: "
+                                 "no backward mapping\n",
+                                 range_keystr, keystr));
+                       is_valid_range_mapping = false;
+               } else if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("Error fetching reverse mapping for "
+                                 "%s -> %s: %s\n",
+                                 range_keystr, keystr, nt_errstr(status)));
+                       goto done;
+               } else if (rangenum != reverse_rangenum) {
+                       is_valid_range_mapping = false;
+               }
+       }
+
+       if (!is_valid_range_mapping && !force) {
+               DEBUG(10, ("Not deleting invalid mapping, since not in force "
+                          "mode.\n"));
+               status = NT_STATUS_FILE_INVALID;
+               goto done;
+       }
+
+       status = dbwrap_delete_bystring(db, range_keystr);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Deletion of '%s' failed: %s\n",
+                         range_keystr, nt_errstr(status)));
+               goto done;
+       }
+
+       if (!is_valid_range_mapping) {
+               goto done;
+       }
+
+       status = dbwrap_delete_bystring(db, keystr);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Deletion of '%s' failed: %s\n",
+                         keystr, nt_errstr(status)));
+               goto done;
+       }
+
+       DEBUG(10, ("Deleted range mapping %s <--> %s\n", range_keystr,
+                  keystr));
+
+done:
+       talloc_free(frame);
+       return status;
+}
+
+NTSTATUS idmap_autorid_delete_range_by_num(struct db_context *db,
+                                          uint32_t rangenum,
+                                          bool force)
+{
+       NTSTATUS status;
+       struct idmap_autorid_delete_range_by_num_ctx ctx;
+
+       ctx.rangenum = rangenum;
+       ctx.force = force;
+
+       status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_num_action,
+                                &ctx);
+       return status;
+}
+
+/**
+ * Open and possibly create the database.
+ */
+NTSTATUS idmap_autorid_db_open(const char *path,
                               TALLOC_CTX *mem_ctx,
                               struct db_context **db)
 {
@@ -379,26 +719,55 @@ NTSTATUS idmap_autorid_db_init(const char *path,
 
        /* Open idmap repository */
        *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
-                     DBWRAP_LOCK_ORDER_1);
+                     DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
 
        if (*db == NULL) {
                DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       /* Initialize high water mark for the currently used range to 0 */
+       return status;
+}
+
+/**
+ * Initialize the high watermark records in the database.
+ */
+NTSTATUS idmap_autorid_init_hwms(struct db_context *db)
+{
+       NTSTATUS status;
+
+       status = idmap_autorid_init_hwm(db, HWM);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = idmap_autorid_init_hwm(db, ALLOC_HWM_UID);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       status = idmap_autorid_init_hwm(*db, HWM);
-       NT_STATUS_NOT_OK_RETURN(status);
+       status = idmap_autorid_init_hwm(db, ALLOC_HWM_GID);
+
+       return status;
+}
 
-       status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
-       NT_STATUS_NOT_OK_RETURN(status);
+NTSTATUS idmap_autorid_db_init(const char *path,
+                              TALLOC_CTX *mem_ctx,
+                              struct db_context **db)
+{
+       NTSTATUS status;
 
-       status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
+       status = idmap_autorid_db_open(path, mem_ctx, db);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
+       status = idmap_autorid_init_hwms(*db);
        return status;
 }
 
+
+
 struct idmap_autorid_fetch_config_state {
        TALLOC_CTX *mem_ctx;
        char *configstr;
@@ -462,8 +831,8 @@ bool idmap_autorid_parse_configstr(const char *configstr,
                   "minvalue:%lu rangesize:%lu maxranges:%lu",
                   &minvalue, &rangesize, &maxranges) != 3) {
                DEBUG(1,
-                     ("Found invalid configuration data"
-                      "creating new config\n"));
+                     ("Found invalid configuration data"
+                      "Creating new config\n"));
                return false;
        }
 
@@ -543,6 +912,8 @@ NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
                DEBUG(5, ("No configuration found. Storing initial "
                          "configuration.\n"));
        } else if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Error loading configuration: %s\n",
+                         nt_errstr(status)));
                goto done;
        }
 
@@ -595,3 +966,292 @@ done:
        TALLOC_FREE(frame);
        return status;
 }
+
+NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
+                                    const char *configstr)
+{
+       bool ok;
+       NTSTATUS status;
+       struct autorid_global_config cfg;
+
+       ok = idmap_autorid_parse_configstr(configstr, &cfg);
+       if (!ok) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       status = idmap_autorid_saveconfig(db, &cfg);
+       return status;
+}
+
+
+/*
+ * iteration: Work on all range mappings for a given domain
+ */
+
+struct domain_range_visitor_ctx {
+       const char *domsid;
+       NTSTATUS (*fn)(struct db_context *db,
+                      const char *domsid,
+                      uint32_t index,
+                      uint32_t rangenum,
+                      void *private_data);
+       void *private_data;
+       int count; /* number of records worked on */
+};
+
+static int idmap_autorid_visit_domain_range(struct db_record *rec,
+                                           void *private_data)
+{
+       struct domain_range_visitor_ctx *vi;
+       char *domsid;
+       char *sep;
+       uint32_t range_index = 0;
+       uint32_t rangenum = 0;
+       TDB_DATA key, value;
+       NTSTATUS status;
+       int ret = 0;
+       struct db_context *db;
+
+       vi = talloc_get_type_abort(private_data,
+                                  struct domain_range_visitor_ctx);
+
+       key = dbwrap_record_get_key(rec);
+
+       /*
+        * split string "<sid>[#<index>]" into sid string and index number
+        */
+
+       domsid = (char *)key.dptr;
+
+       DEBUG(10, ("idmap_autorid_visit_domain_range: visiting key '%s'\n",
+                  domsid));
+
+       sep = strrchr(domsid, '#');
+       if (sep != NULL) {
+               char *index_str;
+               *sep = '\0';
+               index_str = sep+1;
+               if (sscanf(index_str, "%"SCNu32, &range_index) != 1) {
+                       DEBUG(10, ("Found separator '#' but '%s' is not a "
+                                  "valid range index. Skipping record\n",
+                                  index_str));
+                       goto done;
+               }
+       }
+
+       if (!idmap_autorid_validate_sid(domsid)) {
+               DEBUG(10, ("String '%s' is not a valid sid. "
+                          "Skipping record.\n", domsid));
+               goto done;
+       }
+
+       if ((vi->domsid != NULL) && (strcmp(domsid, vi->domsid) != 0)) {
+               DEBUG(10, ("key sid '%s' does not match requested sid '%s'.\n",
+                          domsid, vi->domsid));
+               goto done;
+       }
+
+       value = dbwrap_record_get_value(rec);
+
+       if (value.dsize != sizeof(uint32_t)) {
+               /* it might be a mapping of a well known sid */
+               DEBUG(10, ("value size %u != sizeof(uint32_t) for sid '%s', "
+                          "skipping.\n", (unsigned)value.dsize, vi->domsid));
+               goto done;
+       }
+
+       rangenum = IVAL(value.dptr, 0);
+
+       db = dbwrap_record_get_db(rec);
+
+       status = vi->fn(db, domsid, range_index, rangenum, vi->private_data);
+       if (!NT_STATUS_IS_OK(status)) {
+               ret = -1;
+               goto done;
+       }
+
+       vi->count++;
+       ret = 0;
+
+done:
+       return ret;
+}
+
+static NTSTATUS idmap_autorid_iterate_domain_ranges_int(struct db_context *db,
+                               const char *domsid,
+                               NTSTATUS (*fn)(struct db_context *db,
+                                              const char *domsid,
+                                              uint32_t index,
+                                              uint32_t rangnum,
+                                              void *private_data),
+                               void *private_data,
+                               int *count,
+                               NTSTATUS (*traverse)(struct db_context *db,
+                                         int (*f)(struct db_record *, void *),
+                                         void *private_data,
+                                         int *count))
+{
+       NTSTATUS status;
+       struct domain_range_visitor_ctx *vi;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (domsid == NULL) {
+               DEBUG(10, ("No sid provided, operating on all ranges\n"));
+       }
+
+       if (fn == NULL) {
+               DEBUG(1, ("Error: missing visitor callback\n"));
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+
+       vi = talloc_zero(frame, struct domain_range_visitor_ctx);
+       if (vi == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       vi->domsid = domsid;
+       vi->fn = fn;
+       vi->private_data = private_data;
+
+       status = traverse(db, idmap_autorid_visit_domain_range, vi, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       if (count != NULL) {
+               *count = vi->count;
+       }
+
+done:
+       talloc_free(frame);
+       return status;
+}
+
+NTSTATUS idmap_autorid_iterate_domain_ranges(struct db_context *db,
+                                       const char *domsid,
+                                       NTSTATUS (*fn)(struct db_context *db,
+                                                      const char *domsid,
+                                                      uint32_t index,
+                                                      uint32_t rangenum,
+                                                      void *private_data),
+                                       void *private_data,
+                                       int *count)
+{
+       NTSTATUS status;
+
+       status = idmap_autorid_iterate_domain_ranges_int(db,
+                                                        domsid,
+                                                        fn,
+                                                        private_data,
+                                                        count,
+                                                        dbwrap_traverse);
+
+       return status;
+}
+
+
+NTSTATUS idmap_autorid_iterate_domain_ranges_read(struct db_context *db,
+                                       const char *domsid,
+                                       NTSTATUS (*fn)(struct db_context *db,
+                                                      const char *domsid,
+                                                      uint32_t index,
+                                                      uint32_t rangenum,
+                                                      void *count),
+                                       void *private_data,
+                                       int *count)
+{
+       NTSTATUS status;
+
+       status = idmap_autorid_iterate_domain_ranges_int(db,
+                                                        domsid,
+                                                        fn,
+                                                        private_data,
+                                                        count,
+                                                        dbwrap_traverse_read);
+
+       return status;
+}
+
+
+/*
+ * Delete all ranges configured for a given domain
+ */
+
+struct delete_domain_ranges_visitor_ctx {
+       bool force;
+};
+
+static NTSTATUS idmap_autorid_delete_domain_ranges_visitor(
+                                               struct db_context *db,
+                                               const char *domsid,
+                                               uint32_t domain_range_index,
+                                               uint32_t rangenum,
+                                               void *private_data)
+{
+       struct delete_domain_ranges_visitor_ctx *ctx;
+       NTSTATUS status;
+
+       ctx = (struct delete_domain_ranges_visitor_ctx *)private_data;
+
+       status = idmap_autorid_delete_range_by_sid(
+                               db, domsid, domain_range_index, ctx->force);
+       return status;
+}
+
+struct idmap_autorid_delete_domain_ranges_ctx {
+       const char *domsid;
+       bool force;
+       int count; /* output: count records operated on */
+};
+
+static NTSTATUS idmap_autorid_delete_domain_ranges_action(struct db_context *db,
+                                                         void *private_data)
+{
+       struct idmap_autorid_delete_domain_ranges_ctx *ctx;
+       struct delete_domain_ranges_visitor_ctx visitor_ctx;
+       int count;
+       NTSTATUS status;
+
+       ctx = (struct idmap_autorid_delete_domain_ranges_ctx *)private_data;
+
+       ZERO_STRUCT(visitor_ctx);
+       visitor_ctx.force = ctx->force;
+
+       status = idmap_autorid_iterate_domain_ranges(db,
+                               ctx->domsid,
+                               idmap_autorid_delete_domain_ranges_visitor,
+                               &visitor_ctx,
+                               &count);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       ctx->count = count;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS idmap_autorid_delete_domain_ranges(struct db_context *db,
+                                           const char *domsid,
+                                           bool force,
+                                           int *count)
+{
+       NTSTATUS status;
+       struct idmap_autorid_delete_domain_ranges_ctx ctx;
+
+       ZERO_STRUCT(ctx);
+       ctx.domsid = domsid;
+       ctx.force = force;
+
+       status = dbwrap_trans_do(db, idmap_autorid_delete_domain_ranges_action,
+                                &ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       *count = ctx.count;
+
+       return NT_STATUS_OK;
+}