d0266eda8aa8845f8039e153963797ce48e67d6f
[obnox/samba/samba-obnox.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 #include "dsdb/samdb/ldb_modules/ridalloc.h"
38
39 /*
40   Note: the RID allocation attributes in AD are very badly named. Here
41   is what we think they really do:
42
43   in RID Set object:
44     - rIDPreviousAllocationPool: the pool which a DC is currently
45       pulling RIDs from. Managed by client DC
46
47     - rIDAllocationPool: the pool that the DC will switch to next,
48       when rIDPreviousAllocationPool is exhausted. Managed by RID Manager.
49
50     - rIDNextRID: the last RID allocated by this DC. Managed by client DC
51
52   in RID Manager object:
53     - rIDAvailablePool: the pool where the RID Manager gets new rID
54       pools from when it gets a EXOP_RID_ALLOC getncchanges call (or
55       locally when the DC is the RID Manager)
56  */
57
58
59 /*
60   make a IRPC call to the drepl task to ask it to get the RID
61   Manager to give us another RID pool.
62
63   This function just sends the message to the drepl task then
64   returns immediately. It should be called well before we
65   completely run out of RIDs
66  */
67 static void ridalloc_poke_rid_manager(struct ldb_module *module)
68 {
69         struct imessaging_context *msg;
70         struct server_id *server;
71         struct ldb_context *ldb = ldb_module_get_ctx(module);
72         struct loadparm_context *lp_ctx =
73                 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
74         TALLOC_CTX *tmp_ctx = talloc_new(module);
75
76         msg = imessaging_client_init(tmp_ctx, lp_ctx,
77                                     ldb_get_event_context(ldb));
78         if (!msg) {
79                 DEBUG(3,(__location__ ": Failed to create messaging context\n"));
80                 talloc_free(tmp_ctx);
81                 return;
82         }
83
84         server = irpc_servers_byname(msg, msg, "dreplsrv");
85         if (!server) {
86                 /* this means the drepl service is not running */
87                 talloc_free(tmp_ctx);
88                 return;
89         }
90
91         imessaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
92
93         /* we don't care if the message got through */
94         talloc_free(tmp_ctx);
95 }
96
97
98 static const char * const ridalloc_ridset_attrs[] = {
99         "rIDAllocationPool",
100         "rIDPreviousAllocationPool",
101         "rIDNextRID",
102         "rIDUsedPool",
103         NULL
104 };
105
106 struct ridalloc_ridset_values {
107         uint64_t alloc_pool;
108         uint64_t prev_pool;
109         uint32_t next_rid;
110         uint32_t used_pool;
111 };
112
113 static void ridalloc_get_ridset_values(struct ldb_message *msg, struct ridalloc_ridset_values *v)
114 {
115         v->alloc_pool = ldb_msg_find_attr_as_uint64(msg, "rIDAllocationPool", UINT64_MAX);
116         v->prev_pool = ldb_msg_find_attr_as_uint64(msg, "rIDPreviousAllocationPool", UINT64_MAX);
117         v->next_rid = ldb_msg_find_attr_as_uint(msg, "rIDNextRID", UINT32_MAX);
118         v->used_pool = ldb_msg_find_attr_as_uint(msg, "rIDUsedPool", UINT32_MAX);
119 }
120
121 static int ridalloc_set_ridset_values(struct ldb_module *module,
122                                       struct ldb_message *msg,
123                                       const struct ridalloc_ridset_values *o,
124                                       const struct ridalloc_ridset_values *n)
125 {
126         const uint32_t *o32, *n32;
127         const uint64_t *o64, *n64;
128         int ret;
129
130 #define SETUP_PTRS(field, optr, nptr, max) do { \
131         optr = &o->field; \
132         nptr = &n->field; \
133         if (o->field == max) { \
134                 optr = NULL; \
135         } \
136         if (n->field == max) { \
137                 nptr = NULL; \
138         } \
139         if (o->field == n->field) { \
140                 optr = NULL; \
141                 nptr = NULL; \
142         } \
143 } while(0)
144
145         SETUP_PTRS(alloc_pool, o64, n64, UINT64_MAX);
146         ret = dsdb_msg_constrainted_update_uint64(module, msg,
147                                                   "rIDAllocationPool",
148                                                   o64, n64);
149         if (ret != LDB_SUCCESS) {
150                 return ret;
151         }
152
153         SETUP_PTRS(prev_pool, o64, n64, UINT64_MAX);
154         ret = dsdb_msg_constrainted_update_uint64(module, msg,
155                                                   "rIDPreviousAllocationPool",
156                                                   o64, n64);
157         if (ret != LDB_SUCCESS) {
158                 return ret;
159         }
160
161         SETUP_PTRS(next_rid, o32, n32, UINT32_MAX);
162         ret = dsdb_msg_constrainted_update_uint32(module, msg,
163                                                   "rIDNextRID",
164                                                   o32, n32);
165         if (ret != LDB_SUCCESS) {
166                 return ret;
167         }
168
169         SETUP_PTRS(used_pool, o32, n32, UINT32_MAX);
170         ret = dsdb_msg_constrainted_update_uint32(module, msg,
171                                                   "rIDUsedPool",
172                                                   o32, n32);
173         if (ret != LDB_SUCCESS) {
174                 return ret;
175         }
176 #undef SETUP_PTRS
177
178         return LDB_SUCCESS;
179 }
180
181 /*
182   allocate a new range of RIDs in the RID Manager object
183  */
184 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool,
185                                          struct ldb_request *parent)
186 {
187         int ret;
188         TALLOC_CTX *tmp_ctx = talloc_new(module);
189         const char *attrs[] = { "rIDAvailablePool", NULL };
190         uint64_t rid_pool, new_rid_pool, dc_pool;
191         uint32_t rid_pool_lo, rid_pool_hi;
192         struct ldb_result *res;
193         struct ldb_context *ldb = ldb_module_get_ctx(module);
194         const unsigned alloc_size = 500;
195
196         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn,
197                                     attrs, DSDB_FLAG_NEXT_MODULE, parent);
198         if (ret != LDB_SUCCESS) {
199                 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
200                                        ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
201                 talloc_free(tmp_ctx);
202                 return ret;
203         }
204
205         rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
206         rid_pool_lo = rid_pool & 0xFFFFFFFF;
207         rid_pool_hi = rid_pool >> 32;
208         if (rid_pool_lo >= rid_pool_hi) {
209                 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
210                                        rid_pool_lo, rid_pool_hi);
211                 talloc_free(tmp_ctx);
212                 return ret;
213         }
214
215         /* lower part of new pool is the low part of the rIDAvailablePool */
216         dc_pool = rid_pool_lo;
217
218         /* allocate 500 RIDs to this DC */
219         rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
220
221         /* work out upper part of new pool */
222         dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
223
224         /* and new rIDAvailablePool value */
225         new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
226
227         ret = dsdb_module_constrainted_update_uint64(module, rid_manager_dn, "rIDAvailablePool",
228                                                      &rid_pool, &new_rid_pool, parent);
229         if (ret != LDB_SUCCESS) {
230                 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
231                                        ldb_errstring(ldb));
232                 talloc_free(tmp_ctx);
233                 return ret;
234         }
235
236         (*new_pool) = dc_pool;
237         talloc_free(tmp_ctx);
238         return LDB_SUCCESS;
239 }
240
241 /*
242   create a RID Set object for the specified DC
243  */
244 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
245                                         struct ldb_dn *rid_manager_dn,
246                                         struct ldb_dn *ntds_dn, struct ldb_dn **dn,
247                                         struct ldb_request *parent)
248 {
249         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
250         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
251         int ret;
252         struct ldb_message *msg;
253         struct ldb_context *ldb = ldb_module_get_ctx(module);
254         static const struct ridalloc_ridset_values o = {
255                 .alloc_pool     = UINT64_MAX,
256                 .prev_pool      = UINT64_MAX,
257                 .next_rid       = UINT32_MAX,
258                 .used_pool      = UINT32_MAX,
259         };
260         struct ridalloc_ridset_values n = {
261                 .alloc_pool     = 0,
262                 .prev_pool      = 0,
263                 .next_rid       = 0,
264                 .used_pool      = 0,
265         };
266         const char *no_attrs[] = { NULL };
267         struct ldb_result *res;
268
269         /*
270           steps:
271
272           find the machine object for the DC
273           construct the RID Set DN
274           load rIDAvailablePool to find next available set
275           modify RID Manager object to update rIDAvailablePool
276           add the RID Set object
277           link to the RID Set object in machine object
278          */
279
280         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
281         if (!server_dn) {
282                 talloc_free(tmp_ctx);
283                 return ldb_module_oom(module);
284         }
285
286         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn, parent);
287         if (ret != LDB_SUCCESS) {
288                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
289                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
290                 talloc_free(tmp_ctx);
291                 return ret;
292         }
293
294         rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
295         if (rid_set_dn == NULL) {
296                 talloc_free(tmp_ctx);
297                 return ldb_module_oom(module);
298         }
299
300         if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
301                 talloc_free(tmp_ctx);
302                 return ldb_module_oom(module);
303         }
304
305         /* grab a pool from the RID Manager object */
306         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &n.alloc_pool, parent);
307         if (ret != LDB_SUCCESS) {
308                 talloc_free(tmp_ctx);
309                 return ret;
310         }
311
312         /* create the RID Set object */
313         msg = ldb_msg_new(tmp_ctx);
314         msg->dn = rid_set_dn;
315
316         ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
317         if (ret != LDB_SUCCESS) {
318                 talloc_free(tmp_ctx);
319                 return ret;
320         }
321
322         ret = ridalloc_set_ridset_values(module, msg, &o, &n);
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, parent);
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         /* we need the extended DN of the RID Set object for
345          * rIDSetReferences */
346         ret = dsdb_module_search_dn(module, msg, &res, rid_set_dn, no_attrs,
347                                     DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, parent);
348         if (ret != LDB_SUCCESS) {
349                 ldb_asprintf_errstring(ldb, "Failed to find extended DN of RID Set %s - %s",
350                                        ldb_dn_get_linearized(msg->dn),
351                                        ldb_errstring(ldb));
352                 talloc_free(tmp_ctx);
353                 return ret;
354         }
355         rid_set_dn = res->msgs[0]->dn;
356
357
358         ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_extended_linearized(msg, rid_set_dn, 1));
359         if (ret != LDB_SUCCESS) {
360                 talloc_free(tmp_ctx);
361                 return ret;
362         }
363         msg->elements[0].flags = LDB_FLAG_MOD_ADD;
364
365         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
366         if (ret != LDB_SUCCESS) {
367                 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
368                                        ldb_dn_get_linearized(msg->dn),
369                                        ldb_errstring(ldb));
370                 talloc_free(tmp_ctx);
371                 return ret;
372         }
373
374         (*dn) = talloc_steal(mem_ctx, rid_set_dn);
375
376         talloc_free(tmp_ctx);
377         return LDB_SUCCESS;
378 }
379
380
381 /*
382   create a RID Set object for this DC
383  */
384 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
385                                        struct ldb_dn **dn, struct ldb_request *parent)
386 {
387         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
388         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
389         int ret;
390         struct ldb_context *ldb = ldb_module_get_ctx(module);
391         struct GUID fsmo_role_guid, *our_ntds_guid;
392         NTSTATUS status;
393
394         /* work out who is the RID Manager */
395         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn, parent);
396         if (ret != LDB_SUCCESS) {
397                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
398                                        ldb_errstring(ldb));
399                 talloc_free(tmp_ctx);
400                 return ret;
401         }
402
403         /* find the DN of the RID Manager */
404         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn, parent);
405         if (ret != LDB_SUCCESS) {
406                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
407                                        ldb_errstring(ldb));
408                 talloc_free(tmp_ctx);
409                 return ret;
410         }
411
412         status = dsdb_get_extended_dn_guid(fsmo_role_dn, &fsmo_role_guid, "GUID");
413         if (!NT_STATUS_IS_OK(status)) {
414                 talloc_free(tmp_ctx);
415                 return ldb_operr(ldb_module_get_ctx(module));
416         }
417
418         our_ntds_guid = samdb_ntds_objectGUID(ldb_module_get_ctx(module));
419         if (!our_ntds_guid) {
420                 talloc_free(tmp_ctx);
421                 return ldb_operr(ldb_module_get_ctx(module));
422         }
423
424         if (!GUID_equal(&fsmo_role_guid, our_ntds_guid)) {
425                 ridalloc_poke_rid_manager(module);
426                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
427                 talloc_free(tmp_ctx);
428                 return LDB_ERR_UNWILLING_TO_PERFORM;
429         }
430
431         ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn, parent);
432         talloc_free(tmp_ctx);
433         return ret;
434 }
435
436 /*
437   get a new RID pool for ourselves
438   also returns the first rid for the new pool
439  */
440 static int ridalloc_new_own_pool(struct ldb_module *module, uint64_t *new_pool, struct ldb_request *parent)
441 {
442         TALLOC_CTX *tmp_ctx = talloc_new(module);
443         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
444         int ret;
445         struct ldb_context *ldb = ldb_module_get_ctx(module);
446         bool is_us;
447
448         /* work out who is the RID Manager */
449         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn, parent);
450         if (ret != LDB_SUCCESS) {
451                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
452                                        ldb_errstring(ldb));
453                 talloc_free(tmp_ctx);
454                 return ret;
455         }
456
457         /* find the DN of the RID Manager */
458         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn, parent);
459         if (ret != LDB_SUCCESS) {
460                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
461                                        ldb_errstring(ldb));
462                 talloc_free(tmp_ctx);
463                 return ret;
464         }
465
466         ret = samdb_dn_is_our_ntdsa(ldb, fsmo_role_dn, &is_us);
467         if (ret != LDB_SUCCESS) {
468                 ldb_asprintf_errstring(ldb, "Failed to confirm if our ntdsDsa is %s: %s",
469                                        ldb_dn_get_linearized(fsmo_role_dn), ldb_errstring(ldb));
470                 talloc_free(tmp_ctx);
471                 return ret;
472         }
473         
474         if (!is_us) {
475                 ridalloc_poke_rid_manager(module);
476                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
477                 talloc_free(tmp_ctx);
478                 return LDB_ERR_UNWILLING_TO_PERFORM;
479         }
480
481         /* grab a pool from the RID Manager object */
482         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool, parent);
483         if (ret != LDB_SUCCESS) {
484                 talloc_free(tmp_ctx);
485                 return ret;
486         }
487
488         talloc_free(tmp_ctx);
489         return ret;
490 }
491
492
493 /* allocate a RID using our RID Set
494    If we run out of RIDs then allocate a new pool
495    either locally or by contacting the RID Manager
496 */
497 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid, struct ldb_request *parent)
498 {
499         struct ldb_context *ldb;
500         int ret;
501         struct ldb_dn *rid_set_dn;
502         struct ldb_result *res;
503         struct ldb_message *msg;
504         struct ridalloc_ridset_values oridset;
505         struct ridalloc_ridset_values nridset;
506         uint32_t prev_pool_lo, prev_pool_hi;
507         TALLOC_CTX *tmp_ctx = talloc_new(module);
508
509         (*rid) = 0;
510         ldb = ldb_module_get_ctx(module);
511
512         ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
513         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
514                 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn, parent);
515         }
516         if (ret != LDB_SUCCESS) {
517                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
518                                        ldb_errstring(ldb));
519                 talloc_free(tmp_ctx);
520                 return ret;
521         }
522
523         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
524                                     ridalloc_ridset_attrs, DSDB_FLAG_NEXT_MODULE, parent);
525         if (ret != LDB_SUCCESS) {
526                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
527                                        ldb_dn_get_linearized(rid_set_dn));
528                 talloc_free(tmp_ctx);
529                 return ret;
530         }
531
532         ridalloc_get_ridset_values(res->msgs[0], &oridset);
533         if (oridset.alloc_pool == UINT64_MAX) {
534                 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
535                                        ldb_dn_get_linearized(rid_set_dn));
536                 talloc_free(tmp_ctx);
537                 return LDB_ERR_OPERATIONS_ERROR;
538         }
539
540         nridset = oridset;
541
542         /*
543          * If we never used a pool, setup out first pool
544          */
545         if (nridset.prev_pool == UINT64_MAX ||
546             nridset.next_rid == UINT32_MAX) {
547                 nridset.prev_pool = nridset.alloc_pool;
548                 nridset.next_rid = nridset.prev_pool & 0xFFFFFFFF;
549         }
550
551         /*
552          * Now check if our current pool is still usable
553          */
554         nridset.next_rid += 1;
555         prev_pool_lo = nridset.prev_pool & 0xFFFFFFFF;
556         prev_pool_hi = nridset.prev_pool >> 32;
557         if (nridset.next_rid > prev_pool_hi) {
558                 /*
559                  * We need a new pool, check if we already have a new one
560                  * Otherwise we need to get a new pool.
561                  */
562                 if (nridset.alloc_pool == nridset.prev_pool) {
563                         /*
564                          * if we are the RID Manager,
565                          * we can get a new pool localy.
566                          * Otherwise we fail the operation and
567                          * ask async for a new pool.
568                          */
569                         ret = ridalloc_new_own_pool(module, &nridset.alloc_pool, parent);
570                         if (ret == LDB_ERR_UNWILLING_TO_PERFORM) {
571                                 ridalloc_poke_rid_manager(module);
572                                 talloc_free(tmp_ctx);
573                                 return ret;
574                         }
575                         if (ret != LDB_SUCCESS) {
576                                 talloc_free(tmp_ctx);
577                                 return ret;
578                         }
579                 }
580
581                 /*
582                  * increment the rIDUsedPool attribute
583                  *
584                  * Note: w2k8r2 doesn't update this attribute,
585                  *       at least if it's itself the rid master.
586                  */
587                 nridset.used_pool += 1;
588
589                 /* now use the new pool */
590                 nridset.prev_pool = nridset.alloc_pool;
591                 prev_pool_lo = nridset.prev_pool & 0xFFFFFFFF;
592                 prev_pool_hi = nridset.prev_pool >> 32;
593                 nridset.next_rid = prev_pool_lo;
594         }
595
596         if (nridset.next_rid < prev_pool_lo || nridset.next_rid > prev_pool_hi) {
597                 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
598                                        (unsigned)nridset.next_rid,
599                                        (unsigned)prev_pool_lo,
600                                        (unsigned)prev_pool_hi);
601                 talloc_free(tmp_ctx);
602                 return LDB_ERR_OPERATIONS_ERROR;
603         }
604
605         /*
606          * if we are half-exhausted then try to get a new pool.
607          */
608         if (nridset.next_rid > (prev_pool_hi + prev_pool_lo)/2) {
609                 /*
610                  * if we are the RID Manager,
611                  * we can get a new pool localy.
612                  * Otherwise we fail the operation and
613                  * ask async for a new pool.
614                  */
615                 ret = ridalloc_new_own_pool(module, &nridset.alloc_pool, parent);
616                 if (ret == LDB_ERR_UNWILLING_TO_PERFORM) {
617                         ridalloc_poke_rid_manager(module);
618                         ret = LDB_SUCCESS;
619                 }
620                 if (ret != LDB_SUCCESS) {
621                         talloc_free(tmp_ctx);
622                         return ret;
623                 }
624         }
625
626         /*
627          * update the values
628          */
629         msg = ldb_msg_new(tmp_ctx);
630         if (msg == NULL) {
631                 return ldb_module_oom(module);
632         }
633         msg->dn = rid_set_dn;
634
635         ret = ridalloc_set_ridset_values(module, msg,
636                                          &oridset, &nridset);
637         if (ret != LDB_SUCCESS) {
638                 talloc_free(tmp_ctx);
639                 return ret;
640         }
641
642         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
643         if (ret != LDB_SUCCESS) {
644                 talloc_free(tmp_ctx);
645                 return ret;
646         }
647
648         talloc_free(tmp_ctx);
649         *rid = nridset.next_rid;
650         return LDB_SUCCESS;
651 }
652
653
654 /*
655   called by DSDB_EXTENDED_ALLOCATE_RID_POOL extended operation in samldb
656  */
657 int ridalloc_allocate_rid_pool_fsmo(struct ldb_module *module, struct dsdb_fsmo_extended_op *exop,
658                                     struct ldb_request *parent)
659 {
660         struct ldb_dn *ntds_dn, *server_dn, *machine_dn, *rid_set_dn;
661         struct ldb_dn *rid_manager_dn;
662         TALLOC_CTX *tmp_ctx = talloc_new(module);
663         int ret;
664         struct ldb_context *ldb = ldb_module_get_ctx(module);
665         struct ldb_result *res;
666         struct ldb_message *msg;
667         struct ridalloc_ridset_values oridset, nridset;
668
669         ret = dsdb_module_dn_by_guid(module, tmp_ctx, &exop->destination_dsa_guid, &ntds_dn, parent);
670         if (ret != LDB_SUCCESS) {
671                 ldb_asprintf_errstring(ldb, __location__ ": Unable to find NTDS object for guid %s - %s\n",
672                                        GUID_string(tmp_ctx, &exop->destination_dsa_guid), ldb_errstring(ldb));
673                 talloc_free(tmp_ctx);
674                 return ret;
675         }
676
677         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
678         if (!server_dn) {
679                 talloc_free(tmp_ctx);
680                 return ldb_module_oom(module);
681         }
682
683         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn, parent);
684         if (ret != LDB_SUCCESS) {
685                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find serverReference in %s - %s",
686                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
687                 talloc_free(tmp_ctx);
688                 return ret;
689         }
690
691         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn, parent);
692         if (ret != LDB_SUCCESS) {
693                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find RID Manager object - %s",
694                                        ldb_errstring(ldb));
695                 talloc_free(tmp_ctx);
696                 return ret;
697         }
698
699         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn, parent);
700         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
701                 ret = ridalloc_create_rid_set_ntds(module, tmp_ctx, rid_manager_dn, ntds_dn, &rid_set_dn, parent);
702                 talloc_free(tmp_ctx);
703                 return ret;
704         }
705
706         if (ret != LDB_SUCCESS) {
707                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
708                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
709                 talloc_free(tmp_ctx);
710                 return ret;
711         }
712
713         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
714                                     ridalloc_ridset_attrs, DSDB_FLAG_NEXT_MODULE, parent);
715         if (ret != LDB_SUCCESS) {
716                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
717                                        ldb_dn_get_linearized(rid_set_dn));
718                 talloc_free(tmp_ctx);
719                 return ret;
720         }
721
722         ridalloc_get_ridset_values(res->msgs[0], &oridset);
723         if (oridset.alloc_pool == UINT64_MAX) {
724                 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
725                                        ldb_dn_get_linearized(rid_set_dn));
726                 talloc_free(tmp_ctx);
727                 return LDB_ERR_OPERATIONS_ERROR;
728         }
729
730         nridset = oridset;
731
732         if (exop->fsmo_info != 0) {
733
734                 if (nridset.alloc_pool != exop->fsmo_info) {
735                         /* it has already been updated */
736                         DEBUG(2,(__location__ ": rIDAllocationPool fsmo_info mismatch - already changed (0x%llx 0x%llx)\n",
737                                  (unsigned long long)exop->fsmo_info,
738                                  (unsigned long long)nridset.alloc_pool));
739                         talloc_free(tmp_ctx);
740                         return LDB_SUCCESS;
741                 }
742         }
743
744         /* grab a pool from the RID Manager object */
745         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &nridset.alloc_pool, parent);
746         if (ret != LDB_SUCCESS) {
747                 talloc_free(tmp_ctx);
748                 return ret;
749         }
750
751         /*
752          * update the values
753          */
754         msg = ldb_msg_new(tmp_ctx);
755         if (msg == NULL) {
756                 return ldb_module_oom(module);
757         }
758         msg->dn = rid_set_dn;
759
760         ret = ridalloc_set_ridset_values(module, msg,
761                                          &oridset, &nridset);
762         if (ret != LDB_SUCCESS) {
763                 talloc_free(tmp_ctx);
764                 return ret;
765         }
766
767         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
768         if (ret != LDB_SUCCESS) {
769                 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
770                                        ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
771                 talloc_free(tmp_ctx);
772                 return ret;
773         }
774
775         talloc_free(tmp_ctx);
776         return LDB_SUCCESS;
777 }