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 fetching a new "
179 "domain range value!\n"));
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!\n"));
191 numstr = talloc_asprintf(db, "%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 "
203 "new domain->range assignment!\n"));
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 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
236 struct autorid_range_config *range)
238 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
239 struct autorid_global_config *globalcfg = NULL;
242 if (db == NULL || range == NULL) {
243 DEBUG(3, ("Invalid arguments received\n"));
247 idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
250 DEBUG(10, ("reading domain range for key %s\n", keystr));
251 status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
252 if (!NT_STATUS_IS_OK(status)) {
253 DEBUG(1, ("Failed to read database for key '%s': %s\n",
254 keystr, nt_errstr(status)));
258 status = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
259 if (!NT_STATUS_IS_OK(status)) {
260 DEBUG(1, ("Failed to read global configuration"));
263 range->low_id = globalcfg->minvalue
264 + range->rangenum * globalcfg->rangesize;
266 TALLOC_FREE(globalcfg);
271 NTSTATUS idmap_autorid_getrange(struct db_context *db,
273 uint32_t domain_range_index,
278 struct autorid_range_config range;
280 if (rangenum == NULL) {
281 return NT_STATUS_INVALID_PARAMETER;
285 fstrcpy(range.domsid, domsid);
286 range.domain_range_index = domain_range_index;
288 status = idmap_autorid_getrange_int(db, &range);
289 if (!NT_STATUS_IS_OK(status)) {
293 *rangenum = range.rangenum;
295 if (low_id != NULL) {
296 *low_id = range.low_id;
302 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
303 struct autorid_range_config *range,
308 ret = idmap_autorid_getrange_int(db, range);
309 if (!NT_STATUS_IS_OK(ret)) {
311 return NT_STATUS_NOT_FOUND;
314 ret = idmap_autorid_addrange(db, range, true);
317 DEBUG(10, ("Using range #%d for domain %s "
318 "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
319 range->rangenum, range->domsid, range->domain_range_index,
325 /* initialize the given HWM to 0 if it does not exist yet */
326 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
331 status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
332 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
333 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
334 if (!NT_STATUS_IS_OK(status)) {
336 ("Unable to initialise HWM (%s) in autorid "
337 "database: %s\n", hwm, nt_errstr(status)));
338 return NT_STATUS_INTERNAL_DB_ERROR;
340 } else if (!NT_STATUS_IS_OK(status)) {
341 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
342 "database: %s\n", hwm, nt_errstr(status)));
350 * open and initialize the database which stores the ranges for the domains
352 NTSTATUS idmap_autorid_db_init(const char *path,
354 struct db_context **db)
359 /* its already open */
363 /* Open idmap repository */
364 *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
365 DBWRAP_LOCK_ORDER_1);
368 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
369 return NT_STATUS_UNSUCCESSFUL;
372 /* Initialize high water mark for the currently used range to 0 */
374 status = idmap_autorid_init_hwm(*db, HWM);
375 NT_STATUS_NOT_OK_RETURN(status);
377 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
378 NT_STATUS_NOT_OK_RETURN(status);
380 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
385 struct idmap_autorid_fetch_config_state {
390 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
393 struct idmap_autorid_fetch_config_state *state;
395 state = (struct idmap_autorid_fetch_config_state *)private_data;
398 * strndup because we have non-nullterminated strings in the db
400 state->configstr = talloc_strndup(
401 state->mem_ctx, (const char *)value.dptr, value.dsize);
404 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
409 struct idmap_autorid_fetch_config_state state;
411 if (result == NULL) {
412 return NT_STATUS_INVALID_PARAMETER;
415 key = string_term_tdb_data(CONFIGKEY);
417 state.mem_ctx = mem_ctx;
418 state.configstr = NULL;
420 status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
422 if (!NT_STATUS_IS_OK(status)) {
423 DEBUG(1, ("Error while retrieving config: %s\n",
428 if (state.configstr == NULL) {
429 DEBUG(1, ("Error while retrieving config\n"));
430 return NT_STATUS_NO_MEMORY;
433 DEBUG(5, ("found CONFIG: %s\n", state.configstr));
435 *result = state.configstr;
439 bool idmap_autorid_parse_configstr(const char *configstr,
440 struct autorid_global_config *cfg)
442 unsigned long minvalue, rangesize, maxranges;
444 if (sscanf(configstr,
445 "minvalue:%lu rangesize:%lu maxranges:%lu",
446 &minvalue, &rangesize, &maxranges) != 3) {
448 ("Found invalid configuration data"
449 "creating new config\n"));
453 cfg->minvalue = minvalue;
454 cfg->rangesize = rangesize;
455 cfg->maxranges = maxranges;
460 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
462 struct autorid_global_config **result)
464 struct autorid_global_config *cfg;
467 char *configstr = NULL;
469 if (result == NULL) {
470 return NT_STATUS_INVALID_PARAMETER;
473 status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
474 if (!NT_STATUS_IS_OK(status)) {
478 cfg = talloc_zero(mem_ctx, struct autorid_global_config);
480 return NT_STATUS_NO_MEMORY;
483 ok = idmap_autorid_parse_configstr(configstr, cfg);
486 return NT_STATUS_INVALID_PARAMETER;
489 DEBUG(10, ("Loaded previously stored configuration "
490 "minvalue:%d rangesize:%d\n",
491 cfg->minvalue, cfg->rangesize));
498 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
499 struct autorid_global_config *cfg)
502 struct autorid_global_config *storedconfig = NULL;
503 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
507 TALLOC_CTX *frame = talloc_stackframe();
509 DEBUG(10, ("New configuration provided for storing is "
510 "minvalue:%d rangesize:%d maxranges:%d\n",
511 cfg->minvalue, cfg->rangesize, cfg->maxranges));
513 if (cfg->rangesize < 2000) {
514 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
518 if (cfg->maxranges == 0) {
519 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
520 "Must have at least one range available.\n"));
524 status = idmap_autorid_loadconfig(db, frame, &storedconfig);
525 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
526 DEBUG(5, ("No configuration found. Storing initial "
527 "configuration.\n"));
528 } else if (!NT_STATUS_IS_OK(status)) {
532 /* did the minimum value or rangesize change? */
534 ((storedconfig->minvalue != cfg->minvalue) ||
535 (storedconfig->rangesize != cfg->rangesize)))
537 DEBUG(1, ("New configuration values for rangesize or "
538 "minimum uid value conflict with previously "
539 "used values! Not storing new config.\n"));
540 status = NT_STATUS_INVALID_PARAMETER;
544 status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
545 if (!NT_STATUS_IS_OK(status)) {
546 DEBUG(1, ("Fatal error while fetching current "
547 "HWM value: %s\n", nt_errstr(status)));
548 status = NT_STATUS_INTERNAL_ERROR;
553 * has the highest uid value been reduced to setting that is not
554 * sufficient any more for already existing ranges?
556 if (hwm > cfg->maxranges) {
557 DEBUG(1, ("New upper uid limit is too low to cover "
558 "existing mappings! Not storing new config.\n"));
559 status = NT_STATUS_INVALID_PARAMETER;
564 talloc_asprintf(frame,
565 "minvalue:%u rangesize:%u maxranges:%u",
566 cfg->minvalue, cfg->rangesize, cfg->maxranges);
568 if (cfgstr == NULL) {
569 status = NT_STATUS_NO_MEMORY;
573 data = string_tdb_data(cfgstr);
575 status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);