s4:dsdb:ridalloc: add ridalloc_ridset_values infrastructure
[metze/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / ridalloc.c
1 /*
2    RID allocation helper functions
3
4    Copyright (C) Andrew Bartlett 2010
5    Copyright (C) Andrew Tridgell 2010
6
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.
11
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.
16
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/>.
19 */
20
21 /*
22  *  Name: ldb
23  *
24  *  Component: RID allocation logic
25  *
26  *  Description: manage RID Set and RID Manager objects
27  *
28  */
29
30 #include "includes.h"
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"
37
38 /*
39   Note: the RID allocation attributes in AD are very badly named. Here
40   is what we think they really do:
41
42   in RID Set object:
43     - rIDPreviousAllocationPool: the pool which a DC is currently
44       pulling RIDs from. Managed by client DC
45
46     - rIDAllocationPool: the pool that the DC will switch to next,
47       when rIDPreviousAllocationPool is exhausted. Managed by RID Manager.
48
49     - rIDNextRID: the last RID allocated by this DC. Managed by client DC
50
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)
55  */
56
57
58 /*
59   make a IRPC call to the drepl task to ask it to get the RID
60   Manager to give us another RID pool.
61
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
65  */
66 static void ridalloc_poke_rid_manager(struct ldb_module *module)
67 {
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);
74
75         msg = messaging_client_init(tmp_ctx, lp_messaging_path(tmp_ctx, lp_ctx),
76                                     ldb_get_event_context(ldb));
77         if (!msg) {
78                 DEBUG(3,(__location__ ": Failed to create messaging context\n"));
79                 talloc_free(tmp_ctx);
80                 return;
81         }
82
83         server = irpc_servers_byname(msg, msg, "dreplsrv");
84         if (!server) {
85                 /* this means the drepl service is not running */
86                 talloc_free(tmp_ctx);
87                 return;
88         }
89
90         messaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
91
92         /* we don't care if the message got through */
93         talloc_free(tmp_ctx);
94 }
95
96
97 static const char * const ridalloc_ridset_attrs[] = {
98         "rIDAllocationPool",
99         "rIDPreviousAllocationPool",
100         "rIDNextRID",
101         "rIDUsedPool",
102         NULL
103 };
104
105 struct ridalloc_ridset_values {
106         uint64_t alloc_pool;
107         uint64_t prev_pool;
108         uint32_t next_rid;
109         uint32_t used_pool;
110 };
111
112 static void ridalloc_get_ridset_values(struct ldb_message *msg, struct ridalloc_ridset_values *v)
113 {
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);
118 }
119
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)
124 {
125         const uint32_t *o32, *n32;
126         const uint64_t *o64, *n64;
127         int ret;
128
129 #define SETUP_PTRS(field, optr, nptr, max) do { \
130         optr = &o->field; \
131         nptr = &n->field; \
132         if (o->field == max) { \
133                 optr = NULL; \
134         } \
135         if (n->field == max) { \
136                 nptr = NULL; \
137         } \
138         if (o->field == n->field) { \
139                 optr = NULL; \
140                 nptr = NULL; \
141         } \
142 } while(0)
143
144         SETUP_PTRS(alloc_pool, o64, n64, UINT64_MAX);
145         ret = dsdb_msg_constrainted_update_uint64(module, msg,
146                                                   "rIDAllocationPool",
147                                                   o64, n64);
148         if (ret != LDB_SUCCESS) {
149                 return ret;
150         }
151
152         SETUP_PTRS(prev_pool, o64, n64, UINT64_MAX);
153         ret = dsdb_msg_constrainted_update_uint64(module, msg,
154                                                   "rIDPreviousAllocationPool",
155                                                   o64, n64);
156         if (ret != LDB_SUCCESS) {
157                 return ret;
158         }
159
160         SETUP_PTRS(next_rid, o32, n32, UINT32_MAX);
161         ret = dsdb_msg_constrainted_update_uint32(module, msg,
162                                                   "rIDNextRID",
163                                                   o32, n32);
164         if (ret != LDB_SUCCESS) {
165                 return ret;
166         }
167
168         SETUP_PTRS(used_pool, o32, n32, UINT32_MAX);
169         ret = dsdb_msg_constrainted_update_uint32(module, msg,
170                                                   "rIDUsedPool",
171                                                   o32, n32);
172         if (ret != LDB_SUCCESS) {
173                 return ret;
174         }
175 #undef SETUP_PTRS
176
177         return LDB_SUCCESS;
178 }
179
180 /*
181   allocate a new range of RIDs in the RID Manager object
182  */
183 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
184 {
185         int ret;
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;
193
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);
200                 return ret;
201         }
202
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);
210                 return ret;
211         }
212
213         /* lower part of new pool is the low part of the rIDAvailablePool */
214         dc_pool = rid_pool_lo;
215
216         /* allocate 500 RIDs to this DC */
217         rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
218
219         /* work out upper part of new pool */
220         dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
221
222         /* and new rIDAvailablePool value */
223         new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
224
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",
229                                        ldb_errstring(ldb));
230                 talloc_free(tmp_ctx);
231                 return ret;
232         }
233
234         (*new_pool) = dc_pool;
235         talloc_free(tmp_ctx);
236         return LDB_SUCCESS;
237 }
238
239 /*
240   create a RID Set object for the specified DC
241  */
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)
245 {
246         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
247         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
248         int ret;
249         uint64_t dc_pool;
250         struct ldb_message *msg;
251         struct ldb_context *ldb = ldb_module_get_ctx(module);
252
253         /*
254           steps:
255
256           find the machine object for the DC
257           construct the RID Set DN
258           load rIDAvailablePool to find next available set
259           modify RID Manager object to update rIDAvailablePool
260           add the RID Set object
261           link to the RID Set object in machine object
262          */
263
264         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
265         if (!server_dn) {
266                 talloc_free(tmp_ctx);
267                 return ldb_module_oom(module);
268         }
269
270         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
271         if (ret != LDB_SUCCESS) {
272                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
273                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
274                 talloc_free(tmp_ctx);
275                 return ret;
276         }
277
278         rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
279         if (rid_set_dn == NULL) {
280                 talloc_free(tmp_ctx);
281                 return ldb_module_oom(module);
282         }
283
284         if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
285                 talloc_free(tmp_ctx);
286                 return ldb_module_oom(module);
287         }
288
289         /* grab a pool from the RID Manager object */
290         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &dc_pool);
291         if (ret != LDB_SUCCESS) {
292                 talloc_free(tmp_ctx);
293                 return ret;
294         }
295
296         /* create the RID Set object */
297         msg = ldb_msg_new(tmp_ctx);
298         msg->dn = rid_set_dn;
299
300         ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
301         if (ret != LDB_SUCCESS) {
302                 talloc_free(tmp_ctx);
303                 return ret;
304         }
305         ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
306         if (ret != LDB_SUCCESS) {
307                 talloc_free(tmp_ctx);
308                 return ret;
309         }
310
311         /* w2k8-r2 sets these to zero when first created */
312         ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "0");
313         if (ret != LDB_SUCCESS) {
314                 talloc_free(tmp_ctx);
315                 return ret;
316         }
317         ret = ldb_msg_add_fmt(msg, "rIDUsedPool", "0");
318         if (ret != LDB_SUCCESS) {
319                 talloc_free(tmp_ctx);
320                 return ret;
321         }
322         ret = ldb_msg_add_fmt(msg, "rIDNextRID", "0");
323         if (ret != LDB_SUCCESS) {
324                 talloc_free(tmp_ctx);
325                 return ret;
326         }
327
328         /* we need this to go all the way to the top of the module
329          * stack, as we need all the extra attributes added (including
330          * complex ones like ntsecuritydescriptor) */
331         ret = dsdb_module_add(module, msg, DSDB_FLAG_TOP_MODULE | DSDB_MODIFY_RELAX);
332         if (ret != LDB_SUCCESS) {
333                 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
334                                        ldb_dn_get_linearized(msg->dn),
335                                        ldb_errstring(ldb));
336                 talloc_free(tmp_ctx);
337                 return ret;
338         }
339
340         /* add the rIDSetReferences link */
341         msg = ldb_msg_new(tmp_ctx);
342         msg->dn = machine_dn;
343
344         ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
345         if (ret != LDB_SUCCESS) {
346                 talloc_free(tmp_ctx);
347                 return ret;
348         }
349         msg->elements[0].flags = LDB_FLAG_MOD_ADD;
350
351         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
352         if (ret != LDB_SUCCESS) {
353                 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
354                                        ldb_dn_get_linearized(msg->dn),
355                                        ldb_errstring(ldb));
356                 talloc_free(tmp_ctx);
357                 return ret;
358         }
359
360         (*dn) = talloc_steal(mem_ctx, rid_set_dn);
361
362         talloc_free(tmp_ctx);
363         return LDB_SUCCESS;
364 }
365
366
367 /*
368   create a RID Set object for this DC
369  */
370 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
371                                        struct ldb_dn **dn)
372 {
373         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
374         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
375         int ret;
376         struct ldb_context *ldb = ldb_module_get_ctx(module);
377
378         /* work out who is the RID Manager */
379         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
380         if (ret != LDB_SUCCESS) {
381                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
382                                        ldb_errstring(ldb));
383                 talloc_free(tmp_ctx);
384                 return ret;
385         }
386
387         /* find the DN of the RID Manager */
388         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
389         if (ret != LDB_SUCCESS) {
390                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
391                                        ldb_errstring(ldb));
392                 talloc_free(tmp_ctx);
393                 return ret;
394         }
395
396         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
397                 ridalloc_poke_rid_manager(module);
398                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
399                 talloc_free(tmp_ctx);
400                 return LDB_ERR_UNWILLING_TO_PERFORM;
401         }
402
403         ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
404         talloc_free(tmp_ctx);
405         return ret;
406 }
407
408 /*
409   refresh a RID Set object for the specified DC
410   also returns the first RID for the new pool
411  */
412 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
413                                          struct ldb_dn *rid_manager_dn,
414                                          struct ldb_dn *ntds_dn, uint64_t *new_pool)
415 {
416         TALLOC_CTX *tmp_ctx = talloc_new(module);
417         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
418         struct ldb_context *ldb = ldb_module_get_ctx(module);
419         int ret;
420
421         /* grab a pool from the RID Manager object */
422         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
423         if (ret != LDB_SUCCESS) {
424                 talloc_free(tmp_ctx);
425                 return ret;
426         }
427
428         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
429         if (!server_dn) {
430                 talloc_free(tmp_ctx);
431                 return ldb_module_oom(module);
432         }
433
434         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
435         if (ret != LDB_SUCCESS) {
436                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
437                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
438                 talloc_free(tmp_ctx);
439                 return ret;
440         }
441
442         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
443         if (ret != LDB_SUCCESS) {
444                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
445                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
446                 talloc_free(tmp_ctx);
447                 return ret;
448         }
449
450         ret = dsdb_module_set_integer(module, rid_set_dn, "rIDAllocationPool", *new_pool);
451         if (ret != LDB_SUCCESS) {
452                 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
453                                        ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
454                 talloc_free(tmp_ctx);
455                 return ret;
456         }
457
458         talloc_free(tmp_ctx);
459         return LDB_SUCCESS;
460 }
461
462
463 /*
464   get a new RID pool for ourselves
465   also returns the first rid for the new pool
466  */
467 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
468 {
469         TALLOC_CTX *tmp_ctx = talloc_new(module);
470         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
471         int ret;
472         struct ldb_context *ldb = ldb_module_get_ctx(module);
473
474         /* work out who is the RID Manager */
475         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
476         if (ret != LDB_SUCCESS) {
477                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
478                                        ldb_errstring(ldb));
479                 talloc_free(tmp_ctx);
480                 return ret;
481         }
482
483         /* find the DN of the RID Manager */
484         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
485         if (ret != LDB_SUCCESS) {
486                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
487                                        ldb_errstring(ldb));
488                 talloc_free(tmp_ctx);
489                 return ret;
490         }
491
492         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
493                 ridalloc_poke_rid_manager(module);
494                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
495                 talloc_free(tmp_ctx);
496                 return LDB_ERR_UNWILLING_TO_PERFORM;
497         }
498
499         ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
500         talloc_free(tmp_ctx);
501         return ret;
502 }
503
504
505 /* allocate a RID using our RID Set
506    If we run out of RIDs then allocate a new pool
507    either locally or by contacting the RID Manager
508 */
509 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
510 {
511         struct ldb_context *ldb;
512         static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
513                                               "rIDNextRID" , "rIDUsedPool", NULL };
514         int ret;
515         struct ldb_dn *rid_set_dn;
516         struct ldb_result *res;
517         uint64_t alloc_pool, prev_alloc_pool;
518         uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
519         uint32_t rid_used_pool;
520         int prev_rid;
521         TALLOC_CTX *tmp_ctx = talloc_new(module);
522
523         (*rid) = 0;
524         ldb = ldb_module_get_ctx(module);
525
526         ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
527         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
528                 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
529         }
530         if (ret != LDB_SUCCESS) {
531                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
532                                        ldb_errstring(ldb));
533                 talloc_free(tmp_ctx);
534                 return ret;
535         }
536
537         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
538                                     attrs, DSDB_FLAG_NEXT_MODULE);
539         if (ret != LDB_SUCCESS) {
540                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
541                                        ldb_dn_get_linearized(rid_set_dn));
542                 talloc_free(tmp_ctx);
543                 return ret;
544         }
545
546         prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
547         alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
548         prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", 0);
549         rid_used_pool = ldb_msg_find_attr_as_int(res->msgs[0], "rIDUsedPool", 0);
550         if (alloc_pool == 0) {
551                 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
552                                        ldb_dn_get_linearized(rid_set_dn));
553                 talloc_free(tmp_ctx);
554                 return LDB_ERR_OPERATIONS_ERROR;
555         }
556
557         prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
558         prev_alloc_pool_hi = prev_alloc_pool >> 32;
559         if (prev_rid >= prev_alloc_pool_hi) {
560                 if (prev_alloc_pool == 0) {
561                         ret = dsdb_module_set_integer(module, rid_set_dn, "rIDPreviousAllocationPool", alloc_pool);
562                 } else {
563                         ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
564                                                                       prev_alloc_pool, alloc_pool);
565                 }
566                 if (ret != LDB_SUCCESS) {
567                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
568                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
569                         talloc_free(tmp_ctx);
570                         return ret;
571                 }
572                 prev_alloc_pool = alloc_pool;
573                 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
574                 prev_alloc_pool_hi = prev_alloc_pool >> 32;
575
576                 /*
577                  * update the rIDUsedPool attribute
578                  *
579                  * Note: w2k8r2 doesn't update this attribute,
580                  *       at least if it's itself the rid master.
581                  */
582                 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDUsedPool", rid_used_pool+1);
583                 if (ret != LDB_SUCCESS) {
584                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDUsedPool on %s - %s",
585                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
586                         talloc_free(tmp_ctx);
587                         return ret;
588                 }
589
590                 (*rid) = prev_alloc_pool_lo;
591         }
592
593         /* see if we are still out of RIDs, and if so then ask
594            the RID Manager to give us more */
595         if (prev_rid >= prev_alloc_pool_hi) {
596                 uint64_t new_pool;
597                 ret = ridalloc_refresh_own_pool(module, &new_pool);
598                 if (ret != LDB_SUCCESS) {
599                         talloc_free(tmp_ctx);
600                         return ret;
601                 }
602                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
603                                                               prev_alloc_pool, new_pool);
604                 if (ret != LDB_SUCCESS) {
605                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
606                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
607                         talloc_free(tmp_ctx);
608                         return ret;
609                 }
610                 prev_alloc_pool = new_pool;
611                 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
612                 prev_alloc_pool_hi = prev_alloc_pool >> 32;
613                 (*rid) = prev_alloc_pool_lo;
614         } else {
615                 /* despite the name, rIDNextRID is the value of the last user
616                  * added by this DC, not the next available RID */
617                 if (*rid == 0) {
618                         (*rid) = prev_rid + 1;
619                 }
620         }
621
622         if (*rid < prev_alloc_pool_lo || *rid > prev_alloc_pool_hi) {
623                 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
624                                        (unsigned)*rid, (unsigned)prev_alloc_pool_lo,
625                                        (unsigned)prev_alloc_pool_hi);
626                 talloc_free(tmp_ctx);
627                 return LDB_ERR_OPERATIONS_ERROR;
628         }
629
630         /* now modify the RID Set to use up this RID using a
631          * constrained delete/add if possible */
632         if (prev_rid == 0) {
633                 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDNextRID", *rid);
634         } else {
635                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
636         }
637
638         /* if we are half-exhausted then ask the repl task to start
639          * getting another one */
640         if (*rid > (prev_alloc_pool_hi + prev_alloc_pool_lo)/2) {
641                 ridalloc_poke_rid_manager(module);
642         }
643
644         talloc_free(tmp_ctx);
645
646         return ret;
647 }
648
649
650 /*
651   called by DSDB_EXTENDED_ALLOCATE_RID_POOL extended operation in samldb
652  */
653 int ridalloc_allocate_rid_pool_fsmo(struct ldb_module *module, struct dsdb_fsmo_extended_op *exop)
654 {
655         struct ldb_dn *ntds_dn, *server_dn, *machine_dn, *rid_set_dn;
656         struct ldb_dn *rid_manager_dn;
657         TALLOC_CTX *tmp_ctx = talloc_new(module);
658         int ret;
659         struct ldb_context *ldb = ldb_module_get_ctx(module);
660         uint64_t new_pool;
661
662         ret = dsdb_module_dn_by_guid(module, tmp_ctx, &exop->destination_dsa_guid, &ntds_dn);
663         if (ret != LDB_SUCCESS) {
664                 ldb_asprintf_errstring(ldb, __location__ ": Unable to find NTDS object for guid %s - %s\n",
665                                        GUID_string(tmp_ctx, &exop->destination_dsa_guid), ldb_errstring(ldb));
666                 talloc_free(tmp_ctx);
667                 return ret;
668         }
669
670         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
671         if (!server_dn) {
672                 talloc_free(tmp_ctx);
673                 return ldb_module_oom(module);
674         }
675
676         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
677         if (ret != LDB_SUCCESS) {
678                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find serverReference in %s - %s",
679                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
680                 talloc_free(tmp_ctx);
681                 return ret;
682         }
683
684
685         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
686         if (ret != LDB_SUCCESS) {
687                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find RID Manager object - %s",
688                                        ldb_errstring(ldb));
689                 talloc_free(tmp_ctx);
690                 return ret;
691         }
692
693         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
694         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
695                 ret = ridalloc_create_rid_set_ntds(module, tmp_ctx, rid_manager_dn, ntds_dn, &rid_set_dn);
696                 talloc_free(tmp_ctx);
697                 return ret;
698         }
699
700         if (ret != LDB_SUCCESS) {
701                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
702                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
703                 talloc_free(tmp_ctx);
704                 return ret;
705         }
706
707         if (exop->fsmo_info != 0) {
708                 const char *attrs[] = { "rIDAllocationPool", NULL };
709                 struct ldb_result *res;
710                 uint64_t alloc_pool;
711
712                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
713                                             attrs, DSDB_FLAG_NEXT_MODULE);
714                 if (ret != LDB_SUCCESS) {
715                         ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
716                                                ldb_dn_get_linearized(rid_set_dn));
717                         talloc_free(tmp_ctx);
718                         return ret;
719                 }
720
721                 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
722                 if (alloc_pool != exop->fsmo_info) {
723                         /* it has already been updated */
724                         DEBUG(2,(__location__ ": rIDAllocationPool fsmo_info mismatch - already changed (0x%llx 0x%llx)\n",
725                                  (unsigned long long)exop->fsmo_info,
726                                  (unsigned long long)alloc_pool));
727                         talloc_free(tmp_ctx);
728                         return LDB_SUCCESS;
729                 }
730         }
731
732         ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, ntds_dn, &new_pool);
733         talloc_free(tmp_ctx);
734         return ret;
735 }