2dd4c563c8e2fbdcb5c2baa094186aa90c537d15
[kamenim/samba.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         struct ldb_message *msg;
250         struct ldb_context *ldb = ldb_module_get_ctx(module);
251         static const struct ridalloc_ridset_values o = {
252                 .alloc_pool     = UINT64_MAX,
253                 .prev_pool      = UINT64_MAX,
254                 .next_rid       = UINT32_MAX,
255                 .used_pool      = UINT32_MAX,
256         };
257         struct ridalloc_ridset_values n = {
258                 .alloc_pool     = 0,
259                 .prev_pool      = 0,
260                 .next_rid       = 0,
261                 .used_pool      = 0,
262         };
263
264         /*
265           steps:
266
267           find the machine object for the DC
268           construct the RID Set DN
269           load rIDAvailablePool to find next available set
270           modify RID Manager object to update rIDAvailablePool
271           add the RID Set object
272           link to the RID Set object in machine object
273          */
274
275         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
276         if (!server_dn) {
277                 talloc_free(tmp_ctx);
278                 return ldb_module_oom(module);
279         }
280
281         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
282         if (ret != LDB_SUCCESS) {
283                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
284                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
285                 talloc_free(tmp_ctx);
286                 return ret;
287         }
288
289         rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
290         if (rid_set_dn == NULL) {
291                 talloc_free(tmp_ctx);
292                 return ldb_module_oom(module);
293         }
294
295         if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
296                 talloc_free(tmp_ctx);
297                 return ldb_module_oom(module);
298         }
299
300         /* grab a pool from the RID Manager object */
301         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &n.alloc_pool);
302         if (ret != LDB_SUCCESS) {
303                 talloc_free(tmp_ctx);
304                 return ret;
305         }
306
307         /* create the RID Set object */
308         msg = ldb_msg_new(tmp_ctx);
309         msg->dn = rid_set_dn;
310
311         ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
312         if (ret != LDB_SUCCESS) {
313                 talloc_free(tmp_ctx);
314                 return ret;
315         }
316
317         ret = ridalloc_set_ridset_values(module, msg, &o, &n);
318         if (ret != LDB_SUCCESS) {
319                 talloc_free(tmp_ctx);
320                 return ret;
321         }
322
323         /* we need this to go all the way to the top of the module
324          * stack, as we need all the extra attributes added (including
325          * complex ones like ntsecuritydescriptor) */
326         ret = dsdb_module_add(module, msg, DSDB_FLAG_TOP_MODULE | DSDB_MODIFY_RELAX);
327         if (ret != LDB_SUCCESS) {
328                 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
329                                        ldb_dn_get_linearized(msg->dn),
330                                        ldb_errstring(ldb));
331                 talloc_free(tmp_ctx);
332                 return ret;
333         }
334
335         /* add the rIDSetReferences link */
336         msg = ldb_msg_new(tmp_ctx);
337         msg->dn = machine_dn;
338
339         ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
340         if (ret != LDB_SUCCESS) {
341                 talloc_free(tmp_ctx);
342                 return ret;
343         }
344         msg->elements[0].flags = LDB_FLAG_MOD_ADD;
345
346         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
347         if (ret != LDB_SUCCESS) {
348                 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
349                                        ldb_dn_get_linearized(msg->dn),
350                                        ldb_errstring(ldb));
351                 talloc_free(tmp_ctx);
352                 return ret;
353         }
354
355         (*dn) = talloc_steal(mem_ctx, rid_set_dn);
356
357         talloc_free(tmp_ctx);
358         return LDB_SUCCESS;
359 }
360
361
362 /*
363   create a RID Set object for this DC
364  */
365 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
366                                        struct ldb_dn **dn)
367 {
368         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
369         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
370         int ret;
371         struct ldb_context *ldb = ldb_module_get_ctx(module);
372
373         /* work out who is the RID Manager */
374         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
375         if (ret != LDB_SUCCESS) {
376                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
377                                        ldb_errstring(ldb));
378                 talloc_free(tmp_ctx);
379                 return ret;
380         }
381
382         /* find the DN of the RID Manager */
383         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
384         if (ret != LDB_SUCCESS) {
385                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
386                                        ldb_errstring(ldb));
387                 talloc_free(tmp_ctx);
388                 return ret;
389         }
390
391         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
392                 ridalloc_poke_rid_manager(module);
393                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
394                 talloc_free(tmp_ctx);
395                 return LDB_ERR_UNWILLING_TO_PERFORM;
396         }
397
398         ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
399         talloc_free(tmp_ctx);
400         return ret;
401 }
402
403 /*
404   refresh a RID Set object for the specified DC
405   also returns the first RID for the new pool
406  */
407 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
408                                          struct ldb_dn *rid_manager_dn,
409                                          struct ldb_dn *ntds_dn, uint64_t *new_pool)
410 {
411         TALLOC_CTX *tmp_ctx = talloc_new(module);
412         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
413         struct ldb_context *ldb = ldb_module_get_ctx(module);
414         int ret;
415
416         /* grab a pool from the RID Manager object */
417         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
418         if (ret != LDB_SUCCESS) {
419                 talloc_free(tmp_ctx);
420                 return ret;
421         }
422
423         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
424         if (!server_dn) {
425                 talloc_free(tmp_ctx);
426                 return ldb_module_oom(module);
427         }
428
429         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
430         if (ret != LDB_SUCCESS) {
431                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
432                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
433                 talloc_free(tmp_ctx);
434                 return ret;
435         }
436
437         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
438         if (ret != LDB_SUCCESS) {
439                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
440                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
441                 talloc_free(tmp_ctx);
442                 return ret;
443         }
444
445         ret = dsdb_module_set_integer(module, rid_set_dn, "rIDAllocationPool", *new_pool);
446         if (ret != LDB_SUCCESS) {
447                 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
448                                        ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
449                 talloc_free(tmp_ctx);
450                 return ret;
451         }
452
453         talloc_free(tmp_ctx);
454         return LDB_SUCCESS;
455 }
456
457
458 /*
459   get a new RID pool for ourselves
460   also returns the first rid for the new pool
461  */
462 static int ridalloc_new_own_pool(struct ldb_module *module, uint64_t *new_pool)
463 {
464         TALLOC_CTX *tmp_ctx = talloc_new(module);
465         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
466         int ret;
467         struct ldb_context *ldb = ldb_module_get_ctx(module);
468
469         /* work out who is the RID Manager */
470         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
471         if (ret != LDB_SUCCESS) {
472                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
473                                        ldb_errstring(ldb));
474                 talloc_free(tmp_ctx);
475                 return ret;
476         }
477
478         /* find the DN of the RID Manager */
479         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
480         if (ret != LDB_SUCCESS) {
481                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
482                                        ldb_errstring(ldb));
483                 talloc_free(tmp_ctx);
484                 return ret;
485         }
486
487         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
488                 ridalloc_poke_rid_manager(module);
489                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
490                 talloc_free(tmp_ctx);
491                 return LDB_ERR_UNWILLING_TO_PERFORM;
492         }
493
494         /* grab a pool from the RID Manager object */
495         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
496         if (ret != LDB_SUCCESS) {
497                 talloc_free(tmp_ctx);
498                 return ret;
499         }
500
501         talloc_free(tmp_ctx);
502         return ret;
503 }
504
505
506 /* allocate a RID using our RID Set
507    If we run out of RIDs then allocate a new pool
508    either locally or by contacting the RID Manager
509 */
510 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
511 {
512         struct ldb_context *ldb;
513         int ret;
514         struct ldb_dn *rid_set_dn;
515         struct ldb_result *res;
516         struct ldb_message *msg;
517         struct ridalloc_ridset_values oridset;
518         struct ridalloc_ridset_values nridset;
519         uint32_t prev_pool_lo, prev_pool_hi;
520         TALLOC_CTX *tmp_ctx = talloc_new(module);
521
522         (*rid) = 0;
523         ldb = ldb_module_get_ctx(module);
524
525         ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
526         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
527                 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
528         }
529         if (ret != LDB_SUCCESS) {
530                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
531                                        ldb_errstring(ldb));
532                 talloc_free(tmp_ctx);
533                 return ret;
534         }
535
536         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
537                                     ridalloc_ridset_attrs, DSDB_FLAG_NEXT_MODULE);
538         if (ret != LDB_SUCCESS) {
539                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
540                                        ldb_dn_get_linearized(rid_set_dn));
541                 talloc_free(tmp_ctx);
542                 return ret;
543         }
544
545         ridalloc_get_ridset_values(res->msgs[0], &oridset);
546         if (oridset.alloc_pool == UINT64_MAX) {
547                 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
548                                        ldb_dn_get_linearized(rid_set_dn));
549                 talloc_free(tmp_ctx);
550                 return LDB_ERR_OPERATIONS_ERROR;
551         }
552
553         nridset = oridset;
554
555         /*
556          * If we never used a pool, setup out first pool
557          */
558         if (nridset.prev_pool == UINT64_MAX ||
559             nridset.next_rid == UINT32_MAX) {
560                 nridset.prev_pool = nridset.alloc_pool;
561                 nridset.next_rid = nridset.prev_pool & 0xFFFFFFFF;
562         }
563
564         /*
565          * Now check if our current pool is still usable
566          */
567         nridset.next_rid += 1;
568         prev_pool_lo = nridset.prev_pool & 0xFFFFFFFF;
569         prev_pool_hi = nridset.prev_pool >> 32;
570         if (nridset.next_rid > prev_pool_hi) {
571                 /*
572                  * We need a new pool, check if we already have a new one
573                  * Otherwise we need to get a new pool.
574                  */
575                 if (nridset.alloc_pool == nridset.prev_pool) {
576                         /*
577                          * if we are the RID Manager,
578                          * we can get a new pool localy.
579                          * Otherwise we fail the operation and
580                          * ask async for a new pool.
581                          */
582                         ret = ridalloc_new_own_pool(module, &nridset.alloc_pool);
583                         if (ret == LDB_ERR_UNWILLING_TO_PERFORM) {
584                                 ridalloc_poke_rid_manager(module);
585                                 talloc_free(tmp_ctx);
586                                 return ret;
587                         }
588                         if (ret != LDB_SUCCESS) {
589                                 talloc_free(tmp_ctx);
590                                 return ret;
591                         }
592                 }
593
594                 /*
595                  * increment the rIDUsedPool attribute
596                  *
597                  * Note: w2k8r2 doesn't update this attribute,
598                  *       at least if it's itself the rid master.
599                  */
600                 nridset.used_pool += 1;
601
602                 /* now use the new pool */
603                 nridset.prev_pool = nridset.alloc_pool;
604                 prev_pool_lo = nridset.prev_pool & 0xFFFFFFFF;
605                 prev_pool_hi = nridset.prev_pool >> 32;
606                 nridset.next_rid = prev_pool_lo;
607         }
608
609         if (nridset.next_rid < prev_pool_lo || nridset.next_rid > prev_pool_hi) {
610                 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
611                                        (unsigned)nridset.next_rid,
612                                        (unsigned)prev_pool_lo,
613                                        (unsigned)prev_pool_hi);
614                 talloc_free(tmp_ctx);
615                 return LDB_ERR_OPERATIONS_ERROR;
616         }
617
618         /*
619          * if we are half-exhausted then try to get a new pool.
620          */
621         if (nridset.next_rid > (prev_pool_hi + prev_pool_lo)/2) {
622                 /*
623                  * if we are the RID Manager,
624                  * we can get a new pool localy.
625                  * Otherwise we fail the operation and
626                  * ask async for a new pool.
627                  */
628                 ret = ridalloc_new_own_pool(module, &nridset.alloc_pool);
629                 if (ret == LDB_ERR_UNWILLING_TO_PERFORM) {
630                         ridalloc_poke_rid_manager(module);
631                         ret = LDB_SUCCESS;
632                 }
633                 if (ret != LDB_SUCCESS) {
634                         talloc_free(tmp_ctx);
635                         return ret;
636                 }
637         }
638
639         /*
640          * update the values
641          */
642         msg = ldb_msg_new(tmp_ctx);
643         if (msg == NULL) {
644                 return ldb_module_oom(module);
645         }
646         msg->dn = rid_set_dn;
647
648         ret = ridalloc_set_ridset_values(module, msg,
649                                          &oridset, &nridset);
650         if (ret != LDB_SUCCESS) {
651                 talloc_free(tmp_ctx);
652                 return ret;
653         }
654
655         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
656         if (ret != LDB_SUCCESS) {
657                 talloc_free(tmp_ctx);
658                 return ret;
659         }
660
661         talloc_free(tmp_ctx);
662         *rid = nridset.next_rid;
663         return LDB_SUCCESS;
664 }
665
666
667 /*
668   called by DSDB_EXTENDED_ALLOCATE_RID_POOL extended operation in samldb
669  */
670 int ridalloc_allocate_rid_pool_fsmo(struct ldb_module *module, struct dsdb_fsmo_extended_op *exop)
671 {
672         struct ldb_dn *ntds_dn, *server_dn, *machine_dn, *rid_set_dn;
673         struct ldb_dn *rid_manager_dn;
674         TALLOC_CTX *tmp_ctx = talloc_new(module);
675         int ret;
676         struct ldb_context *ldb = ldb_module_get_ctx(module);
677         uint64_t new_pool;
678
679         ret = dsdb_module_dn_by_guid(module, tmp_ctx, &exop->destination_dsa_guid, &ntds_dn);
680         if (ret != LDB_SUCCESS) {
681                 ldb_asprintf_errstring(ldb, __location__ ": Unable to find NTDS object for guid %s - %s\n",
682                                        GUID_string(tmp_ctx, &exop->destination_dsa_guid), ldb_errstring(ldb));
683                 talloc_free(tmp_ctx);
684                 return ret;
685         }
686
687         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
688         if (!server_dn) {
689                 talloc_free(tmp_ctx);
690                 return ldb_module_oom(module);
691         }
692
693         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
694         if (ret != LDB_SUCCESS) {
695                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find serverReference in %s - %s",
696                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
697                 talloc_free(tmp_ctx);
698                 return ret;
699         }
700
701
702         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
703         if (ret != LDB_SUCCESS) {
704                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find RID Manager object - %s",
705                                        ldb_errstring(ldb));
706                 talloc_free(tmp_ctx);
707                 return ret;
708         }
709
710         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
711         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
712                 ret = ridalloc_create_rid_set_ntds(module, tmp_ctx, rid_manager_dn, ntds_dn, &rid_set_dn);
713                 talloc_free(tmp_ctx);
714                 return ret;
715         }
716
717         if (ret != LDB_SUCCESS) {
718                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
719                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
720                 talloc_free(tmp_ctx);
721                 return ret;
722         }
723
724         if (exop->fsmo_info != 0) {
725                 const char *attrs[] = { "rIDAllocationPool", NULL };
726                 struct ldb_result *res;
727                 uint64_t alloc_pool;
728
729                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
730                                             attrs, DSDB_FLAG_NEXT_MODULE);
731                 if (ret != LDB_SUCCESS) {
732                         ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
733                                                ldb_dn_get_linearized(rid_set_dn));
734                         talloc_free(tmp_ctx);
735                         return ret;
736                 }
737
738                 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
739                 if (alloc_pool != exop->fsmo_info) {
740                         /* it has already been updated */
741                         DEBUG(2,(__location__ ": rIDAllocationPool fsmo_info mismatch - already changed (0x%llx 0x%llx)\n",
742                                  (unsigned long long)exop->fsmo_info,
743                                  (unsigned long long)alloc_pool));
744                         talloc_free(tmp_ctx);
745                         return LDB_SUCCESS;
746                 }
747         }
748
749         ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, ntds_dn, &new_pool);
750         talloc_free(tmp_ctx);
751         return ret;
752 }