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"
36 Note: the RID allocation attributes in AD are very badly named. Here
37 is what we think they really do:
40 - rIDPreviousAllocationPool: the pool which a DC is currently
41 pulling RIDs from. Managed by client DC
43 - rIDAllocationPool: the pool that the DC will switch to next,
44 when rIDPreviousAllocationPool is exhausted. Managed by RID Manager.
46 - rIDNextRID: the last RID allocated by this DC. Managed by client DC
48 in RID Manager object:
49 - rIDAvailablePool: the pool where the RID Manager gets new rID
50 pools from when it gets a EXOP_RID_ALLOC getncchanges call (or
51 locally when the DC is the RID Manager)
56 allocate a new range of RIDs in the RID Manager object
58 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
61 TALLOC_CTX *tmp_ctx = talloc_new(module);
62 const char *attrs[] = { "rIDAvailablePool", NULL };
63 uint64_t rid_pool, new_rid_pool, dc_pool;
64 uint32_t rid_pool_lo, rid_pool_hi;
65 struct ldb_result *res;
66 struct ldb_context *ldb = ldb_module_get_ctx(module);
67 const unsigned alloc_size = 500;
69 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn, attrs, 0);
70 if (ret != LDB_SUCCESS) {
71 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
72 ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
77 rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
78 rid_pool_lo = rid_pool & 0xFFFFFFFF;
79 rid_pool_hi = rid_pool >> 32;
80 if (rid_pool_lo >= rid_pool_hi) {
81 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
82 rid_pool_lo, rid_pool_hi);
87 /* lower part of new pool is the low part of the rIDAvailablePool */
88 dc_pool = rid_pool_lo;
90 /* allocate 500 RIDs to this DC */
91 rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
93 /* work out upper part of new pool */
94 dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
96 /* and new rIDAvailablePool value */
97 new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
99 ret = dsdb_module_constrainted_update_integer(module, rid_manager_dn, "rIDAvailablePool",
100 rid_pool, new_rid_pool);
101 if (ret != LDB_SUCCESS) {
102 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
104 talloc_free(tmp_ctx);
108 (*new_pool) = dc_pool;
109 talloc_free(tmp_ctx);
114 create a RID Set object for the specified DC
116 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
117 struct ldb_dn *rid_manager_dn,
118 struct ldb_dn *ntds_dn, struct ldb_dn **dn)
120 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
121 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
124 struct ldb_message *msg;
125 struct ldb_context *ldb = ldb_module_get_ctx(module);
130 find the machine object for the DC
131 construct the RID Set DN
132 load rIDAvailablePool to find next available set
133 modify RID Manager object to update rIDAvailablePool
134 add the RID Set object
135 link to the RID Set object in machine object
138 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
140 ldb_module_oom(module);
141 return LDB_ERR_OPERATIONS_ERROR;
144 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
145 if (ret != LDB_SUCCESS) {
146 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
147 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
148 talloc_free(tmp_ctx);
152 rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
153 if (rid_set_dn == NULL) {
154 ldb_module_oom(module);
155 return LDB_ERR_OPERATIONS_ERROR;
158 if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
159 ldb_module_oom(module);
160 return LDB_ERR_OPERATIONS_ERROR;
163 /* grab a pool from the RID Manager object */
164 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &dc_pool);
165 if (ret != LDB_SUCCESS) {
166 talloc_free(tmp_ctx);
170 /* create the RID Set object */
171 msg = ldb_msg_new(tmp_ctx);
172 msg->dn = rid_set_dn;
174 ret = ldb_msg_add_string(msg, "objectClass", "top");
175 if (ret != LDB_SUCCESS) {
176 talloc_free(tmp_ctx);
179 ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
180 if (ret != LDB_SUCCESS) {
181 talloc_free(tmp_ctx);
184 ret = ldb_msg_add_string(msg, "cn", "RID Set");
185 if (ret != LDB_SUCCESS) {
186 talloc_free(tmp_ctx);
189 ret = ldb_msg_add_string(msg, "name", "RID Set");
190 if (ret != LDB_SUCCESS) {
191 talloc_free(tmp_ctx);
194 ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
195 if (ret != LDB_SUCCESS) {
196 talloc_free(tmp_ctx);
200 /* w2k8-r2 sets these to zero when first created */
201 ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "0");
202 if (ret != LDB_SUCCESS) {
203 talloc_free(tmp_ctx);
206 ret = ldb_msg_add_fmt(msg, "rIDUsedPool", "0");
207 if (ret != LDB_SUCCESS) {
208 talloc_free(tmp_ctx);
211 ret = ldb_msg_add_fmt(msg, "rIDNextRID", "0");
212 if (ret != LDB_SUCCESS) {
213 talloc_free(tmp_ctx);
217 ret = dsdb_module_add(module, msg, 0);
218 if (ret != LDB_SUCCESS) {
219 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
220 ldb_dn_get_linearized(msg->dn),
222 talloc_free(tmp_ctx);
226 /* add the rIDSetReferences link */
227 msg = ldb_msg_new(tmp_ctx);
228 msg->dn = machine_dn;
230 ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
231 if (ret != LDB_SUCCESS) {
232 talloc_free(tmp_ctx);
235 msg->elements[0].flags = LDB_FLAG_MOD_ADD;
237 ret = dsdb_module_modify(module, msg, 0);
238 if (ret != LDB_SUCCESS) {
239 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
240 ldb_dn_get_linearized(msg->dn),
242 talloc_free(tmp_ctx);
246 (*dn) = talloc_steal(mem_ctx, rid_set_dn);
248 talloc_free(tmp_ctx);
254 create a RID Set object for this DC
256 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
259 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
260 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
262 struct ldb_context *ldb = ldb_module_get_ctx(module);
264 /* work out who is the RID Manager */
265 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
266 if (ret != LDB_SUCCESS) {
267 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
269 talloc_free(tmp_ctx);
273 /* find the DN of the RID Manager */
274 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
275 if (ret != LDB_SUCCESS) {
276 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
278 talloc_free(tmp_ctx);
282 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
283 ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
284 talloc_free(tmp_ctx);
285 return LDB_ERR_UNWILLING_TO_PERFORM;
288 ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
289 talloc_free(tmp_ctx);
294 refresh a RID Set object for the specified DC
295 also returns the first RID for the new pool
297 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
298 struct ldb_dn *rid_manager_dn,
299 struct ldb_dn *ntds_dn, uint64_t *new_pool)
301 TALLOC_CTX *tmp_ctx = talloc_new(module);
302 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
303 struct ldb_context *ldb = ldb_module_get_ctx(module);
306 /* grab a pool from the RID Manager object */
307 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
308 if (ret != LDB_SUCCESS) {
309 talloc_free(tmp_ctx);
313 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
315 ldb_module_oom(module);
316 return LDB_ERR_OPERATIONS_ERROR;
319 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
320 if (ret != LDB_SUCCESS) {
321 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
322 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
323 talloc_free(tmp_ctx);
327 ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
328 if (ret != LDB_SUCCESS) {
329 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
330 ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
331 talloc_free(tmp_ctx);
335 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDAllocationPool", *new_pool);
336 if (ret != LDB_SUCCESS) {
337 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
338 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
339 talloc_free(tmp_ctx);
343 talloc_free(tmp_ctx);
350 get a new RID pool for ourselves
351 also returns the first rid for the new pool
353 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
355 TALLOC_CTX *tmp_ctx = talloc_new(module);
356 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
358 struct ldb_context *ldb = ldb_module_get_ctx(module);
360 /* work out who is the RID Manager */
361 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
362 if (ret != LDB_SUCCESS) {
363 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
365 talloc_free(tmp_ctx);
369 /* find the DN of the RID Manager */
370 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
371 if (ret != LDB_SUCCESS) {
372 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
374 talloc_free(tmp_ctx);
378 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
379 ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
380 talloc_free(tmp_ctx);
381 return LDB_ERR_UNWILLING_TO_PERFORM;
384 ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
385 talloc_free(tmp_ctx);
390 /* allocate a RID using our RID Set
391 If we run out of RIDs then allocate a new pool
392 either locally or by contacting the RID Manager
394 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
396 struct ldb_context *ldb;
397 static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
398 "rIDNextRID" , NULL };
400 struct ldb_dn *rid_set_dn;
401 struct ldb_result *res;
402 uint64_t alloc_pool, prev_alloc_pool;
403 uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
405 TALLOC_CTX *tmp_ctx = talloc_new(module);
407 ldb = ldb_module_get_ctx(module);
409 ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
410 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
411 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
413 if (ret != LDB_SUCCESS) {
414 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
416 talloc_free(tmp_ctx);
420 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
421 if (ret != LDB_SUCCESS) {
422 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
423 ldb_dn_get_linearized(rid_set_dn));
424 talloc_free(tmp_ctx);
428 prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
429 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
430 prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", 0);
431 if (alloc_pool == 0) {
432 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
433 ldb_dn_get_linearized(rid_set_dn));
434 talloc_free(tmp_ctx);
435 return LDB_ERR_OPERATIONS_ERROR;
438 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
439 prev_alloc_pool_hi = prev_alloc_pool >> 32;
440 if (prev_rid >= prev_alloc_pool_hi) {
441 if (prev_alloc_pool == 0) {
442 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDPreviousAllocationPool", alloc_pool);
444 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
445 prev_alloc_pool, alloc_pool);
447 if (ret != LDB_SUCCESS) {
448 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
449 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
450 talloc_free(tmp_ctx);
453 prev_alloc_pool = alloc_pool;
454 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
455 prev_alloc_pool_hi = prev_alloc_pool >> 32;
457 /* see if we are still out of RIDs, and if so then ask
458 the RID Manager to give us more */
459 if (prev_rid >= prev_alloc_pool_hi) {
461 ret = ridalloc_refresh_own_pool(module, &new_pool);
462 if (ret != LDB_SUCCESS) {
465 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
466 prev_alloc_pool, new_pool);
467 if (ret != LDB_SUCCESS) {
468 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
469 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
470 talloc_free(tmp_ctx);
473 prev_alloc_pool = new_pool;
474 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
475 prev_alloc_pool_hi = prev_alloc_pool >> 32;
476 (*rid) = prev_alloc_pool_lo;
478 /* despite the name, rIDNextRID is the value of the last user
479 * added by this DC, not the next available RID */
481 (*rid) = prev_alloc_pool_lo;
483 (*rid) = prev_rid + 1;
487 if (*rid < prev_alloc_pool_lo || *rid > prev_alloc_pool_hi) {
488 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
489 (unsigned)*rid, (unsigned)prev_alloc_pool_lo,
490 (unsigned)prev_alloc_pool_hi);
491 talloc_free(tmp_ctx);
492 return LDB_ERR_OPERATIONS_ERROR;
495 /* now modify the RID Set to use up this RID using a
496 * constrained delete/add if possible */
498 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDNextRID", *rid);
500 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
502 talloc_free(tmp_ctx);