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"
28 static NTSTATUS idmap_autorid_get_domainrange_action(struct db_context *db,
32 uint32_t rangenum, hwm;
34 struct autorid_range_config *range;
35 struct autorid_global_config *globalcfg;
38 range = (struct autorid_range_config *)private_data;
40 if (range->domain_range_index > 0) {
41 snprintf(keystr, FSTRING_LEN, "%s#%"PRIu32,
42 range->domsid, range->domain_range_index);
44 fstrcpy(keystr, range->domsid);
47 ret = dbwrap_fetch_uint32_bystring(db, keystr,
50 if (NT_STATUS_IS_OK(ret)) {
51 /* entry is already present*/
55 DEBUG(10, ("Acquiring new range for domain %s "
56 "(domain_range_index=%"PRIu32")\n",
57 range->domsid, range->domain_range_index));
59 /* fetch the current HWM */
60 ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
61 if (!NT_STATUS_IS_OK(ret)) {
62 DEBUG(1, ("Fatal error while fetching current "
63 "HWM value: %s\n", nt_errstr(ret)));
64 ret = NT_STATUS_INTERNAL_ERROR;
68 ret = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
69 if (!NT_STATUS_IS_OK(ret)) {
73 /* do we have a range left? */
74 if (hwm >= globalcfg->maxranges) {
75 DEBUG(1, ("No more domain ranges available!\n"));
76 talloc_free(globalcfg);
77 ret = NT_STATUS_NO_MEMORY;
80 TALLOC_FREE(globalcfg);
82 /* increase the HWM */
83 ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &rangenum, 1);
84 if (!NT_STATUS_IS_OK(ret)) {
85 DEBUG(1, ("Fatal error while fetching a new "
86 "domain range value!\n"));
90 /* store away the new mapping in both directions */
91 ret = dbwrap_store_uint32_bystring(db, keystr, rangenum);
92 if (!NT_STATUS_IS_OK(ret)) {
93 DEBUG(1, ("Fatal error while storing new "
94 "domain->range assignment!\n"));
98 numstr = talloc_asprintf(db, "%u", rangenum);
100 ret = NT_STATUS_NO_MEMORY;
104 ret = dbwrap_store_bystring(db, numstr,
105 string_term_tdb_data(keystr), TDB_INSERT);
108 if (!NT_STATUS_IS_OK(ret)) {
109 DEBUG(1, ("Fatal error while storing "
110 "new domain->range assignment!\n"));
113 DEBUG(5, ("Acquired new range #%d for domain %s "
114 "(domain_range_index=%"PRIu32")\n", rangenum, keystr,
115 range->domain_range_index));
117 range->rangenum = rangenum;
126 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
127 struct autorid_range_config *range,
131 struct autorid_global_config *globalcfg;
135 * try to find mapping without locking the database,
136 * if it is not found create a mapping in a transaction unless
137 * read-only mode has been set
139 if (range->domain_range_index > 0) {
140 snprintf(keystr, FSTRING_LEN, "%s#%"PRIu32,
141 range->domsid, range->domain_range_index);
143 fstrcpy(keystr, range->domsid);
146 ret = dbwrap_fetch_uint32_bystring(db, keystr,
149 if (!NT_STATUS_IS_OK(ret)) {
151 return NT_STATUS_NOT_FOUND;
153 ret = dbwrap_trans_do(db,
154 idmap_autorid_get_domainrange_action, range);
157 ret = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
158 if (!NT_STATUS_IS_OK(ret)) {
161 range->low_id = globalcfg->minvalue
162 + range->rangenum * globalcfg->rangesize;
164 DEBUG(10, ("Using range #%d for domain %s "
165 "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
166 range->rangenum, range->domsid, range->domain_range_index,
169 TALLOC_FREE(globalcfg);
173 /* initialize the given HWM to 0 if it does not exist yet */
174 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
179 status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
180 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
181 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
182 if (!NT_STATUS_IS_OK(status)) {
184 ("Unable to initialise HWM (%s) in autorid "
185 "database: %s\n", hwm, nt_errstr(status)));
186 return NT_STATUS_INTERNAL_DB_ERROR;
188 } else if (!NT_STATUS_IS_OK(status)) {
189 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
190 "database: %s\n", hwm, nt_errstr(status)));
198 * open and initialize the database which stores the ranges for the domains
200 NTSTATUS idmap_autorid_db_init(const char *path,
202 struct db_context **db)
207 /* its already open */
211 /* Open idmap repository */
212 *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
213 DBWRAP_LOCK_ORDER_1);
216 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
217 return NT_STATUS_UNSUCCESSFUL;
220 /* Initialize high water mark for the currently used range to 0 */
222 status = idmap_autorid_init_hwm(*db, HWM);
223 NT_STATUS_NOT_OK_RETURN(status);
225 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
226 NT_STATUS_NOT_OK_RETURN(status);
228 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
233 struct idmap_autorid_fetch_config_state {
238 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
241 struct idmap_autorid_fetch_config_state *state;
243 state = (struct idmap_autorid_fetch_config_state *)private_data;
246 * strndup because we have non-nullterminated strings in the db
248 state->configstr = talloc_strndup(
249 state->mem_ctx, (const char *)value.dptr, value.dsize);
252 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
257 struct idmap_autorid_fetch_config_state state;
259 if (result == NULL) {
260 return NT_STATUS_INVALID_PARAMETER;
263 key = string_term_tdb_data(CONFIGKEY);
265 state.mem_ctx = mem_ctx;
266 state.configstr = NULL;
268 status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
270 if (!NT_STATUS_IS_OK(status)) {
271 DEBUG(1, ("Error while retrieving config: %s\n",
276 if (state.configstr == NULL) {
277 DEBUG(1, ("Error while retrieving config\n"));
278 return NT_STATUS_NO_MEMORY;
281 DEBUG(5, ("found CONFIG: %s\n", state.configstr));
283 *result = state.configstr;
287 bool idmap_autorid_parse_configstr(const char *configstr,
288 struct autorid_global_config *cfg)
290 unsigned long minvalue, rangesize, maxranges;
292 if (sscanf(configstr,
293 "minvalue:%lu rangesize:%lu maxranges:%lu",
294 &minvalue, &rangesize, &maxranges) != 3) {
296 ("Found invalid configuration data"
297 "creating new config\n"));
301 cfg->minvalue = minvalue;
302 cfg->rangesize = rangesize;
303 cfg->maxranges = maxranges;
308 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
310 struct autorid_global_config **result)
312 struct autorid_global_config *cfg;
315 char *configstr = NULL;
317 if (result == NULL) {
318 return NT_STATUS_INVALID_PARAMETER;
321 status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
322 if (!NT_STATUS_IS_OK(status)) {
326 cfg = talloc_zero(mem_ctx, struct autorid_global_config);
328 return NT_STATUS_NO_MEMORY;
331 ok = idmap_autorid_parse_configstr(configstr, cfg);
334 return NT_STATUS_INVALID_PARAMETER;
337 DEBUG(10, ("Loaded previously stored configuration "
338 "minvalue:%d rangesize:%d\n",
339 cfg->minvalue, cfg->rangesize));
346 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
347 struct autorid_global_config *cfg)
350 struct autorid_global_config *storedconfig = NULL;
351 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
355 TALLOC_CTX *frame = talloc_stackframe();
357 DEBUG(10, ("New configuration provided for storing is "
358 "minvalue:%d rangesize:%d maxranges:%d\n",
359 cfg->minvalue, cfg->rangesize, cfg->maxranges));
361 if (cfg->rangesize < 2000) {
362 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
366 if (cfg->maxranges == 0) {
367 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
368 "Must have at least one range available.\n"));
372 status = idmap_autorid_loadconfig(db, frame, &storedconfig);
373 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
374 DEBUG(5, ("No configuration found. Storing initial "
375 "configuration.\n"));
376 } else if (!NT_STATUS_IS_OK(status)) {
380 /* did the minimum value or rangesize change? */
382 ((storedconfig->minvalue != cfg->minvalue) ||
383 (storedconfig->rangesize != cfg->rangesize)))
385 DEBUG(1, ("New configuration values for rangesize or "
386 "minimum uid value conflict with previously "
387 "used values! Not storing new config.\n"));
388 status = NT_STATUS_INVALID_PARAMETER;
392 status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
393 if (!NT_STATUS_IS_OK(status)) {
394 DEBUG(1, ("Fatal error while fetching current "
395 "HWM value: %s\n", nt_errstr(status)));
396 status = NT_STATUS_INTERNAL_ERROR;
401 * has the highest uid value been reduced to setting that is not
402 * sufficient any more for already existing ranges?
404 if (hwm > cfg->maxranges) {
405 DEBUG(1, ("New upper uid limit is too low to cover "
406 "existing mappings! Not storing new config.\n"));
407 status = NT_STATUS_INVALID_PARAMETER;
412 talloc_asprintf(frame,
413 "minvalue:%u rangesize:%u maxranges:%u",
414 cfg->minvalue, cfg->rangesize, cfg->maxranges);
416 if (cfgstr == NULL) {
417 status = NT_STATUS_NO_MEMORY;
421 data = string_tdb_data(cfgstr);
423 status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);