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"
38 Note: the RID allocation attributes in AD are very badly named. Here
39 is what we think they really do:
42 - rIDPreviousAllocationPool: the pool which a DC is currently
43 pulling RIDs from. Managed by client DC
45 - rIDAllocationPool: the pool that the DC will switch to next,
46 when rIDPreviousAllocationPool is exhausted. Managed by RID Manager.
48 - rIDNextRID: the last RID allocated by this DC. Managed by client DC
50 in RID Manager object:
51 - rIDAvailablePool: the pool where the RID Manager gets new rID
52 pools from when it gets a EXOP_RID_ALLOC getncchanges call (or
53 locally when the DC is the RID Manager)
58 allocate a new range of RIDs in the RID Manager object
60 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
63 TALLOC_CTX *tmp_ctx = talloc_new(module);
64 const char *attrs[] = { "rIDAvailablePool", NULL };
65 uint64_t rid_pool, new_rid_pool, dc_pool;
66 uint32_t rid_pool_lo, rid_pool_hi;
67 struct ldb_result *res;
68 struct ldb_context *ldb = ldb_module_get_ctx(module);
69 const unsigned alloc_size = 500;
71 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn, attrs, 0);
72 if (ret != LDB_SUCCESS) {
73 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
74 ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
79 rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
80 rid_pool_lo = rid_pool & 0xFFFFFFFF;
81 rid_pool_hi = rid_pool >> 32;
82 if (rid_pool_lo >= rid_pool_hi) {
83 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
84 rid_pool_lo, rid_pool_hi);
89 /* lower part of new pool is the low part of the rIDAvailablePool */
90 dc_pool = rid_pool_lo;
92 /* allocate 500 RIDs to this DC */
93 rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
95 /* work out upper part of new pool */
96 dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
98 /* and new rIDAvailablePool value */
99 new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
101 ret = dsdb_module_constrainted_update_integer(module, rid_manager_dn, "rIDAvailablePool",
102 rid_pool, new_rid_pool);
103 if (ret != LDB_SUCCESS) {
104 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
106 talloc_free(tmp_ctx);
110 (*new_pool) = dc_pool;
111 talloc_free(tmp_ctx);
116 create a RID Set object for the specified DC
118 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
119 struct ldb_dn *rid_manager_dn,
120 struct ldb_dn *ntds_dn, struct ldb_dn **dn)
122 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
123 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
126 struct ldb_message *msg;
127 struct ldb_context *ldb = ldb_module_get_ctx(module);
132 find the machine object for the DC
133 construct the RID Set DN
134 load rIDAvailablePool to find next available set
135 modify RID Manager object to update rIDAvailablePool
136 add the RID Set object
137 link to the RID Set object in machine object
140 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
142 ldb_module_oom(module);
143 return LDB_ERR_OPERATIONS_ERROR;
146 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
147 if (ret != LDB_SUCCESS) {
148 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
149 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
150 talloc_free(tmp_ctx);
154 rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
155 if (rid_set_dn == NULL) {
156 ldb_module_oom(module);
157 return LDB_ERR_OPERATIONS_ERROR;
160 if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
161 ldb_module_oom(module);
162 return LDB_ERR_OPERATIONS_ERROR;
165 /* grab a pool from the RID Manager object */
166 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &dc_pool);
167 if (ret != LDB_SUCCESS) {
168 talloc_free(tmp_ctx);
172 /* create the RID Set object */
173 msg = ldb_msg_new(tmp_ctx);
174 msg->dn = rid_set_dn;
176 ret = ldb_msg_add_string(msg, "objectClass", "top");
177 if (ret != LDB_SUCCESS) {
178 talloc_free(tmp_ctx);
181 ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
182 if (ret != LDB_SUCCESS) {
183 talloc_free(tmp_ctx);
186 ret = ldb_msg_add_string(msg, "cn", "RID Set");
187 if (ret != LDB_SUCCESS) {
188 talloc_free(tmp_ctx);
191 ret = ldb_msg_add_string(msg, "name", "RID Set");
192 if (ret != LDB_SUCCESS) {
193 talloc_free(tmp_ctx);
196 ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
197 if (ret != LDB_SUCCESS) {
198 talloc_free(tmp_ctx);
202 /* w2k8-r2 sets these to zero when first created */
203 ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "0");
204 if (ret != LDB_SUCCESS) {
205 talloc_free(tmp_ctx);
208 ret = ldb_msg_add_fmt(msg, "rIDUsedPool", "0");
209 if (ret != LDB_SUCCESS) {
210 talloc_free(tmp_ctx);
213 ret = ldb_msg_add_fmt(msg, "rIDNextRID", "0");
214 if (ret != LDB_SUCCESS) {
215 talloc_free(tmp_ctx);
219 ret = dsdb_module_add(module, msg, 0);
220 if (ret != LDB_SUCCESS) {
221 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
222 ldb_dn_get_linearized(msg->dn),
224 talloc_free(tmp_ctx);
228 /* add the rIDSetReferences link */
229 msg = ldb_msg_new(tmp_ctx);
230 msg->dn = machine_dn;
232 ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
233 if (ret != LDB_SUCCESS) {
234 talloc_free(tmp_ctx);
237 msg->elements[0].flags = LDB_FLAG_MOD_ADD;
239 ret = dsdb_module_modify(module, msg, 0);
240 if (ret != LDB_SUCCESS) {
241 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
242 ldb_dn_get_linearized(msg->dn),
244 talloc_free(tmp_ctx);
248 (*dn) = talloc_steal(mem_ctx, rid_set_dn);
250 talloc_free(tmp_ctx);
256 create a RID Set object for this DC
258 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
261 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
262 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
264 struct ldb_context *ldb = ldb_module_get_ctx(module);
266 /* work out who is the RID Manager */
267 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
268 if (ret != LDB_SUCCESS) {
269 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
271 talloc_free(tmp_ctx);
275 /* find the DN of the RID Manager */
276 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
277 if (ret != LDB_SUCCESS) {
278 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
280 talloc_free(tmp_ctx);
284 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
285 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
286 talloc_free(tmp_ctx);
287 return LDB_ERR_UNWILLING_TO_PERFORM;
290 ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
291 talloc_free(tmp_ctx);
296 refresh a RID Set object for the specified DC
297 also returns the first RID for the new pool
299 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
300 struct ldb_dn *rid_manager_dn,
301 struct ldb_dn *ntds_dn, uint64_t *new_pool)
303 TALLOC_CTX *tmp_ctx = talloc_new(module);
304 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
305 struct ldb_context *ldb = ldb_module_get_ctx(module);
308 /* grab a pool from the RID Manager object */
309 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
310 if (ret != LDB_SUCCESS) {
311 talloc_free(tmp_ctx);
315 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
317 ldb_module_oom(module);
318 return LDB_ERR_OPERATIONS_ERROR;
321 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
322 if (ret != LDB_SUCCESS) {
323 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
324 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
325 talloc_free(tmp_ctx);
329 ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
330 if (ret != LDB_SUCCESS) {
331 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
332 ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
333 talloc_free(tmp_ctx);
337 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDAllocationPool", *new_pool);
338 if (ret != LDB_SUCCESS) {
339 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
340 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
341 talloc_free(tmp_ctx);
345 talloc_free(tmp_ctx);
351 make a IRPC call to the drepl task to ask it to get the RID
352 Manager to give us another RID pool.
354 This function just sends the message to the drepl task then
355 returns immediately. It should be called well before we
356 completely run out of RIDs
358 static void ridalloc_poke_rid_manager(struct ldb_module *module)
360 struct messaging_context *msg;
361 struct server_id *server;
362 struct ldb_context *ldb = ldb_module_get_ctx(module);
363 struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
364 TALLOC_CTX *tmp_ctx = talloc_new(module);
366 msg = messaging_client_init(tmp_ctx, lp_messaging_path(tmp_ctx, lp_ctx),
367 lp_iconv_convenience(lp_ctx),
368 ldb_get_event_context(ldb));
370 DEBUG(3,(__location__ ": Failed to create messaging context\n"));
371 talloc_free(tmp_ctx);
375 server = irpc_servers_byname(msg, msg, "dreplsrv");
377 /* this means the drepl service is not running */
378 talloc_free(tmp_ctx);
382 messaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
384 /* we don't care if the message got through */
385 talloc_free(tmp_ctx);
389 get a new RID pool for ourselves
390 also returns the first rid for the new pool
392 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
394 TALLOC_CTX *tmp_ctx = talloc_new(module);
395 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
397 struct ldb_context *ldb = ldb_module_get_ctx(module);
399 /* work out who is the RID Manager */
400 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
401 if (ret != LDB_SUCCESS) {
402 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
404 talloc_free(tmp_ctx);
408 /* find the DN of the RID Manager */
409 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
410 if (ret != LDB_SUCCESS) {
411 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
413 talloc_free(tmp_ctx);
417 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
418 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
419 talloc_free(tmp_ctx);
420 return LDB_ERR_UNWILLING_TO_PERFORM;
423 ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
424 talloc_free(tmp_ctx);
429 /* allocate a RID using our RID Set
430 If we run out of RIDs then allocate a new pool
431 either locally or by contacting the RID Manager
433 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
435 struct ldb_context *ldb;
436 static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
437 "rIDNextRID" , "rIDUsedPool", NULL };
439 struct ldb_dn *rid_set_dn;
440 struct ldb_result *res;
441 uint64_t alloc_pool, prev_alloc_pool;
442 uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
443 uint32_t rid_used_pool;
445 TALLOC_CTX *tmp_ctx = talloc_new(module);
448 ldb = ldb_module_get_ctx(module);
450 ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
451 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
452 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
454 if (ret != LDB_SUCCESS) {
455 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
457 talloc_free(tmp_ctx);
461 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
462 if (ret != LDB_SUCCESS) {
463 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
464 ldb_dn_get_linearized(rid_set_dn));
465 talloc_free(tmp_ctx);
469 prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
470 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
471 prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", 0);
472 rid_used_pool = ldb_msg_find_attr_as_int(res->msgs[0], "rIDUsedPool", 0);
473 if (alloc_pool == 0) {
474 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
475 ldb_dn_get_linearized(rid_set_dn));
476 talloc_free(tmp_ctx);
477 return LDB_ERR_OPERATIONS_ERROR;
480 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
481 prev_alloc_pool_hi = prev_alloc_pool >> 32;
482 if (prev_rid >= prev_alloc_pool_hi) {
483 if (prev_alloc_pool == 0) {
484 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDPreviousAllocationPool", alloc_pool);
486 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
487 prev_alloc_pool, alloc_pool);
489 if (ret != LDB_SUCCESS) {
490 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
491 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
492 talloc_free(tmp_ctx);
495 prev_alloc_pool = alloc_pool;
496 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
497 prev_alloc_pool_hi = prev_alloc_pool >> 32;
499 /* update the rIDUsedPool attribute */
500 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDUsedPool", rid_used_pool+1);
501 if (ret != LDB_SUCCESS) {
502 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDUsedPool on %s - %s",
503 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
504 talloc_free(tmp_ctx);
508 (*rid) = prev_alloc_pool_lo;
511 /* see if we are still out of RIDs, and if so then ask
512 the RID Manager to give us more */
513 if (prev_rid >= prev_alloc_pool_hi) {
515 ret = ridalloc_refresh_own_pool(module, &new_pool);
516 if (ret != LDB_SUCCESS) {
519 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
520 prev_alloc_pool, new_pool);
521 if (ret != LDB_SUCCESS) {
522 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
523 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
524 talloc_free(tmp_ctx);
527 prev_alloc_pool = new_pool;
528 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
529 prev_alloc_pool_hi = prev_alloc_pool >> 32;
530 (*rid) = prev_alloc_pool_lo;
532 /* despite the name, rIDNextRID is the value of the last user
533 * added by this DC, not the next available RID */
535 (*rid) = prev_rid + 1;
539 if (*rid < prev_alloc_pool_lo || *rid > prev_alloc_pool_hi) {
540 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
541 (unsigned)*rid, (unsigned)prev_alloc_pool_lo,
542 (unsigned)prev_alloc_pool_hi);
543 talloc_free(tmp_ctx);
544 return LDB_ERR_OPERATIONS_ERROR;
547 /* now modify the RID Set to use up this RID using a
548 * constrained delete/add if possible */
550 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDNextRID", *rid);
552 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
555 /* if we are half-exhausted then ask the repl task to start
556 * getting another one */
557 if (*rid > (prev_alloc_pool_hi + prev_alloc_pool_lo)/2) {
558 ridalloc_poke_rid_manager(module);
561 talloc_free(tmp_ctx);