s4:rpc_srv:getncchanges: 4.5 anc emulation uses qsort(), not ldb_qsort()
[samba.git] / source3 / winbindd / idmap_autorid_tdb.c
blob68c4d2f3355102ae406d73da4a42aa19581d0ffa
1 /*
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
5 * on autorid.tdb
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"
28 #include "lib/util/string_wrappers.h"
30 /**
31 * Build the database keystring for getting a range
32 * belonging to a domain sid and a range index.
34 static void idmap_autorid_build_keystr(const char *domsid,
35 uint32_t domain_range_index,
36 fstring keystr)
38 if (domain_range_index > 0) {
39 fstr_sprintf(keystr, "%s#%"PRIu32,
40 domsid, domain_range_index);
41 } else {
42 fstrcpy(keystr, domsid);
46 static char *idmap_autorid_build_keystr_talloc(TALLOC_CTX *mem_ctx,
47 const char *domsid,
48 uint32_t domain_range_index)
50 char *keystr;
52 if (domain_range_index > 0) {
53 keystr = talloc_asprintf(mem_ctx, "%s#%"PRIu32, domsid,
54 domain_range_index);
55 } else {
56 keystr = talloc_strdup(mem_ctx, domsid);
59 return keystr;
63 static bool idmap_autorid_validate_sid(const char *sid)
65 struct dom_sid ignore;
66 if (sid == NULL) {
67 return false;
70 if (strcmp(sid, ALLOC_RANGE) == 0) {
71 return true;
74 return dom_sid_parse(sid, &ignore);
77 struct idmap_autorid_addrange_ctx {
78 struct autorid_range_config *range;
79 bool acquire;
82 static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
83 void *private_data)
85 struct idmap_autorid_addrange_ctx *ctx;
86 uint32_t requested_rangenum, stored_rangenum;
87 struct autorid_range_config *range;
88 bool acquire;
89 NTSTATUS ret;
90 uint32_t hwm;
91 char *numstr;
92 struct autorid_global_config globalcfg = {0};
93 fstring keystr;
94 uint32_t increment;
95 TALLOC_CTX *mem_ctx = NULL;
97 ctx = (struct idmap_autorid_addrange_ctx *)private_data;
98 range = ctx->range;
99 acquire = ctx->acquire;
101 if (db == NULL) {
102 DEBUG(3, ("Invalid database argument: NULL\n"));
103 return NT_STATUS_INVALID_PARAMETER;
106 if (range == NULL) {
107 DEBUG(3, ("Invalid range argument: NULL\n"));
108 return NT_STATUS_INVALID_PARAMETER;
111 requested_rangenum = range->rangenum;
113 DEBUG(10, ("Adding new range for domain %s "
114 "(domain_range_index=%"PRIu32")\n",
115 range->domsid, range->domain_range_index));
117 if (!idmap_autorid_validate_sid(range->domsid)) {
118 DEBUG(3, ("Invalid SID: %s\n", range->domsid));
119 return NT_STATUS_INVALID_PARAMETER;
122 idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
123 keystr);
125 ret = dbwrap_fetch_uint32_bystring(db, keystr, &stored_rangenum);
127 if (NT_STATUS_IS_OK(ret)) {
128 /* entry is already present*/
129 if (acquire) {
130 DEBUG(10, ("domain range already allocated - "
131 "Not adding!\n"));
133 ret = idmap_autorid_loadconfig(db, &globalcfg);
134 if (!NT_STATUS_IS_OK(ret)) {
135 DEBUG(1, ("Fatal error while fetching "
136 "configuration: %s\n",
137 nt_errstr(ret)));
138 goto error;
141 range->rangenum = stored_rangenum;
142 range->low_id = globalcfg.minvalue
143 + range->rangenum * globalcfg.rangesize;
144 range->high_id =
145 range->low_id + globalcfg.rangesize - 1;
147 return NT_STATUS_OK;
150 if (stored_rangenum != requested_rangenum) {
151 DEBUG(1, ("Error: requested rangenumber (%u) differs "
152 "from stored one (%u).\n",
153 requested_rangenum, stored_rangenum));
154 return NT_STATUS_UNSUCCESSFUL;
157 DEBUG(10, ("Note: stored range agrees with requested "
158 "one - ok\n"));
159 return NT_STATUS_OK;
162 /* fetch the current HWM */
163 ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
164 if (!NT_STATUS_IS_OK(ret)) {
165 DEBUG(1, ("Fatal error while fetching current "
166 "HWM value: %s\n", nt_errstr(ret)));
167 return NT_STATUS_INTERNAL_ERROR;
170 mem_ctx = talloc_stackframe();
172 ret = idmap_autorid_loadconfig(db, &globalcfg);
173 if (!NT_STATUS_IS_OK(ret)) {
174 DEBUG(1, ("Fatal error while fetching configuration: %s\n",
175 nt_errstr(ret)));
176 goto error;
179 if (acquire) {
181 * automatically acquire the next range
183 requested_rangenum = hwm;
186 if (requested_rangenum >= globalcfg.maxranges) {
187 DBG_WARNING("Not enough ranges available: New range %u can't "
188 "be allocated. Consider increasing the range "
189 "[%u-%u] by %u.\n",
190 requested_rangenum,
191 globalcfg.minvalue,
192 globalcfg.minvalue +
193 (globalcfg.maxranges * globalcfg.rangesize),
194 globalcfg.rangesize);
195 ret = NT_STATUS_NO_MEMORY;
196 goto error;
200 * Check that it is not yet taken.
201 * If the range is requested and < HWM, we need
202 * to check anyways, and otherwise, we also better
203 * check in order to prevent further corruption
204 * in case the db has been externally modified.
207 numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
208 if (!numstr) {
209 DEBUG(1, ("Talloc failed!\n"));
210 ret = NT_STATUS_NO_MEMORY;
211 goto error;
214 if (dbwrap_exists(db, string_term_tdb_data(numstr))) {
215 DEBUG(1, ("Requested range '%s' is already in use.\n", numstr));
217 if (requested_rangenum < hwm) {
218 ret = NT_STATUS_INVALID_PARAMETER;
219 } else {
220 ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
223 goto error;
226 if (requested_rangenum >= hwm) {
228 * requested or automatic range >= HWM:
229 * increment the HWM.
232 /* HWM always contains current max range + 1 */
233 increment = requested_rangenum + 1 - hwm;
235 /* increase the HWM */
236 ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm,
237 increment);
238 if (!NT_STATUS_IS_OK(ret)) {
239 DEBUG(1, ("Fatal error while incrementing the HWM "
240 "value in the database: %s\n",
241 nt_errstr(ret)));
242 goto error;
247 * store away the new mapping in both directions
250 ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
251 if (!NT_STATUS_IS_OK(ret)) {
252 DEBUG(1, ("Fatal error while storing new "
253 "domain->range assignment: %s\n", nt_errstr(ret)));
254 goto error;
257 numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
258 if (!numstr) {
259 ret = NT_STATUS_NO_MEMORY;
260 goto error;
263 ret = dbwrap_store_bystring(db, numstr,
264 string_term_tdb_data(keystr), TDB_INSERT);
266 if (!NT_STATUS_IS_OK(ret)) {
267 DEBUG(1, ("Fatal error while storing new "
268 "domain->range assignment: %s\n", nt_errstr(ret)));
269 goto error;
272 DEBUG(5, ("%s new range #%d for domain %s "
273 "(domain_range_index=%"PRIu32")\n",
274 (acquire?"Acquired":"Stored"),
275 requested_rangenum, keystr,
276 range->domain_range_index));
278 range->rangenum = requested_rangenum;
280 range->low_id = globalcfg.minvalue
281 + range->rangenum * globalcfg.rangesize;
282 range->high_id = range->low_id + globalcfg.rangesize - 1;
284 ret = NT_STATUS_OK;
286 error:
287 talloc_free(mem_ctx);
288 return ret;
291 static NTSTATUS idmap_autorid_addrange(struct db_context *db,
292 struct autorid_range_config *range,
293 bool acquire)
295 NTSTATUS status;
296 struct idmap_autorid_addrange_ctx ctx;
298 ctx.acquire = acquire;
299 ctx.range = range;
301 status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx);
302 return status;
305 NTSTATUS idmap_autorid_setrange(struct db_context *db,
306 const char *domsid,
307 uint32_t domain_range_index,
308 uint32_t rangenum)
310 NTSTATUS status;
311 struct autorid_range_config range;
313 ZERO_STRUCT(range);
314 fstrcpy(range.domsid, domsid);
315 range.domain_range_index = domain_range_index;
316 range.rangenum = rangenum;
318 status = idmap_autorid_addrange(db, &range, false);
319 return status;
322 NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
323 struct autorid_range_config *range)
325 return idmap_autorid_addrange(db, range, true);
328 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
329 struct autorid_range_config *range)
331 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
332 struct autorid_global_config globalcfg = {0};
333 fstring keystr;
335 if (db == NULL || range == NULL) {
336 DEBUG(3, ("Invalid arguments received\n"));
337 goto done;
340 if (!idmap_autorid_validate_sid(range->domsid)) {
341 DEBUG(3, ("Invalid SID: '%s'\n", range->domsid));
342 status = NT_STATUS_INVALID_PARAMETER;
343 goto done;
346 idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
347 keystr);
349 DEBUG(10, ("reading domain range for key %s\n", keystr));
350 status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
351 if (!NT_STATUS_IS_OK(status)) {
352 DEBUG(1, ("Failed to read database record for key '%s': %s\n",
353 keystr, nt_errstr(status)));
354 goto done;
357 status = idmap_autorid_loadconfig(db, &globalcfg);
358 if (!NT_STATUS_IS_OK(status)) {
359 DEBUG(1, ("Failed to read global configuration\n"));
360 goto done;
362 range->low_id = globalcfg.minvalue
363 + range->rangenum * globalcfg.rangesize;
364 range->high_id = range->low_id + globalcfg.rangesize - 1;
365 done:
366 return status;
369 NTSTATUS idmap_autorid_getrange(struct db_context *db,
370 const char *domsid,
371 uint32_t domain_range_index,
372 uint32_t *rangenum,
373 uint32_t *low_id)
375 NTSTATUS status;
376 struct autorid_range_config range;
378 if (rangenum == NULL) {
379 return NT_STATUS_INVALID_PARAMETER;
382 ZERO_STRUCT(range);
383 fstrcpy(range.domsid, domsid);
384 range.domain_range_index = domain_range_index;
386 status = idmap_autorid_getrange_int(db, &range);
387 if (!NT_STATUS_IS_OK(status)) {
388 return status;
391 *rangenum = range.rangenum;
393 if (low_id != NULL) {
394 *low_id = range.low_id;
397 return NT_STATUS_OK;
400 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
401 struct autorid_range_config *range,
402 bool read_only)
404 NTSTATUS ret;
406 ret = idmap_autorid_getrange_int(db, range);
407 if (!NT_STATUS_IS_OK(ret)) {
408 DEBUG(10, ("Failed to read range config for '%s': %s\n",
409 range->domsid, nt_errstr(ret)));
410 if (read_only) {
411 DEBUG(10, ("Not allocating new range for '%s' because "
412 "read-only is enabled.\n", range->domsid));
413 return NT_STATUS_NOT_FOUND;
416 ret = idmap_autorid_acquire_range(db, range);
419 DEBUG(10, ("Using range #%d for domain %s "
420 "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
421 range->rangenum, range->domsid, range->domain_range_index,
422 range->low_id));
424 return ret;
427 /* initialize the given HWM to 0 if it does not exist yet */
428 static NTSTATUS idmap_autorid_init_hwm_action(struct db_context *db,
429 void *private_data)
431 NTSTATUS status;
432 uint32_t hwmval;
433 const char *hwm;
435 hwm = (char *)private_data;
437 status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
438 if (NT_STATUS_IS_OK(status)) {
439 DEBUG(1, ("HWM (%s) already initialized in autorid database "
440 "(value %"PRIu32").\n", hwm, hwmval));
441 return NT_STATUS_OK;
443 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
444 DEBUG(0, ("Error fetching HWM (%s) from autorid "
445 "database: %s\n", hwm, nt_errstr(status)));
446 return status;
449 status = dbwrap_trans_store_uint32_bystring(db, hwm, 0);
450 if (!NT_STATUS_IS_OK(status)) {
451 DEBUG(0, ("Error storing HWM (%s) in autorid database: %s\n",
452 hwm, nt_errstr(status)));
453 return status;
456 return NT_STATUS_OK;
459 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
461 NTSTATUS status;
462 uint32_t hwmval;
464 status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
465 if (NT_STATUS_IS_OK(status)) {
466 DEBUG(1, ("HWM (%s) already initialized in autorid database "
467 "(value %"PRIu32").\n", hwm, hwmval));
468 return NT_STATUS_OK;
470 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
471 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
472 "database: %s\n", hwm, nt_errstr(status)));
473 return status;
476 status = dbwrap_trans_do(db, idmap_autorid_init_hwm_action,
477 discard_const(hwm));
478 if (!NT_STATUS_IS_OK(status)) {
479 DEBUG(0, ("Error initializing HWM (%s) in autorid database: "
480 "%s\n", hwm, nt_errstr(status)));
481 return NT_STATUS_INTERNAL_DB_ERROR;
484 DEBUG(1, ("Initialized HWM (%s) in autorid database.\n", hwm));
486 return NT_STATUS_OK;
490 * Delete a domain#index <-> range mapping from the database.
491 * The mapping is specified by the sid and index.
492 * If force == true, invalid mapping records are deleted as far
493 * as possible, otherwise they are left untouched.
496 struct idmap_autorid_delete_range_by_sid_ctx {
497 const char *domsid;
498 uint32_t domain_range_index;
499 bool force;
502 static NTSTATUS idmap_autorid_delete_range_by_sid_action(struct db_context *db,
503 void *private_data)
505 struct idmap_autorid_delete_range_by_sid_ctx *ctx =
506 (struct idmap_autorid_delete_range_by_sid_ctx *)private_data;
507 const char *domsid;
508 uint32_t domain_range_index;
509 uint32_t rangenum;
510 char *keystr;
511 char *range_keystr;
512 TDB_DATA data;
513 NTSTATUS status;
514 TALLOC_CTX *frame = talloc_stackframe();
515 bool is_valid_range_mapping = true;
516 bool force;
518 domsid = ctx->domsid;
519 domain_range_index = ctx->domain_range_index;
520 force = ctx->force;
522 keystr = idmap_autorid_build_keystr_talloc(frame, domsid,
523 domain_range_index);
524 if (keystr == NULL) {
525 status = NT_STATUS_NO_MEMORY;
526 goto done;
529 status = dbwrap_fetch_uint32_bystring(db, keystr, &rangenum);
530 if (!NT_STATUS_IS_OK(status)) {
531 goto done;
534 range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
535 if (range_keystr == NULL) {
536 status = NT_STATUS_NO_MEMORY;
537 goto done;
540 status = dbwrap_fetch_bystring(db, frame, range_keystr, &data);
541 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
542 DEBUG(1, ("Incomplete mapping %s -> %s: no backward mapping\n",
543 keystr, range_keystr));
544 is_valid_range_mapping = false;
545 } else if (!NT_STATUS_IS_OK(status)) {
546 DEBUG(1, ("Error fetching reverse mapping for %s -> %s: %s\n",
547 keystr, range_keystr, nt_errstr(status)));
548 goto done;
549 } else if (strncmp((const char *)data.dptr, keystr, strlen(keystr))
550 != 0)
552 DEBUG(1, ("Invalid mapping: %s -> %s -> %s\n",
553 keystr, range_keystr, (const char *)data.dptr));
554 is_valid_range_mapping = false;
557 if (!is_valid_range_mapping && !force) {
558 DEBUG(10, ("Not deleting invalid mapping, since not in force "
559 "mode.\n"));
560 status = NT_STATUS_FILE_INVALID;
561 goto done;
564 status = dbwrap_delete_bystring(db, keystr);
565 if (!NT_STATUS_IS_OK(status)) {
566 DEBUG(1, ("Deletion of '%s' failed: %s\n",
567 keystr, nt_errstr(status)));
568 goto done;
571 if (!is_valid_range_mapping) {
572 goto done;
575 status = dbwrap_delete_bystring(db, range_keystr);
576 if (!NT_STATUS_IS_OK(status)) {
577 DEBUG(1, ("Deletion of '%s' failed: %s\n",
578 range_keystr, nt_errstr(status)));
579 goto done;
582 DEBUG(10, ("Deleted range mapping %s <--> %s\n", keystr,
583 range_keystr));
585 done:
586 TALLOC_FREE(frame);
587 return status;
590 NTSTATUS idmap_autorid_delete_range_by_sid(struct db_context *db,
591 const char *domsid,
592 uint32_t domain_range_index,
593 bool force)
595 NTSTATUS status;
596 struct idmap_autorid_delete_range_by_sid_ctx ctx;
598 ctx.domain_range_index = domain_range_index;
599 ctx.domsid = domsid;
600 ctx.force = force;
602 status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_sid_action,
603 &ctx);
604 return status;
608 * Delete a domain#index <-> range mapping from the database.
609 * The mapping is specified by the range number.
610 * If force == true, invalid mapping records are deleted as far
611 * as possible, otherwise they are left untouched.
613 struct idmap_autorid_delete_range_by_num_ctx {
614 uint32_t rangenum;
615 bool force;
618 static NTSTATUS idmap_autorid_delete_range_by_num_action(struct db_context *db,
619 void *private_data)
621 struct idmap_autorid_delete_range_by_num_ctx *ctx =
622 (struct idmap_autorid_delete_range_by_num_ctx *)private_data;
623 uint32_t rangenum;
624 char *keystr = NULL;
625 char *range_keystr;
626 TDB_DATA val;
627 NTSTATUS status;
628 TALLOC_CTX *frame = talloc_stackframe();
629 bool is_valid_range_mapping = true;
630 bool force;
632 rangenum = ctx->rangenum;
633 force = ctx->force;
635 range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
636 if (range_keystr == NULL) {
637 status = NT_STATUS_NO_MEMORY;
638 goto done;
641 ZERO_STRUCT(val);
643 status = dbwrap_fetch_bystring(db, frame, range_keystr, &val);
644 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
645 DEBUG(10, ("Did not find range '%s' in database.\n",
646 range_keystr));
647 goto done;
648 } else if (!NT_STATUS_IS_OK(status)) {
649 DEBUG(5, ("Error fetching rang key: %s\n", nt_errstr(status)));
650 goto done;
653 if (val.dptr == NULL) {
654 DEBUG(1, ("Invalid mapping: %s -> empty value\n",
655 range_keystr));
656 is_valid_range_mapping = false;
657 } else {
658 uint32_t reverse_rangenum = 0;
660 keystr = (char *)val.dptr;
662 status = dbwrap_fetch_uint32_bystring(db, keystr,
663 &reverse_rangenum);
664 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
665 DEBUG(1, ("Incomplete mapping %s -> %s: "
666 "no backward mapping\n",
667 range_keystr, keystr));
668 is_valid_range_mapping = false;
669 } else if (!NT_STATUS_IS_OK(status)) {
670 DEBUG(1, ("Error fetching reverse mapping for "
671 "%s -> %s: %s\n",
672 range_keystr, keystr, nt_errstr(status)));
673 goto done;
674 } else if (rangenum != reverse_rangenum) {
675 is_valid_range_mapping = false;
679 if (!is_valid_range_mapping && !force) {
680 DEBUG(10, ("Not deleting invalid mapping, since not in force "
681 "mode.\n"));
682 status = NT_STATUS_FILE_INVALID;
683 goto done;
686 status = dbwrap_delete_bystring(db, range_keystr);
687 if (!NT_STATUS_IS_OK(status)) {
688 DEBUG(1, ("Deletion of '%s' failed: %s\n",
689 range_keystr, nt_errstr(status)));
690 goto done;
693 if (!is_valid_range_mapping) {
694 goto done;
697 status = dbwrap_delete_bystring(db, keystr);
698 if (!NT_STATUS_IS_OK(status)) {
699 DEBUG(1, ("Deletion of '%s' failed: %s\n",
700 keystr, nt_errstr(status)));
701 goto done;
704 DEBUG(10, ("Deleted range mapping %s <--> %s\n", range_keystr,
705 keystr));
707 done:
708 talloc_free(frame);
709 return status;
712 NTSTATUS idmap_autorid_delete_range_by_num(struct db_context *db,
713 uint32_t rangenum,
714 bool force)
716 NTSTATUS status;
717 struct idmap_autorid_delete_range_by_num_ctx ctx;
719 ctx.rangenum = rangenum;
720 ctx.force = force;
722 status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_num_action,
723 &ctx);
724 return status;
728 * Open and possibly create the database.
730 NTSTATUS idmap_autorid_db_open(const char *path,
731 TALLOC_CTX *mem_ctx,
732 struct db_context **db)
734 if (*db != NULL) {
735 /* its already open */
736 return NT_STATUS_OK;
739 /* Open idmap repository */
740 *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
741 DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
743 if (*db == NULL) {
744 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
745 return NT_STATUS_UNSUCCESSFUL;
748 return NT_STATUS_OK;
752 * Initialize the high watermark records in the database.
754 NTSTATUS idmap_autorid_init_hwms(struct db_context *db)
756 NTSTATUS status;
758 status = idmap_autorid_init_hwm(db, HWM);
759 if (!NT_STATUS_IS_OK(status)) {
760 return status;
763 status = idmap_autorid_init_hwm(db, ALLOC_HWM_UID);
764 if (!NT_STATUS_IS_OK(status)) {
765 return status;
768 status = idmap_autorid_init_hwm(db, ALLOC_HWM_GID);
770 return status;
773 NTSTATUS idmap_autorid_db_init(const char *path,
774 TALLOC_CTX *mem_ctx,
775 struct db_context **db)
777 NTSTATUS status;
779 status = idmap_autorid_db_open(path, mem_ctx, db);
780 if (!NT_STATUS_IS_OK(status)) {
781 return status;
784 status = idmap_autorid_init_hwms(*db);
785 return status;
790 struct idmap_autorid_fetch_config_state {
791 TALLOC_CTX *mem_ctx;
792 char *configstr;
795 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
796 void *private_data)
798 struct idmap_autorid_fetch_config_state *state;
800 state = (struct idmap_autorid_fetch_config_state *)private_data;
803 * strndup because we have non-nullterminated strings in the db
805 state->configstr = talloc_strndup(
806 state->mem_ctx, (const char *)value.dptr, value.dsize);
809 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
810 char **result)
812 TDB_DATA key;
813 NTSTATUS status;
814 struct idmap_autorid_fetch_config_state state;
816 if (result == NULL) {
817 return NT_STATUS_INVALID_PARAMETER;
820 key = string_term_tdb_data(CONFIGKEY);
822 state.mem_ctx = mem_ctx;
823 state.configstr = NULL;
825 status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
826 &state);
827 if (!NT_STATUS_IS_OK(status)) {
828 DEBUG(1, ("Error while retrieving config: %s\n",
829 nt_errstr(status)));
830 return status;
833 if (state.configstr == NULL) {
834 DEBUG(1, ("Error while retrieving config\n"));
835 return NT_STATUS_NO_MEMORY;
838 DEBUG(5, ("found CONFIG: %s\n", state.configstr));
840 *result = state.configstr;
841 return NT_STATUS_OK;
844 bool idmap_autorid_parse_configstr(const char *configstr,
845 struct autorid_global_config *cfg)
847 unsigned long minvalue, rangesize, maxranges;
849 if (sscanf(configstr,
850 "minvalue:%lu rangesize:%lu maxranges:%lu",
851 &minvalue, &rangesize, &maxranges) != 3) {
852 DEBUG(1,
853 ("Found invalid configuration data. "
854 "Creating new config\n"));
855 return false;
858 cfg->minvalue = minvalue;
859 cfg->rangesize = rangesize;
860 cfg->maxranges = maxranges;
862 return true;
865 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
866 struct autorid_global_config *result)
868 struct autorid_global_config cfg = {0};
869 NTSTATUS status;
870 bool ok;
871 char *configstr = NULL;
873 if (result == NULL) {
874 return NT_STATUS_INVALID_PARAMETER;
877 status = idmap_autorid_getconfigstr(db, db, &configstr);
878 if (!NT_STATUS_IS_OK(status)) {
879 return status;
882 ok = idmap_autorid_parse_configstr(configstr, &cfg);
883 TALLOC_FREE(configstr);
884 if (!ok) {
885 return NT_STATUS_INVALID_PARAMETER;
888 DEBUG(10, ("Loaded previously stored configuration "
889 "minvalue:%d rangesize:%d\n",
890 cfg.minvalue, cfg.rangesize));
892 *result = cfg;
894 return NT_STATUS_OK;
897 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
898 struct autorid_global_config *cfg)
901 struct autorid_global_config storedconfig = {0};
902 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
903 TDB_DATA data;
904 char *cfgstr;
905 uint32_t hwm;
906 TALLOC_CTX *frame = talloc_stackframe();
908 DEBUG(10, ("New configuration provided for storing is "
909 "minvalue:%d rangesize:%d maxranges:%d\n",
910 cfg->minvalue, cfg->rangesize, cfg->maxranges));
912 if (cfg->rangesize < 2000) {
913 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
914 goto done;
917 if (cfg->maxranges == 0) {
918 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
919 "Must have at least one range available.\n"));
920 goto done;
923 status = idmap_autorid_loadconfig(db, &storedconfig);
924 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
925 DEBUG(5, ("No configuration found. Storing initial "
926 "configuration.\n"));
927 storedconfig = *cfg;
928 } else if (!NT_STATUS_IS_OK(status)) {
929 DEBUG(1, ("Error loading configuration: %s\n",
930 nt_errstr(status)));
931 goto done;
934 /* did the minimum value or rangesize change? */
935 if ((storedconfig.minvalue != cfg->minvalue) ||
936 (storedconfig.rangesize != cfg->rangesize))
938 DEBUG(1, ("New configuration values for rangesize or "
939 "minimum uid value conflict with previously "
940 "used values! Not storing new config.\n"));
941 status = NT_STATUS_INVALID_PARAMETER;
942 goto done;
945 status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
946 if (!NT_STATUS_IS_OK(status)) {
947 DEBUG(1, ("Fatal error while fetching current "
948 "HWM value: %s\n", nt_errstr(status)));
949 status = NT_STATUS_INTERNAL_ERROR;
950 goto done;
954 * has the highest uid value been reduced to setting that is not
955 * sufficient any more for already existing ranges?
957 if (hwm > cfg->maxranges) {
958 DEBUG(1, ("New upper uid limit is too low to cover "
959 "existing mappings! Not storing new config.\n"));
960 status = NT_STATUS_INVALID_PARAMETER;
961 goto done;
964 cfgstr =
965 talloc_asprintf(frame,
966 "minvalue:%u rangesize:%u maxranges:%u",
967 cfg->minvalue, cfg->rangesize, cfg->maxranges);
969 if (cfgstr == NULL) {
970 status = NT_STATUS_NO_MEMORY;
971 goto done;
974 data = string_tdb_data(cfgstr);
976 status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
978 done:
979 TALLOC_FREE(frame);
980 return status;
983 NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
984 const char *configstr)
986 bool ok;
987 NTSTATUS status;
988 struct autorid_global_config cfg;
990 ok = idmap_autorid_parse_configstr(configstr, &cfg);
991 if (!ok) {
992 return NT_STATUS_INVALID_PARAMETER;
995 status = idmap_autorid_saveconfig(db, &cfg);
996 return status;
1001 * iteration: Work on all range mappings for a given domain
1004 struct domain_range_visitor_ctx {
1005 const char *domsid;
1006 NTSTATUS (*fn)(struct db_context *db,
1007 const char *domsid,
1008 uint32_t index,
1009 uint32_t rangenum,
1010 void *private_data);
1011 void *private_data;
1012 int count; /* number of records worked on */
1015 static int idmap_autorid_visit_domain_range(struct db_record *rec,
1016 void *private_data)
1018 struct domain_range_visitor_ctx *vi;
1019 char *domsid;
1020 char *sep;
1021 uint32_t range_index = 0;
1022 uint32_t rangenum = 0;
1023 TDB_DATA key, value;
1024 NTSTATUS status;
1025 int ret = 0;
1026 struct db_context *db;
1028 vi = talloc_get_type_abort(private_data,
1029 struct domain_range_visitor_ctx);
1031 key = dbwrap_record_get_key(rec);
1034 * split string "<sid>[#<index>]" into sid string and index number
1037 domsid = (char *)key.dptr;
1039 DEBUG(10, ("idmap_autorid_visit_domain_range: visiting key '%s'\n",
1040 domsid));
1042 sep = strrchr(domsid, '#');
1043 if (sep != NULL) {
1044 char *index_str;
1045 *sep = '\0';
1046 index_str = sep+1;
1047 if (sscanf(index_str, "%"SCNu32, &range_index) != 1) {
1048 DEBUG(10, ("Found separator '#' but '%s' is not a "
1049 "valid range index. Skipping record\n",
1050 index_str));
1051 goto done;
1055 if (!idmap_autorid_validate_sid(domsid)) {
1056 DEBUG(10, ("String '%s' is not a valid sid. "
1057 "Skipping record.\n", domsid));
1058 goto done;
1061 if ((vi->domsid != NULL) && (strcmp(domsid, vi->domsid) != 0)) {
1062 DEBUG(10, ("key sid '%s' does not match requested sid '%s'.\n",
1063 domsid, vi->domsid));
1064 goto done;
1067 value = dbwrap_record_get_value(rec);
1069 if (value.dsize != sizeof(uint32_t)) {
1070 /* it might be a mapping of a well known sid */
1071 DEBUG(10, ("value size %u != sizeof(uint32_t) for sid '%s', "
1072 "skipping.\n", (unsigned)value.dsize, vi->domsid));
1073 goto done;
1076 rangenum = IVAL(value.dptr, 0);
1078 db = dbwrap_record_get_db(rec);
1080 status = vi->fn(db, domsid, range_index, rangenum, vi->private_data);
1081 if (!NT_STATUS_IS_OK(status)) {
1082 ret = -1;
1083 goto done;
1086 vi->count++;
1087 ret = 0;
1089 done:
1090 return ret;
1093 static NTSTATUS idmap_autorid_iterate_domain_ranges_int(struct db_context *db,
1094 const char *domsid,
1095 NTSTATUS (*fn)(struct db_context *db,
1096 const char *domsid,
1097 uint32_t index,
1098 uint32_t rangnum,
1099 void *private_data),
1100 void *private_data,
1101 int *count,
1102 NTSTATUS (*traverse)(struct db_context *db,
1103 int (*f)(struct db_record *, void *),
1104 void *private_data,
1105 int *count))
1107 NTSTATUS status;
1108 struct domain_range_visitor_ctx *vi;
1109 TALLOC_CTX *frame = talloc_stackframe();
1111 if (domsid == NULL) {
1112 DEBUG(10, ("No sid provided, operating on all ranges\n"));
1115 if (fn == NULL) {
1116 DEBUG(1, ("Error: missing visitor callback\n"));
1117 status = NT_STATUS_INVALID_PARAMETER;
1118 goto done;
1121 vi = talloc_zero(frame, struct domain_range_visitor_ctx);
1122 if (vi == NULL) {
1123 status = NT_STATUS_NO_MEMORY;
1124 goto done;
1127 vi->domsid = domsid;
1128 vi->fn = fn;
1129 vi->private_data = private_data;
1131 status = traverse(db, idmap_autorid_visit_domain_range, vi, NULL);
1132 if (!NT_STATUS_IS_OK(status)) {
1133 goto done;
1136 if (count != NULL) {
1137 *count = vi->count;
1140 done:
1141 talloc_free(frame);
1142 return status;
1145 NTSTATUS idmap_autorid_iterate_domain_ranges(struct db_context *db,
1146 const char *domsid,
1147 NTSTATUS (*fn)(struct db_context *db,
1148 const char *domsid,
1149 uint32_t index,
1150 uint32_t rangenum,
1151 void *private_data),
1152 void *private_data,
1153 int *count)
1155 NTSTATUS status;
1157 status = idmap_autorid_iterate_domain_ranges_int(db,
1158 domsid,
1160 private_data,
1161 count,
1162 dbwrap_traverse);
1164 return status;
1168 NTSTATUS idmap_autorid_iterate_domain_ranges_read(struct db_context *db,
1169 const char *domsid,
1170 NTSTATUS (*fn)(struct db_context *db,
1171 const char *domsid,
1172 uint32_t index,
1173 uint32_t rangenum,
1174 void *count),
1175 void *private_data,
1176 int *count)
1178 NTSTATUS status;
1180 status = idmap_autorid_iterate_domain_ranges_int(db,
1181 domsid,
1183 private_data,
1184 count,
1185 dbwrap_traverse_read);
1187 return status;
1192 * Delete all ranges configured for a given domain
1195 struct delete_domain_ranges_visitor_ctx {
1196 bool force;
1199 static NTSTATUS idmap_autorid_delete_domain_ranges_visitor(
1200 struct db_context *db,
1201 const char *domsid,
1202 uint32_t domain_range_index,
1203 uint32_t rangenum,
1204 void *private_data)
1206 struct delete_domain_ranges_visitor_ctx *ctx;
1207 NTSTATUS status;
1209 ctx = (struct delete_domain_ranges_visitor_ctx *)private_data;
1211 status = idmap_autorid_delete_range_by_sid(
1212 db, domsid, domain_range_index, ctx->force);
1213 return status;
1216 struct idmap_autorid_delete_domain_ranges_ctx {
1217 const char *domsid;
1218 bool force;
1219 int count; /* output: count records operated on */
1222 static NTSTATUS idmap_autorid_delete_domain_ranges_action(struct db_context *db,
1223 void *private_data)
1225 struct idmap_autorid_delete_domain_ranges_ctx *ctx;
1226 struct delete_domain_ranges_visitor_ctx visitor_ctx;
1227 int count;
1228 NTSTATUS status;
1230 ctx = (struct idmap_autorid_delete_domain_ranges_ctx *)private_data;
1232 ZERO_STRUCT(visitor_ctx);
1233 visitor_ctx.force = ctx->force;
1235 status = idmap_autorid_iterate_domain_ranges(db,
1236 ctx->domsid,
1237 idmap_autorid_delete_domain_ranges_visitor,
1238 &visitor_ctx,
1239 &count);
1240 if (!NT_STATUS_IS_OK(status)) {
1241 return status;
1244 ctx->count = count;
1246 return NT_STATUS_OK;
1249 NTSTATUS idmap_autorid_delete_domain_ranges(struct db_context *db,
1250 const char *domsid,
1251 bool force,
1252 int *count)
1254 NTSTATUS status;
1255 struct idmap_autorid_delete_domain_ranges_ctx ctx;
1257 ZERO_STRUCT(ctx);
1258 ctx.domsid = domsid;
1259 ctx.force = force;
1261 status = dbwrap_trans_do(db, idmap_autorid_delete_domain_ranges_action,
1262 &ctx);
1263 if (!NT_STATUS_IS_OK(status)) {
1264 return status;
1267 *count = ctx.count;
1269 return NT_STATUS_OK;