2 * idmap_autorid_tdb: This file contains common code used by
3 * idmap_autorid and net idmap autorid utilities. The common
4 * code provides functions for performing various operations
7 * Copyright (C) Christian Ambach, 2010-2012
8 * Copyright (C) Atul Kulkarni, 2013
9 * Copyright (C) Michael Adam, 2012-2013
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <http://www.gnu.org/licenses/>.
26 #include "idmap_autorid_tdb.h"
27 #include "../libcli/security/dom_sid.h"
30 * Build the database keystring for getting a range
31 * belonging to a domain sid and a range index.
33 static void idmap_autorid_build_keystr(const char *domsid,
34 uint32_t domain_range_index,
37 if (domain_range_index > 0) {
38 fstr_sprintf(keystr, "%s#%"PRIu32,
39 domsid, domain_range_index);
41 fstrcpy(keystr, domsid);
45 static char *idmap_autorid_build_keystr_talloc(TALLOC_CTX *mem_ctx,
47 uint32_t domain_range_index)
51 if (domain_range_index > 0) {
52 keystr = talloc_asprintf(mem_ctx, "%s#%"PRIu32, domsid,
55 keystr = talloc_strdup(mem_ctx, domsid);
62 static bool idmap_autorid_validate_sid(const char *sid)
64 struct dom_sid ignore;
69 if (strcmp(sid, ALLOC_RANGE) == 0) {
73 return dom_sid_parse(sid, &ignore);
76 struct idmap_autorid_addrange_ctx {
77 struct autorid_range_config *range;
81 static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
84 struct idmap_autorid_addrange_ctx *ctx;
85 uint32_t requested_rangenum, stored_rangenum;
86 struct autorid_range_config *range;
91 struct autorid_global_config *globalcfg;
94 TALLOC_CTX *mem_ctx = NULL;
96 ctx = (struct idmap_autorid_addrange_ctx *)private_data;
98 acquire = ctx->acquire;
99 requested_rangenum = range->rangenum;
102 DEBUG(3, ("Invalid database argument: NULL"));
103 return NT_STATUS_INVALID_PARAMETER;
107 DEBUG(3, ("Invalid range argument: NULL"));
108 return NT_STATUS_INVALID_PARAMETER;
111 DEBUG(10, ("Adding new range for domain %s "
112 "(domain_range_index=%"PRIu32")\n",
113 range->domsid, range->domain_range_index));
115 if (!idmap_autorid_validate_sid(range->domsid)) {
116 DEBUG(3, ("Invalid SID: %s\n", range->domsid));
117 return NT_STATUS_INVALID_PARAMETER;
120 idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
123 ret = dbwrap_fetch_uint32_bystring(db, keystr, &stored_rangenum);
125 if (NT_STATUS_IS_OK(ret)) {
126 /* entry is already present*/
128 DEBUG(10, ("domain range already allocated - "
133 if (stored_rangenum != requested_rangenum) {
134 DEBUG(1, ("Error: requested rangenumber (%u) differs "
135 "from stored one (%u).\n",
136 requested_rangenum, stored_rangenum));
137 return NT_STATUS_UNSUCCESSFUL;
140 DEBUG(10, ("Note: stored range agrees with requested "
145 /* fetch the current HWM */
146 ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
147 if (!NT_STATUS_IS_OK(ret)) {
148 DEBUG(1, ("Fatal error while fetching current "
149 "HWM value: %s\n", nt_errstr(ret)));
150 return NT_STATUS_INTERNAL_ERROR;
153 mem_ctx = talloc_stackframe();
155 ret = idmap_autorid_loadconfig(db, mem_ctx, &globalcfg);
156 if (!NT_STATUS_IS_OK(ret)) {
157 DEBUG(1, ("Fatal error while fetching configuration: %s\n",
164 * automatically acquire the next range
166 requested_rangenum = hwm;
169 if (requested_rangenum >= globalcfg->maxranges) {
170 DEBUG(1, ("Not enough ranges available: New range %u must be "
171 "smaller than configured maximum number of ranges "
173 requested_rangenum, globalcfg->maxranges));
174 ret = NT_STATUS_NO_MEMORY;
178 if (requested_rangenum < hwm) {
180 * Set a specified range below the HWM:
181 * We need to check that it is not yet taken.
184 numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
186 ret = NT_STATUS_NO_MEMORY;
190 if (dbwrap_exists(db, string_term_tdb_data(numstr))) {
191 DEBUG(1, ("Requested range already in use.\n"));
192 ret = NT_STATUS_INVALID_PARAMETER;
199 * requested or automatic range >= HWM:
203 /* HWM always contains current max range + 1 */
204 increment = requested_rangenum + 1 - hwm;
206 /* increase the HWM */
207 ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm,
209 if (!NT_STATUS_IS_OK(ret)) {
210 DEBUG(1, ("Fatal error while incrementing the HWM "
211 "value in the database: %s\n",
218 * store away the new mapping in both directions
221 ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
222 if (!NT_STATUS_IS_OK(ret)) {
223 DEBUG(1, ("Fatal error while storing new "
224 "domain->range assignment: %s\n", nt_errstr(ret)));
228 numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
230 ret = NT_STATUS_NO_MEMORY;
234 ret = dbwrap_store_bystring(db, numstr,
235 string_term_tdb_data(keystr), TDB_INSERT);
237 if (!NT_STATUS_IS_OK(ret)) {
238 DEBUG(1, ("Fatal error while storing new "
239 "domain->range assignment: %s\n", nt_errstr(ret)));
242 DEBUG(5, ("Acquired new range #%d for domain %s "
243 "(domain_range_index=%"PRIu32")\n", requested_rangenum, keystr,
244 range->domain_range_index));
246 range->rangenum = requested_rangenum;
248 range->low_id = globalcfg->minvalue
249 + range->rangenum * globalcfg->rangesize;
254 talloc_free(mem_ctx);
258 static NTSTATUS idmap_autorid_addrange(struct db_context *db,
259 struct autorid_range_config *range,
263 struct idmap_autorid_addrange_ctx ctx;
265 ctx.acquire = acquire;
268 status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx);
272 NTSTATUS idmap_autorid_setrange(struct db_context *db,
274 uint32_t domain_range_index,
278 struct autorid_range_config range;
281 fstrcpy(range.domsid, domsid);
282 range.domain_range_index = domain_range_index;
283 range.rangenum = rangenum;
285 status = idmap_autorid_addrange(db, &range, false);
289 static NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
290 struct autorid_range_config *range)
292 return idmap_autorid_addrange(db, range, true);
295 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
296 struct autorid_range_config *range)
298 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
299 struct autorid_global_config *globalcfg = NULL;
302 if (db == NULL || range == NULL) {
303 DEBUG(3, ("Invalid arguments received\n"));
307 idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
310 DEBUG(10, ("reading domain range for key %s\n", keystr));
311 status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
312 if (!NT_STATUS_IS_OK(status)) {
313 DEBUG(1, ("Failed to read database for key '%s': %s\n",
314 keystr, nt_errstr(status)));
318 status = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
319 if (!NT_STATUS_IS_OK(status)) {
320 DEBUG(1, ("Failed to read global configuration"));
323 range->low_id = globalcfg->minvalue
324 + range->rangenum * globalcfg->rangesize;
326 TALLOC_FREE(globalcfg);
331 NTSTATUS idmap_autorid_getrange(struct db_context *db,
333 uint32_t domain_range_index,
338 struct autorid_range_config range;
340 if (rangenum == NULL) {
341 return NT_STATUS_INVALID_PARAMETER;
345 fstrcpy(range.domsid, domsid);
346 range.domain_range_index = domain_range_index;
348 status = idmap_autorid_getrange_int(db, &range);
349 if (!NT_STATUS_IS_OK(status)) {
353 *rangenum = range.rangenum;
355 if (low_id != NULL) {
356 *low_id = range.low_id;
362 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
363 struct autorid_range_config *range,
368 ret = idmap_autorid_getrange_int(db, range);
369 if (!NT_STATUS_IS_OK(ret)) {
371 return NT_STATUS_NOT_FOUND;
374 ret = idmap_autorid_acquire_range(db, range);
377 DEBUG(10, ("Using range #%d for domain %s "
378 "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
379 range->rangenum, range->domsid, range->domain_range_index,
385 /* initialize the given HWM to 0 if it does not exist yet */
386 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
391 status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
392 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
393 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
394 if (!NT_STATUS_IS_OK(status)) {
396 ("Unable to initialise HWM (%s) in autorid "
397 "database: %s\n", hwm, nt_errstr(status)));
398 return NT_STATUS_INTERNAL_DB_ERROR;
400 } else if (!NT_STATUS_IS_OK(status)) {
401 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
402 "database: %s\n", hwm, nt_errstr(status)));
410 * Delete a domain#index <-> range mapping from the database.
411 * The mapping is specified by the sid and index.
412 * If force == true, invalid mapping records are deleted as far
413 * as possible, otherwise they are left untouched.
416 struct idmap_autorid_delete_range_by_sid_ctx {
418 uint32_t domain_range_index;
422 static NTSTATUS idmap_autorid_delete_range_by_sid_action(struct db_context *db,
425 struct idmap_autorid_delete_range_by_sid_ctx *ctx =
426 (struct idmap_autorid_delete_range_by_sid_ctx *)private_data;
428 uint32_t domain_range_index;
434 TALLOC_CTX *frame = talloc_stackframe();
435 bool is_valid_range_mapping = true;
438 domsid = ctx->domsid;
439 domain_range_index = ctx->domain_range_index;
442 keystr = idmap_autorid_build_keystr_talloc(frame, domsid,
444 if (keystr == NULL) {
445 status = NT_STATUS_NO_MEMORY;
449 status = dbwrap_fetch_uint32_bystring(db, keystr, &rangenum);
450 if (!NT_STATUS_IS_OK(status)) {
454 range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
455 if (range_keystr == NULL) {
456 status = NT_STATUS_NO_MEMORY;
460 status = dbwrap_fetch_bystring(db, frame, range_keystr, &data);
461 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
462 DEBUG(1, ("Incomplete mapping %s -> %s: no backward mapping\n",
463 keystr, range_keystr));
464 is_valid_range_mapping = false;
465 } else if (!NT_STATUS_IS_OK(status)) {
466 DEBUG(1, ("Error fetching reverse mapping for %s -> %s: %s\n",
467 keystr, range_keystr, nt_errstr(status)));
469 } else if (strncmp((const char *)data.dptr, keystr, strlen(keystr))
472 DEBUG(1, ("Invalid mapping: %s -> %s -> %s\n",
473 keystr, range_keystr, (const char *)data.dptr));
474 is_valid_range_mapping = false;
477 if (!is_valid_range_mapping && !force) {
478 DEBUG(10, ("Not deleting invalid mapping, since not in force "
480 status = NT_STATUS_FILE_INVALID;
484 status = dbwrap_delete_bystring(db, keystr);
485 if (!NT_STATUS_IS_OK(status)) {
486 DEBUG(1, ("Deletion of '%s' failed: %s\n",
487 keystr, nt_errstr(status)));
491 if (!is_valid_range_mapping) {
495 status = dbwrap_delete_bystring(db, range_keystr);
496 if (!NT_STATUS_IS_OK(status)) {
497 DEBUG(1, ("Deletion of '%s' failed: %s\n",
498 range_keystr, nt_errstr(status)));
502 DEBUG(10, ("Deleted range mapping %s <--> %s\n", keystr,
510 NTSTATUS idmap_autorid_delete_range_by_sid(struct db_context *db,
512 uint32_t domain_range_index,
516 struct idmap_autorid_delete_range_by_sid_ctx ctx;
518 ctx.domain_range_index = domain_range_index;
522 status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_sid_action,
529 * open and initialize the database which stores the ranges for the domains
531 NTSTATUS idmap_autorid_db_init(const char *path,
533 struct db_context **db)
538 /* its already open */
542 /* Open idmap repository */
543 *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
544 DBWRAP_LOCK_ORDER_1);
547 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
548 return NT_STATUS_UNSUCCESSFUL;
551 /* Initialize high water mark for the currently used range to 0 */
553 status = idmap_autorid_init_hwm(*db, HWM);
554 NT_STATUS_NOT_OK_RETURN(status);
556 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
557 NT_STATUS_NOT_OK_RETURN(status);
559 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
564 struct idmap_autorid_fetch_config_state {
569 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
572 struct idmap_autorid_fetch_config_state *state;
574 state = (struct idmap_autorid_fetch_config_state *)private_data;
577 * strndup because we have non-nullterminated strings in the db
579 state->configstr = talloc_strndup(
580 state->mem_ctx, (const char *)value.dptr, value.dsize);
583 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
588 struct idmap_autorid_fetch_config_state state;
590 if (result == NULL) {
591 return NT_STATUS_INVALID_PARAMETER;
594 key = string_term_tdb_data(CONFIGKEY);
596 state.mem_ctx = mem_ctx;
597 state.configstr = NULL;
599 status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
601 if (!NT_STATUS_IS_OK(status)) {
602 DEBUG(1, ("Error while retrieving config: %s\n",
607 if (state.configstr == NULL) {
608 DEBUG(1, ("Error while retrieving config\n"));
609 return NT_STATUS_NO_MEMORY;
612 DEBUG(5, ("found CONFIG: %s\n", state.configstr));
614 *result = state.configstr;
618 bool idmap_autorid_parse_configstr(const char *configstr,
619 struct autorid_global_config *cfg)
621 unsigned long minvalue, rangesize, maxranges;
623 if (sscanf(configstr,
624 "minvalue:%lu rangesize:%lu maxranges:%lu",
625 &minvalue, &rangesize, &maxranges) != 3) {
627 ("Found invalid configuration data"
628 "creating new config\n"));
632 cfg->minvalue = minvalue;
633 cfg->rangesize = rangesize;
634 cfg->maxranges = maxranges;
639 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
641 struct autorid_global_config **result)
643 struct autorid_global_config *cfg;
646 char *configstr = NULL;
648 if (result == NULL) {
649 return NT_STATUS_INVALID_PARAMETER;
652 status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
653 if (!NT_STATUS_IS_OK(status)) {
657 cfg = talloc_zero(mem_ctx, struct autorid_global_config);
659 return NT_STATUS_NO_MEMORY;
662 ok = idmap_autorid_parse_configstr(configstr, cfg);
665 return NT_STATUS_INVALID_PARAMETER;
668 DEBUG(10, ("Loaded previously stored configuration "
669 "minvalue:%d rangesize:%d\n",
670 cfg->minvalue, cfg->rangesize));
677 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
678 struct autorid_global_config *cfg)
681 struct autorid_global_config *storedconfig = NULL;
682 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
686 TALLOC_CTX *frame = talloc_stackframe();
688 DEBUG(10, ("New configuration provided for storing is "
689 "minvalue:%d rangesize:%d maxranges:%d\n",
690 cfg->minvalue, cfg->rangesize, cfg->maxranges));
692 if (cfg->rangesize < 2000) {
693 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
697 if (cfg->maxranges == 0) {
698 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
699 "Must have at least one range available.\n"));
703 status = idmap_autorid_loadconfig(db, frame, &storedconfig);
704 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
705 DEBUG(5, ("No configuration found. Storing initial "
706 "configuration.\n"));
707 } else if (!NT_STATUS_IS_OK(status)) {
711 /* did the minimum value or rangesize change? */
713 ((storedconfig->minvalue != cfg->minvalue) ||
714 (storedconfig->rangesize != cfg->rangesize)))
716 DEBUG(1, ("New configuration values for rangesize or "
717 "minimum uid value conflict with previously "
718 "used values! Not storing new config.\n"));
719 status = NT_STATUS_INVALID_PARAMETER;
723 status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
724 if (!NT_STATUS_IS_OK(status)) {
725 DEBUG(1, ("Fatal error while fetching current "
726 "HWM value: %s\n", nt_errstr(status)));
727 status = NT_STATUS_INTERNAL_ERROR;
732 * has the highest uid value been reduced to setting that is not
733 * sufficient any more for already existing ranges?
735 if (hwm > cfg->maxranges) {
736 DEBUG(1, ("New upper uid limit is too low to cover "
737 "existing mappings! Not storing new config.\n"));
738 status = NT_STATUS_INVALID_PARAMETER;
743 talloc_asprintf(frame,
744 "minvalue:%u rangesize:%u maxranges:%u",
745 cfg->minvalue, cfg->rangesize, cfg->maxranges);
747 if (cfgstr == NULL) {
748 status = NT_STATUS_NO_MEMORY;
752 data = string_tdb_data(cfgstr);
754 status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
761 NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
762 const char *configstr)
766 struct autorid_global_config cfg;
768 ok = idmap_autorid_parse_configstr(configstr, &cfg);
770 return NT_STATUS_INVALID_PARAMETER;
773 status = idmap_autorid_saveconfig(db, &cfg);