mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
ac0f599cab
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
1270 lines
30 KiB
C
1270 lines
30 KiB
C
/*
|
|
* 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;
|
|
}
|