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 bool idmap_autorid_validate_sid(const char *sid)
47 struct dom_sid ignore;
52 if (strcmp(sid, ALLOC_RANGE) == 0) {
56 return dom_sid_parse(sid, &ignore);
59 struct idmap_autorid_addrange_ctx {
60 struct autorid_range_config *range;
64 static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
67 struct idmap_autorid_addrange_ctx *ctx;
68 uint32_t requested_rangenum, stored_rangenum;
69 struct autorid_range_config *range;
74 struct autorid_global_config *globalcfg;
78 ctx = (struct idmap_autorid_addrange_ctx *)private_data;
80 acquire = ctx->acquire;
81 requested_rangenum = range->rangenum;
84 DEBUG(3, ("Invalid database argument: NULL"));
85 return NT_STATUS_INVALID_PARAMETER;
89 DEBUG(3, ("Invalid range argument: NULL"));
90 return NT_STATUS_INVALID_PARAMETER;
93 DEBUG(10, ("Adding new range for domain %s "
94 "(domain_range_index=%"PRIu32")\n",
95 range->domsid, range->domain_range_index));
97 if (!idmap_autorid_validate_sid(range->domsid)) {
98 DEBUG(3, ("Invalid SID: %s\n", range->domsid));
99 return NT_STATUS_INVALID_PARAMETER;
102 idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
105 ret = dbwrap_fetch_uint32_bystring(db, keystr, &stored_rangenum);
107 if (NT_STATUS_IS_OK(ret)) {
108 /* entry is already present*/
110 DEBUG(10, ("domain range already allocated - "
115 if (stored_rangenum != requested_rangenum) {
116 DEBUG(1, ("Error: requested rangenumber (%u) differs "
117 "from stored one (%u).\n",
118 requested_rangenum, stored_rangenum));
119 return NT_STATUS_UNSUCCESSFUL;
122 DEBUG(10, ("Note: stored range agrees with requested "
127 /* fetch the current HWM */
128 ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
129 if (!NT_STATUS_IS_OK(ret)) {
130 DEBUG(1, ("Fatal error while fetching current "
131 "HWM value: %s\n", nt_errstr(ret)));
132 ret = NT_STATUS_INTERNAL_ERROR;
136 ret = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
137 if (!NT_STATUS_IS_OK(ret)) {
138 DEBUG(1, ("Fatal error while fetching configuration: %s\n",
145 * automatically acquire the next range
147 requested_rangenum = hwm;
150 * set a specified range
153 if (requested_rangenum < hwm) {
154 DEBUG(3, ("Invalid range %u requested: Range may not "
155 "be smaller than %u (current HWM)\n",
156 requested_rangenum, hwm));
157 ret = NT_STATUS_INVALID_PARAMETER;
162 if (requested_rangenum >= globalcfg->maxranges) {
163 DEBUG(1, ("Not enough ranges available: New range %u must be "
164 "smaller than configured maximum number of ranges "
166 requested_rangenum, globalcfg->maxranges));
167 ret = NT_STATUS_NO_MEMORY;
170 TALLOC_FREE(globalcfg);
172 /* HWM always contains current max range + 1 */
173 increment = requested_rangenum + 1 - hwm;
175 /* increase the HWM */
176 ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm, increment);
177 if (!NT_STATUS_IS_OK(ret)) {
178 DEBUG(1, ("Fatal error while incrementing the HWM value "
179 "in the database: %s\n", nt_errstr(ret)));
183 /* store away the new mapping in both directions */
184 ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
185 if (!NT_STATUS_IS_OK(ret)) {
186 DEBUG(1, ("Fatal error while storing new "
187 "domain->range assignment: %s\n", nt_errstr(ret)));
191 numstr = talloc_asprintf(talloc_tos(), "%u", requested_rangenum);
193 ret = NT_STATUS_NO_MEMORY;
197 ret = dbwrap_store_bystring(db, numstr,
198 string_term_tdb_data(keystr), TDB_INSERT);
201 if (!NT_STATUS_IS_OK(ret)) {
202 DEBUG(1, ("Fatal error while storing new "
203 "domain->range assignment: %s\n", nt_errstr(ret)));
206 DEBUG(5, ("Acquired new range #%d for domain %s "
207 "(domain_range_index=%"PRIu32")\n", requested_rangenum, keystr,
208 range->domain_range_index));
210 range->rangenum = requested_rangenum;
212 range->low_id = globalcfg->minvalue
213 + range->rangenum * globalcfg->rangesize;
221 static NTSTATUS idmap_autorid_addrange(struct db_context *db,
222 struct autorid_range_config *range,
226 struct idmap_autorid_addrange_ctx ctx;
228 ctx.acquire = acquire;
231 status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx);
235 NTSTATUS idmap_autorid_setrange(struct db_context *db,
237 uint32_t domain_range_index,
241 struct autorid_range_config range;
244 fstrcpy(range.domsid, domsid);
245 range.domain_range_index = domain_range_index;
246 range.rangenum = rangenum;
248 status = idmap_autorid_addrange(db, &range, false);
252 static NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
253 struct autorid_range_config *range)
255 return idmap_autorid_addrange(db, range, true);
258 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
259 struct autorid_range_config *range)
261 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
262 struct autorid_global_config *globalcfg = NULL;
265 if (db == NULL || range == NULL) {
266 DEBUG(3, ("Invalid arguments received\n"));
270 idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
273 DEBUG(10, ("reading domain range for key %s\n", keystr));
274 status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
275 if (!NT_STATUS_IS_OK(status)) {
276 DEBUG(1, ("Failed to read database for key '%s': %s\n",
277 keystr, nt_errstr(status)));
281 status = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
282 if (!NT_STATUS_IS_OK(status)) {
283 DEBUG(1, ("Failed to read global configuration"));
286 range->low_id = globalcfg->minvalue
287 + range->rangenum * globalcfg->rangesize;
289 TALLOC_FREE(globalcfg);
294 NTSTATUS idmap_autorid_getrange(struct db_context *db,
296 uint32_t domain_range_index,
301 struct autorid_range_config range;
303 if (rangenum == NULL) {
304 return NT_STATUS_INVALID_PARAMETER;
308 fstrcpy(range.domsid, domsid);
309 range.domain_range_index = domain_range_index;
311 status = idmap_autorid_getrange_int(db, &range);
312 if (!NT_STATUS_IS_OK(status)) {
316 *rangenum = range.rangenum;
318 if (low_id != NULL) {
319 *low_id = range.low_id;
325 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
326 struct autorid_range_config *range,
331 ret = idmap_autorid_getrange_int(db, range);
332 if (!NT_STATUS_IS_OK(ret)) {
334 return NT_STATUS_NOT_FOUND;
337 ret = idmap_autorid_acquire_range(db, range);
340 DEBUG(10, ("Using range #%d for domain %s "
341 "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
342 range->rangenum, range->domsid, range->domain_range_index,
348 /* initialize the given HWM to 0 if it does not exist yet */
349 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
354 status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
355 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
356 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
357 if (!NT_STATUS_IS_OK(status)) {
359 ("Unable to initialise HWM (%s) in autorid "
360 "database: %s\n", hwm, nt_errstr(status)));
361 return NT_STATUS_INTERNAL_DB_ERROR;
363 } else if (!NT_STATUS_IS_OK(status)) {
364 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
365 "database: %s\n", hwm, nt_errstr(status)));
373 * open and initialize the database which stores the ranges for the domains
375 NTSTATUS idmap_autorid_db_init(const char *path,
377 struct db_context **db)
382 /* its already open */
386 /* Open idmap repository */
387 *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
388 DBWRAP_LOCK_ORDER_1);
391 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
392 return NT_STATUS_UNSUCCESSFUL;
395 /* Initialize high water mark for the currently used range to 0 */
397 status = idmap_autorid_init_hwm(*db, HWM);
398 NT_STATUS_NOT_OK_RETURN(status);
400 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
401 NT_STATUS_NOT_OK_RETURN(status);
403 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
408 struct idmap_autorid_fetch_config_state {
413 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
416 struct idmap_autorid_fetch_config_state *state;
418 state = (struct idmap_autorid_fetch_config_state *)private_data;
421 * strndup because we have non-nullterminated strings in the db
423 state->configstr = talloc_strndup(
424 state->mem_ctx, (const char *)value.dptr, value.dsize);
427 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
432 struct idmap_autorid_fetch_config_state state;
434 if (result == NULL) {
435 return NT_STATUS_INVALID_PARAMETER;
438 key = string_term_tdb_data(CONFIGKEY);
440 state.mem_ctx = mem_ctx;
441 state.configstr = NULL;
443 status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
445 if (!NT_STATUS_IS_OK(status)) {
446 DEBUG(1, ("Error while retrieving config: %s\n",
451 if (state.configstr == NULL) {
452 DEBUG(1, ("Error while retrieving config\n"));
453 return NT_STATUS_NO_MEMORY;
456 DEBUG(5, ("found CONFIG: %s\n", state.configstr));
458 *result = state.configstr;
462 bool idmap_autorid_parse_configstr(const char *configstr,
463 struct autorid_global_config *cfg)
465 unsigned long minvalue, rangesize, maxranges;
467 if (sscanf(configstr,
468 "minvalue:%lu rangesize:%lu maxranges:%lu",
469 &minvalue, &rangesize, &maxranges) != 3) {
471 ("Found invalid configuration data"
472 "creating new config\n"));
476 cfg->minvalue = minvalue;
477 cfg->rangesize = rangesize;
478 cfg->maxranges = maxranges;
483 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
485 struct autorid_global_config **result)
487 struct autorid_global_config *cfg;
490 char *configstr = NULL;
492 if (result == NULL) {
493 return NT_STATUS_INVALID_PARAMETER;
496 status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
497 if (!NT_STATUS_IS_OK(status)) {
501 cfg = talloc_zero(mem_ctx, struct autorid_global_config);
503 return NT_STATUS_NO_MEMORY;
506 ok = idmap_autorid_parse_configstr(configstr, cfg);
509 return NT_STATUS_INVALID_PARAMETER;
512 DEBUG(10, ("Loaded previously stored configuration "
513 "minvalue:%d rangesize:%d\n",
514 cfg->minvalue, cfg->rangesize));
521 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
522 struct autorid_global_config *cfg)
525 struct autorid_global_config *storedconfig = NULL;
526 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
530 TALLOC_CTX *frame = talloc_stackframe();
532 DEBUG(10, ("New configuration provided for storing is "
533 "minvalue:%d rangesize:%d maxranges:%d\n",
534 cfg->minvalue, cfg->rangesize, cfg->maxranges));
536 if (cfg->rangesize < 2000) {
537 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
541 if (cfg->maxranges == 0) {
542 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
543 "Must have at least one range available.\n"));
547 status = idmap_autorid_loadconfig(db, frame, &storedconfig);
548 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
549 DEBUG(5, ("No configuration found. Storing initial "
550 "configuration.\n"));
551 } else if (!NT_STATUS_IS_OK(status)) {
555 /* did the minimum value or rangesize change? */
557 ((storedconfig->minvalue != cfg->minvalue) ||
558 (storedconfig->rangesize != cfg->rangesize)))
560 DEBUG(1, ("New configuration values for rangesize or "
561 "minimum uid value conflict with previously "
562 "used values! Not storing new config.\n"));
563 status = NT_STATUS_INVALID_PARAMETER;
567 status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
568 if (!NT_STATUS_IS_OK(status)) {
569 DEBUG(1, ("Fatal error while fetching current "
570 "HWM value: %s\n", nt_errstr(status)));
571 status = NT_STATUS_INTERNAL_ERROR;
576 * has the highest uid value been reduced to setting that is not
577 * sufficient any more for already existing ranges?
579 if (hwm > cfg->maxranges) {
580 DEBUG(1, ("New upper uid limit is too low to cover "
581 "existing mappings! Not storing new config.\n"));
582 status = NT_STATUS_INVALID_PARAMETER;
587 talloc_asprintf(frame,
588 "minvalue:%u rangesize:%u maxranges:%u",
589 cfg->minvalue, cfg->rangesize, cfg->maxranges);
591 if (cfgstr == NULL) {
592 status = NT_STATUS_NO_MEMORY;
596 data = string_tdb_data(cfgstr);
598 status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);