s4-dsdb: In rootdse module, catch rename on any DN that has an impact on our NTDS...
[mat/samba.git] / source4 / dsdb / samdb / ldb_modules / rootdse.c
index 6c2a1e630d6e695d523080ffd41c131de024c3f6..5fc89f42f1d1e55dd21b6f7f258b695d3751f0d1 100644 (file)
@@ -21,8 +21,8 @@
 */
 
 #include "includes.h"
-#include "lib/ldb/include/ldb.h"
-#include "lib/ldb/include/ldb_module.h"
+#include <ldb.h>
+#include <ldb_module.h>
 #include "system/time.h"
 #include "dsdb/samdb/samdb.h"
 #include "version.h"
@@ -39,6 +39,7 @@ struct private_data {
        char **controls;
        unsigned int num_partitions;
        struct ldb_dn **partitions;
+       bool block_anonymous;
 };
 
 /*
@@ -102,7 +103,7 @@ static int expand_dn_in_message(struct ldb_module *module, struct ldb_message *m
        }
 
        dn = ldb_dn_new(tmp_ctx, ldb, dn_string);
-       if (!ldb_dn_validate(dn)) {
+       if (dn == NULL) {
                talloc_free(tmp_ctx);
                return ldb_operr(ldb);
        }
@@ -179,6 +180,7 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
                "rootDomainNamingContext",
                "schemaNamingContext",
                "serverName",
+               "namingContexts",
                NULL
        };
 
@@ -200,10 +202,18 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
        }
 
        if (do_attribute(attrs, "dnsHostName")) {
-               if (ldb_msg_add_string(msg, "dnsHostName",
-                       samdb_search_string(ldb, msg, samdb_server_dn(ldb, msg),
-                                           "dNSHostName", NULL)) != LDB_SUCCESS) {
-                       goto failed;
+               struct ldb_result *res;
+               int ret;
+               const char *dns_attrs[] = { "dNSHostName", NULL };
+               ret = dsdb_module_search_dn(module, msg, &res, samdb_server_dn(ldb, msg),
+                                           dns_attrs, DSDB_FLAG_NEXT_MODULE, req);
+               if (ret == LDB_SUCCESS) {
+                       const char *hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
+                       if (hostname != NULL) {
+                               if (ldb_msg_add_string(msg, "dNSHostName", hostname)) {
+                                       goto failed;
+                               }
+                       }
                }
        }
 
@@ -213,11 +223,10 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
                                          struct loadparm_context);
                char *ldap_service_name, *hostname;
 
-               hostname = talloc_strdup(msg, lpcfg_netbios_name(lp_ctx));
+               hostname = strlower_talloc(msg, lpcfg_netbios_name(lp_ctx));
                if (hostname == NULL) {
                        goto failed;
                }
-               strlower_m(hostname);
 
                ldap_service_name = talloc_asprintf(msg, "%s:%s$@%s",
                                                    samdb_forest_name(ldb, msg),
@@ -284,8 +293,9 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
                uint64_t seq_num;
                int ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &seq_num);
                if (ret == LDB_SUCCESS) {
-                       if (ldb_msg_add_fmt(msg, "highestCommittedUSN",
-                                           "%llu", (unsigned long long)seq_num) != LDB_SUCCESS) {
+                       if (samdb_msg_add_uint64(ldb, msg, msg,
+                                                "highestCommittedUSN",
+                                                seq_num) != LDB_SUCCESS) {
                                goto failed;
                        }
                }
@@ -299,8 +309,8 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
                        n++;
                }
 
-               if (ldb_msg_add_fmt(msg, "dsSchemaAttrCount",
-                                   "%u", n) != LDB_SUCCESS) {
+               if (samdb_msg_add_uint(ldb, msg, msg, "dsSchemaAttrCount",
+                                      n) != LDB_SUCCESS) {
                        goto failed;
                }
        }
@@ -313,15 +323,15 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
                        n++;
                }
 
-               if (ldb_msg_add_fmt(msg, "dsSchemaClassCount",
-                                   "%u", n) != LDB_SUCCESS) {
+               if (samdb_msg_add_uint(ldb, msg, msg, "dsSchemaClassCount",
+                                      n) != LDB_SUCCESS) {
                        goto failed;
                }
        }
 
        if (schema && do_attribute_explicit(attrs, "dsSchemaPrefixCount")) {
-               if (ldb_msg_add_fmt(msg, "dsSchemaPrefixCount",
-                                   "%u", schema->prefixmap->length) != LDB_SUCCESS) {
+               if (samdb_msg_add_uint(ldb, msg, msg, "dsSchemaPrefixCount",
+                                      schema->prefixmap->length) != LDB_SUCCESS) {
                        goto failed;
                }
        }
@@ -371,23 +381,24 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
        }
 
        if (do_attribute(attrs, "domainFunctionality")) {
-               if (ldb_msg_add_fmt(msg, "domainFunctionality",
-                                   "%d", dsdb_functional_level(ldb)) != LDB_SUCCESS) {
+               if (samdb_msg_add_int(ldb, msg, msg, "domainFunctionality",
+                                     dsdb_functional_level(ldb)) != LDB_SUCCESS) {
                        goto failed;
                }
        }
 
        if (do_attribute(attrs, "forestFunctionality")) {
-               if (ldb_msg_add_fmt(msg, "forestFunctionality",
-                                   "%d", dsdb_forest_functional_level(ldb)) != LDB_SUCCESS) {
+               if (samdb_msg_add_int(ldb, msg, msg, "forestFunctionality",
+                                     dsdb_forest_functional_level(ldb)) != LDB_SUCCESS) {
                        goto failed;
                }
        }
 
        if (do_attribute(attrs, "domainControllerFunctionality")
            && (val = talloc_get_type(ldb_get_opaque(ldb, "domainControllerFunctionality"), int))) {
-               if (ldb_msg_add_fmt(msg, "domainControllerFunctionality",
-                                   "%d", *val) != LDB_SUCCESS) {
+               if (samdb_msg_add_int(ldb, msg, msg,
+                                     "domainControllerFunctionality",
+                                     *val) != LDB_SUCCESS) {
                        goto failed;
                }
        }
@@ -533,32 +544,115 @@ static int rootdse_callback(struct ldb_request *req, struct ldb_reply *ares)
 }
 
 /*
-  mark our registered controls as non-critical in the request
-
-  This is needed as clients may mark controls as critical even if they
-  are not needed at all in a request. For example, the centrify client
-  sets the SD_FLAGS control as critical on ldap modify requests which
-  are setting the dNSHostName attribute on the machine account. That
-  request doesn't need SD_FLAGS at all, but centrify adds it on all
-  ldap requests.
+  filter from controls from clients in several ways
+
+  1) mark our registered controls as non-critical in the request
+
+    This is needed as clients may mark controls as critical even if
+    they are not needed at all in a request. For example, the centrify
+    client sets the SD_FLAGS control as critical on ldap modify
+    requests which are setting the dNSHostName attribute on the
+    machine account. That request doesn't need SD_FLAGS at all, but
+    centrify adds it on all ldap requests.
+
+  2) if this request is untrusted then remove any non-registered
+     controls that are non-critical
+
+    This is used on ldap:// connections to prevent remote users from
+    setting an internal control that may be dangerous
+
+  3) if this request is untrusted then fail any request that includes
+     a critical non-registered control
  */
-static void rootdse_mark_noncritical(struct ldb_module *module, struct ldb_control **controls)
+static int rootdse_filter_controls(struct ldb_module *module, struct ldb_request *req)
 {
        unsigned int i, j;
        struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+       bool is_untrusted;
 
-       if (!controls) return;
+       if (!req->controls) {
+               return LDB_SUCCESS;
+       }
+
+       is_untrusted = ldb_req_is_untrusted(req);
 
-       for (i=0; controls[i]; i++) {
-               if (controls[i]->critical == 0) {
+       for (i=0; req->controls[i]; i++) {
+               bool is_registered = false;
+               bool is_critical = (req->controls[i]->critical != 0);
+
+               if (req->controls[i]->oid == NULL) {
                        continue;
                }
-               for (j=0; j<priv->num_controls; j++) {
-                       if (strcasecmp(priv->controls[j], controls[i]->oid) == 0) {
-                               controls[i]->critical = 0;
+
+               if (is_untrusted || is_critical) {
+                       for (j=0; j<priv->num_controls; j++) {
+                               if (strcasecmp(priv->controls[j], req->controls[i]->oid) == 0) {
+                                       is_registered = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (is_untrusted && !is_registered) {
+                       if (!is_critical) {
+                               /* remove it by marking the oid NULL */
+                               req->controls[i]->oid = NULL;
+                               req->controls[i]->data = NULL;
+                               req->controls[i]->critical = 0;
+                               continue;
                        }
+                       /* its a critical unregistered control - give
+                          an error */
+                       ldb_asprintf_errstring(ldb_module_get_ctx(module),
+                                              "Attempt to use critical non-registered control '%s'",
+                                              req->controls[i]->oid);
+                       return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+               }
+
+               if (!is_critical) {
+                       continue;
+               }
+
+               /* If the control is DIRSYNC control then we keep the critical
+                * flag as the dirsync module will need to act upon it
+                */
+               if (is_registered && strcmp(req->controls[i]->oid,
+                                       LDB_CONTROL_DIRSYNC_OID)!= 0) {
+                       req->controls[i]->critical = 0;
+               }
+       }
+
+       return LDB_SUCCESS;
+}
+
+/* Ensure that anonymous users are not allowed to make anything other than rootDSE search operations */
+
+static int rootdse_filter_operations(struct ldb_module *module, struct ldb_request *req)
+{
+       struct auth_session_info *session_info;
+       struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+       bool is_untrusted = ldb_req_is_untrusted(req);
+       bool is_anonymous = true;
+       if (is_untrusted == false) {
+               return LDB_SUCCESS;
+       }
+
+       session_info = (struct auth_session_info *)ldb_get_opaque(ldb_module_get_ctx(module), "sessionInfo");
+       if (session_info) {
+               is_anonymous = security_token_is_anonymous(session_info->security_token);
+       }
+       
+       if (is_anonymous == false || (priv && priv->block_anonymous == false)) {
+               return LDB_SUCCESS;
+       }
+       
+       if (req->operation == LDB_SEARCH) {
+               if (req->op.search.scope == LDB_SCOPE_BASE && ldb_dn_is_null(req->op.search.base)) {
+                       return LDB_SUCCESS;
                }
        }
+       ldb_set_errstring(ldb_module_get_ctx(module), "Operation unavailable without authentication");
+       return LDB_ERR_OPERATIONS_ERROR;
 }
 
 static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
@@ -568,7 +662,15 @@ static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
        struct ldb_request *down_req;
        int ret;
 
-       rootdse_mark_noncritical(module, req->controls);
+       ret = rootdse_filter_operations(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = rootdse_filter_controls(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
        ldb = ldb_module_get_ctx(module);
 
@@ -679,6 +781,8 @@ static int rootdse_init(struct ldb_module *module)
        data->controls = NULL;
        data->num_partitions = 0;
        data->partitions = NULL;
+       data->block_anonymous = true;
+
        ldb_module_set_private(module, data);
 
        ldb_set_default_dns(ldb);
@@ -701,9 +805,9 @@ static int rootdse_init(struct ldb_module *module)
 
           Then stuff these values into an opaque
        */
-       ret = ldb_search(ldb, mem_ctx, &res,
-                        ldb_get_default_basedn(ldb),
-                        LDB_SCOPE_BASE, attrs, NULL);
+       ret = dsdb_module_search(module, mem_ctx, &res,
+                                ldb_get_default_basedn(ldb),
+                                LDB_SCOPE_BASE, attrs, DSDB_FLAG_NEXT_MODULE, NULL, NULL);
        if (ret == LDB_SUCCESS && res->count == 1) {
                int domain_behaviour_version
                        = ldb_msg_find_attr_as_int(res->msgs[0],
@@ -723,9 +827,9 @@ static int rootdse_init(struct ldb_module *module)
                }
        }
 
-       ret = ldb_search(ldb, mem_ctx, &res,
-                        samdb_partitions_dn(ldb, mem_ctx),
-                        LDB_SCOPE_BASE, attrs, NULL);
+       ret = dsdb_module_search(module, mem_ctx, &res,
+                                samdb_partitions_dn(ldb, mem_ctx),
+                                LDB_SCOPE_BASE, attrs, DSDB_FLAG_NEXT_MODULE, NULL, NULL);
        if (ret == LDB_SUCCESS && res->count == 1) {
                int forest_behaviour_version
                        = ldb_msg_find_attr_as_int(res->msgs[0],
@@ -745,16 +849,18 @@ static int rootdse_init(struct ldb_module *module)
                }
        }
 
-       ret = ldb_search(ldb, mem_ctx, &res,
-                        ldb_dn_new(mem_ctx, ldb, ""),
-                        LDB_SCOPE_BASE, ds_attrs, NULL);
+       /* For now, our own server's location in the DB is recorded in
+        * the @ROOTDSE record */
+       ret = dsdb_module_search(module, mem_ctx, &res,
+                                ldb_dn_new(mem_ctx, ldb, "@ROOTDSE"),
+                                LDB_SCOPE_BASE, ds_attrs, DSDB_FLAG_NEXT_MODULE, NULL, NULL);
        if (ret == LDB_SUCCESS && res->count == 1) {
                struct ldb_dn *ds_dn
                        = ldb_msg_find_attr_as_dn(ldb, mem_ctx, res->msgs[0],
                                                  "dsServiceName");
                if (ds_dn) {
-                       ret = ldb_search(ldb, mem_ctx, &res, ds_dn,
-                                        LDB_SCOPE_BASE, attrs, NULL);
+                       ret = dsdb_module_search(module, mem_ctx, &res, ds_dn,
+                                                LDB_SCOPE_BASE, attrs, DSDB_FLAG_NEXT_MODULE, NULL, NULL);
                        if (ret == LDB_SUCCESS && res->count == 1) {
                                int domain_controller_behaviour_version
                                        = ldb_msg_find_attr_as_int(res->msgs[0],
@@ -777,6 +883,8 @@ static int rootdse_init(struct ldb_module *module)
                }
        }
 
+       data->block_anonymous = dsdb_block_anonymous_ops(module, NULL);
+
        talloc_free(mem_ctx);
 
        return LDB_SUCCESS;
@@ -835,16 +943,18 @@ static int get_optional_feature_dn_guid(struct ldb_request *req, struct ldb_cont
  * ldb_message object.
  */
 static int dsdb_find_optional_feature(struct ldb_module *module, struct ldb_context *ldb,
-                               TALLOC_CTX *mem_ctx, struct GUID op_feature_guid, struct ldb_message **msg)
+                                     TALLOC_CTX *mem_ctx, struct GUID op_feature_guid, struct ldb_message **msg,
+                                     struct ldb_request *parent)
 {
        struct ldb_result *res;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
        int ret;
 
        ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
-                               NULL,
-                               DSDB_FLAG_NEXT_MODULE |
-                               DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
+                                NULL,
+                                DSDB_FLAG_NEXT_MODULE |
+                                DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
+                                parent,
                                 "(&(objectClass=msDS-OptionalFeature)"
                                 "(msDS-OptionalFeatureGUID=%s))",GUID_string(tmp_ctx, &op_feature_guid));
 
@@ -871,8 +981,8 @@ static int dsdb_find_optional_feature(struct ldb_module *module, struct ldb_cont
 }
 
 static int rootdse_enable_recycle_bin(struct ldb_module *module,struct ldb_context *ldb,
-                       TALLOC_CTX *mem_ctx, struct ldb_dn *op_feature_scope_dn,
-                       struct ldb_message *op_feature_msg)
+                                     TALLOC_CTX *mem_ctx, struct ldb_dn *op_feature_scope_dn,
+                                     struct ldb_message *op_feature_msg, struct ldb_request *parent)
 {
        int ret;
        const int domain_func_level = dsdb_functional_level(ldb);
@@ -892,18 +1002,14 @@ static int rootdse_enable_recycle_bin(struct ldb_module *module,struct ldb_conte
        tmp_ctx = talloc_new(mem_ctx);
        ntds_settings_dn = samdb_ntds_settings_dn(ldb);
        if (!ntds_settings_dn) {
-               DEBUG(0, (__location__ ": Failed to find NTDS settings DN\n"));
-               ret = LDB_ERR_OPERATIONS_ERROR;
                talloc_free(tmp_ctx);
-               return ret;
+               return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Failed to find NTDS settings DN");
        }
 
        ntds_settings_dn = ldb_dn_copy(tmp_ctx, ntds_settings_dn);
        if (!ntds_settings_dn) {
-               DEBUG(0, (__location__ ": Failed to copy NTDS settings DN\n"));
-               ret = LDB_ERR_OPERATIONS_ERROR;
                talloc_free(tmp_ctx);
-               return ret;
+               return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Failed to copy NTDS settings DN");
        }
 
        msg = ldb_msg_new(tmp_ctx);
@@ -912,7 +1018,7 @@ static int rootdse_enable_recycle_bin(struct ldb_module *module,struct ldb_conte
        ldb_msg_add_linearized_dn(msg, "msDS-EnabledFeature", op_feature_msg->dn);
        msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
 
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb,
                                       "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
@@ -923,7 +1029,7 @@ static int rootdse_enable_recycle_bin(struct ldb_module *module,struct ldb_conte
        }
 
        msg->dn = op_feature_scope_dn;
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb,
                                       "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
@@ -975,7 +1081,7 @@ static int rootdse_enableoptionalfeature(struct ldb_module *module, struct ldb_r
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
-       ret = dsdb_find_optional_feature(module, ldb, tmp_ctx, op_feature_guid, &op_feature_msg);
+       ret = dsdb_find_optional_feature(module, ldb, tmp_ctx, op_feature_guid, &op_feature_msg, req);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb,
                                       "rootdse: unable to find optional feature for %s - %s",
@@ -987,7 +1093,7 @@ static int rootdse_enableoptionalfeature(struct ldb_module *module, struct ldb_r
        if (strcasecmp(DS_GUID_FEATURE_RECYCLE_BIN, guid_string) == 0) {
                        ret = rootdse_enable_recycle_bin(module, ldb,
                                                         tmp_ctx, op_feature_scope_dn,
-                                                        op_feature_msg);
+                                                        op_feature_msg, req);
        } else {
                ldb_asprintf_errstring(ldb,
                                       "rootdse: unknown optional feature %s",
@@ -1034,8 +1140,17 @@ static int rootdse_schemaupdatenow(struct ldb_module *module, struct ldb_request
 static int rootdse_add(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
+       int ret;
+
+       ret = rootdse_filter_operations(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
-       rootdse_mark_noncritical(module, req->controls);
+       ret = rootdse_filter_controls(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
        /*
                If dn is not "" we should let it pass through
@@ -1048,20 +1163,59 @@ static int rootdse_add(struct ldb_module *module, struct ldb_request *req)
        return LDB_ERR_NAMING_VIOLATION;
 }
 
+struct fsmo_transfer_state {
+       struct ldb_context *ldb;
+       struct ldb_request *req;
+};
+
+/*
+  called when a FSMO transfer operation has completed
+ */
+static void rootdse_fsmo_transfer_callback(struct tevent_req *treq)
+{
+       struct fsmo_transfer_state *fsmo = tevent_req_callback_data(treq, struct fsmo_transfer_state);
+       NTSTATUS status;
+       WERROR werr;
+       struct ldb_request *req = fsmo->req;
+       struct ldb_context *ldb = fsmo->ldb;
+
+       status = dcerpc_drepl_takeFSMORole_recv(treq, fsmo, &werr);
+       talloc_free(fsmo);
+       if (!NT_STATUS_IS_OK(status)) {
+               ldb_asprintf_errstring(ldb, "Failed FSMO transfer: %s", nt_errstr(status));
+               ldb_module_done(req, NULL, NULL, LDB_ERR_UNAVAILABLE);
+               return;
+       }
+       if (!W_ERROR_IS_OK(werr)) {
+               ldb_asprintf_errstring(ldb, "Failed FSMO transfer: %s", win_errstr(werr));
+               ldb_module_done(req, NULL, NULL, LDB_ERR_UNAVAILABLE);
+               return;
+       }
+
+       ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+}
+
 static int rootdse_become_master(struct ldb_module *module,
                                 struct ldb_request *req,
-                                uint32_t role)
+                                enum drepl_role_master role)
 {
-       struct drepl_takeFSMORole r;
-       struct messaging_context *msg;
+       struct imessaging_context *msg;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        TALLOC_CTX *tmp_ctx = talloc_new(req);
        struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
-       NTSTATUS status_call;
-       WERROR status_fn;
        bool am_rodc;
        struct dcerpc_binding_handle *irpc_handle;
        int ret;
+       struct auth_session_info *session_info;
+       enum security_user_level level;
+       struct fsmo_transfer_state *fsmo;
+       struct tevent_req *treq;
+
+       session_info = (struct auth_session_info *)ldb_get_opaque(ldb_module_get_ctx(module), "sessionInfo");
+       level = security_session_user_level(session_info, NULL);
+       if (level < SECURITY_ADMINISTRATOR) {
+               return ldb_error(ldb, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS, "Denied rootDSE modify for non-administrator");
+       }
 
        ret = samdb_rodc(ldb, &am_rodc);
        if (ret != LDB_SUCCESS) {
@@ -1073,10 +1227,10 @@ static int rootdse_become_master(struct ldb_module *module,
                                 "RODC cannot become a role master.");
        }
 
-       msg = messaging_client_init(tmp_ctx, lpcfg_messaging_path(tmp_ctx, lp_ctx),
+       msg = imessaging_client_init(tmp_ctx, lpcfg_imessaging_path(tmp_ctx, lp_ctx),
                                    ldb_get_event_context(ldb));
        if (!msg) {
-               ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s", lpcfg_messaging_path(tmp_ctx, lp_ctx));
+               ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s", lpcfg_imessaging_path(tmp_ctx, lp_ctx));
                return LDB_ERR_OPERATIONS_ERROR;
        }
        irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg,
@@ -1085,24 +1239,40 @@ static int rootdse_become_master(struct ldb_module *module,
        if (irpc_handle == NULL) {
                return ldb_oom(ldb);
        }
-       r.in.role = role;
-
-       status_call = dcerpc_drepl_takeFSMORole_r(irpc_handle, tmp_ctx, &r);
-       if (!NT_STATUS_IS_OK(status_call)) {
-               return LDB_ERR_OPERATIONS_ERROR;
+       fsmo = talloc_zero(req, struct fsmo_transfer_state);
+       if (fsmo == NULL) {
+               return ldb_oom(ldb);
        }
-       status_fn = r.out.result;
-       if (!W_ERROR_IS_OK(status_fn)) {
-               return LDB_ERR_OPERATIONS_ERROR;
+       fsmo->ldb = ldb;
+       fsmo->req = req;
+
+       /* we send the call asynchronously, as the ldap client is
+        * expecting to get an error back if the role transfer fails
+        */
+
+       treq = dcerpc_drepl_takeFSMORole_send(req, ldb_get_event_context(ldb), irpc_handle, role);
+       if (treq == NULL) {
+               return ldb_oom(ldb);
        }
-       return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+
+       tevent_req_set_callback(treq, rootdse_fsmo_transfer_callback, fsmo);
+       return LDB_SUCCESS;
 }
 
 static int rootdse_modify(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
+       int ret;
 
-       rootdse_mark_noncritical(module, req->controls);
+       ret = rootdse_filter_operations(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = rootdse_filter_controls(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
        /*
                If dn is not "" we should let it pass through
@@ -1141,11 +1311,102 @@ static int rootdse_modify(struct ldb_module *module, struct ldb_request *req)
        return LDB_ERR_UNWILLING_TO_PERFORM;
 }
 
+static int rootdse_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       int ret;
+       const struct GUID *ntds_guid = NULL;
+       struct ldb_dn *old_ntds_dn;
+       struct ldb_dn *new_ntds_dn;
+       struct ldb_message *msg;
+       bool ok = false;
+
+       ret = rootdse_filter_operations(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       ret = rootdse_filter_controls(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       /*
+               If dn is not "" we should let it pass through
+       */
+       if (ldb_dn_is_null(req->op.rename.olddn)) {
+               ldb_set_errstring(ldb, "rootdse_remove: you cannot rename the rootdse entry!");
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+
+       old_ntds_dn = samdb_ntds_settings_dn(ldb);
+       if (ldb_dn_compare_base(req->op.rename.olddn, old_ntds_dn) == 0) {
+               ntds_guid = samdb_ntds_objectGUID(ldb);
+               if (ntds_guid == NULL) {
+                       return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Unable to find the GUID of the NTDS Settings object");
+               }
+       }
+
+       if (ntds_guid == NULL) {
+               return ldb_next_request(module, req);
+       }
+
+       ret = dsdb_module_rename(module,
+                                req->op.rename.olddn,
+                                req->op.rename.newdn,
+                                DSDB_FLAG_NEXT_MODULE,
+                                req);
+       if (ret != LDB_SUCCESS) {
+               /* Error msg is most probably done by module bellow*/
+               return ret;
+       }
+
+       ret = dsdb_module_dn_by_guid(module, req, ntds_guid, &new_ntds_dn, req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Unable to find the new DN of NTDS Settings object");
+       }
+
+       msg = ldb_msg_new(req);
+       if (msg == NULL) {
+               return ldb_module_oom(module);
+       }
+
+       msg->dn = ldb_dn_new(msg, ldb, "@ROOTDSE");
+       if (msg->dn == NULL) {
+               return ldb_module_oom(module);
+       }
+
+       ret = ldb_msg_add_linearized_dn(msg, "dsServiceName", new_ntds_dn);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_oom(module);
+       }
+
+       ret = dsdb_replace(ldb, msg, DSDB_FLAG_AS_SYSTEM);
+       if (ret != LDB_SUCCESS) {
+               return ldb_error(ldb, ret, "Unable to update dsServiceName of ROOTDSE");
+       }
+
+       ok = samdb_set_ntds_settings_dn(ldb, new_ntds_dn);
+       if (!ok) {
+               return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Call to samdb_set_ntds_settings_dn has failed");
+       }
+
+       return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+}
+
 static int rootdse_delete(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
+       int ret;
+
+       ret = rootdse_filter_operations(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
-       rootdse_mark_noncritical(module, req->controls);
+       ret = rootdse_filter_controls(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
        /*
                If dn is not "" we should let it pass through
@@ -1158,12 +1419,37 @@ static int rootdse_delete(struct ldb_module *module, struct ldb_request *req)
        return LDB_ERR_NO_SUCH_OBJECT;
 }
 
-_PUBLIC_ const struct ldb_module_ops ldb_rootdse_module_ops = {
+static int rootdse_extended(struct ldb_module *module, struct ldb_request *req)
+{
+       int ret;
+
+       ret = rootdse_filter_operations(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = rootdse_filter_controls(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       return ldb_next_request(module, req);
+}
+
+static const struct ldb_module_ops ldb_rootdse_module_ops = {
        .name           = "rootdse",
        .init_context   = rootdse_init,
        .search         = rootdse_search,
        .request        = rootdse_request,
        .add            = rootdse_add,
        .modify         = rootdse_modify,
+       .rename         = rootdse_rename,
+       .extended       = rootdse_extended,
        .del            = rootdse_delete
 };
+
+int ldb_rootdse_module_init(const char *version)
+{
+       LDB_MODULE_CHECK_VERSION(version);
+       return ldb_register_module(&ldb_rootdse_module_ops);
+}