diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/winbindd/idmap_autorid_tdb.c | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | source3/winbindd/idmap_autorid_tdb.c | 1269 |
1 files changed, 1269 insertions, 0 deletions
diff --git a/source3/winbindd/idmap_autorid_tdb.c b/source3/winbindd/idmap_autorid_tdb.c new file mode 100644 index 0000000..6c76764 --- /dev/null +++ b/source3/winbindd/idmap_autorid_tdb.c @@ -0,0 +1,1269 @@ +/* + * idmap_autorid_tdb: This file contains common code used by + * idmap_autorid and net idmap autorid utilities. The common + * code provides functions for performing various operations + * on autorid.tdb + * + * Copyright (C) Christian Ambach, 2010-2012 + * Copyright (C) Atul Kulkarni, 2013 + * Copyright (C) Michael Adam, 2012-2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "idmap_autorid_tdb.h" +#include "../libcli/security/dom_sid.h" +#include "lib/util/string_wrappers.h" + +/** + * Build the database keystring for getting a range + * belonging to a domain sid and a range index. + */ +static void idmap_autorid_build_keystr(const char *domsid, + uint32_t domain_range_index, + fstring keystr) +{ + if (domain_range_index > 0) { + fstr_sprintf(keystr, "%s#%"PRIu32, + domsid, domain_range_index); + } else { + fstrcpy(keystr, 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; + if (sid == NULL) { + return false; + } + + if (strcmp(sid, ALLOC_RANGE) == 0) { + return true; + } + + return dom_sid_parse(sid, &ignore); +} + +struct idmap_autorid_addrange_ctx { + struct autorid_range_config *range; + bool acquire; +}; + +static NTSTATUS idmap_autorid_addrange_action(struct db_context *db, + void *private_data) +{ + struct idmap_autorid_addrange_ctx *ctx; + uint32_t requested_rangenum, stored_rangenum; + struct autorid_range_config *range; + bool acquire; + NTSTATUS ret; + uint32_t hwm; + char *numstr; + struct autorid_global_config globalcfg = {0}; + fstring keystr; + uint32_t increment; + TALLOC_CTX *mem_ctx = NULL; + + ctx = (struct idmap_autorid_addrange_ctx *)private_data; + range = ctx->range; + acquire = ctx->acquire; + requested_rangenum = range->rangenum; + + if (db == NULL) { + DEBUG(3, ("Invalid database argument: NULL\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + if (range == NULL) { + DEBUG(3, ("Invalid range argument: NULL\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(10, ("Adding new range for domain %s " + "(domain_range_index=%"PRIu32")\n", + range->domsid, range->domain_range_index)); + + if (!idmap_autorid_validate_sid(range->domsid)) { + DEBUG(3, ("Invalid SID: %s\n", range->domsid)); + return NT_STATUS_INVALID_PARAMETER; + } + + idmap_autorid_build_keystr(range->domsid, range->domain_range_index, + keystr); + + ret = dbwrap_fetch_uint32_bystring(db, keystr, &stored_rangenum); + + if (NT_STATUS_IS_OK(ret)) { + /* entry is already present*/ + if (acquire) { + DEBUG(10, ("domain range already allocated - " + "Not adding!\n")); + + ret = idmap_autorid_loadconfig(db, &globalcfg); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("Fatal error while fetching " + "configuration: %s\n", + nt_errstr(ret))); + goto error; + } + + range->rangenum = stored_rangenum; + range->low_id = globalcfg.minvalue + + range->rangenum * globalcfg.rangesize; + range->high_id = + range->low_id + globalcfg.rangesize - 1; + + return NT_STATUS_OK; + } + + if (stored_rangenum != requested_rangenum) { + DEBUG(1, ("Error: requested rangenumber (%u) differs " + "from stored one (%u).\n", + requested_rangenum, stored_rangenum)); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(10, ("Note: stored range agrees with requested " + "one - ok\n")); + return NT_STATUS_OK; + } + + /* fetch the current HWM */ + ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("Fatal error while fetching current " + "HWM value: %s\n", nt_errstr(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + + mem_ctx = talloc_stackframe(); + + ret = idmap_autorid_loadconfig(db, &globalcfg); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("Fatal error while fetching configuration: %s\n", + nt_errstr(ret))); + goto error; + } + + if (acquire) { + /* + * automatically acquire the next range + */ + requested_rangenum = hwm; + } + + if (requested_rangenum >= globalcfg.maxranges) { + DBG_WARNING("Not enough ranges available: New range %u can't " + "be allocated. Consider increasing the range " + "[%u-%u] by %u.\n", + requested_rangenum, + globalcfg.minvalue, + globalcfg.minvalue + + (globalcfg.maxranges * globalcfg.rangesize), + globalcfg.rangesize); + ret = NT_STATUS_NO_MEMORY; + goto error; + } + + /* + * 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; + } + + goto error; + } + + 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: %s\n", nt_errstr(ret))); + goto error; + } + + numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum); + if (!numstr) { + ret = NT_STATUS_NO_MEMORY; + goto error; + } + + ret = dbwrap_store_bystring(db, numstr, + string_term_tdb_data(keystr), TDB_INSERT); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("Fatal error while storing new " + "domain->range assignment: %s\n", nt_errstr(ret))); + goto error; + } + + 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; + + range->low_id = globalcfg.minvalue + + range->rangenum * globalcfg.rangesize; + range->high_id = range->low_id + globalcfg.rangesize - 1; + + ret = NT_STATUS_OK; + +error: + talloc_free(mem_ctx); + return ret; +} + +static NTSTATUS idmap_autorid_addrange(struct db_context *db, + struct autorid_range_config *range, + bool acquire) +{ + NTSTATUS status; + struct idmap_autorid_addrange_ctx ctx; + + ctx.acquire = acquire; + ctx.range = range; + + status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx); + return status; +} + +NTSTATUS idmap_autorid_setrange(struct db_context *db, + const char *domsid, + uint32_t domain_range_index, + uint32_t rangenum) +{ + NTSTATUS status; + struct autorid_range_config range; + + ZERO_STRUCT(range); + fstrcpy(range.domsid, domsid); + range.domain_range_index = domain_range_index; + range.rangenum = rangenum; + + status = idmap_autorid_addrange(db, &range, false); + return status; +} + +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) +{ + NTSTATUS status = NT_STATUS_INVALID_PARAMETER; + struct autorid_global_config globalcfg = {0}; + fstring keystr; + + if (db == NULL || range == NULL) { + DEBUG(3, ("Invalid arguments received\n")); + 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 record for key '%s': %s\n", + keystr, nt_errstr(status))); + goto done; + } + + status = idmap_autorid_loadconfig(db, &globalcfg); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to read global configuration\n")); + goto done; + } + range->low_id = globalcfg.minvalue + + range->rangenum * globalcfg.rangesize; + range->high_id = range->low_id + globalcfg.rangesize - 1; +done: + return status; +} + +NTSTATUS idmap_autorid_getrange(struct db_context *db, + const char *domsid, + uint32_t domain_range_index, + uint32_t *rangenum, + uint32_t *low_id) +{ + NTSTATUS status; + struct autorid_range_config range; + + if (rangenum == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(range); + fstrcpy(range.domsid, domsid); + range.domain_range_index = domain_range_index; + + status = idmap_autorid_getrange_int(db, &range); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *rangenum = range.rangenum; + + if (low_id != NULL) { + *low_id = range.low_id; + } + + return NT_STATUS_OK; +} + +NTSTATUS idmap_autorid_get_domainrange(struct db_context *db, + struct autorid_range_config *range, + bool read_only) +{ + NTSTATUS ret; + + 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_acquire_range(db, range); + } + + DEBUG(10, ("Using range #%d for domain %s " + "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n", + range->rangenum, range->domsid, range->domain_range_index, + range->low_id)); + + return ret; +} + +/* 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_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, + discard_const(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; +} + +/* + * 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. + */ + +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 = NULL; + 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) +{ + if (*db != NULL) { + /* its already open */ + return NT_STATUS_OK; + } + + /* Open idmap repository */ + *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644, + 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; + } + + return NT_STATUS_OK; +} + +/** + * 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, ALLOC_HWM_GID); + + return status; +} + +NTSTATUS idmap_autorid_db_init(const char *path, + TALLOC_CTX *mem_ctx, + struct db_context **db) +{ + NTSTATUS status; + + 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; +}; + +static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value, + void *private_data) +{ + struct idmap_autorid_fetch_config_state *state; + + state = (struct idmap_autorid_fetch_config_state *)private_data; + + /* + * strndup because we have non-nullterminated strings in the db + */ + state->configstr = talloc_strndup( + state->mem_ctx, (const char *)value.dptr, value.dsize); +} + +NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx, + char **result) +{ + TDB_DATA key; + NTSTATUS status; + struct idmap_autorid_fetch_config_state state; + + if (result == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + key = string_term_tdb_data(CONFIGKEY); + + state.mem_ctx = mem_ctx; + state.configstr = NULL; + + status = dbwrap_parse_record(db, key, idmap_autorid_config_parser, + &state); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Error while retrieving config: %s\n", + nt_errstr(status))); + return status; + } + + if (state.configstr == NULL) { + DEBUG(1, ("Error while retrieving config\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(5, ("found CONFIG: %s\n", state.configstr)); + + *result = state.configstr; + return NT_STATUS_OK; +} + +bool idmap_autorid_parse_configstr(const char *configstr, + struct autorid_global_config *cfg) +{ + unsigned long minvalue, rangesize, maxranges; + + if (sscanf(configstr, + "minvalue:%lu rangesize:%lu maxranges:%lu", + &minvalue, &rangesize, &maxranges) != 3) { + DEBUG(1, + ("Found invalid configuration data. " + "Creating new config\n")); + return false; + } + + cfg->minvalue = minvalue; + cfg->rangesize = rangesize; + cfg->maxranges = maxranges; + + return true; +} + +NTSTATUS idmap_autorid_loadconfig(struct db_context *db, + struct autorid_global_config *result) +{ + struct autorid_global_config cfg = {0}; + NTSTATUS status; + bool ok; + char *configstr = NULL; + + if (result == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = idmap_autorid_getconfigstr(db, db, &configstr); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ok = idmap_autorid_parse_configstr(configstr, &cfg); + TALLOC_FREE(configstr); + if (!ok) { + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(10, ("Loaded previously stored configuration " + "minvalue:%d rangesize:%d\n", + cfg.minvalue, cfg.rangesize)); + + *result = cfg; + + return NT_STATUS_OK; +} + +NTSTATUS idmap_autorid_saveconfig(struct db_context *db, + struct autorid_global_config *cfg) +{ + + struct autorid_global_config storedconfig = {0}; + NTSTATUS status = NT_STATUS_INVALID_PARAMETER; + TDB_DATA data; + char *cfgstr; + uint32_t hwm; + TALLOC_CTX *frame = talloc_stackframe(); + + DEBUG(10, ("New configuration provided for storing is " + "minvalue:%d rangesize:%d maxranges:%d\n", + cfg->minvalue, cfg->rangesize, cfg->maxranges)); + + if (cfg->rangesize < 2000) { + DEBUG(1, ("autorid rangesize must be at least 2000\n")); + goto done; + } + + if (cfg->maxranges == 0) { + DEBUG(1, ("An autorid maxranges value of 0 is invalid. " + "Must have at least one range available.\n")); + goto done; + } + + status = idmap_autorid_loadconfig(db, &storedconfig); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + DEBUG(5, ("No configuration found. Storing initial " + "configuration.\n")); + storedconfig = *cfg; + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Error loading configuration: %s\n", + nt_errstr(status))); + goto done; + } + + /* did the minimum value or rangesize change? */ + if ((storedconfig.minvalue != cfg->minvalue) || + (storedconfig.rangesize != cfg->rangesize)) + { + DEBUG(1, ("New configuration values for rangesize or " + "minimum uid value conflict with previously " + "used values! Not storing new config.\n")); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Fatal error while fetching current " + "HWM value: %s\n", nt_errstr(status))); + status = NT_STATUS_INTERNAL_ERROR; + goto done; + } + + /* + * has the highest uid value been reduced to setting that is not + * sufficient any more for already existing ranges? + */ + if (hwm > cfg->maxranges) { + DEBUG(1, ("New upper uid limit is too low to cover " + "existing mappings! Not storing new config.\n")); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + cfgstr = + talloc_asprintf(frame, + "minvalue:%u rangesize:%u maxranges:%u", + cfg->minvalue, cfg->rangesize, cfg->maxranges); + + if (cfgstr == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + data = string_tdb_data(cfgstr); + + status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE); + +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; +} |