s4:dsdb:ridalloc: use dsdb_module_constrainted_update_uint64() to update rIDAvailablePool
[kamenim/samba.git] / source4 / dsdb / samdb / ldb_modules / ridalloc.c
index 02737dd88be11a5d9c1b0422346b80d6fe349dc2..d81717259b8fb72b8da6ddde925a50d34b733e7a 100644 (file)
@@ -31,6 +31,9 @@
 #include "ldb_module.h"
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/samdb/ldb_modules/util.h"
+#include "lib/messaging/irpc.h"
+#include "param/param.h"
+#include "librpc/gen_ndr/ndr_misc.h"
 
 /*
   Note: the RID allocation attributes in AD are very badly named. Here
  */
 
 
+/*
+  make a IRPC call to the drepl task to ask it to get the RID
+  Manager to give us another RID pool.
+
+  This function just sends the message to the drepl task then
+  returns immediately. It should be called well before we
+  completely run out of RIDs
+ */
+static void ridalloc_poke_rid_manager(struct ldb_module *module)
+{
+       struct messaging_context *msg;
+       struct server_id *server;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct loadparm_context *lp_ctx =
+               (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
+
+       msg = messaging_client_init(tmp_ctx, lp_messaging_path(tmp_ctx, lp_ctx),
+                                   ldb_get_event_context(ldb));
+       if (!msg) {
+               DEBUG(3,(__location__ ": Failed to create messaging context\n"));
+               talloc_free(tmp_ctx);
+               return;
+       }
+
+       server = irpc_servers_byname(msg, msg, "dreplsrv");
+       if (!server) {
+               /* this means the drepl service is not running */
+               talloc_free(tmp_ctx);
+               return;
+       }
+
+       messaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
+
+       /* we don't care if the message got through */
+       talloc_free(tmp_ctx);
+}
+
+
 /*
   allocate a new range of RIDs in the RID Manager object
  */
@@ -66,7 +108,8 @@ static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_d
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        const unsigned alloc_size = 500;
 
-       ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn, attrs, 0);
+       ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_manager_dn,
+                                   attrs, DSDB_FLAG_NEXT_MODULE);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "Failed to find rIDAvailablePool in %s - %s",
                                       ldb_dn_get_linearized(rid_manager_dn), ldb_errstring(ldb));
@@ -96,8 +139,8 @@ static int ridalloc_rid_manager_allocate(struct ldb_module *module, struct ldb_d
        /* and new rIDAvailablePool value */
        new_rid_pool = rid_pool_lo | (((uint64_t)rid_pool_hi)<<32);
 
-       ret = dsdb_module_constrainted_update_integer(module, rid_manager_dn, "rIDAvailablePool",
-                                                     rid_pool, new_rid_pool);
+       ret = dsdb_module_constrainted_update_uint64(module, rid_manager_dn, "rIDAvailablePool",
+                                                    &rid_pool, &new_rid_pool);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "Failed to update rIDAvailablePool - %s",
                                       ldb_errstring(ldb));
@@ -137,8 +180,8 @@ static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *m
 
        server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
        if (!server_dn) {
-               ldb_module_oom(module);
-               return LDB_ERR_OPERATIONS_ERROR;
+               talloc_free(tmp_ctx);
+               return ldb_module_oom(module);
        }
 
        ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
@@ -151,13 +194,13 @@ static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *m
 
        rid_set_dn = ldb_dn_copy(tmp_ctx, machine_dn);
        if (rid_set_dn == NULL) {
-               ldb_module_oom(module);
-               return LDB_ERR_OPERATIONS_ERROR;
+               talloc_free(tmp_ctx);
+               return ldb_module_oom(module);
        }
 
        if (! ldb_dn_add_child_fmt(rid_set_dn, "CN=RID Set")) {
-               ldb_module_oom(module);
-               return LDB_ERR_OPERATIONS_ERROR;
+               talloc_free(tmp_ctx);
+               return ldb_module_oom(module);
        }
 
        /* grab a pool from the RID Manager object */
@@ -171,26 +214,11 @@ static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *m
        msg = ldb_msg_new(tmp_ctx);
        msg->dn = rid_set_dn;
 
-       ret = ldb_msg_add_string(msg, "objectClass", "top");
-       if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
-       }
        ret = ldb_msg_add_string(msg, "objectClass", "rIDSet");
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
-       ret = ldb_msg_add_string(msg, "cn", "RID Set");
-       if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
-       }
-       ret = ldb_msg_add_string(msg, "name", "RID Set");
-       if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
-       }
        ret = ldb_msg_add_fmt(msg, "rIDAllocationPool", "%llu", (unsigned long long)dc_pool);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -214,7 +242,10 @@ static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *m
                return ret;
        }
 
-       ret = dsdb_module_add(module, msg, 0);
+       /* we need this to go all the way to the top of the module
+        * stack, as we need all the extra attributes added (including
+        * complex ones like ntsecuritydescriptor) */
+       ret = dsdb_module_add(module, msg, DSDB_FLAG_TOP_MODULE | DSDB_MODIFY_RELAX);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "Failed to add RID Set %s - %s",
                                       ldb_dn_get_linearized(msg->dn),
@@ -234,7 +265,7 @@ static int ridalloc_create_rid_set_ntds(struct ldb_module *module, TALLOC_CTX *m
        }
        msg->elements[0].flags = LDB_FLAG_MOD_ADD;
 
-       ret = dsdb_module_modify(module, msg, 0);
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "Failed to add rIDSetReferences to %s - %s",
                                       ldb_dn_get_linearized(msg->dn),
@@ -280,7 +311,8 @@ static int ridalloc_create_own_rid_set(struct ldb_module *module, TALLOC_CTX *me
        }
 
        if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
-               ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
+               ridalloc_poke_rid_manager(module);
+               ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
                talloc_free(tmp_ctx);
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
@@ -312,8 +344,8 @@ static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
 
        server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
        if (!server_dn) {
-               ldb_module_oom(module);
-               return LDB_ERR_OPERATIONS_ERROR;
+               talloc_free(tmp_ctx);
+               return ldb_module_oom(module);
        }
 
        ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
@@ -345,7 +377,6 @@ static int ridalloc_refresh_rid_set_ntds(struct ldb_module *module,
 }
 
 
-
 /*
   get a new RID pool for ourselves
   also returns the first rid for the new pool
@@ -376,7 +407,8 @@ static int ridalloc_refresh_own_pool(struct ldb_module *module, uint64_t *new_po
        }
 
        if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
-               ldb_asprintf_errstring(ldb, "Remote RID Set allocation not implemented");
+               ridalloc_poke_rid_manager(module);
+               ldb_asprintf_errstring(ldb, "Remote RID Set allocation needs refresh");
                talloc_free(tmp_ctx);
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
@@ -395,15 +427,17 @@ int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
 {
        struct ldb_context *ldb;
        static const char * const attrs[] = { "rIDAllocationPool", "rIDPreviousAllocationPool",
-                                             "rIDNextRID" , NULL };
+                                             "rIDNextRID" , "rIDUsedPool", NULL };
        int ret;
        struct ldb_dn *rid_set_dn;
        struct ldb_result *res;
        uint64_t alloc_pool, prev_alloc_pool;
        uint32_t prev_alloc_pool_lo, prev_alloc_pool_hi;
+       uint32_t rid_used_pool;
        int prev_rid;
        TALLOC_CTX *tmp_ctx = talloc_new(module);
 
+       (*rid) = 0;
        ldb = ldb_module_get_ctx(module);
 
        ret = samdb_rid_set_dn(ldb, tmp_ctx, &rid_set_dn);
@@ -417,7 +451,8 @@ int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
                return ret;
        }
 
-       ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn, attrs, 0);
+       ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
+                                   attrs, DSDB_FLAG_NEXT_MODULE);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
                                       ldb_dn_get_linearized(rid_set_dn));
@@ -428,6 +463,7 @@ int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
        prev_alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
        alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
        prev_rid = ldb_msg_find_attr_as_int(res->msgs[0], "rIDNextRID", 0);
+       rid_used_pool = ldb_msg_find_attr_as_int(res->msgs[0], "rIDUsedPool", 0);
        if (alloc_pool == 0) {
                ldb_asprintf_errstring(ldb, __location__ ": Bad RID Set %s",
                                       ldb_dn_get_linearized(rid_set_dn));
@@ -453,13 +489,31 @@ int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
                prev_alloc_pool = alloc_pool;
                prev_alloc_pool_lo = prev_alloc_pool & 0xFFFFFFFF;
                prev_alloc_pool_hi = prev_alloc_pool >> 32;
+
+               /*
+                * update the rIDUsedPool attribute
+                *
+                * Note: w2k8r2 doesn't update this attribute,
+                *       at least if it's itself the rid master.
+                */
+               ret = dsdb_module_set_integer(module, rid_set_dn, "rIDUsedPool", rid_used_pool+1);
+               if (ret != LDB_SUCCESS) {
+                       ldb_asprintf_errstring(ldb, __location__ ": Failed to update rIDUsedPool on %s - %s",
+                                              ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb));
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+
+               (*rid) = prev_alloc_pool_lo;
        }
+
        /* see if we are still out of RIDs, and if so then ask
           the RID Manager to give us more */
        if (prev_rid >= prev_alloc_pool_hi) {
                uint64_t new_pool;
                ret = ridalloc_refresh_own_pool(module, &new_pool);
                if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
                        return ret;
                }
                ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDPreviousAllocationPool",
@@ -477,9 +531,7 @@ int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
        } else {
                /* despite the name, rIDNextRID is the value of the last user
                 * added by this DC, not the next available RID */
-               if (prev_rid == 0) {
-                       (*rid) = prev_alloc_pool_lo;
-               } else {
+               if (*rid == 0) {
                        (*rid) = prev_rid + 1;
                }
        }
@@ -499,7 +551,102 @@ int ridalloc_allocate_rid(struct ldb_module *module, uint32_t *rid)
        } else {
                ret = dsdb_module_constrainted_update_integer(module, rid_set_dn, "rIDNextRID", prev_rid, *rid);
        }
+
+       /* if we are half-exhausted then ask the repl task to start
+        * getting another one */
+       if (*rid > (prev_alloc_pool_hi + prev_alloc_pool_lo)/2) {
+               ridalloc_poke_rid_manager(module);
+       }
+
        talloc_free(tmp_ctx);
 
        return ret;
 }
+
+
+/*
+  called by DSDB_EXTENDED_ALLOCATE_RID_POOL extended operation in samldb
+ */
+int ridalloc_allocate_rid_pool_fsmo(struct ldb_module *module, struct dsdb_fsmo_extended_op *exop)
+{
+       struct ldb_dn *ntds_dn, *server_dn, *machine_dn, *rid_set_dn;
+       struct ldb_dn *rid_manager_dn;
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
+       int ret;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       uint64_t new_pool;
+
+       ret = dsdb_module_dn_by_guid(module, tmp_ctx, &exop->destination_dsa_guid, &ntds_dn);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, __location__ ": Unable to find NTDS object for guid %s - %s\n",
+                                      GUID_string(tmp_ctx, &exop->destination_dsa_guid), ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       server_dn = ldb_dn_get_parent(tmp_ctx, ntds_dn);
+       if (!server_dn) {
+               talloc_free(tmp_ctx);
+               return ldb_module_oom(module);
+       }
+
+       ret = dsdb_module_reference_dn(module, tmp_ctx, server_dn, "serverReference", &machine_dn);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, __location__ ": Failed to find serverReference in %s - %s",
+                                      ldb_dn_get_linearized(server_dn), ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+
+       ret = dsdb_module_rid_manager_dn(module, tmp_ctx, &rid_manager_dn);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, __location__ ": Failed to find RID Manager object - %s",
+                                      ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = dsdb_module_reference_dn(module, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
+       if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+               ret = ridalloc_create_rid_set_ntds(module, tmp_ctx, rid_manager_dn, ntds_dn, &rid_set_dn);
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "Failed to find rIDSetReferences in %s - %s",
+                                      ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       if (exop->fsmo_info != 0) {
+               const char *attrs[] = { "rIDAllocationPool", NULL };
+               struct ldb_result *res;
+               uint64_t alloc_pool;
+
+               ret = dsdb_module_search_dn(module, tmp_ctx, &res, rid_set_dn,
+                                           attrs, DSDB_FLAG_NEXT_MODULE);
+               if (ret != LDB_SUCCESS) {
+                       ldb_asprintf_errstring(ldb, __location__ ": No RID Set %s",
+                                              ldb_dn_get_linearized(rid_set_dn));
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+
+               alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
+               if (alloc_pool != exop->fsmo_info) {
+                       /* it has already been updated */
+                       DEBUG(2,(__location__ ": rIDAllocationPool fsmo_info mismatch - already changed (0x%llx 0x%llx)\n",
+                                (unsigned long long)exop->fsmo_info,
+                                (unsigned long long)alloc_pool));
+                       talloc_free(tmp_ctx);
+                       return LDB_SUCCESS;
+               }
+       }
+
+       ret = ridalloc_refresh_rid_set_ntds(module, rid_manager_dn, ntds_dn, &new_pool);
+       talloc_free(tmp_ctx);
+       return ret;
+}