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