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,
528 * Delete a domain#index <-> range mapping from the database.
529 * The mapping is specified by the range number.
530 * If force == true, invalid mapping records are deleted as far
531 * as possible, otherwise they are left untouched.
533 struct idmap_autorid_delete_range_by_num_ctx {
538 static NTSTATUS idmap_autorid_delete_range_by_num_action(struct db_context *db,
541 struct idmap_autorid_delete_range_by_num_ctx *ctx =
542 (struct idmap_autorid_delete_range_by_num_ctx *)private_data;
548 TALLOC_CTX *frame = talloc_stackframe();
549 bool is_valid_range_mapping = true;
552 rangenum = ctx->rangenum;
555 range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
556 if (range_keystr == NULL) {
557 status = NT_STATUS_NO_MEMORY;
563 status = dbwrap_fetch_bystring(db, frame, range_keystr, &val);
564 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
565 DEBUG(10, ("Did not find range '%s' in database.\n",
568 } else if (!NT_STATUS_IS_OK(status)) {
569 DEBUG(5, ("Error fetching rang key: %s\n", nt_errstr(status)));
573 if (val.dptr == NULL) {
574 DEBUG(1, ("Invalid mapping: %s -> empty value\n",
576 is_valid_range_mapping = false;
578 uint32_t reverse_rangenum = 0;
580 keystr = (char *)val.dptr;
582 status = dbwrap_fetch_uint32_bystring(db, keystr,
584 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
585 DEBUG(1, ("Incomplete mapping %s -> %s: "
586 "no backward mapping\n",
587 range_keystr, keystr));
588 is_valid_range_mapping = false;
589 } else if (!NT_STATUS_IS_OK(status)) {
590 DEBUG(1, ("Error fetching reverse mapping for "
592 range_keystr, keystr, nt_errstr(status)));
594 } else if (rangenum != reverse_rangenum) {
595 is_valid_range_mapping = false;
599 if (!is_valid_range_mapping && !force) {
600 DEBUG(10, ("Not deleting invalid mapping, since not in force "
602 status = NT_STATUS_FILE_INVALID;
606 status = dbwrap_delete_bystring(db, range_keystr);
607 if (!NT_STATUS_IS_OK(status)) {
608 DEBUG(1, ("Deletion of '%s' failed: %s\n",
609 range_keystr, nt_errstr(status)));
613 if (!is_valid_range_mapping) {
617 status = dbwrap_delete_bystring(db, keystr);
618 if (!NT_STATUS_IS_OK(status)) {
619 DEBUG(1, ("Deletion of '%s' failed: %s\n",
620 keystr, nt_errstr(status)));
624 DEBUG(10, ("Deleted range mapping %s <--> %s\n", range_keystr,
632 NTSTATUS idmap_autorid_delete_range_by_num(struct db_context *db,
637 struct idmap_autorid_delete_range_by_num_ctx ctx;
639 ctx.rangenum = rangenum;
642 status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_num_action,
648 * open and initialize the database which stores the ranges for the domains
650 NTSTATUS idmap_autorid_db_init(const char *path,
652 struct db_context **db)
657 /* its already open */
661 /* Open idmap repository */
662 *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
663 DBWRAP_LOCK_ORDER_1);
666 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
667 return NT_STATUS_UNSUCCESSFUL;
670 /* Initialize high water mark for the currently used range to 0 */
672 status = idmap_autorid_init_hwm(*db, HWM);
673 NT_STATUS_NOT_OK_RETURN(status);
675 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
676 NT_STATUS_NOT_OK_RETURN(status);
678 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
683 struct idmap_autorid_fetch_config_state {
688 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
691 struct idmap_autorid_fetch_config_state *state;
693 state = (struct idmap_autorid_fetch_config_state *)private_data;
696 * strndup because we have non-nullterminated strings in the db
698 state->configstr = talloc_strndup(
699 state->mem_ctx, (const char *)value.dptr, value.dsize);
702 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
707 struct idmap_autorid_fetch_config_state state;
709 if (result == NULL) {
710 return NT_STATUS_INVALID_PARAMETER;
713 key = string_term_tdb_data(CONFIGKEY);
715 state.mem_ctx = mem_ctx;
716 state.configstr = NULL;
718 status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
720 if (!NT_STATUS_IS_OK(status)) {
721 DEBUG(1, ("Error while retrieving config: %s\n",
726 if (state.configstr == NULL) {
727 DEBUG(1, ("Error while retrieving config\n"));
728 return NT_STATUS_NO_MEMORY;
731 DEBUG(5, ("found CONFIG: %s\n", state.configstr));
733 *result = state.configstr;
737 bool idmap_autorid_parse_configstr(const char *configstr,
738 struct autorid_global_config *cfg)
740 unsigned long minvalue, rangesize, maxranges;
742 if (sscanf(configstr,
743 "minvalue:%lu rangesize:%lu maxranges:%lu",
744 &minvalue, &rangesize, &maxranges) != 3) {
746 ("Found invalid configuration data"
747 "creating new config\n"));
751 cfg->minvalue = minvalue;
752 cfg->rangesize = rangesize;
753 cfg->maxranges = maxranges;
758 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
760 struct autorid_global_config **result)
762 struct autorid_global_config *cfg;
765 char *configstr = NULL;
767 if (result == NULL) {
768 return NT_STATUS_INVALID_PARAMETER;
771 status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
772 if (!NT_STATUS_IS_OK(status)) {
776 cfg = talloc_zero(mem_ctx, struct autorid_global_config);
778 return NT_STATUS_NO_MEMORY;
781 ok = idmap_autorid_parse_configstr(configstr, cfg);
784 return NT_STATUS_INVALID_PARAMETER;
787 DEBUG(10, ("Loaded previously stored configuration "
788 "minvalue:%d rangesize:%d\n",
789 cfg->minvalue, cfg->rangesize));
796 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
797 struct autorid_global_config *cfg)
800 struct autorid_global_config *storedconfig = NULL;
801 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
805 TALLOC_CTX *frame = talloc_stackframe();
807 DEBUG(10, ("New configuration provided for storing is "
808 "minvalue:%d rangesize:%d maxranges:%d\n",
809 cfg->minvalue, cfg->rangesize, cfg->maxranges));
811 if (cfg->rangesize < 2000) {
812 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
816 if (cfg->maxranges == 0) {
817 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
818 "Must have at least one range available.\n"));
822 status = idmap_autorid_loadconfig(db, frame, &storedconfig);
823 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
824 DEBUG(5, ("No configuration found. Storing initial "
825 "configuration.\n"));
826 } else if (!NT_STATUS_IS_OK(status)) {
830 /* did the minimum value or rangesize change? */
832 ((storedconfig->minvalue != cfg->minvalue) ||
833 (storedconfig->rangesize != cfg->rangesize)))
835 DEBUG(1, ("New configuration values for rangesize or "
836 "minimum uid value conflict with previously "
837 "used values! Not storing new config.\n"));
838 status = NT_STATUS_INVALID_PARAMETER;
842 status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
843 if (!NT_STATUS_IS_OK(status)) {
844 DEBUG(1, ("Fatal error while fetching current "
845 "HWM value: %s\n", nt_errstr(status)));
846 status = NT_STATUS_INTERNAL_ERROR;
851 * has the highest uid value been reduced to setting that is not
852 * sufficient any more for already existing ranges?
854 if (hwm > cfg->maxranges) {
855 DEBUG(1, ("New upper uid limit is too low to cover "
856 "existing mappings! Not storing new config.\n"));
857 status = NT_STATUS_INVALID_PARAMETER;
862 talloc_asprintf(frame,
863 "minvalue:%u rangesize:%u maxranges:%u",
864 cfg->minvalue, cfg->rangesize, cfg->maxranges);
866 if (cfgstr == NULL) {
867 status = NT_STATUS_NO_MEMORY;
871 data = string_tdb_data(cfgstr);
873 status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
880 NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
881 const char *configstr)
885 struct autorid_global_config cfg;
887 ok = idmap_autorid_parse_configstr(configstr, &cfg);
889 return NT_STATUS_INVALID_PARAMETER;
892 status = idmap_autorid_saveconfig(db, &cfg);