rodc: Force all RODC add and delete to cause a referral
authorGarming Sam <garming@catalyst.net.nz>
Tue, 20 Sep 2016 04:25:34 +0000 (04:25 +0000)
committerGarming Sam <garming@samba.org>
Thu, 13 Apr 2017 05:29:17 +0000 (07:29 +0200)
Previously, you could add or delete and cause replication conflicts on
an RODC. Modifies are already partly restricted in repl_meta_data and
have more specific requirements, so they cannot be handled here.

We still differ against Windows for modifies of non-replicated
attributes over LDAP.

Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Pair-programmed-with: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12008

selftest/knownfail
source4/dsdb/samdb/ldb_modules/samldb.c

index 7e204edcd017b81f6a61984eb093c9411a4cda03..571904a000998fab768f8237405c9ecb89c3608b 100644 (file)
 ^samba4.blackbox.trust_ntlm.Test08.*client.*with.ADDOM.SAMBA.EXAMPLE.COM\\Administrator%locDCpass1\(fl2003dc:local\)
 ^samba4.blackbox.trust_ntlm.Test09.*client.*with.Administrator@ADDOMAIN%locDCpass1\(fl2003dc:local\)
 ^samba4.blackbox.trust_ntlm.Test10.*client.*with.Administrator@ADDOM.SAMBA.EXAMPLE.COM%locDCpass1\(fl2003dc:local\)
-^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_add.*
-^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_delete.*
+# We currently don't send referrals for LDAP modify of non-replicated attrs
 ^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.*
index 48edc28967dbfea258c8279378668ed15fa6525a..971048d455f7ee6a7aa5a33ea070775b919c6de9 100644 (file)
@@ -44,6 +44,7 @@
 #include "param/param.h"
 #include "libds/common/flag_mapping.h"
 #include "system/network.h"
+#include "librpc/gen_ndr/irpc.h"
 
 struct samldb_ctx;
 enum samldb_add_type {
@@ -3380,6 +3381,58 @@ static int samldb_verify_subnet(struct samldb_ctx *ac)
        return LDB_SUCCESS;
 }
 
+static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req,
+                          struct ldb_dn *dn)
+{
+       bool rodc = false;
+       struct loadparm_context *lp_ctx;
+       char *referral;
+       int ret;
+       WERROR err;
+
+       if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID) ||
+           ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
+               return NULL;
+       }
+
+       ret = samdb_rodc(ldb, &rodc);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
+               return NULL;
+       }
+
+       if (rodc) {
+               const char *domain = NULL;
+               struct ldb_dn *fsmo_role_dn;
+               struct ldb_dn *role_owner_dn;
+               ldb_set_errstring(ldb, "RODC modify is forbidden!");
+               lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+                                        struct loadparm_context);
+
+               err = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
+                                             &fsmo_role_dn, &role_owner_dn);
+               if (W_ERROR_IS_OK(err)) {
+                       struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
+                       if (server_dn != NULL) {
+                               ldb_dn_remove_child_components(server_dn, 1);
+
+                               domain = samdb_dn_to_dnshostname(ldb, req,
+                                                                server_dn);
+                       }
+               }
+               if (domain == NULL) {
+                       domain = lpcfg_dnsdomain(lp_ctx);
+               }
+               referral = talloc_asprintf(req,
+                                          "ldap://%s/%s",
+                                          domain,
+                                          ldb_dn_get_linearized(dn));
+               return referral;
+       }
+
+       return NULL;
+}
+
 
 /* add */
 static int samldb_add(struct ldb_module *module, struct ldb_request *req)
@@ -3388,6 +3441,7 @@ static int samldb_add(struct ldb_module *module, struct ldb_request *req)
        struct samldb_ctx *ac;
        struct ldb_message_element *el;
        int ret;
+       char *referral = NULL;
 
        ldb = ldb_module_get_ctx(module);
        ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");
@@ -3397,6 +3451,12 @@ static int samldb_add(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
+       referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
+       if (referral != NULL) {
+               ret = ldb_module_send_referral(req, referral);
+               return ret;
+       }
+
        el = ldb_msg_find_element(req->op.add.message, "userParameters");
        if (el != NULL && ldb_req_is_untrusted(req)) {
                const char *reason = "samldb_add: "
@@ -3831,13 +3891,23 @@ static int samldb_prim_group_users_check(struct samldb_ctx *ac)
 static int samldb_delete(struct ldb_module *module, struct ldb_request *req)
 {
        struct samldb_ctx *ac;
+       char *referral = NULL;
        int ret;
+       struct ldb_context *ldb;
 
        if (ldb_dn_is_special(req->op.del.dn)) {
                /* do not manipulate our control entries */
                return ldb_next_request(module, req);
        }
 
+       ldb = ldb_module_get_ctx(module);
+
+       referral = refer_if_rodc(ldb, req, req->op.del.dn);
+       if (referral != NULL) {
+               ret = ldb_module_send_referral(req, referral);
+               return ret;
+       }
+
        ac = samldb_ctx_init(module, req);
        if (ac == NULL) {
                return ldb_operr(ldb_module_get_ctx(module));