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