s4-dsdb: ensure we will in all the attributes for RID Set
[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   allocate a new range of RIDs in the RID Manager object
60  */
61 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
62 {
63         int ret;
64         TALLOC_CTX *tmp_ctx = talloc_new(module);
65         const char *attrs[] = { "rIDAvailablePool", NULL };
66         uint64_t rid_pool, new_rid_pool, dc_pool;
67         uint32_t rid_pool_lo, rid_pool_hi;
68         struct ldb_result *res;
69         struct ldb_context *ldb = ldb_module_get_ctx(module);
70         const unsigned alloc_size = 500;
71
72         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn, attrs, 0);
73         if (ret != LDB_SUCCESS) {
74                 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
75                                        ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
76                 talloc_free(tmp_ctx);
77                 return ret;
78         }
79
80         rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
81         rid_pool_lo = rid_pool & 0xFFFFFFFF;
82         rid_pool_hi = rid_pool >> 32;
83         if (rid_pool_lo >= rid_pool_hi) {
84                 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
85                                        rid_pool_lo, rid_pool_hi);
86                 talloc_free(tmp_ctx);
87                 return ret;
88         }
89
90         /* lower part of new pool is the low part of the rIDAvailablePool */
91         dc_pool = rid_pool_lo;
92
93         /* allocate 500 RIDs to this DC */
94         rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
95
96         /* work out upper part of new pool */
97         dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
98
99         /* and new rIDAvailablePool value */
100         new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
101
102         ret = dsdb_module_constrainted_update_integer(module, rid_manager_dn, "rIDAvailablePool",
103                                                       rid_pool, new_rid_pool);
104         if (ret != LDB_SUCCESS) {
105                 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
106                                        ldb_errstring(ldb));
107                 talloc_free(tmp_ctx);
108                 return ret;
109         }
110
111         (*new_pool) = dc_pool;
112         talloc_free(tmp_ctx);
113         return LDB_SUCCESS;
114 }
115
116 /*
117   create a RID Set object for the specified DC
118  */
119 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
120                                         struct ldb_dn *rid_manager_dn,
121                                         struct ldb_dn *ntds_dn, struct ldb_dn **dn)
122 {
123         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
124         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
125         int ret;
126         uint64_t dc_pool;
127         struct ldb_message *msg;
128         struct ldb_context *ldb = ldb_module_get_ctx(module);
129
130         /*
131           steps:
132
133           find the machine object for the DC
134           construct the RID Set DN
135           load rIDAvailablePool to find next available set
136           modify RID Manager object to update rIDAvailablePool
137           add the RID Set object
138           link to the RID Set object in machine object
139          */
140
141         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
142         if (!server_dn) {
143                 ldb_module_oom(module);
144                 talloc_free(tmp_ctx);
145                 return LDB_ERR_OPERATIONS_ERROR;
146         }
147
148         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
149         if (ret != LDB_SUCCESS) {
150                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
151                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
152                 talloc_free(tmp_ctx);
153                 return ret;
154         }
155
156         rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
157         if (rid_set_dn == NULL) {
158                 ldb_module_oom(module);
159                 return LDB_ERR_OPERATIONS_ERROR;
160         }
161
162         if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
163                 ldb_module_oom(module);
164                 return LDB_ERR_OPERATIONS_ERROR;
165         }
166
167         /* grab a pool from the RID Manager object */
168         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &dc_pool);
169         if (ret != LDB_SUCCESS) {
170                 talloc_free(tmp_ctx);
171                 return ret;
172         }
173
174         /* create the RID Set object */
175         msg = ldb_msg_new(tmp_ctx);
176         msg->dn = rid_set_dn;
177
178         ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
179         if (ret != LDB_SUCCESS) {
180                 talloc_free(tmp_ctx);
181                 return ret;
182         }
183         ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
184         if (ret != LDB_SUCCESS) {
185                 talloc_free(tmp_ctx);
186                 return ret;
187         }
188
189         /* w2k8-r2 sets these to zero when first created */
190         ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "0");
191         if (ret != LDB_SUCCESS) {
192                 talloc_free(tmp_ctx);
193                 return ret;
194         }
195         ret = ldb_msg_add_fmt(msg, "rIDUsedPool", "0");
196         if (ret != LDB_SUCCESS) {
197                 talloc_free(tmp_ctx);
198                 return ret;
199         }
200         ret = ldb_msg_add_fmt(msg, "rIDNextRID", "0");
201         if (ret != LDB_SUCCESS) {
202                 talloc_free(tmp_ctx);
203                 return ret;
204         }
205
206         /* we need this to go all the way to the top of the module
207          * stack, as we need all the extra attributes added (including
208          * complex ones like ntsecuritydescriptor) */
209         ret = dsdb_module_add(module, msg, DSDB_FLAG_TOP_MODULE | DSDB_MODIFY_RELAX);
210         if (ret != LDB_SUCCESS) {
211                 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
212                                        ldb_dn_get_linearized(msg->dn),
213                                        ldb_errstring(ldb));
214                 talloc_free(tmp_ctx);
215                 return ret;
216         }
217
218         /* add the rIDSetReferences link */
219         msg = ldb_msg_new(tmp_ctx);
220         msg->dn = machine_dn;
221
222         ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
223         if (ret != LDB_SUCCESS) {
224                 talloc_free(tmp_ctx);
225                 return ret;
226         }
227         msg->elements[0].flags = LDB_FLAG_MOD_ADD;
228
229         ret = dsdb_module_modify(module, msg, 0);
230         if (ret != LDB_SUCCESS) {
231                 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
232                                        ldb_dn_get_linearized(msg->dn),
233                                        ldb_errstring(ldb));
234                 talloc_free(tmp_ctx);
235                 return ret;
236         }
237
238         (*dn) = talloc_steal(mem_ctx, rid_set_dn);
239
240         talloc_free(tmp_ctx);
241         return LDB_SUCCESS;
242 }
243
244
245 /*
246   create a RID Set object for this DC
247  */
248 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
249                                        struct ldb_dn **dn)
250 {
251         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
252         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
253         int ret;
254         struct ldb_context *ldb = ldb_module_get_ctx(module);
255
256         /* work out who is the RID Manager */
257         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
258         if (ret != LDB_SUCCESS) {
259                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
260                                        ldb_errstring(ldb));
261                 talloc_free(tmp_ctx);
262                 return ret;
263         }
264
265         /* find the DN of the RID Manager */
266         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
267         if (ret != LDB_SUCCESS) {
268                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
269                                        ldb_errstring(ldb));
270                 talloc_free(tmp_ctx);
271                 return ret;
272         }
273
274         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
275                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
276                 talloc_free(tmp_ctx);
277                 return LDB_ERR_UNWILLING_TO_PERFORM;
278         }
279
280         ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
281         talloc_free(tmp_ctx);
282         return ret;
283 }
284
285 /*
286   refresh a RID Set object for the specified DC
287   also returns the first RID for the new pool
288  */
289 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
290                                          struct ldb_dn *rid_manager_dn,
291                                          struct ldb_dn *ntds_dn, uint64_t *new_pool)
292 {
293         TALLOC_CTX *tmp_ctx = talloc_new(module);
294         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
295         struct ldb_context *ldb = ldb_module_get_ctx(module);
296         int ret;
297
298         /* grab a pool from the RID Manager object */
299         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
300         if (ret != LDB_SUCCESS) {
301                 talloc_free(tmp_ctx);
302                 return ret;
303         }
304
305         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
306         if (!server_dn) {
307                 ldb_module_oom(module);
308                 talloc_free(tmp_ctx);
309                 return LDB_ERR_OPERATIONS_ERROR;
310         }
311
312         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
313         if (ret != LDB_SUCCESS) {
314                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
315                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
316                 talloc_free(tmp_ctx);
317                 return ret;
318         }
319
320         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
321         if (ret != LDB_SUCCESS) {
322                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
323                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
324                 talloc_free(tmp_ctx);
325                 return ret;
326         }
327
328         ret = dsdb_module_set_integer(module, rid_set_dn, "rIDAllocationPool", *new_pool);
329         if (ret != LDB_SUCCESS) {
330                 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
331                                        ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
332                 talloc_free(tmp_ctx);
333                 return ret;
334         }
335
336         talloc_free(tmp_ctx);
337         return LDB_SUCCESS;
338 }
339
340
341 /*
342   make a IRPC call to the drepl task to ask it to get the RID
343   Manager to give us another RID pool.
344
345   This function just sends the message to the drepl task then
346   returns immediately. It should be called well before we
347   completely run out of RIDs
348  */
349 static void ridalloc_poke_rid_manager(struct ldb_module *module)
350 {
351         struct messaging_context *msg;
352         struct server_id *server;
353         struct ldb_context *ldb = ldb_module_get_ctx(module);
354         struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
355         TALLOC_CTX *tmp_ctx = talloc_new(module);
356
357         msg = messaging_client_init(tmp_ctx, lp_messaging_path(tmp_ctx, lp_ctx),
358                                     lp_iconv_convenience(lp_ctx),
359                                     ldb_get_event_context(ldb));
360         if (!msg) {
361                 DEBUG(3,(__location__ ": Failed to create messaging context\n"));
362                 talloc_free(tmp_ctx);
363                 return;
364         }
365
366         server = irpc_servers_byname(msg, msg, "dreplsrv");
367         if (!server) {
368                 /* this means the drepl service is not running */
369                 talloc_free(tmp_ctx);
370                 return;
371         }
372
373         messaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
374
375         /* we don't care if the message got through */
376         talloc_free(tmp_ctx);
377 }
378
379 /*
380   get a new RID pool for ourselves
381   also returns the first rid for the new pool
382  */
383 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
384 {
385         TALLOC_CTX *tmp_ctx = talloc_new(module);
386         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
387         int ret;
388         struct ldb_context *ldb = ldb_module_get_ctx(module);
389
390         /* work out who is the RID Manager */
391         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
392         if (ret != LDB_SUCCESS) {
393                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
394                                        ldb_errstring(ldb));
395                 talloc_free(tmp_ctx);
396                 return ret;
397         }
398
399         /* find the DN of the RID Manager */
400         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
401         if (ret != LDB_SUCCESS) {
402                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
403                                        ldb_errstring(ldb));
404                 talloc_free(tmp_ctx);
405                 return ret;
406         }
407
408         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
409                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
410                 talloc_free(tmp_ctx);
411                 return LDB_ERR_UNWILLING_TO_PERFORM;
412         }
413
414         ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
415         talloc_free(tmp_ctx);
416         return ret;
417 }
418
419
420 /* allocate a RID using our RID Set
421    If we run out of RIDs then allocate a new pool
422    either locally or by contacting the RID Manager
423 */
424 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
425 {
426         struct ldb_context *ldb;
427         static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
428                                               "rIDNextRID" , "rIDUsedPool", NULL };
429         int ret;
430         struct ldb_dn *rid_set_dn;
431         struct ldb_result *res;
432         uint64_t alloc_pool, prev_alloc_pool;
433         uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
434         uint32_t rid_used_pool;
435         int prev_rid;
436         TALLOC_CTX *tmp_ctx = talloc_new(module);
437
438         (*rid) = 0;
439         ldb = ldb_module_get_ctx(module);
440
441         ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
442         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
443                 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
444         }
445         if (ret != LDB_SUCCESS) {
446                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
447                                        ldb_errstring(ldb));
448                 talloc_free(tmp_ctx);
449                 return ret;
450         }
451
452         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
453         if (ret != LDB_SUCCESS) {
454                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
455                                        ldb_dn_get_linearized(rid_set_dn));
456                 talloc_free(tmp_ctx);
457                 return ret;
458         }
459
460         prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
461         alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
462         prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", 0);
463         rid_used_pool = ldb_msg_find_attr_as_int(res->msgs[0], "rIDUsedPool", 0);
464         if (alloc_pool == 0) {
465                 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
466                                        ldb_dn_get_linearized(rid_set_dn));
467                 talloc_free(tmp_ctx);
468                 return LDB_ERR_OPERATIONS_ERROR;
469         }
470
471         prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
472         prev_alloc_pool_hi = prev_alloc_pool >> 32;
473         if (prev_rid >= prev_alloc_pool_hi) {
474                 if (prev_alloc_pool == 0) {
475                         ret = dsdb_module_set_integer(module, rid_set_dn, "rIDPreviousAllocationPool", alloc_pool);
476                 } else {
477                         ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
478                                                                       prev_alloc_pool, alloc_pool);
479                 }
480                 if (ret != LDB_SUCCESS) {
481                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
482                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
483                         talloc_free(tmp_ctx);
484                         return ret;
485                 }
486                 prev_alloc_pool = alloc_pool;
487                 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
488                 prev_alloc_pool_hi = prev_alloc_pool >> 32;
489
490                 /* update the rIDUsedPool attribute */
491                 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDUsedPool", rid_used_pool+1);
492                 if (ret != LDB_SUCCESS) {
493                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDUsedPool on %s - %s",
494                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
495                         talloc_free(tmp_ctx);
496                         return ret;
497                 }
498
499                 (*rid) = prev_alloc_pool_lo;
500         }
501
502         /* see if we are still out of RIDs, and if so then ask
503            the RID Manager to give us more */
504         if (prev_rid >= prev_alloc_pool_hi) {
505                 uint64_t new_pool;
506                 ret = ridalloc_refresh_own_pool(module, &new_pool);
507                 if (ret != LDB_SUCCESS) {
508                         return ret;
509                 }
510                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
511                                                               prev_alloc_pool, new_pool);
512                 if (ret != LDB_SUCCESS) {
513                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
514                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
515                         talloc_free(tmp_ctx);
516                         return ret;
517                 }
518                 prev_alloc_pool = new_pool;
519                 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
520                 prev_alloc_pool_hi = prev_alloc_pool >> 32;
521                 (*rid) = prev_alloc_pool_lo;
522         } else {
523                 /* despite the name, rIDNextRID is the value of the last user
524                  * added by this DC, not the next available RID */
525                 if (*rid == 0) {
526                         (*rid) = prev_rid + 1;
527                 }
528         }
529
530         if (*rid < prev_alloc_pool_lo || *rid > prev_alloc_pool_hi) {
531                 ldb_asprintf_errstring(ldb, __location__ ": Bad rid chosen %u from range %u-%u",
532                                        (unsigned)*rid, (unsigned)prev_alloc_pool_lo,
533                                        (unsigned)prev_alloc_pool_hi);
534                 talloc_free(tmp_ctx);
535                 return LDB_ERR_OPERATIONS_ERROR;
536         }
537
538         /* now modify the RID Set to use up this RID using a
539          * constrained delete/add if possible */
540         if (prev_rid == 0) {
541                 ret = dsdb_module_set_integer(module, rid_set_dn, "rIDNextRID", *rid);
542         } else {
543                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
544         }
545
546         /* if we are half-exhausted then ask the repl task to start
547          * getting another one */
548         if (*rid > (prev_alloc_pool_hi + prev_alloc_pool_lo)/2) {
549                 ridalloc_poke_rid_manager(module);
550         }
551
552         talloc_free(tmp_ctx);
553
554         return ret;
555 }
556
557
558 /*
559   called by DSDB_EXTENDED_ALLOCATE_RID_POOL extended operation in samldb
560  */
561 int ridalloc_allocate_rid_pool_fsmo(struct ldb_module *module, struct dsdb_fsmo_extended_op *exop)
562 {
563         struct ldb_dn *ntds_dn, *server_dn, *machine_dn, *rid_set_dn;
564         struct ldb_dn *rid_manager_dn;
565         TALLOC_CTX *tmp_ctx = talloc_new(module);
566         int ret;
567         struct ldb_context *ldb = ldb_module_get_ctx(module);
568         uint64_t new_pool;
569
570         ret = dsdb_module_dn_by_guid(module, tmp_ctx, &exop->destination_dsa_guid, &ntds_dn);
571         if (ret != LDB_SUCCESS) {
572                 ldb_asprintf_errstring(ldb, __location__ ": Unable to find NTDS object for guid %s - %s\n",
573                                        GUID_string(tmp_ctx, &exop->destination_dsa_guid), ldb_errstring(ldb));
574                 talloc_free(tmp_ctx);
575                 return ret;
576         }
577
578         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
579         if (!server_dn) {
580                 ldb_module_oom(module);
581                 talloc_free(tmp_ctx);
582                 return LDB_ERR_OPERATIONS_ERROR;
583         }
584
585         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
586         if (ret != LDB_SUCCESS) {
587                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find serverReference in %s - %s",
588                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
589                 talloc_free(tmp_ctx);
590                 return ret;
591         }
592
593
594         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
595         if (ret != LDB_SUCCESS) {
596                 ldb_asprintf_errstring(ldb, __location__ ": Failed to find RID Manager object - %s",
597                                        ldb_errstring(ldb));
598                 talloc_free(tmp_ctx);
599                 return ret;
600         }
601
602         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
603         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
604                 ret = ridalloc_create_rid_set_ntds(module, tmp_ctx, rid_manager_dn, ntds_dn, &rid_set_dn);
605                 talloc_free(tmp_ctx);
606                 return ret;
607         }
608
609         if (ret != LDB_SUCCESS) {
610                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
611                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
612                 talloc_free(tmp_ctx);
613                 return ret;
614         }
615
616         if (exop->fsmo_info != 0) {
617                 const char *attrs[] = { "rIDAllocationPool", NULL };
618                 struct ldb_result *res;
619                 uint64_t alloc_pool;
620
621                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
622                 if (ret != LDB_SUCCESS) {
623                         ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
624                                                ldb_dn_get_linearized(rid_set_dn));
625                         talloc_free(tmp_ctx);
626                         return ret;
627                 }
628
629                 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
630                 if (alloc_pool != exop->fsmo_info) {
631                         /* it has already been updated */
632                         DEBUG(2,(__location__ ": rIDAllocationPool fsmo_info mismatch - already changed (0x%llx 0x%llx)\n",
633                                  (unsigned long long)exop->fsmo_info,
634                                  (unsigned long long)alloc_pool));
635                         talloc_free(tmp_ctx);
636                         return LDB_SUCCESS;
637                 }
638         }
639
640         ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, ntds_dn, &new_pool);
641         talloc_free(tmp_ctx);
642         return ret;
643 }