2 RID allocation helper functions
4 Copyright (C) Andrew Bartlett 2010
5 Copyright (C) Andrew Tridgell 2010
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * Component: RID allocation logic
26 * Description: manage RID Set and RID Manager objects
31 #include "ldb_module.h"
32 #include "dsdb/samdb/samdb.h"
33 #include "dsdb/samdb/ldb_modules/util.h"
34 #include "lib/messaging/irpc.h"
35 #include "param/param.h"
36 #include "librpc/gen_ndr/ndr_misc.h"
39 Note: the RID allocation attributes in AD are very badly named. Here
40 is what we think they really do:
43 - rIDPreviousAllocationPool: the pool which a DC is currently
44 pulling RIDs from. Managed by client DC
46 - rIDAllocationPool: the pool that the DC will switch to next,
47 when rIDPreviousAllocationPool is exhausted. Managed by RID Manager.
49 - rIDNextRID: the last RID allocated by this DC. Managed by client DC
51 in RID Manager object:
52 - rIDAvailablePool: the pool where the RID Manager gets new rID
53 pools from when it gets a EXOP_RID_ALLOC getncchanges call (or
54 locally when the DC is the RID Manager)
59 make a IRPC call to the drepl task to ask it to get the RID
60 Manager to give us another RID pool.
62 This function just sends the message to the drepl task then
63 returns immediately. It should be called well before we
64 completely run out of RIDs
66 static void ridalloc_poke_rid_manager(struct ldb_module *module)
68 struct messaging_context *msg;
69 struct server_id *server;
70 struct ldb_context *ldb = ldb_module_get_ctx(module);
71 struct loadparm_context *lp_ctx =
72 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
73 TALLOC_CTX *tmp_ctx = talloc_new(module);
75 msg = messaging_client_init(tmp_ctx, lp_messaging_path(tmp_ctx, lp_ctx),
76 ldb_get_event_context(ldb));
78 DEBUG(3,(__location__ ": Failed to create messaging context\n"));
83 server = irpc_servers_byname(msg, msg, "dreplsrv");
85 /* this means the drepl service is not running */
90 messaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
92 /* we don't care if the message got through */
97 static const char * const ridalloc_ridset_attrs[] = {
99 "rIDPreviousAllocationPool",
105 struct ridalloc_ridset_values {
112 static void ridalloc_get_ridset_values(struct ldb_message *msg, struct ridalloc_ridset_values *v)
114 v->alloc_pool = ldb_msg_find_attr_as_uint64(msg, "rIDAllocationPool", UINT64_MAX);
115 v->prev_pool = ldb_msg_find_attr_as_uint64(msg, "rIDPreviousAllocationPool", UINT64_MAX);
116 v->next_rid = ldb_msg_find_attr_as_uint(msg, "rIDNextRID", UINT32_MAX);
117 v->used_pool = ldb_msg_find_attr_as_uint(msg, "rIDUsedPool", UINT32_MAX);
120 static int ridalloc_set_ridset_values(struct ldb_module *module,
121 struct ldb_message *msg,
122 const struct ridalloc_ridset_values *o,
123 const struct ridalloc_ridset_values *n)
125 const uint32_t *o32, *n32;
126 const uint64_t *o64, *n64;
129 #define SETUP_PTRS(field, optr, nptr, max) do { \
132 if (o->field == max) { \
135 if (n->field == max) { \
138 if (o->field == n->field) { \
144 SETUP_PTRS(alloc_pool, o64, n64, UINT64_MAX);
145 ret = dsdb_msg_constrainted_update_uint64(module, msg,
148 if (ret != LDB_SUCCESS) {
152 SETUP_PTRS(prev_pool, o64, n64, UINT64_MAX);
153 ret = dsdb_msg_constrainted_update_uint64(module, msg,
154 "rIDPreviousAllocationPool",
156 if (ret != LDB_SUCCESS) {
160 SETUP_PTRS(next_rid, o32, n32, UINT32_MAX);
161 ret = dsdb_msg_constrainted_update_uint32(module, msg,
164 if (ret != LDB_SUCCESS) {
168 SETUP_PTRS(used_pool, o32, n32, UINT32_MAX);
169 ret = dsdb_msg_constrainted_update_uint32(module, msg,
172 if (ret != LDB_SUCCESS) {
181 allocate a new range of RIDs in the RID Manager object
183 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
186 TALLOC_CTX *tmp_ctx = talloc_new(module);
187 const char *attrs[] = { "rIDAvailablePool", NULL };
188 uint64_t rid_pool, new_rid_pool, dc_pool;
189 uint32_t rid_pool_lo, rid_pool_hi;
190 struct ldb_result *res;
191 struct ldb_context *ldb = ldb_module_get_ctx(module);
192 const unsigned alloc_size = 500;
194 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn,
195 attrs, DSDB_FLAG_NEXT_MODULE);
196 if (ret != LDB_SUCCESS) {
197 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
198 ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
199 talloc_free(tmp_ctx);
203 rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
204 rid_pool_lo = rid_pool & 0xFFFFFFFF;
205 rid_pool_hi = rid_pool >> 32;
206 if (rid_pool_lo >= rid_pool_hi) {
207 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
208 rid_pool_lo, rid_pool_hi);
209 talloc_free(tmp_ctx);
213 /* lower part of new pool is the low part of the rIDAvailablePool */
214 dc_pool = rid_pool_lo;
216 /* allocate 500 RIDs to this DC */
217 rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
219 /* work out upper part of new pool */
220 dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
222 /* and new rIDAvailablePool value */
223 new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
225 ret = dsdb_module_constrainted_update_uint64(module, rid_manager_dn, "rIDAvailablePool",
226 &rid_pool, &new_rid_pool);
227 if (ret != LDB_SUCCESS) {
228 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
230 talloc_free(tmp_ctx);
234 (*new_pool) = dc_pool;
235 talloc_free(tmp_ctx);
240 create a RID Set object for the specified DC
242 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
243 struct ldb_dn *rid_manager_dn,
244 struct ldb_dn *ntds_dn, struct ldb_dn **dn)
246 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
247 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
249 struct ldb_message *msg;
250 struct ldb_context *ldb = ldb_module_get_ctx(module);
251 static const struct ridalloc_ridset_values o = {
252 .alloc_pool = UINT64_MAX,
253 .prev_pool = UINT64_MAX,
254 .next_rid = UINT32_MAX,
255 .used_pool = UINT32_MAX,
257 struct ridalloc_ridset_values n = {
267 find the machine object for the DC
268 construct the RID Set DN
269 load rIDAvailablePool to find next available set
270 modify RID Manager object to update rIDAvailablePool
271 add the RID Set object
272 link to the RID Set object in machine object
275 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
277 talloc_free(tmp_ctx);
278 return ldb_module_oom(module);
281 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
282 if (ret != LDB_SUCCESS) {
283 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
284 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
285 talloc_free(tmp_ctx);
289 rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
290 if (rid_set_dn == NULL) {
291 talloc_free(tmp_ctx);
292 return ldb_module_oom(module);
295 if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
296 talloc_free(tmp_ctx);
297 return ldb_module_oom(module);
300 /* grab a pool from the RID Manager object */
301 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &n.alloc_pool);
302 if (ret != LDB_SUCCESS) {
303 talloc_free(tmp_ctx);
307 /* create the RID Set object */
308 msg = ldb_msg_new(tmp_ctx);
309 msg->dn = rid_set_dn;
311 ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
312 if (ret != LDB_SUCCESS) {
313 talloc_free(tmp_ctx);
317 ret = ridalloc_set_ridset_values(module, msg, &o, &n);
318 if (ret != LDB_SUCCESS) {
319 talloc_free(tmp_ctx);
323 /* we need this to go all the way to the top of the module
324 * stack, as we need all the extra attributes added (including
325 * complex ones like ntsecuritydescriptor) */
326 ret = dsdb_module_add(module, msg, DSDB_FLAG_TOP_MODULE | DSDB_MODIFY_RELAX);
327 if (ret != LDB_SUCCESS) {
328 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
329 ldb_dn_get_linearized(msg->dn),
331 talloc_free(tmp_ctx);
335 /* add the rIDSetReferences link */
336 msg = ldb_msg_new(tmp_ctx);
337 msg->dn = machine_dn;
339 ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
340 if (ret != LDB_SUCCESS) {
341 talloc_free(tmp_ctx);
344 msg->elements[0].flags = LDB_FLAG_MOD_ADD;
346 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
347 if (ret != LDB_SUCCESS) {
348 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
349 ldb_dn_get_linearized(msg->dn),
351 talloc_free(tmp_ctx);
355 (*dn) = talloc_steal(mem_ctx, rid_set_dn);
357 talloc_free(tmp_ctx);
363 create a RID Set object for this DC
365 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
368 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
369 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
371 struct ldb_context *ldb = ldb_module_get_ctx(module);
373 /* work out who is the RID Manager */
374 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
375 if (ret != LDB_SUCCESS) {
376 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
378 talloc_free(tmp_ctx);
382 /* find the DN of the RID Manager */
383 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
384 if (ret != LDB_SUCCESS) {
385 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
387 talloc_free(tmp_ctx);
391 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
392 ridalloc_poke_rid_manager(module);
393 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
394 talloc_free(tmp_ctx);
395 return LDB_ERR_UNWILLING_TO_PERFORM;
398 ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
399 talloc_free(tmp_ctx);
404 get a new RID pool for ourselves
405 also returns the first rid for the new pool
407 static int ridalloc_new_own_pool(struct ldb_module *module, uint64_t *new_pool)
409 TALLOC_CTX *tmp_ctx = talloc_new(module);
410 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
412 struct ldb_context *ldb = ldb_module_get_ctx(module);
414 /* work out who is the RID Manager */
415 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
416 if (ret != LDB_SUCCESS) {
417 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
419 talloc_free(tmp_ctx);
423 /* find the DN of the RID Manager */
424 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
425 if (ret != LDB_SUCCESS) {
426 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
428 talloc_free(tmp_ctx);
432 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
433 ridalloc_poke_rid_manager(module);
434 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
435 talloc_free(tmp_ctx);
436 return LDB_ERR_UNWILLING_TO_PERFORM;
439 /* grab a pool from the RID Manager object */
440 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
441 if (ret != LDB_SUCCESS) {
442 talloc_free(tmp_ctx);
446 talloc_free(tmp_ctx);
451 /* allocate a RID using our RID Set
452 If we run out of RIDs then allocate a new pool
453 either locally or by contacting the RID Manager
455 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
457 struct ldb_context *ldb;
459 struct ldb_dn *rid_set_dn;
460 struct ldb_result *res;
461 struct ldb_message *msg;
462 struct ridalloc_ridset_values oridset;
463 struct ridalloc_ridset_values nridset;
464 uint32_t prev_pool_lo, prev_pool_hi;
465 TALLOC_CTX *tmp_ctx = talloc_new(module);
468 ldb = ldb_module_get_ctx(module);
470 ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
471 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
472 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
474 if (ret != LDB_SUCCESS) {
475 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
477 talloc_free(tmp_ctx);
481 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
482 ridalloc_ridset_attrs, DSDB_FLAG_NEXT_MODULE);
483 if (ret != LDB_SUCCESS) {
484 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
485 ldb_dn_get_linearized(rid_set_dn));
486 talloc_free(tmp_ctx);
490 ridalloc_get_ridset_values(res->msgs[0], &oridset);
491 if (oridset.alloc_pool == UINT64_MAX) {
492 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
493 ldb_dn_get_linearized(rid_set_dn));
494 talloc_free(tmp_ctx);
495 return LDB_ERR_OPERATIONS_ERROR;
501 * If we never used a pool, setup out first pool
503 if (nridset.prev_pool == UINT64_MAX ||
504 nridset.next_rid == UINT32_MAX) {
505 nridset.prev_pool = nridset.alloc_pool;
506 nridset.next_rid = nridset.prev_pool & 0xFFFFFFFF;
510 * Now check if our current pool is still usable
512 nridset.next_rid += 1;
513 prev_pool_lo = nridset.prev_pool & 0xFFFFFFFF;
514 prev_pool_hi = nridset.prev_pool >> 32;
515 if (nridset.next_rid > prev_pool_hi) {
517 * We need a new pool, check if we already have a new one
518 * Otherwise we need to get a new pool.
520 if (nridset.alloc_pool == nridset.prev_pool) {
522 * if we are the RID Manager,
523 * we can get a new pool localy.
524 * Otherwise we fail the operation and
525 * ask async for a new pool.
527 ret = ridalloc_new_own_pool(module, &nridset.alloc_pool);
528 if (ret == LDB_ERR_UNWILLING_TO_PERFORM) {
529 ridalloc_poke_rid_manager(module);
530 talloc_free(tmp_ctx);
533 if (ret != LDB_SUCCESS) {
534 talloc_free(tmp_ctx);
540 * increment the rIDUsedPool attribute
542 * Note: w2k8r2 doesn't update this attribute,
543 * at least if it's itself the rid master.
545 nridset.used_pool += 1;
547 /* now use the new pool */
548 nridset.prev_pool = nridset.alloc_pool;
549 prev_pool_lo = nridset.prev_pool & 0xFFFFFFFF;
550 prev_pool_hi = nridset.prev_pool >> 32;
551 nridset.next_rid = prev_pool_lo;
554 if (nridset.next_rid < prev_pool_lo || nridset.next_rid > prev_pool_hi) {
555 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
556 (unsigned)nridset.next_rid,
557 (unsigned)prev_pool_lo,
558 (unsigned)prev_pool_hi);
559 talloc_free(tmp_ctx);
560 return LDB_ERR_OPERATIONS_ERROR;
564 * if we are half-exhausted then try to get a new pool.
566 if (nridset.next_rid > (prev_pool_hi + prev_pool_lo)/2) {
568 * if we are the RID Manager,
569 * we can get a new pool localy.
570 * Otherwise we fail the operation and
571 * ask async for a new pool.
573 ret = ridalloc_new_own_pool(module, &nridset.alloc_pool);
574 if (ret == LDB_ERR_UNWILLING_TO_PERFORM) {
575 ridalloc_poke_rid_manager(module);
578 if (ret != LDB_SUCCESS) {
579 talloc_free(tmp_ctx);
587 msg = ldb_msg_new(tmp_ctx);
589 return ldb_module_oom(module);
591 msg->dn = rid_set_dn;
593 ret = ridalloc_set_ridset_values(module, msg,
595 if (ret != LDB_SUCCESS) {
596 talloc_free(tmp_ctx);
600 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
601 if (ret != LDB_SUCCESS) {
602 talloc_free(tmp_ctx);
606 talloc_free(tmp_ctx);
607 *rid = nridset.next_rid;
613 called by DSDB_EXTENDED_ALLOCATE_RID_POOL extended operation in samldb
615 int ridalloc_allocate_rid_pool_fsmo(struct ldb_module *module, struct dsdb_fsmo_extended_op *exop)
617 struct ldb_dn *ntds_dn, *server_dn, *machine_dn, *rid_set_dn;
618 struct ldb_dn *rid_manager_dn;
619 TALLOC_CTX *tmp_ctx = talloc_new(module);
621 struct ldb_context *ldb = ldb_module_get_ctx(module);
622 struct ldb_result *res;
623 struct ldb_message *msg;
624 struct ridalloc_ridset_values oridset, nridset;
626 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &exop->destination_dsa_guid, &ntds_dn);
627 if (ret != LDB_SUCCESS) {
628 ldb_asprintf_errstring(ldb, __location__ ": Unable to find NTDS object for guid %s - %s\n",
629 GUID_string(tmp_ctx, &exop->destination_dsa_guid), ldb_errstring(ldb));
630 talloc_free(tmp_ctx);
634 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
636 talloc_free(tmp_ctx);
637 return ldb_module_oom(module);
640 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
641 if (ret != LDB_SUCCESS) {
642 ldb_asprintf_errstring(ldb, __location__ ": Failed to find serverReference in %s - %s",
643 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
644 talloc_free(tmp_ctx);
648 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
649 if (ret != LDB_SUCCESS) {
650 ldb_asprintf_errstring(ldb, __location__ ": Failed to find RID Manager object - %s",
652 talloc_free(tmp_ctx);
656 ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
657 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
658 ret = ridalloc_create_rid_set_ntds(module, tmp_ctx, rid_manager_dn, ntds_dn, &rid_set_dn);
659 talloc_free(tmp_ctx);
663 if (ret != LDB_SUCCESS) {
664 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
665 ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
666 talloc_free(tmp_ctx);
670 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
671 ridalloc_ridset_attrs, DSDB_FLAG_NEXT_MODULE);
672 if (ret != LDB_SUCCESS) {
673 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
674 ldb_dn_get_linearized(rid_set_dn));
675 talloc_free(tmp_ctx);
679 ridalloc_get_ridset_values(res->msgs[0], &oridset);
680 if (oridset.alloc_pool == UINT64_MAX) {
681 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
682 ldb_dn_get_linearized(rid_set_dn));
683 talloc_free(tmp_ctx);
684 return LDB_ERR_OPERATIONS_ERROR;
689 if (exop->fsmo_info != 0) {
691 if (nridset.alloc_pool != exop->fsmo_info) {
692 /* it has already been updated */
693 DEBUG(2,(__location__ ": rIDAllocationPool fsmo_info mismatch - already changed (0x%llx 0x%llx)\n",
694 (unsigned long long)exop->fsmo_info,
695 (unsigned long long)nridset.alloc_pool));
696 talloc_free(tmp_ctx);
701 /* grab a pool from the RID Manager object */
702 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &nridset.alloc_pool);
703 if (ret != LDB_SUCCESS) {
704 talloc_free(tmp_ctx);
711 msg = ldb_msg_new(tmp_ctx);
713 return ldb_module_oom(module);
715 msg->dn = rid_set_dn;
717 ret = ridalloc_set_ridset_values(module, msg,
719 if (ret != LDB_SUCCESS) {
720 talloc_free(tmp_ctx);
724 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
725 if (ret != LDB_SUCCESS) {
726 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
727 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
728 talloc_free(tmp_ctx);
732 talloc_free(tmp_ctx);