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