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