idmap_autorid: remove the ignore_builtin bool from the global_config struct
[Samba/wip.git] / source3 / winbindd / idmap_autorid.c
blob72bd3846622eb00903fba4f7acf4d0da6977a735
1 /*
2 * idmap_autorid: static map between Active Directory/NT RIDs
3 * and RFC 2307 accounts
5 * based on the idmap_rid module, but this module defines the ranges
6 * for the domains by automatically allocating a range for each domain
8 * Copyright (C) Christian Ambach, 2010-2012
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
26 * This module allocates ranges for domains to be used in a
27 * algorithmic mode like idmap_rid. Multiple ranges are supported
28 * for a single domain: If a rid exceeds the range size, a matching
29 * range is allocated to hold the rid's id.
31 * Here are the formulas applied:
34 * For a sid of the form domain_sid-rid, we have
36 * rid = reduced_rid + domain_range_index * range_size
38 * with
39 * reduced_rid := rid % range_size
40 * domain_range_index := rid / range_size
42 * And reduced_rid fits into a range.
44 * In the database, we associate a range_number to
45 * the pair domain_sid,domain_range_index.
47 * Now the unix id for the given sid calculates as:
49 * id = reduced_rid + range_low_id
51 * with
53 * range_low_id = low_id + range_number * range_size
56 * The inverse calculation goes like this:
58 * Given a unix id, let
60 * normalized_id := id - low_id
61 * reduced_rid := normalized_id % range_size
62 * range_number = normalized_id / range_size
64 * Then we have
66 * id = reduced_rid + low_id + range_number * range_size
68 * From the database, get the domain_sid,domain_range_index pair
69 * belonging to the range_number (if there is already one).
71 * Then the rid for the unix id calculates as:
73 * rid = reduced_rid + domain_range_index * range_size
76 #include "includes.h"
77 #include "system/filesys.h"
78 #include "winbindd.h"
79 #include "dbwrap/dbwrap.h"
80 #include "dbwrap/dbwrap_open.h"
81 #include "idmap.h"
82 #include "idmap_rw.h"
83 #include "../libcli/security/dom_sid.h"
84 #include "util_tdb.h"
85 #include "winbindd/idmap_tdb_common.h"
87 #undef DBGC_CLASS
88 #define DBGC_CLASS DBGC_IDMAP
90 #define HWM "NEXT RANGE"
91 #define ALLOC_HWM_UID "NEXT ALLOC UID"
92 #define ALLOC_HWM_GID "NEXT ALLOC GID"
93 #define ALLOC_RANGE "ALLOC"
94 #define CONFIGKEY "CONFIG"
96 struct autorid_global_config {
97 uint32_t minvalue;
98 uint32_t rangesize;
99 uint32_t maxranges;
102 struct autorid_range_config {
103 fstring domsid;
104 fstring keystr;
105 uint32_t rangenum;
106 uint32_t domain_range_index;
107 uint32_t low_id;
108 struct autorid_global_config *globalcfg;
111 /* handle to the tdb storing domain <-> range assignments */
112 static struct db_context *autorid_db;
114 static bool ignore_builtin = false;
116 static NTSTATUS idmap_autorid_get_domainrange_action(struct db_context *db,
117 void *private_data)
119 NTSTATUS ret;
120 uint32_t rangenum, hwm;
121 char *numstr;
122 struct autorid_range_config *range;
124 range = (struct autorid_range_config *)private_data;
126 ret = dbwrap_fetch_uint32_bystring(db, range->keystr,
127 &(range->rangenum));
129 if (NT_STATUS_IS_OK(ret)) {
130 /* entry is already present*/
131 return ret;
134 DEBUG(10, ("Acquiring new range for domain %s "
135 "(domain_range_index=%"PRIu32")\n",
136 range->domsid, range->domain_range_index));
138 /* fetch the current HWM */
139 ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
140 if (!NT_STATUS_IS_OK(ret)) {
141 DEBUG(1, ("Fatal error while fetching current "
142 "HWM value: %s\n", nt_errstr(ret)));
143 ret = NT_STATUS_INTERNAL_ERROR;
144 goto error;
147 /* do we have a range left? */
148 if (hwm >= range->globalcfg->maxranges) {
149 DEBUG(1, ("No more domain ranges available!\n"));
150 ret = NT_STATUS_NO_MEMORY;
151 goto error;
154 /* increase the HWM */
155 ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &rangenum, 1);
156 if (!NT_STATUS_IS_OK(ret)) {
157 DEBUG(1, ("Fatal error while fetching a new "
158 "domain range value!\n"));
159 goto error;
162 /* store away the new mapping in both directions */
163 ret = dbwrap_store_uint32_bystring(db, range->keystr, rangenum);
164 if (!NT_STATUS_IS_OK(ret)) {
165 DEBUG(1, ("Fatal error while storing new "
166 "domain->range assignment!\n"));
167 goto error;
170 numstr = talloc_asprintf(db, "%u", rangenum);
171 if (!numstr) {
172 ret = NT_STATUS_NO_MEMORY;
173 goto error;
176 ret = dbwrap_store_bystring(db, numstr,
177 string_term_tdb_data(range->keystr), TDB_INSERT);
179 talloc_free(numstr);
180 if (!NT_STATUS_IS_OK(ret)) {
181 DEBUG(1, ("Fatal error while storing "
182 "new domain->range assignment!\n"));
183 goto error;
185 DEBUG(5, ("Acquired new range #%d for domain %s "
186 "(domain_range_index=%"PRIu32")\n", rangenum, range->keystr,
187 range->domain_range_index));
189 range->rangenum = rangenum;
191 return NT_STATUS_OK;
193 error:
194 return ret;
198 static NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
199 struct autorid_range_config *range,
200 bool read_only)
202 NTSTATUS ret;
205 * try to find mapping without locking the database,
206 * if it is not found create a mapping in a transaction unless
207 * read-only mode has been set
209 if (range->domain_range_index > 0) {
210 snprintf(range->keystr, FSTRING_LEN, "%s#%"PRIu32,
211 range->domsid, range->domain_range_index);
212 } else {
213 fstrcpy(range->keystr, range->domsid);
216 ret = dbwrap_fetch_uint32_bystring(db, range->keystr,
217 &(range->rangenum));
219 if (!NT_STATUS_IS_OK(ret)) {
220 if (read_only) {
221 return NT_STATUS_NOT_FOUND;
223 ret = dbwrap_trans_do(db,
224 idmap_autorid_get_domainrange_action, range);
227 range->low_id = range->globalcfg->minvalue
228 + range->rangenum * range->globalcfg->rangesize;
230 DEBUG(10, ("Using range #%d for domain %s "
231 "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
232 range->rangenum, range->domsid, range->domain_range_index,
233 range->low_id));
235 return ret;
238 static NTSTATUS idmap_autorid_allocate_id(struct idmap_domain *dom,
239 struct unixid *xid) {
241 NTSTATUS ret;
242 struct idmap_tdb_common_context *commoncfg;
243 struct autorid_global_config *globalcfg;
244 struct autorid_range_config range;
246 commoncfg =
247 talloc_get_type_abort(dom->private_data,
248 struct idmap_tdb_common_context);
250 globalcfg = talloc_get_type(commoncfg->private_data,
251 struct autorid_global_config);
253 if (dom->read_only) {
254 DEBUG(3, ("Backend is read-only, refusing "
255 "new allocation request\n"));
256 return NT_STATUS_UNSUCCESSFUL;
259 /* fetch the range for the allocation pool */
261 ZERO_STRUCT(range);
263 range.globalcfg = globalcfg;
264 fstrcpy(range.domsid, ALLOC_RANGE);
266 ret = idmap_autorid_get_domainrange(autorid_db, &range, dom->read_only);
268 if (!NT_STATUS_IS_OK(ret)) {
269 DEBUG(3, ("Could not determine range for allocation pool, "
270 "check previous messages for reason\n"));
271 return ret;
274 ret = idmap_tdb_common_get_new_id(dom, xid);
276 if (!NT_STATUS_IS_OK(ret)) {
277 DEBUG(1, ("Fatal error while allocating new ID!\n"));
278 return ret;
281 xid->id = xid->id + range.low_id;
283 DEBUG(10, ("Returned new %s %d from allocation range\n",
284 (xid->type==ID_TYPE_UID)?"uid":"gid", xid->id));
286 return ret;
290 * map a SID to xid using the idmap_tdb like pool
292 static NTSTATUS idmap_autorid_map_id_to_sid(struct idmap_domain *dom,
293 struct id_map *map)
295 NTSTATUS ret;
297 /* look out for the mapping */
298 ret = idmap_tdb_common_unixid_to_sid(dom, map);
300 if (NT_STATUS_IS_OK(ret)) {
301 map->status = ID_MAPPED;
302 return ret;
305 map->status = ID_UNKNOWN;
307 DEBUG(10, ("no ID->SID mapping for %d could be found\n", map->xid.id));
309 return ret;
312 static NTSTATUS idmap_autorid_id_to_sid(struct autorid_global_config *cfg,
313 struct idmap_domain *dom,
314 struct id_map *map)
316 uint32_t range_number;
317 uint32_t domain_range_index = 0;
318 uint32_t normalized_id;
319 uint32_t reduced_rid;
320 uint32_t rid;
321 TDB_DATA data = tdb_null;
322 char *keystr;
323 struct dom_sid domsid;
324 NTSTATUS status;
325 bool ok;
326 const char *q = NULL;
328 /* can this be one of our ids? */
329 if (map->xid.id < cfg->minvalue) {
330 DEBUG(10, ("id %d is lower than minimum value, "
331 "ignoring mapping request\n", map->xid.id));
332 map->status = ID_UNKNOWN;
333 return NT_STATUS_OK;
336 if (map->xid.id > (cfg->minvalue + cfg->rangesize * cfg->maxranges)) {
337 DEBUG(10, ("id %d is outside of maximum id value, "
338 "ignoring mapping request\n", map->xid.id));
339 map->status = ID_UNKNOWN;
340 return NT_STATUS_OK;
343 /* determine the range of this uid */
345 normalized_id = map->xid.id - cfg->minvalue;
346 range_number = normalized_id / cfg->rangesize;
348 keystr = talloc_asprintf(talloc_tos(), "%u", range_number);
349 if (!keystr) {
350 return NT_STATUS_NO_MEMORY;
353 status = dbwrap_fetch_bystring(autorid_db, talloc_tos(), keystr, &data);
354 TALLOC_FREE(keystr);
356 if (!NT_STATUS_IS_OK(status)) {
357 DEBUG(4, ("id %d belongs to range %d which does not have "
358 "domain mapping, ignoring mapping request\n",
359 map->xid.id, range_number));
360 TALLOC_FREE(data.dptr);
361 map->status = ID_UNKNOWN;
362 return NT_STATUS_OK;
365 if (strncmp((const char *)data.dptr,
366 ALLOC_RANGE,
367 strlen(ALLOC_RANGE)) == 0) {
369 * this is from the alloc range, check if there is a mapping
371 DEBUG(5, ("id %d belongs to allocation range, "
372 "checking for mapping\n",
373 map->xid.id));
374 TALLOC_FREE(data.dptr);
375 return idmap_autorid_map_id_to_sid(dom, map);
378 ok = dom_sid_parse_endp((const char *)data.dptr, &domsid, &q);
379 TALLOC_FREE(data.dptr);
380 if (!ok) {
381 map->status = ID_UNKNOWN;
382 return NT_STATUS_OK;
384 if (q != NULL)
385 if (sscanf(q+1, "%"SCNu32, &domain_range_index) != 1) {
386 DEBUG(10, ("Domain range index not found, "
387 "ignoring mapping request\n"));
388 map->status = ID_UNKNOWN;
389 return NT_STATUS_OK;
392 reduced_rid = normalized_id % cfg->rangesize;
393 rid = reduced_rid + domain_range_index * cfg->rangesize;
395 sid_compose(map->sid, &domsid, rid);
397 /* We **really** should have some way of validating
398 the SID exists and is the correct type here. But
399 that is a deficiency in the idmap_rid design. */
401 map->status = ID_MAPPED;
402 map->xid.type = ID_TYPE_BOTH;
404 return NT_STATUS_OK;
407 /**********************************
408 Single sid to id lookup function.
409 **********************************/
411 static NTSTATUS idmap_autorid_sid_to_id(struct autorid_global_config *global,
412 struct autorid_range_config *range,
413 struct id_map *map)
415 uint32_t rid;
416 uint32_t reduced_rid;
418 sid_peek_rid(map->sid, &rid);
420 reduced_rid = rid % global->rangesize;
422 map->xid.id = reduced_rid + range->low_id;
423 map->xid.type = ID_TYPE_BOTH;
425 /* We **really** should have some way of validating
426 the SID exists and is the correct type here. But
427 that is a deficiency in the idmap_rid design. */
429 map->status = ID_MAPPED;
431 return NT_STATUS_OK;
434 /**********************************
435 lookup a set of unix ids.
436 **********************************/
438 static NTSTATUS idmap_autorid_unixids_to_sids(struct idmap_domain *dom,
439 struct id_map **ids)
441 struct idmap_tdb_common_context *commoncfg;
442 struct autorid_global_config *globalcfg;
443 NTSTATUS ret;
444 int i;
445 int num_tomap = 0;
446 int num_mapped = 0;
448 /* initialize the status to avoid surprise */
449 for (i = 0; ids[i]; i++) {
450 ids[i]->status = ID_UNKNOWN;
451 num_tomap++;
454 commoncfg =
455 talloc_get_type_abort(dom->private_data,
456 struct idmap_tdb_common_context);
458 globalcfg = talloc_get_type(commoncfg->private_data,
459 struct autorid_global_config);
461 for (i = 0; ids[i]; i++) {
463 ret = idmap_autorid_id_to_sid(globalcfg, dom, ids[i]);
465 if ((!NT_STATUS_IS_OK(ret)) &&
466 (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
467 /* some fatal error occurred, log it */
468 DEBUG(3, ("Unexpected error resolving an ID "
469 " (%d)\n", ids[i]->xid.id));
470 goto failure;
473 if (NT_STATUS_IS_OK(ret) && ids[i]->status == ID_MAPPED) {
474 num_mapped++;
479 if (num_tomap == num_mapped) {
480 return NT_STATUS_OK;
481 } else if (num_mapped == 0) {
482 return NT_STATUS_NONE_MAPPED;
485 return STATUS_SOME_UNMAPPED;
488 failure:
489 return ret;
493 * map a SID to xid using the idmap_tdb like pool
495 static NTSTATUS idmap_autorid_map_sid_to_id(struct idmap_domain *dom,
496 struct id_map *map,
497 struct idmap_tdb_common_context *ctx)
499 NTSTATUS ret;
500 int res;
502 /* see if we already have a mapping */
503 ret = idmap_tdb_common_sid_to_unixid(dom, map);
505 if (NT_STATUS_IS_OK(ret)) {
506 map->status = ID_MAPPED;
507 return ret;
510 /* bad things happened */
511 if (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
512 DEBUG(1, ("Looking up SID->ID mapping for %s failed\n",
513 sid_string_dbg(map->sid)));
514 return ret;
517 if (dom->read_only) {
518 DEBUG(3, ("Not allocating new mapping for %s, because backend "
519 "is read-only\n", sid_string_dbg(map->sid)));
520 return NT_STATUS_NONE_MAPPED;
523 DEBUG(10, ("Creating new mapping in pool for %s\n",
524 sid_string_dbg(map->sid)));
526 /* create new mapping */
527 res = dbwrap_transaction_start(ctx->db);
528 if (res != 0) {
529 DEBUG(2, ("transaction_start failed\n"));
530 return NT_STATUS_INTERNAL_DB_CORRUPTION;
533 ret = idmap_tdb_common_new_mapping(dom, map);
535 map->status = (NT_STATUS_IS_OK(ret))?ID_MAPPED:ID_UNMAPPED;
537 if (!NT_STATUS_IS_OK(ret)) {
538 if (dbwrap_transaction_cancel(ctx->db) != 0) {
539 smb_panic("Cancelling transaction failed");
541 return ret;
544 res = dbwrap_transaction_commit(ctx->db);
545 if (res == 0) {
546 return ret;
549 DEBUG(2, ("transaction_commit failed\n"));
550 return NT_STATUS_INTERNAL_DB_CORRUPTION;
554 /**********************************
555 lookup a set of sids.
556 **********************************/
558 static NTSTATUS idmap_autorid_sids_to_unixids(struct idmap_domain *dom,
559 struct id_map **ids)
561 struct idmap_tdb_common_context *commoncfg;
562 struct autorid_global_config *global;
563 NTSTATUS ret;
564 int i;
565 int num_tomap = 0;
566 int num_mapped = 0;
568 /* initialize the status to avoid surprise */
569 for (i = 0; ids[i]; i++) {
570 ids[i]->status = ID_UNKNOWN;
571 num_tomap++;
574 commoncfg =
575 talloc_get_type_abort(dom->private_data,
576 struct idmap_tdb_common_context);
578 global = talloc_get_type(commoncfg->private_data,
579 struct autorid_global_config);
581 for (i = 0; ids[i]; i++) {
582 struct winbindd_tdc_domain *domain;
583 struct autorid_range_config range;
584 uint32_t rid;
585 struct dom_sid domainsid;
587 ZERO_STRUCT(range);
589 DEBUG(10, ("Trying to map %s\n", sid_string_dbg(ids[i]->sid)));
591 sid_copy(&domainsid, ids[i]->sid);
592 if (!sid_split_rid(&domainsid, &rid)) {
593 DEBUG(4, ("Could not determine domain SID from %s, "
594 "ignoring mapping request\n",
595 sid_string_dbg(ids[i]->sid)));
596 continue;
599 /* is this a well-known SID? */
601 if (sid_check_is_wellknown_domain(&domainsid, NULL)) {
603 DEBUG(10, ("SID %s is well-known, using pool\n",
604 sid_string_dbg(ids[i]->sid)));
606 ret = idmap_autorid_map_sid_to_id(dom, ids[i],
607 commoncfg);
609 if (!NT_STATUS_IS_OK(ret) &&
610 !NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
611 DEBUG(3, ("Unexpected error resolving "
612 "SID (%s)\n",
613 sid_string_dbg(ids[i]->sid)));
614 goto failure;
617 if (ids[i]->status == ID_MAPPED) {
618 num_mapped++;
621 continue;
624 /* BUILTIN is passdb's job */
625 if (dom_sid_equal(&domainsid, &global_sid_Builtin) &&
626 ignore_builtin) {
627 DEBUG(10, ("Ignoring request for BUILTIN domain\n"));
628 continue;
632 * Check if the domain is around
634 domain = wcache_tdc_fetch_domainbysid(talloc_tos(),
635 &domainsid);
636 if (domain == NULL) {
637 DEBUG(10, ("Ignoring unknown domain sid %s\n",
638 sid_string_dbg(&domainsid)));
639 continue;
641 TALLOC_FREE(domain);
643 range.globalcfg = global;
644 sid_to_fstring(range.domsid, &domainsid);
646 /* Calculate domain_range_index for multi-range support */
647 range.domain_range_index = rid / (global->rangesize);
649 ret = idmap_autorid_get_domainrange(autorid_db, &range,
650 dom->read_only);
652 /* read-only mode and a new domain range would be required? */
653 if (NT_STATUS_EQUAL(ret, NT_STATUS_NOT_FOUND) &&
654 dom->read_only) {
655 DEBUG(10, ("read-only is enabled, did not allocate "
656 "new range for domain %s\n",
657 sid_string_dbg(&domainsid)));
658 continue;
661 if (!NT_STATUS_IS_OK(ret)) {
662 DEBUG(3, ("Could not determine range for domain, "
663 "check previous messages for reason\n"));
664 goto failure;
667 ret = idmap_autorid_sid_to_id(global, &range, ids[i]);
669 if ((!NT_STATUS_IS_OK(ret)) &&
670 (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
671 /* some fatal error occurred, log it */
672 DEBUG(3, ("Unexpected error resolving a SID (%s)\n",
673 sid_string_dbg(ids[i]->sid)));
674 goto failure;
677 if (NT_STATUS_IS_OK(ret)) {
678 num_mapped++;
682 if (num_tomap == num_mapped) {
683 return NT_STATUS_OK;
684 } else if (num_mapped == 0) {
685 return NT_STATUS_NONE_MAPPED;
688 return STATUS_SOME_UNMAPPED;
690 failure:
691 return ret;
695 /* initialize the given HWM to 0 if it does not exist yet */
696 static NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
698 NTSTATUS status;
699 uint32_t hwmval;
701 status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
702 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
703 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
704 if (!NT_STATUS_IS_OK(status)) {
705 DEBUG(0,
706 ("Unable to initialise HWM (%s) in autorid "
707 "database: %s\n", hwm, nt_errstr(status)));
708 return NT_STATUS_INTERNAL_DB_ERROR;
710 } else if (!NT_STATUS_IS_OK(status)) {
711 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
712 "database: %s\n", hwm, nt_errstr(status)));
713 return status;
716 return NT_STATUS_OK;
720 * open and initialize the database which stores the ranges for the domains
722 static NTSTATUS idmap_autorid_db_init(const char *path,
723 TALLOC_CTX *mem_ctx,
724 struct db_context **db)
726 NTSTATUS status;
728 if (*db != NULL) {
729 /* its already open */
730 return NT_STATUS_OK;
733 /* Open idmap repository */
734 *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
735 DBWRAP_LOCK_ORDER_1);
737 if (*db == NULL) {
738 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
739 return NT_STATUS_UNSUCCESSFUL;
742 /* Initialize high water mark for the currently used range to 0 */
744 status = idmap_autorid_init_hwm(*db, HWM);
745 NT_STATUS_NOT_OK_RETURN(status);
747 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
748 NT_STATUS_NOT_OK_RETURN(status);
750 status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
752 return status;
755 static struct autorid_global_config *idmap_autorid_loadconfig(struct db_context *db,
756 TALLOC_CTX *ctx)
759 TDB_DATA data;
760 struct autorid_global_config *cfg;
761 unsigned long minvalue, rangesize, maxranges;
762 NTSTATUS status;
764 status = dbwrap_fetch_bystring(db, ctx, CONFIGKEY, &data);
766 if (!NT_STATUS_IS_OK(status)) {
767 DEBUG(10, ("No saved config found\n"));
768 return NULL;
771 cfg = talloc_zero(ctx, struct autorid_global_config);
772 if (!cfg) {
773 return NULL;
776 if (sscanf((char *)data.dptr,
777 "minvalue:%lu rangesize:%lu maxranges:%lu",
778 &minvalue, &rangesize, &maxranges) != 3) {
779 DEBUG(1,
780 ("Found invalid configuration data"
781 "creating new config\n"));
782 return NULL;
785 cfg->minvalue = minvalue;
786 cfg->rangesize = rangesize;
787 cfg->maxranges = maxranges;
789 DEBUG(10, ("Loaded previously stored configuration "
790 "minvalue:%d rangesize:%d\n",
791 cfg->minvalue, cfg->rangesize));
793 return cfg;
797 static NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
798 struct autorid_global_config *cfg)
801 NTSTATUS status;
802 TDB_DATA data;
803 char *cfgstr;
805 cfgstr =
806 talloc_asprintf(talloc_tos(),
807 "minvalue:%u rangesize:%u maxranges:%u",
808 cfg->minvalue, cfg->rangesize, cfg->maxranges);
810 if (!cfgstr) {
811 return NT_STATUS_NO_MEMORY;
814 data = string_tdb_data(cfgstr);
816 status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
818 talloc_free(cfgstr);
820 return status;
823 static NTSTATUS idmap_autorid_preallocate_wellknown(struct idmap_domain *dom)
825 const char *groups[] = { "S-1-1-0", "S-1-2-0", "S-1-2-1",
826 "S-1-3-0", "S-1-3-1", "S-1-3-2", "S-1-3-3", "S-1-3-4",
827 "S-1-5-1", "S-1-5-2", "S-1-5-3", "S-1-5-4", "S-1-5-6",
828 "S-1-5-7", "S-1-5-8", "S-1-5-9", "S-1-5-10", "S-1-5-11",
829 "S-1-5-12", "S-1-5-13", "S-1-5-14", "S-1-5-15",
830 "S-1-5-17", "S-1-5-18", "S-1-5-19", "S-1-5-20"
833 struct id_map **maps;
834 int i, num;
835 NTSTATUS status;
837 if (dom->read_only) {
838 return NT_STATUS_OK;
841 num = ARRAY_SIZE(groups);
843 maps = talloc_array(talloc_tos(), struct id_map*, num+1);
844 if (!maps) {
845 return NT_STATUS_NO_MEMORY;
848 for (i = 0; i < num; i++) {
849 maps[i] = talloc(maps, struct id_map);
850 if (maps[i] == NULL) {
851 talloc_free(maps);
852 return NT_STATUS_NO_MEMORY;
854 maps[i]->xid.type = ID_TYPE_GID;
855 maps[i]->sid = dom_sid_parse_talloc(maps, groups[i]);
858 maps[num] = NULL;
860 status = idmap_autorid_sids_to_unixids(dom, maps);
862 DEBUG(10,("Preallocation run finished with status %s\n",
863 nt_errstr(status)));
865 talloc_free(maps);
867 return NT_STATUS_IS_OK(status)?NT_STATUS_OK:NT_STATUS_UNSUCCESSFUL;
870 static NTSTATUS idmap_autorid_initialize(struct idmap_domain *dom)
872 struct idmap_tdb_common_context *commonconfig;
873 struct autorid_global_config *config;
874 struct autorid_global_config *storedconfig = NULL;
875 NTSTATUS status;
876 uint32_t hwm;
878 if (!strequal(dom->name, "*")) {
879 DEBUG(0, ("idmap_autorid_initialize: Error: autorid configured "
880 "for domain '%s'. But autorid can only be used for "
881 "the default idmap configuration.\n", dom->name));
882 return NT_STATUS_INVALID_PARAMETER;
885 commonconfig = talloc_zero(dom, struct idmap_tdb_common_context);
886 if (!commonconfig) {
887 DEBUG(0, ("Out of memory!\n"));
888 return NT_STATUS_NO_MEMORY;
891 commonconfig->rw_ops = talloc_zero(commonconfig, struct idmap_rw_ops);
892 if (commonconfig->rw_ops == NULL) {
893 DEBUG(0, ("Out of memory!\n"));
894 return NT_STATUS_NO_MEMORY;
897 config = talloc_zero(commonconfig, struct autorid_global_config);
898 if (!config) {
899 DEBUG(0, ("Out of memory!\n"));
900 return NT_STATUS_NO_MEMORY;
903 status = idmap_autorid_db_init(state_path("autorid.tdb"),
904 NULL, /* TALLOC_CTX */
905 &autorid_db);
906 if (!NT_STATUS_IS_OK(status)) {
907 goto error;
910 config->minvalue = dom->low_id;
911 config->rangesize = lp_parm_int(-1, "idmap config *",
912 "rangesize", 100000);
914 if (config->rangesize < 2000) {
915 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
916 status = NT_STATUS_INVALID_PARAMETER;
917 goto error;
920 config->maxranges = (dom->high_id - dom->low_id + 1) /
921 config->rangesize;
923 if (config->maxranges == 0) {
924 DEBUG(1, ("allowed uid range is smaller then rangesize, "
925 "increase uid range or decrease rangesize\n"));
926 status = NT_STATUS_INVALID_PARAMETER;
927 goto error;
930 /* check if the high-low limit is a multiple of the rangesize */
931 if ((dom->high_id - dom->low_id + 1) % config->rangesize != 0) {
932 DEBUG(5, ("High uid-low uid difference of %d "
933 "is not a multiple of the rangesize %d, "
934 "limiting ranges to lower boundary number of %d\n",
935 (dom->high_id - dom->low_id + 1), config->rangesize,
936 config->maxranges));
939 DEBUG(10, ("Current configuration in config is "
940 "minvalue:%d rangesize:%d maxranges:%d\n",
941 config->minvalue, config->rangesize, config->maxranges));
943 /* read previously stored config and current HWM */
944 storedconfig = idmap_autorid_loadconfig(autorid_db, talloc_tos());
946 status = dbwrap_fetch_uint32_bystring(autorid_db, HWM, &hwm);
947 if (!NT_STATUS_IS_OK(status)) {
948 DEBUG(1, ("Fatal error while fetching current "
949 "HWM value: %s\n", nt_errstr(status)));
950 status = NT_STATUS_INTERNAL_ERROR;
951 goto error;
954 /* did the minimum value or rangesize change? */
955 if (storedconfig &&
956 ((storedconfig->minvalue != config->minvalue) ||
957 (storedconfig->rangesize != config->rangesize))) {
958 DEBUG(1, ("New configuration values for rangesize or "
959 "minimum uid value conflict with previously "
960 "used values! Aborting initialization\n"));
961 status = NT_STATUS_INVALID_PARAMETER;
962 goto error;
966 * has the highest uid value been reduced to setting that is not
967 * sufficient any more for already existing ranges?
969 if (hwm > config->maxranges) {
970 DEBUG(1, ("New upper uid limit is too low to cover "
971 "existing mappings! Aborting initialization\n"));
972 status = NT_STATUS_INVALID_PARAMETER;
973 goto error;
976 status = idmap_autorid_saveconfig(autorid_db, config);
978 if (!NT_STATUS_IS_OK(status)) {
979 DEBUG(1, ("Failed to store configuration data!\n"));
980 goto error;
983 DEBUG(5, ("%d domain ranges with a size of %d are available\n",
984 config->maxranges, config->rangesize));
986 ignore_builtin = lp_parm_bool(-1, "idmap config *",
987 "ignore builtin", false);
989 /* fill the TDB common configuration */
990 commonconfig->private_data = config;
992 commonconfig->db = autorid_db;
993 commonconfig->max_id = config->rangesize -1;
994 commonconfig->hwmkey_uid = ALLOC_HWM_UID;
995 commonconfig->hwmkey_gid = ALLOC_HWM_GID;
996 commonconfig->rw_ops->get_new_id = idmap_autorid_allocate_id;
997 commonconfig->rw_ops->set_mapping = idmap_tdb_common_set_mapping;
999 dom->private_data = commonconfig;
1001 /* preallocate well-known SIDs in the pool */
1002 status = idmap_autorid_preallocate_wellknown(dom);
1004 goto done;
1006 error:
1007 talloc_free(config);
1009 done:
1010 talloc_free(storedconfig);
1012 return status;
1016 Close the idmap tdb instance
1018 static struct idmap_methods autorid_methods = {
1019 .init = idmap_autorid_initialize,
1020 .unixids_to_sids = idmap_autorid_unixids_to_sids,
1021 .sids_to_unixids = idmap_autorid_sids_to_unixids,
1022 .allocate_id = idmap_autorid_allocate_id
1025 NTSTATUS samba_init_module(void)
1027 return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
1028 "autorid", &autorid_methods);