repl_meta_data: Correctly use msDS-IntId for custom schema, not the prefixMap value
authorAndrew Bartlett <abartlet@samba.org>
Tue, 22 Dec 2015 06:33:39 +0000 (19:33 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 22 Dec 2015 06:45:51 +0000 (19:45 +1300)
We must, when dealing with custom schema, respect the msDC-IntId value recorded
in the schema.  If we do not, then we will create multiple replPropertyMetaData
records for the one attribute.  This may cause confusion during replication.

This fixes the issue by always calling dsdb_attribute_get_attid() to obtain
the correct local (32 bit integer) attribute ID

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
source4/dsdb/samdb/ldb_modules/repl_meta_data.c
source4/dsdb/tests/python/ldap_schema.py

index 3e4880e131c2391a0b11a11304cfa622c4f0ec27..6ba2caa4d0d6b4141e9f0fa130bbee42e1c6424e 100644 (file)
@@ -890,6 +890,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        bool allow_add_guid = false;
        bool remove_current_guid = false;
        bool is_urgent = false;
        bool allow_add_guid = false;
        bool remove_current_guid = false;
        bool is_urgent = false;
+       bool is_schema_nc = false;
        struct ldb_message_element *objectclass_el;
        struct replmd_private *replmd_private =
                talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
        struct ldb_message_element *objectclass_el;
        struct replmd_private *replmd_private =
                talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
@@ -1007,6 +1008,8 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
+
        for (i=0; i < msg->num_elements; i++) {
                struct ldb_message_element *e = &msg->elements[i];
                struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
        for (i=0; i < msg->num_elements; i++) {
                struct ldb_message_element *e = &msg->elements[i];
                struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
@@ -1041,8 +1044,8 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
                        continue;
                }
 
                        continue;
                }
 
-               m->attid                        = sa->attributeID_id;
-               m->version                      = 1;
+               m->attid   = dsdb_attribute_get_attid(sa, is_schema_nc);
+               m->version = 1;
                if (m->attid == 0x20030) {
                        const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
                        const char* rdn;
                if (m->attid == 0x20030) {
                        const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
                        const char* rdn;
@@ -1209,12 +1212,14 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
                                      uint64_t *seq_num,
                                      const struct GUID *our_invocation_id,
                                      NTTIME now,
                                      uint64_t *seq_num,
                                      const struct GUID *our_invocation_id,
                                      NTTIME now,
+                                     bool is_schema_nc,
                                      struct ldb_request *req)
 {
        uint32_t i;
        const struct dsdb_attribute *a;
        struct replPropertyMetaData1 *md1;
        bool may_skip = false;
                                      struct ldb_request *req)
 {
        uint32_t i;
        const struct dsdb_attribute *a;
        struct replPropertyMetaData1 *md1;
        bool may_skip = false;
+       uint32_t attid;
 
        a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
        if (a == NULL) {
 
        a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
        if (a == NULL) {
@@ -1229,6 +1234,8 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
+       attid = dsdb_attribute_get_attid(a, is_schema_nc);
+
        if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
                return LDB_SUCCESS;
        }
        if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
                return LDB_SUCCESS;
        }
@@ -1272,7 +1279,22 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
        }
 
        for (i=0; i<omd->ctr.ctr1.count; i++) {
        }
 
        for (i=0; i<omd->ctr.ctr1.count; i++) {
-               if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
+               /*
+                * First check if we find it under the msDS-IntID,
+                * then check if we find it under the OID and
+                * prefixMap ID.
+                *
+                * This allows the administrator to simply re-write
+                * the attributes and so restore replication, which is
+                * likely what they will try to do.
+                */
+               if (attid == omd->ctr.ctr1.array[i].attid) {
+                       break;
+               }
+
+               if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
+                       break;
+               }
        }
 
        if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
        }
 
        if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
@@ -1311,7 +1333,7 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
 
        md1 = &omd->ctr.ctr1.array[i];
        md1->version++;
 
        md1 = &omd->ctr.ctr1.array[i];
        md1->version++;
-       md1->attid                     = a->attributeID_id;
+       md1->attid = attid;
        if (md1->attid == 0x20030) {
                const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
                const char* rdn;
        if (md1->attid == 0x20030) {
                const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
                const char* rdn;
@@ -1365,7 +1387,7 @@ static int replmd_update_rpmd(struct ldb_module *module,
                              struct ldb_request *req,
                              const char * const *rename_attrs,
                              struct ldb_message *msg, uint64_t *seq_num,
                              struct ldb_request *req,
                              const char * const *rename_attrs,
                              struct ldb_message *msg, uint64_t *seq_num,
-                             time_t t,
+                             time_t t, bool is_schema_nc,
                              bool *is_urgent, bool *rodc)
 {
        const struct ldb_val *omd_value;
                              bool *is_urgent, bool *rodc)
 {
        const struct ldb_val *omd_value;
@@ -1524,7 +1546,9 @@ static int replmd_update_rpmd(struct ldb_module *module,
                        struct ldb_message_element *old_el;
                        old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
                        ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
                        struct ldb_message_element *old_el;
                        old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
                        ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
-                                                        our_invocation_id, now, req);
+                                                        our_invocation_id,
+                                                        now, is_schema_nc,
+                                                        req);
                        if (ret != LDB_SUCCESS) {
                                return ret;
                        }
                        if (ret != LDB_SUCCESS) {
                                return ret;
                        }
@@ -2486,6 +2510,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        time_t t = time(NULL);
        int ret;
        bool is_urgent = false, rodc = false;
        time_t t = time(NULL);
        int ret;
        bool is_urgent = false, rodc = false;
+       bool is_schema_nc = false;
        unsigned int functional_level;
        const struct ldb_message_element *guid_el = NULL;
        struct ldb_control *sd_propagation_control;
        unsigned int functional_level;
        const struct ldb_message_element *guid_el = NULL;
        struct ldb_control *sd_propagation_control;
@@ -2541,8 +2566,11 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        ldb_msg_remove_attr(msg, "whenChanged");
        ldb_msg_remove_attr(msg, "uSNChanged");
 
        ldb_msg_remove_attr(msg, "whenChanged");
        ldb_msg_remove_attr(msg, "uSNChanged");
 
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
+
        ret = replmd_update_rpmd(module, ac->schema, req, NULL,
        ret = replmd_update_rpmd(module, ac->schema, req, NULL,
-                                msg, &ac->seq_num, t, &is_urgent, &rodc);
+                                msg, &ac->seq_num, t, is_schema_nc,
+                                &is_urgent, &rodc);
        if (rodc && (ret == LDB_ERR_REFERRAL)) {
                struct loadparm_context *lp_ctx;
                char *referral;
        if (rodc && (ret == LDB_ERR_REFERRAL)) {
                struct loadparm_context *lp_ctx;
                char *referral;
@@ -2690,7 +2718,6 @@ static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct ldb_context *ldb;
 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct ldb_context *ldb;
-       struct replmd_replicated_request *ac;
        struct ldb_request *down_req;
        struct ldb_message *msg;
        const struct dsdb_attribute *rdn_attr;
        struct ldb_request *down_req;
        struct ldb_message *msg;
        const struct dsdb_attribute *rdn_attr;
@@ -2700,8 +2727,13 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
        time_t t = time(NULL);
        int ret;
        bool is_urgent = false, rodc = false;
        time_t t = time(NULL);
        int ret;
        bool is_urgent = false, rodc = false;
+       bool is_schema_nc;
+       struct replmd_replicated_request *ac =
+               talloc_get_type(req->context, struct replmd_replicated_request);
+       struct replmd_private *replmd_private =
+               talloc_get_type(ldb_module_get_private(ac->module),
+                               struct replmd_private);
 
 
-       ac = talloc_get_type(req->context, struct replmd_replicated_request);
        ldb = ldb_module_get_ctx(ac->module);
 
        if (ares->error != LDB_SUCCESS) {
        ldb = ldb_module_get_ctx(ac->module);
 
        if (ares->error != LDB_SUCCESS) {
@@ -2729,6 +2761,8 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
 
        msg->dn = ac->req->op.rename.newdn;
 
 
        msg->dn = ac->req->op.rename.newdn;
 
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
+
        rdn_name = ldb_dn_get_rdn_name(msg->dn);
        if (rdn_name == NULL) {
                talloc_free(ares);
        rdn_name = ldb_dn_get_rdn_name(msg->dn);
        if (rdn_name == NULL) {
                talloc_free(ares);
@@ -2814,7 +2848,8 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
        attrs[4] = NULL;
 
        ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
        attrs[4] = NULL;
 
        ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
-                                msg, &ac->seq_num, t, &is_urgent, &rodc);
+                                msg, &ac->seq_num, t,
+                                is_schema_nc, &is_urgent, &rodc);
        if (rodc && (ret == LDB_ERR_REFERRAL)) {
                struct ldb_dn *olddn = ac->req->op.rename.olddn;
                struct loadparm_context *lp_ctx;
        if (rodc && (ret == LDB_ERR_REFERRAL)) {
                struct ldb_dn *olddn = ac->req->op.rename.olddn;
                struct loadparm_context *lp_ctx;
index 1de65191e8e910f1c5039ad14366588e21fbb2bc..2d20b48768494b07936635cb3498d4586a030a43 100755 (executable)
@@ -41,6 +41,8 @@ from ldb import FLAG_MOD_REPLACE
 from samba.samdb import SamDB
 from samba.dsdb import DS_DOMAIN_FUNCTION_2003
 from samba.tests import delete_force
 from samba.samdb import SamDB
 from samba.dsdb import DS_DOMAIN_FUNCTION_2003
 from samba.tests import delete_force
+from samba.ndr import ndr_unpack
+from samba.dcerpc import drsblobs
 
 parser = optparse.OptionParser("ldap_schema.py [options] <host>")
 sambaopts = options.SambaOptions(parser)
 
 parser = optparse.OptionParser("ldap_schema.py [options] <host>")
 sambaopts = options.SambaOptions(parser)
@@ -124,10 +126,16 @@ schemaUpdateNow: 1
         # Search for created attribute
         res = []
         res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE,
         # Search for created attribute
         res = []
         res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE,
-                              attrs=["lDAPDisplayName","schemaIDGUID"])
+                              attrs=["lDAPDisplayName","schemaIDGUID", "msDS-IntID"])
         self.assertEquals(len(res), 1)
         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
         self.assertTrue("schemaIDGUID" in res[0])
         self.assertEquals(len(res), 1)
         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
         self.assertTrue("schemaIDGUID" in res[0])
+        if "msDS-IntId" in res[0]:
+            msDS_IntId = int(res[0]["msDS-IntId"][0])
+            if msDS_IntId < 0:
+                msDS_IntId += (1 << 32)
+        else:
+            msDS_IntId = None
 
         class_name = "test-Class" + time.strftime("%s", time.gmtime())
         class_ldap_display_name = class_name.replace("-", "")
 
         class_name = "test-Class" + time.strftime("%s", time.gmtime())
         class_ldap_display_name = class_name.replace("-", "")
@@ -211,9 +219,24 @@ name: """ + object_name + """
         self.ldb.add_ldif(ldif)
 
         # Search for created object
         self.ldb.add_ldif(ldif)
 
         # Search for created object
-        res = []
-        res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
-        self.assertEquals(len(res), 1)
+        obj_res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["replPropertyMetaData"])
+
+        self.assertEquals(len(obj_res), 1)
+        self.assertTrue("replPropertyMetaData" in obj_res[0])
+        val = obj_res[0]["replPropertyMetaData"][0]
+        repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
+        obj = repl.ctr
+
+        # Windows 2000 functional level won't have this.  It is too
+        # hard to work it out from the prefixmap however, so we skip
+        # this test in that case.
+        if msDS_IntId is not None:
+            found = False
+            for o in repl.ctr.array:
+                if o.attid == msDS_IntId:
+                    found = True
+                    break
+            self.assertTrue(found, "Did not find 0x%08x in replPropertyMetaData" % msDS_IntId)
         # Delete the object
         delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
 
         # Delete the object
         delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))