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