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