s4-dsdb: fixed usage of rIDAllocationPool and rIDPreviousAllocationPool
[metze/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / ridalloc.c
1 /*
2    RID allocation helper functions
3
4    Copyright (C) Andrew Bartlett 2010
5    Copyright (C) Andrew Tridgell 2010
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22  *  Name: ldb
23  *
24  *  Component: RID allocation logic
25  *
26  *  Description: manage RID Set and RID Manager objects
27  *
28  */
29
30 #include "includes.h"
31 #include "ldb_module.h"
32 #include "dsdb/samdb/samdb.h"
33 #include "dsdb/samdb/ldb_modules/util.h"
34
35 /*
36   Note: the RID allocation attributes in AD are very badly named. Here
37   is what we think they really do:
38
39   in RID Set object:
40     - rIDPreviousAllocationPool: the pool which a DC is currently
41       pulling RIDs from
42     - rIDAllocationPool: the pool that the DC will switch to next,
43       when rIDPreviousAllocationPool is exhausted
44     - rIDNextRID: the last RID allocated by this DC
45
46   in RID Manager object:
47     - rIDAvailablePool: the pool where the RID Manager gets new rID
48       pools from when it gets a EXOP_RID_ALLOC getncchanges call (or
49       locally when the DC is the RID Manager)
50  */
51
52
53 /*
54   allocate a new range of RIDs in the RID Manager object
55  */
56 static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_dn *rid_manager_dn, uint64_t *new_pool)
57 {
58         int ret;
59         TALLOC_CTX *tmp_ctx = talloc_new(module);
60         const char *attrs[] = { "rIDAvailablePool", NULL };
61         uint64_t rid_pool, new_rid_pool, dc_pool;
62         uint32_t rid_pool_lo, rid_pool_hi;
63         struct ldb_result *res;
64         struct ldb_context *ldb = ldb_module_get_ctx(module);
65         const unsigned alloc_size = 500;
66
67         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn, attrs, 0);
68         if (ret != LDB_SUCCESS) {
69                 ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
70                                        ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
71                 talloc_free(tmp_ctx);
72                 return ret;
73         }
74
75         rid_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAvailablePool", 0);
76         rid_pool_lo = rid_pool & 0xFFFFFFFF;
77         rid_pool_hi = rid_pool >> 32;
78         if (rid_pool_lo >= rid_pool_hi) {
79                 ldb_asprintf_errstring(ldb, "Out of RIDs in RID Manager - rIDAvailablePool is %u-%u",
80                                        rid_pool_lo, rid_pool_hi);
81                 talloc_free(tmp_ctx);
82                 return ret;
83         }
84
85         /* lower part of new pool is the low part of the rIDAvailablePool */
86         dc_pool = rid_pool_lo;
87
88         /* allocate 500 RIDs to this DC */
89         rid_pool_lo = MIN(rid_pool_hi, rid_pool_lo + alloc_size);
90
91         /* work out upper part of new pool */
92         dc_pool |= (((uint64_t)rid_pool_lo-1)<<32);
93
94         /* and new rIDAvailablePool value */
95         new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
96
97         ret = dsdb_module_constrainted_update_integer(module, rid_manager_dn, "rIDAvailablePool",
98                                                       rid_pool, new_rid_pool);
99         if (ret != LDB_SUCCESS) {
100                 ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
101                                        ldb_errstring(ldb));
102                 talloc_free(tmp_ctx);
103                 return ret;
104         }
105
106         (*new_pool) = dc_pool;
107         talloc_free(tmp_ctx);
108         return LDB_SUCCESS;
109 }
110
111 /*
112   create a RID Set object for the specified DC
113  */
114 static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *mem_ctx,
115                                         struct ldb_dn *rid_manager_dn,
116                                         struct ldb_dn *ntds_dn, struct ldb_dn **dn)
117 {
118         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
119         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
120         int ret;
121         uint64_t dc_pool;
122         struct ldb_message *msg;
123         struct ldb_context *ldb = ldb_module_get_ctx(module);
124
125         /*
126           steps:
127
128           find the machine object for the DC
129           construct the RID Set DN
130           load rIDAvailablePool to find next available set
131           modify RID Manager object to update rIDAvailablePool
132           add the RID Set object
133           link to the RID Set object in machine object
134          */
135
136         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
137         if (!server_dn) {
138                 ldb_module_oom(module);
139                 return LDB_ERR_OPERATIONS_ERROR;
140         }
141
142         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
143         if (ret != LDB_SUCCESS) {
144                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
145                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
146                 talloc_free(tmp_ctx);
147                 return ret;
148         }
149
150         rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
151         if (rid_set_dn == NULL) {
152                 ldb_module_oom(module);
153                 return LDB_ERR_OPERATIONS_ERROR;
154         }
155
156         if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
157                 ldb_module_oom(module);
158                 return LDB_ERR_OPERATIONS_ERROR;
159         }
160
161         /* grab a pool from the RID Manager object */
162         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, &dc_pool);
163         if (ret != LDB_SUCCESS) {
164                 talloc_free(tmp_ctx);
165                 return ret;
166         }
167
168         /* create the RID Set object */
169         msg = ldb_msg_new(tmp_ctx);
170         msg->dn = rid_set_dn;
171
172         ret = ldb_msg_add_string(msg, "objectClass", "top");
173         if (ret != LDB_SUCCESS) {
174                 talloc_free(tmp_ctx);
175                 return ret;
176         }
177         ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
178         if (ret != LDB_SUCCESS) {
179                 talloc_free(tmp_ctx);
180                 return ret;
181         }
182         ret = ldb_msg_add_string(msg, "cn", "RID Set");
183         if (ret != LDB_SUCCESS) {
184                 talloc_free(tmp_ctx);
185                 return ret;
186         }
187         ret = ldb_msg_add_string(msg, "name", "RID Set");
188         if (ret != LDB_SUCCESS) {
189                 talloc_free(tmp_ctx);
190                 return ret;
191         }
192         ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
193         if (ret != LDB_SUCCESS) {
194                 talloc_free(tmp_ctx);
195                 return ret;
196         }
197         /* TODO: check if the RID Manager adds these fields, or if the
198            client DC does it */
199         ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "%llu", (unsigned long long)dc_pool);
200         if (ret != LDB_SUCCESS) {
201                 talloc_free(tmp_ctx);
202                 return ret;
203         }
204         ret = ldb_msg_add_fmt(msg, "rIDUsedPool", "0");
205         if (ret != LDB_SUCCESS) {
206                 talloc_free(tmp_ctx);
207                 return ret;
208         }
209         ret = ldb_msg_add_fmt(msg, "rIDNextRID", "%lu", (unsigned long)(dc_pool & 0xFFFFFFFF));
210         if (ret != LDB_SUCCESS) {
211                 talloc_free(tmp_ctx);
212                 return ret;
213         }
214
215         ret = dsdb_module_add(module, msg, 0);
216         if (ret != LDB_SUCCESS) {
217                 ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
218                                        ldb_dn_get_linearized(msg->dn),
219                                        ldb_errstring(ldb));
220                 talloc_free(tmp_ctx);
221                 return ret;
222         }
223
224         /* add the rIDSetReferences link */
225         msg = ldb_msg_new(tmp_ctx);
226         msg->dn = machine_dn;
227
228         ret = ldb_msg_add_string(msg, "rIDSetReferences", ldb_dn_get_linearized(rid_set_dn));
229         if (ret != LDB_SUCCESS) {
230                 talloc_free(tmp_ctx);
231                 return ret;
232         }
233         msg->elements[0].flags = LDB_FLAG_MOD_ADD;
234
235         ret = dsdb_module_modify(module, msg, 0);
236         if (ret != LDB_SUCCESS) {
237                 ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
238                                        ldb_dn_get_linearized(msg->dn),
239                                        ldb_errstring(ldb));
240                 talloc_free(tmp_ctx);
241                 return ret;
242         }
243
244         (*dn) = talloc_steal(mem_ctx, rid_set_dn);
245
246         talloc_free(tmp_ctx);
247         return LDB_SUCCESS;
248 }
249
250
251 /*
252   create a RID Set object for this DC
253  */
254 static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *mem_ctx,
255                                        struct ldb_dn **dn)
256 {
257         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
258         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
259         int ret;
260         struct ldb_context *ldb = ldb_module_get_ctx(module);
261
262         /* work out who is the RID Manager */
263         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
264         if (ret != LDB_SUCCESS) {
265                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
266                                        ldb_errstring(ldb));
267                 talloc_free(tmp_ctx);
268                 return ret;
269         }
270
271         /* find the DN of the RID Manager */
272         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
273         if (ret != LDB_SUCCESS) {
274                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
275                                        ldb_errstring(ldb));
276                 talloc_free(tmp_ctx);
277                 return ret;
278         }
279
280         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
281                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
282                 talloc_free(tmp_ctx);
283                 return LDB_ERR_UNWILLING_TO_PERFORM;
284         }
285
286         ret = ridalloc_create_rid_set_ntds(module, mem_ctx, rid_manager_dn, fsmo_role_dn, dn);
287         talloc_free(tmp_ctx);
288         return ret;
289 }
290
291 /*
292   refresh a RID Set object for the specified DC
293   also returns the first RID for the new pool
294  */
295 static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
296                                          struct ldb_dn *rid_manager_dn,
297                                          struct ldb_dn *ntds_dn, uint64_t *new_pool)
298 {
299         TALLOC_CTX *tmp_ctx = talloc_new(module);
300         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
301         struct ldb_message *msg;
302         struct ldb_context *ldb = ldb_module_get_ctx(module);
303         int ret;
304
305         /* grab a pool from the RID Manager object */
306         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
307         if (ret != LDB_SUCCESS) {
308                 talloc_free(tmp_ctx);
309                 return ret;
310         }
311
312         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
313         if (!server_dn) {
314                 ldb_module_oom(module);
315                 return LDB_ERR_OPERATIONS_ERROR;
316         }
317
318         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
319         if (ret != LDB_SUCCESS) {
320                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
321                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
322                 talloc_free(tmp_ctx);
323                 return ret;
324         }
325
326         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
327         if (ret != LDB_SUCCESS) {
328                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
329                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
330                 talloc_free(tmp_ctx);
331                 return ret;
332         }
333
334         msg = ldb_msg_new(tmp_ctx);
335         msg->dn = rid_set_dn;
336
337         ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)*new_pool);
338         if (ret != LDB_SUCCESS) {
339                 talloc_free(tmp_ctx);
340                 return ret;
341         }
342         msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
343
344         ret = dsdb_module_modify(module, msg, 0);
345         if (ret != LDB_SUCCESS) {
346                 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
347                                        ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
348                 talloc_free(tmp_ctx);
349                 return ret;
350         }
351
352         talloc_free(tmp_ctx);
353         return LDB_SUCCESS;
354 }
355
356
357
358 /*
359   get a new RID pool for ourselves
360   also returns the first rid for the new pool
361  */
362 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
363 {
364         TALLOC_CTX *tmp_ctx = talloc_new(module);
365         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
366         int ret;
367         struct ldb_context *ldb = ldb_module_get_ctx(module);
368
369         /* work out who is the RID Manager */
370         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
371         if (ret != LDB_SUCCESS) {
372                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
373                                        ldb_errstring(ldb));
374                 talloc_free(tmp_ctx);
375                 return ret;
376         }
377
378         /* find the DN of the RID Manager */
379         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
380         if (ret != LDB_SUCCESS) {
381                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
382                                        ldb_errstring(ldb));
383                 talloc_free(tmp_ctx);
384                 return ret;
385         }
386
387         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
388                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
389                 talloc_free(tmp_ctx);
390                 return LDB_ERR_UNWILLING_TO_PERFORM;
391         }
392
393         ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
394         talloc_free(tmp_ctx);
395         return ret;
396 }
397
398
399 /* allocate a RID using our RID Set
400    If we run out of RIDs then allocate a new pool
401    either locally or by contacting the RID Manager
402 */
403 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
404 {
405         struct ldb_context *ldb;
406         static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
407                                               "rIDNextRID" , NULL };
408         int ret;
409         struct ldb_dn *rid_set_dn;
410         struct ldb_result *res;
411         uint64_t alloc_pool, prev_alloc_pool;
412         uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
413         int prev_rid;
414         TALLOC_CTX *tmp_ctx = talloc_new(module);
415
416         ldb = ldb_module_get_ctx(module);
417
418         ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
419         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
420                 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
421         }
422         if (ret != LDB_SUCCESS) {
423                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
424                                        ldb_errstring(ldb));
425                 talloc_free(tmp_ctx);
426                 return ret;
427         }
428
429         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
430         if (ret != LDB_SUCCESS) {
431                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
432                                        ldb_dn_get_linearized(rid_set_dn));
433                 talloc_free(tmp_ctx);
434                 return ret;
435         }
436
437         prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
438         alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
439         prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", -1);
440         if (prev_rid == -1 || alloc_pool == 0) {
441                 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
442                                        ldb_dn_get_linearized(rid_set_dn));
443                 talloc_free(tmp_ctx);
444                 return LDB_ERR_OPERATIONS_ERROR;
445         }
446
447         prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
448         prev_alloc_pool_hi = prev_alloc_pool >> 32;
449         if (prev_rid >= prev_alloc_pool_hi) {
450                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
451                                                               prev_alloc_pool, alloc_pool);
452                 if (ret != LDB_SUCCESS) {
453                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
454                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
455                         talloc_free(tmp_ctx);
456                         return ret;
457                 }
458                 prev_alloc_pool = alloc_pool;
459                 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
460                 prev_alloc_pool_hi = prev_alloc_pool >> 32;
461         }
462         /* see if we are still out of RIDs, and if so then ask
463            the RID Manager to give us more */
464         if (prev_rid >= prev_alloc_pool_hi) {
465                 uint64_t new_pool;
466                 ret = ridalloc_refresh_own_pool(module, &new_pool);
467                 if (ret != LDB_SUCCESS) {
468                         return ret;
469                 }
470                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
471                                                               prev_alloc_pool, new_pool);
472                 if (ret != LDB_SUCCESS) {
473                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
474                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
475                         talloc_free(tmp_ctx);
476                         return ret;
477                 }
478                 (*rid) = (new_pool & 0xFFFFFFFF);
479         } else {
480                 /* despite the name, rIDNextRID is the value of the last user
481                  * added by this DC, not the next available RID */
482                 (*rid) = prev_rid + 1;
483         }
484
485         /* now modify the RID Set to use up this RID using a
486          * constrained delete/add */
487         ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
488         talloc_free(tmp_ctx);
489
490         return ret;
491 }