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