s4-dsdb: clarify who is responsible for each attribute
[kamenim/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
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         /* TODO: check if the RID Manager adds these fields, or if the
200            client DC does it */
201         ret = ldb_msg_add_fmt(msg, "rIDPreviousAllocationPool", "%llu", (unsigned long long)dc_pool);
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", "%lu", (unsigned long)(dc_pool & 0xFFFFFFFF));
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_message *msg;
304         struct ldb_context *ldb = ldb_module_get_ctx(module);
305         int ret;
306
307         /* grab a pool from the RID Manager object */
308         ret = ridalloc_rid_manager_allocate(module, rid_manager_dn, new_pool);
309         if (ret != LDB_SUCCESS) {
310                 talloc_free(tmp_ctx);
311                 return ret;
312         }
313
314         server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
315         if (!server_dn) {
316                 ldb_module_oom(module);
317                 return LDB_ERR_OPERATIONS_ERROR;
318         }
319
320         ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
321         if (ret != LDB_SUCCESS) {
322                 ldb_asprintf_errstring(ldb, "Failed to find serverReference in %s - %s",
323                                        ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
324                 talloc_free(tmp_ctx);
325                 return ret;
326         }
327
328         ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
329         if (ret != LDB_SUCCESS) {
330                 ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
331                                        ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
332                 talloc_free(tmp_ctx);
333                 return ret;
334         }
335
336         msg = ldb_msg_new(tmp_ctx);
337         msg->dn = rid_set_dn;
338
339         ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)*new_pool);
340         if (ret != LDB_SUCCESS) {
341                 talloc_free(tmp_ctx);
342                 return ret;
343         }
344         msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
345
346         ret = dsdb_module_modify(module, msg, 0);
347         if (ret != LDB_SUCCESS) {
348                 ldb_asprintf_errstring(ldb, "Failed to modify RID Set object %s - %s",
349                                        ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
350                 talloc_free(tmp_ctx);
351                 return ret;
352         }
353
354         talloc_free(tmp_ctx);
355         return LDB_SUCCESS;
356 }
357
358
359
360 /*
361   get a new RID pool for ourselves
362   also returns the first rid for the new pool
363  */
364 static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_pool)
365 {
366         TALLOC_CTX *tmp_ctx = talloc_new(module);
367         struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
368         int ret;
369         struct ldb_context *ldb = ldb_module_get_ctx(module);
370
371         /* work out who is the RID Manager */
372         ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
373         if (ret != LDB_SUCCESS) {
374                 ldb_asprintf_errstring(ldb, "Failed to find RID Manager object - %s",
375                                        ldb_errstring(ldb));
376                 talloc_free(tmp_ctx);
377                 return ret;
378         }
379
380         /* find the DN of the RID Manager */
381         ret = dsdb_module_reference_dn(module, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
382         if (ret != LDB_SUCCESS) {
383                 ldb_asprintf_errstring(ldb, "Failed to find fSMORoleOwner in RID Manager object - %s",
384                                        ldb_errstring(ldb));
385                 talloc_free(tmp_ctx);
386                 return ret;
387         }
388
389         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
390                 ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
391                 talloc_free(tmp_ctx);
392                 return LDB_ERR_UNWILLING_TO_PERFORM;
393         }
394
395         ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, fsmo_role_dn, new_pool);
396         talloc_free(tmp_ctx);
397         return ret;
398 }
399
400
401 /* allocate a RID using our RID Set
402    If we run out of RIDs then allocate a new pool
403    either locally or by contacting the RID Manager
404 */
405 int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
406 {
407         struct ldb_context *ldb;
408         static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
409                                               "rIDNextRID" , NULL };
410         int ret;
411         struct ldb_dn *rid_set_dn;
412         struct ldb_result *res;
413         uint64_t alloc_pool, prev_alloc_pool;
414         uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
415         int prev_rid;
416         TALLOC_CTX *tmp_ctx = talloc_new(module);
417
418         ldb = ldb_module_get_ctx(module);
419
420         ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
421         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
422                 ret = ridalloc_create_own_rid_set(module, tmp_ctx, &rid_set_dn);
423         }
424         if (ret != LDB_SUCCESS) {
425                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set DN - %s",
426                                        ldb_errstring(ldb));
427                 talloc_free(tmp_ctx);
428                 return ret;
429         }
430
431         ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
432         if (ret != LDB_SUCCESS) {
433                 ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
434                                        ldb_dn_get_linearized(rid_set_dn));
435                 talloc_free(tmp_ctx);
436                 return ret;
437         }
438
439         prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
440         alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
441         prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", -1);
442         if (prev_rid == -1 || alloc_pool == 0) {
443                 ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
444                                        ldb_dn_get_linearized(rid_set_dn));
445                 talloc_free(tmp_ctx);
446                 return LDB_ERR_OPERATIONS_ERROR;
447         }
448
449         prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
450         prev_alloc_pool_hi = prev_alloc_pool >> 32;
451         if (prev_rid >= prev_alloc_pool_hi) {
452                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
453                                                               prev_alloc_pool, alloc_pool);
454                 if (ret != LDB_SUCCESS) {
455                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
456                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
457                         talloc_free(tmp_ctx);
458                         return ret;
459                 }
460                 prev_alloc_pool = alloc_pool;
461                 prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
462                 prev_alloc_pool_hi = prev_alloc_pool >> 32;
463         }
464         /* see if we are still out of RIDs, and if so then ask
465            the RID Manager to give us more */
466         if (prev_rid >= prev_alloc_pool_hi) {
467                 uint64_t new_pool;
468                 ret = ridalloc_refresh_own_pool(module, &new_pool);
469                 if (ret != LDB_SUCCESS) {
470                         return ret;
471                 }
472                 ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
473                                                               prev_alloc_pool, new_pool);
474                 if (ret != LDB_SUCCESS) {
475                         ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDPreviousAllocationPool on %s - %s",
476                                                ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
477                         talloc_free(tmp_ctx);
478                         return ret;
479                 }
480                 (*rid) = (new_pool & 0xFFFFFFFF);
481         } else {
482                 /* despite the name, rIDNextRID is the value of the last user
483                  * added by this DC, not the next available RID */
484                 (*rid) = prev_rid + 1;
485         }
486
487         /* now modify the RID Set to use up this RID using a
488          * constrained delete/add */
489         ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
490         talloc_free(tmp_ctx);
491
492         return ret;
493 }