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 allocate a new range of RIDs in the RID Manager object
61 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
64 TALLOC_CTX *tmp_ctx = talloc_new(module);
65 const char *attrs[] = { "rIDAvailablePool", NULL };
66 uint64_t rid_pool, new_rid_pool, dc_pool;
67 uint32_t rid_pool_lo, rid_pool_hi;
68 struct ldb_result *res;
69 struct ldb_context *ldb = ldb_module_get_ctx(module);
70 const unsigned alloc_size = 500;
72 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn, attrs, 0);
73 if (ret != LDB_SUCCESS) {
74 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
75 ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
80 rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
81 rid_pool_lo = rid_pool & 0xFFFFFFFF;
82 rid_pool_hi = rid_pool >> 32;
83 if (rid_pool_lo >= rid_pool_hi) {
84 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
85 rid_pool_lo, rid_pool_hi);
90 /* lower part of new pool is the low part of the rIDAvailablePool */
91 dc_pool = rid_pool_lo;
93 /* allocate 500 RIDs to this DC */
94 rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
96 /* work out upper part of new pool */
97 dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
99 /* and new rIDAvailablePool value */
100 new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
102 ret = dsdb_module_constrainted_update_integer(module, rid_manager_dn, "rIDAvailablePool",
103 rid_pool, new_rid_pool);
104 if (ret != LDB_SUCCESS) {
105 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
107 talloc_free(tmp_ctx);
111 (*new_pool) = dc_pool;
112 talloc_free(tmp_ctx);
117 create a RID Set object for the specified DC
119 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
120 struct ldb_dn *rid_manager_dn,
121 struct ldb_dn *ntds_dn, struct ldb_dn **dn)
123 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
124 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
127 struct ldb_message *msg;
128 struct ldb_context *ldb = ldb_module_get_ctx(module);
133 find the machine object for the DC
134 construct the RID Set DN
135 load rIDAvailablePool to find next available set
136 modify RID Manager object to update rIDAvailablePool
137 add the RID Set object
138 link to the RID Set object in machine object
141 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
143 ldb_module_oom(module);
144 talloc_free(tmp_ctx);
145 return LDB_ERR_OPERATIONS_ERROR;
148 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
149 if (ret != LDB_SUCCESS) {
150 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
151 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
152 talloc_free(tmp_ctx);
156 rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
157 if (rid_set_dn == NULL) {
158 ldb_module_oom(module);
159 return LDB_ERR_OPERATIONS_ERROR;
162 if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
163 ldb_module_oom(module);
164 return LDB_ERR_OPERATIONS_ERROR;
167 /* grab a pool from the RID Manager object */
168 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &dc_pool);
169 if (ret != LDB_SUCCESS) {
170 talloc_free(tmp_ctx);
174 /* create the RID Set object */
175 msg = ldb_msg_new(tmp_ctx);
176 msg->dn = rid_set_dn;
178 ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
179 if (ret != LDB_SUCCESS) {
180 talloc_free(tmp_ctx);
183 ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
184 if (ret != LDB_SUCCESS) {
185 talloc_free(tmp_ctx);
189 /* w2k8-r2 sets these to zero when first created */
190 ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "0");
191 if (ret != LDB_SUCCESS) {
192 talloc_free(tmp_ctx);
195 ret = ldb_msg_add_fmt(msg, "rIDUsedPool", "0");
196 if (ret != LDB_SUCCESS) {
197 talloc_free(tmp_ctx);
200 ret = ldb_msg_add_fmt(msg, "rIDNextRID", "0");
201 if (ret != LDB_SUCCESS) {
202 talloc_free(tmp_ctx);
206 /* we need this to go all the way to the top of the module
207 * stack, as we need all the extra attributes added (including
208 * complex ones like ntsecuritydescriptor) */
209 ret = dsdb_module_add(module, msg, DSDB_FLAG_TOP_MODULE | DSDB_MODIFY_RELAX);
210 if (ret != LDB_SUCCESS) {
211 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
212 ldb_dn_get_linearized(msg->dn),
214 talloc_free(tmp_ctx);
218 /* add the rIDSetReferences link */
219 msg = ldb_msg_new(tmp_ctx);
220 msg->dn = machine_dn;
222 ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
223 if (ret != LDB_SUCCESS) {
224 talloc_free(tmp_ctx);
227 msg->elements[0].flags = LDB_FLAG_MOD_ADD;
229 ret = dsdb_module_modify(module, msg, 0);
230 if (ret != LDB_SUCCESS) {
231 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
232 ldb_dn_get_linearized(msg->dn),
234 talloc_free(tmp_ctx);
238 (*dn) = talloc_steal(mem_ctx, rid_set_dn);
240 talloc_free(tmp_ctx);
246 create a RID Set object for this DC
248 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
251 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
252 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
254 struct ldb_context *ldb = ldb_module_get_ctx(module);
256 /* work out who is the RID Manager */
257 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
258 if (ret != LDB_SUCCESS) {
259 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
261 talloc_free(tmp_ctx);
265 /* find the DN of the RID Manager */
266 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
267 if (ret != LDB_SUCCESS) {
268 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
270 talloc_free(tmp_ctx);
274 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
275 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
276 talloc_free(tmp_ctx);
277 return LDB_ERR_UNWILLING_TO_PERFORM;
280 ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
281 talloc_free(tmp_ctx);
286 refresh a RID Set object for the specified DC
287 also returns the first RID for the new pool
289 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
290 struct ldb_dn *rid_manager_dn,
291 struct ldb_dn *ntds_dn, uint64_t *new_pool)
293 TALLOC_CTX *tmp_ctx = talloc_new(module);
294 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
295 struct ldb_context *ldb = ldb_module_get_ctx(module);
298 /* grab a pool from the RID Manager object */
299 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
300 if (ret != LDB_SUCCESS) {
301 talloc_free(tmp_ctx);
305 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
307 ldb_module_oom(module);
308 talloc_free(tmp_ctx);
309 return LDB_ERR_OPERATIONS_ERROR;
312 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
313 if (ret != LDB_SUCCESS) {
314 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
315 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
316 talloc_free(tmp_ctx);
320 ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
321 if (ret != LDB_SUCCESS) {
322 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
323 ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
324 talloc_free(tmp_ctx);
328 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDAllocationPool", *new_pool);
329 if (ret != LDB_SUCCESS) {
330 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
331 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
332 talloc_free(tmp_ctx);
336 talloc_free(tmp_ctx);
342 make a IRPC call to the drepl task to ask it to get the RID
343 Manager to give us another RID pool.
345 This function just sends the message to the drepl task then
346 returns immediately. It should be called well before we
347 completely run out of RIDs
349 static void ridalloc_poke_rid_manager(struct ldb_module *module)
351 struct messaging_context *msg;
352 struct server_id *server;
353 struct ldb_context *ldb = ldb_module_get_ctx(module);
354 struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
355 TALLOC_CTX *tmp_ctx = talloc_new(module);
357 msg = messaging_client_init(tmp_ctx, lp_messaging_path(tmp_ctx, lp_ctx),
358 lp_iconv_convenience(lp_ctx),
359 ldb_get_event_context(ldb));
361 DEBUG(3,(__location__ ": Failed to create messaging context\n"));
362 talloc_free(tmp_ctx);
366 server = irpc_servers_byname(msg, msg, "dreplsrv");
368 /* this means the drepl service is not running */
369 talloc_free(tmp_ctx);
373 messaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
375 /* we don't care if the message got through */
376 talloc_free(tmp_ctx);
380 get a new RID pool for ourselves
381 also returns the first rid for the new pool
383 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
385 TALLOC_CTX *tmp_ctx = talloc_new(module);
386 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
388 struct ldb_context *ldb = ldb_module_get_ctx(module);
390 /* work out who is the RID Manager */
391 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
392 if (ret != LDB_SUCCESS) {
393 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
395 talloc_free(tmp_ctx);
399 /* find the DN of the RID Manager */
400 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
401 if (ret != LDB_SUCCESS) {
402 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
404 talloc_free(tmp_ctx);
408 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
409 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
410 talloc_free(tmp_ctx);
411 return LDB_ERR_UNWILLING_TO_PERFORM;
414 ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
415 talloc_free(tmp_ctx);
420 /* allocate a RID using our RID Set
421 If we run out of RIDs then allocate a new pool
422 either locally or by contacting the RID Manager
424 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
426 struct ldb_context *ldb;
427 static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
428 "rIDNextRID" , "rIDUsedPool", NULL };
430 struct ldb_dn *rid_set_dn;
431 struct ldb_result *res;
432 uint64_t alloc_pool, prev_alloc_pool;
433 uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
434 uint32_t rid_used_pool;
436 TALLOC_CTX *tmp_ctx = talloc_new(module);
439 ldb = ldb_module_get_ctx(module);
441 ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
442 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
443 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
445 if (ret != LDB_SUCCESS) {
446 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
448 talloc_free(tmp_ctx);
452 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
453 if (ret != LDB_SUCCESS) {
454 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
455 ldb_dn_get_linearized(rid_set_dn));
456 talloc_free(tmp_ctx);
460 prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
461 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
462 prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", 0);
463 rid_used_pool = ldb_msg_find_attr_as_int(res->msgs[0], "rIDUsedPool", 0);
464 if (alloc_pool == 0) {
465 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
466 ldb_dn_get_linearized(rid_set_dn));
467 talloc_free(tmp_ctx);
468 return LDB_ERR_OPERATIONS_ERROR;
471 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
472 prev_alloc_pool_hi = prev_alloc_pool >> 32;
473 if (prev_rid >= prev_alloc_pool_hi) {
474 if (prev_alloc_pool == 0) {
475 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDPreviousAllocationPool", alloc_pool);
477 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
478 prev_alloc_pool, alloc_pool);
480 if (ret != LDB_SUCCESS) {
481 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
482 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
483 talloc_free(tmp_ctx);
486 prev_alloc_pool = alloc_pool;
487 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
488 prev_alloc_pool_hi = prev_alloc_pool >> 32;
490 /* update the rIDUsedPool attribute */
491 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDUsedPool", rid_used_pool+1);
492 if (ret != LDB_SUCCESS) {
493 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDUsedPool on %s - %s",
494 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
495 talloc_free(tmp_ctx);
499 (*rid) = prev_alloc_pool_lo;
502 /* see if we are still out of RIDs, and if so then ask
503 the RID Manager to give us more */
504 if (prev_rid >= prev_alloc_pool_hi) {
506 ret = ridalloc_refresh_own_pool(module, &new_pool);
507 if (ret != LDB_SUCCESS) {
510 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
511 prev_alloc_pool, new_pool);
512 if (ret != LDB_SUCCESS) {
513 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
514 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
515 talloc_free(tmp_ctx);
518 prev_alloc_pool = new_pool;
519 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
520 prev_alloc_pool_hi = prev_alloc_pool >> 32;
521 (*rid) = prev_alloc_pool_lo;
523 /* despite the name, rIDNextRID is the value of the last user
524 * added by this DC, not the next available RID */
526 (*rid) = prev_rid + 1;
530 if (*rid < prev_alloc_pool_lo || *rid > prev_alloc_pool_hi) {
531 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
532 (unsigned)*rid, (unsigned)prev_alloc_pool_lo,
533 (unsigned)prev_alloc_pool_hi);
534 talloc_free(tmp_ctx);
535 return LDB_ERR_OPERATIONS_ERROR;
538 /* now modify the RID Set to use up this RID using a
539 * constrained delete/add if possible */
541 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDNextRID", *rid);
543 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
546 /* if we are half-exhausted then ask the repl task to start
547 * getting another one */
548 if (*rid > (prev_alloc_pool_hi + prev_alloc_pool_lo)/2) {
549 ridalloc_poke_rid_manager(module);
552 talloc_free(tmp_ctx);
559 called by DSDB_EXTENDED_ALLOCATE_RID_POOL extended operation in samldb
561 int ridalloc_allocate_rid_pool_fsmo(struct ldb_module *module, struct dsdb_fsmo_extended_op *exop)
563 struct ldb_dn *ntds_dn, *server_dn, *machine_dn, *rid_set_dn;
564 struct ldb_dn *rid_manager_dn;
565 TALLOC_CTX *tmp_ctx = talloc_new(module);
567 struct ldb_context *ldb = ldb_module_get_ctx(module);
570 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &exop->destination_dsa_guid, &ntds_dn);
571 if (ret != LDB_SUCCESS) {
572 ldb_asprintf_errstring(ldb, __location__ ": Unable to find NTDS object for guid %s - %s\n",
573 GUID_string(tmp_ctx, &exop->destination_dsa_guid), ldb_errstring(ldb));
574 talloc_free(tmp_ctx);
578 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
580 ldb_module_oom(module);
581 talloc_free(tmp_ctx);
582 return LDB_ERR_OPERATIONS_ERROR;
585 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
586 if (ret != LDB_SUCCESS) {
587 ldb_asprintf_errstring(ldb, __location__ ": Failed to find serverReference in %s - %s",
588 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
589 talloc_free(tmp_ctx);
594 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
595 if (ret != LDB_SUCCESS) {
596 ldb_asprintf_errstring(ldb, __location__ ": Failed to find RID Manager object - %s",
598 talloc_free(tmp_ctx);
602 ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
603 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
604 ret = ridalloc_create_rid_set_ntds(module, tmp_ctx, rid_manager_dn, ntds_dn, &rid_set_dn);
605 talloc_free(tmp_ctx);
609 if (ret != LDB_SUCCESS) {
610 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
611 ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
612 talloc_free(tmp_ctx);
616 if (exop->fsmo_info != 0) {
617 const char *attrs[] = { "rIDAllocationPool", NULL };
618 struct ldb_result *res;
621 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
622 if (ret != LDB_SUCCESS) {
623 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
624 ldb_dn_get_linearized(rid_set_dn));
625 talloc_free(tmp_ctx);
629 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
630 if (alloc_pool != exop->fsmo_info) {
631 /* it has already been updated */
632 DEBUG(2,(__location__ ": rIDAllocationPool fsmo_info mismatch - already changed (0x%llx 0x%llx)\n",
633 (unsigned long long)exop->fsmo_info,
634 (unsigned long long)alloc_pool));
635 talloc_free(tmp_ctx);
640 ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, ntds_dn, &new_pool);
641 talloc_free(tmp_ctx);