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
42 - rIDAllocationPool: the pool that the DC will switch to next,
43 when rIDPreviousAllocationPool is exhausted
44 - rIDNextRID: the last RID allocated by this DC
46 in RID Manager object:
47 - rIDAvailablePool: the pool where the RID Manager gets new rID
48 pools from when it gets a EXOP_RID_ALLOC getncchanges call (or
49 locally when the DC is the RID Manager)
54 allocate a new range of RIDs in the RID Manager object
56 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
59 TALLOC_CTX *tmp_ctx = talloc_new(module);
60 const char *attrs[] = { "rIDAvailablePool", NULL };
61 uint64_t rid_pool, new_rid_pool, dc_pool;
62 uint32_t rid_pool_lo, rid_pool_hi;
63 struct ldb_result *res;
64 struct ldb_context *ldb = ldb_module_get_ctx(module);
65 const unsigned alloc_size = 500;
67 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn, attrs, 0);
68 if (ret != LDB_SUCCESS) {
69 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
70 ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
75 rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
76 rid_pool_lo = rid_pool & 0xFFFFFFFF;
77 rid_pool_hi = rid_pool >> 32;
78 if (rid_pool_lo >= rid_pool_hi) {
79 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
80 rid_pool_lo, rid_pool_hi);
85 /* lower part of new pool is the low part of the rIDAvailablePool */
86 dc_pool = rid_pool_lo;
88 /* allocate 500 RIDs to this DC */
89 rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
91 /* work out upper part of new pool */
92 dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
94 /* and new rIDAvailablePool value */
95 new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
97 ret = dsdb_module_constrainted_update_integer(module, rid_manager_dn, "rIDAvailablePool",
98 rid_pool, new_rid_pool);
99 if (ret != LDB_SUCCESS) {
100 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
102 talloc_free(tmp_ctx);
106 (*new_pool) = dc_pool;
107 talloc_free(tmp_ctx);
112 create a RID Set object for the specified DC
114 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
115 struct ldb_dn *rid_manager_dn,
116 struct ldb_dn *ntds_dn, struct ldb_dn **dn)
118 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
119 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
122 struct ldb_message *msg;
123 struct ldb_context *ldb = ldb_module_get_ctx(module);
128 find the machine object for the DC
129 construct the RID Set DN
130 load rIDAvailablePool to find next available set
131 modify RID Manager object to update rIDAvailablePool
132 add the RID Set object
133 link to the RID Set object in machine object
136 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
138 ldb_module_oom(module);
139 return LDB_ERR_OPERATIONS_ERROR;
142 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
143 if (ret != LDB_SUCCESS) {
144 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
145 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
146 talloc_free(tmp_ctx);
150 rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
151 if (rid_set_dn == NULL) {
152 ldb_module_oom(module);
153 return LDB_ERR_OPERATIONS_ERROR;
156 if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
157 ldb_module_oom(module);
158 return LDB_ERR_OPERATIONS_ERROR;
161 /* grab a pool from the RID Manager object */
162 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &dc_pool);
163 if (ret != LDB_SUCCESS) {
164 talloc_free(tmp_ctx);
168 /* create the RID Set object */
169 msg = ldb_msg_new(tmp_ctx);
170 msg->dn = rid_set_dn;
172 ret = ldb_msg_add_string(msg, "objectClass", "top");
173 if (ret != LDB_SUCCESS) {
174 talloc_free(tmp_ctx);
177 ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
178 if (ret != LDB_SUCCESS) {
179 talloc_free(tmp_ctx);
182 ret = ldb_msg_add_string(msg, "cn", "RID Set");
183 if (ret != LDB_SUCCESS) {
184 talloc_free(tmp_ctx);
187 ret = ldb_msg_add_string(msg, "name", "RID Set");
188 if (ret != LDB_SUCCESS) {
189 talloc_free(tmp_ctx);
192 ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
193 if (ret != LDB_SUCCESS) {
194 talloc_free(tmp_ctx);
197 /* TODO: check if the RID Manager adds these fields, or if the
199 ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "%llu", (unsigned long long)dc_pool);
200 if (ret != LDB_SUCCESS) {
201 talloc_free(tmp_ctx);
204 ret = ldb_msg_add_fmt(msg, "rIDUsedPool", "0");
205 if (ret != LDB_SUCCESS) {
206 talloc_free(tmp_ctx);
209 ret = ldb_msg_add_fmt(msg, "rIDNextRID", "%lu", (unsigned long)(dc_pool & 0xFFFFFFFF));
210 if (ret != LDB_SUCCESS) {
211 talloc_free(tmp_ctx);
215 ret = dsdb_module_add(module, msg, 0);
216 if (ret != LDB_SUCCESS) {
217 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
218 ldb_dn_get_linearized(msg->dn),
220 talloc_free(tmp_ctx);
224 /* add the rIDSetReferences link */
225 msg = ldb_msg_new(tmp_ctx);
226 msg->dn = machine_dn;
228 ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
229 if (ret != LDB_SUCCESS) {
230 talloc_free(tmp_ctx);
233 msg->elements[0].flags = LDB_FLAG_MOD_ADD;
235 ret = dsdb_module_modify(module, msg, 0);
236 if (ret != LDB_SUCCESS) {
237 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
238 ldb_dn_get_linearized(msg->dn),
240 talloc_free(tmp_ctx);
244 (*dn) = talloc_steal(mem_ctx, rid_set_dn);
246 talloc_free(tmp_ctx);
252 create a RID Set object for this DC
254 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
257 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
258 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
260 struct ldb_context *ldb = ldb_module_get_ctx(module);
262 /* work out who is the RID Manager */
263 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
264 if (ret != LDB_SUCCESS) {
265 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
267 talloc_free(tmp_ctx);
271 /* find the DN of the RID Manager */
272 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
273 if (ret != LDB_SUCCESS) {
274 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
276 talloc_free(tmp_ctx);
280 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
281 ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
282 talloc_free(tmp_ctx);
283 return LDB_ERR_UNWILLING_TO_PERFORM;
286 ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
287 talloc_free(tmp_ctx);
292 refresh a RID Set object for the specified DC
293 also returns the first RID for the new pool
295 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
296 struct ldb_dn *rid_manager_dn,
297 struct ldb_dn *ntds_dn, uint64_t *new_pool)
299 TALLOC_CTX *tmp_ctx = talloc_new(module);
300 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
301 struct ldb_message *msg;
302 struct ldb_context *ldb = ldb_module_get_ctx(module);
305 /* grab a pool from the RID Manager object */
306 ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
307 if (ret != LDB_SUCCESS) {
308 talloc_free(tmp_ctx);
312 server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
314 ldb_module_oom(module);
315 return LDB_ERR_OPERATIONS_ERROR;
318 ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
319 if (ret != LDB_SUCCESS) {
320 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
321 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
322 talloc_free(tmp_ctx);
326 ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
327 if (ret != LDB_SUCCESS) {
328 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
329 ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
330 talloc_free(tmp_ctx);
334 msg = ldb_msg_new(tmp_ctx);
335 msg->dn = rid_set_dn;
337 ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)*new_pool);
338 if (ret != LDB_SUCCESS) {
339 talloc_free(tmp_ctx);
342 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
344 ret = dsdb_module_modify(module, msg, 0);
345 if (ret != LDB_SUCCESS) {
346 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
347 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
348 talloc_free(tmp_ctx);
352 talloc_free(tmp_ctx);
359 get a new RID pool for ourselves
360 also returns the first rid for the new pool
362 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
364 TALLOC_CTX *tmp_ctx = talloc_new(module);
365 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
367 struct ldb_context *ldb = ldb_module_get_ctx(module);
369 /* work out who is the RID Manager */
370 ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
371 if (ret != LDB_SUCCESS) {
372 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
374 talloc_free(tmp_ctx);
378 /* find the DN of the RID Manager */
379 ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
380 if (ret != LDB_SUCCESS) {
381 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
383 talloc_free(tmp_ctx);
387 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
388 ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
389 talloc_free(tmp_ctx);
390 return LDB_ERR_UNWILLING_TO_PERFORM;
393 ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
394 talloc_free(tmp_ctx);
399 /* allocate a RID using our RID Set
400 If we run out of RIDs then allocate a new pool
401 either locally or by contacting the RID Manager
403 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
405 struct ldb_context *ldb;
406 static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
407 "rIDNextRID" , NULL };
409 struct ldb_dn *rid_set_dn;
410 struct ldb_result *res;
411 uint64_t alloc_pool, prev_alloc_pool;
412 uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
414 TALLOC_CTX *tmp_ctx = talloc_new(module);
416 ldb = ldb_module_get_ctx(module);
418 ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
419 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
420 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
422 if (ret != LDB_SUCCESS) {
423 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
425 talloc_free(tmp_ctx);
429 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
430 if (ret != LDB_SUCCESS) {
431 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
432 ldb_dn_get_linearized(rid_set_dn));
433 talloc_free(tmp_ctx);
437 prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
438 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
439 prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", -1);
440 if (prev_rid == -1 || alloc_pool == 0) {
441 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
442 ldb_dn_get_linearized(rid_set_dn));
443 talloc_free(tmp_ctx);
444 return LDB_ERR_OPERATIONS_ERROR;
447 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
448 prev_alloc_pool_hi = prev_alloc_pool >> 32;
449 if (prev_rid >= prev_alloc_pool_hi) {
450 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
451 prev_alloc_pool, alloc_pool);
452 if (ret != LDB_SUCCESS) {
453 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
454 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
455 talloc_free(tmp_ctx);
458 prev_alloc_pool = alloc_pool;
459 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
460 prev_alloc_pool_hi = prev_alloc_pool >> 32;
462 /* see if we are still out of RIDs, and if so then ask
463 the RID Manager to give us more */
464 if (prev_rid >= prev_alloc_pool_hi) {
466 ret = ridalloc_refresh_own_pool(module, &new_pool);
467 if (ret != LDB_SUCCESS) {
470 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
471 prev_alloc_pool, new_pool);
472 if (ret != LDB_SUCCESS) {
473 ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
474 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
475 talloc_free(tmp_ctx);
478 (*rid) = (new_pool & 0xFFFFFFFF);
480 /* despite the name, rIDNextRID is the value of the last user
481 * added by this DC, not the next available RID */
482 (*rid) = prev_rid + 1;
485 /* now modify the RID Set to use up this RID using a
486 * constrained delete/add */
487 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
488 talloc_free(tmp_ctx);