From ad9236560cf1371060f8c9099b7a8c1b4ad787a1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 13 Mar 2014 23:12:39 +0100 Subject: [PATCH 1/3] dbchecker: make the deleted objects container detection more generic Signed-off-by: Stefan Metzmacher --- python/samba/dbchecker.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index f276cc5..0cdf6af 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -1043,6 +1043,10 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) got_repl_property_meta_data = False got_objectclass = False + nc_dn = self.samdb.get_nc_root(obj.dn) + deleted_objects_dn = self.samdb.get_wellknown_dn(nc_dn, + samba.dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER) + for attrname in obj: if attrname == 'dn': continue @@ -1143,8 +1147,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) show_dn = True if got_repl_property_meta_data: - rdn = (str(dn).split(","))[0] - if rdn == "CN=Deleted Objects": + if obj.dn == deleted_objects_dn: isDeletedAttId = 131120 # It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container -- 1.7.9.5 From c02ddfb6a5cdc6bd1baa80c3aa7a8111b28ba294 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 13 Mar 2014 23:12:39 +0100 Subject: [PATCH 2/3] dbchecker: verify and fix broken dn values With older Samba versions (4.0.x) the following could happen: - On account was created on DC1 - It was replicated to DC2 - The connection between the dcs is offline - The account gets modified on DC2 - The account gets deleted on DC1 - The connection becomes online again - DC1 replicates the modification from DC2, this resets the dn to the original value. 'name' and 'cn' are correct (with '\nDEL${GUID}'), but 'dn' is wrong. - DC2 replicates the deletion from DC1. this doesn't include a changed dn as DC1 had a bug. 'name' is correct (with '\nDEL${GUID}'), but 'cn' and 'dn' are wrong. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10536 Signed-off-by: Stefan Metzmacher --- python/samba/dbchecker.py | 95 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index 0cdf6af..b2caee9 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -63,6 +63,7 @@ class dbcheck(object): self.fix_instancetype = False self.fix_replmetadata_zero_invocationid = False self.fix_deleted_deleted_objects = False + self.fix_dn = False self.reset_well_known_acls = reset_well_known_acls self.reset_all_well_known_acls = False self.in_transaction = in_transaction @@ -486,6 +487,26 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) else: self.samdb.transaction_cancel() + def err_wrong_dn(self, obj, new_dn, rdn_attr, rdn_val, name_val): + '''handle a wrong dn''' + + new_rdn = ldb.Dn(self.samdb, str(new_dn)) + new_rdn.remove_base_components(len(new_rdn) - 1) + new_parent = new_dn.parent() + + attributes = "" + if rdn_val != name_val: + attributes += "%s=%r " % (rdn_attr, rdn_val) + attributes += "name=%r" % (name_val) + + self.report("ERROR: wrong dn[%s] %s new_dn[%s]" % (obj.dn, attributes, new_dn)) + if not self.confirm_all("Rename %s to %s?" % (obj.dn, new_dn), 'fix_dn'): + self.report("Not renaming %s to %s" % (obj.dn, new_dn)) + return + + if self.do_rename(obj.dn, new_rdn, new_parent, ["show_recycled:1", "relax:0"], + "Failed to rename object %s into %s" % (obj.dn, new_dn)): + self.report("Renamed %s into %s" % (obj.dn, new_dn)) def err_wrong_instancetype(self, obj, calculated_instancetype): '''handle a wrong instanceType''' @@ -1008,6 +1029,18 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) '''check one object''' if self.verbose: self.report("Checking object %s" % dn) + rdn0 = (str(dn).split(",", 1))[0] + rdn0_attr = (str(rdn0).split("=", 1))[0] + if "dn" in map(str.lower, attrs): + attrs.append("name") + if "distinguishedname" in map(str.lower, attrs): + attrs.append("name") + if str(rdn0_attr).lower() in map(str.lower, attrs): + attrs.append("name") + if 'name' in map(str.lower, attrs): + attrs.append(rdn0_attr) + attrs.append("isDeleted") + attrs.append("systemFlags") if '*' in attrs: attrs.append("replPropertyMetaData") @@ -1047,6 +1080,17 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) deleted_objects_dn = self.samdb.get_wellknown_dn(nc_dn, samba.dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER) + rdn1 = (str(obj.dn).split(",", 1))[0] + rdn1_attr = (str(rdn1).split("=", 1))[0] + rdn1_vale = (str(rdn1).split("=", 1))[1] + rdn1_val = ldb.binary_decode(rdn1_vale) + + rdn2_attr = None + rdn2_val = None + name_val = None + isDeleted = False + systemFlags = 0 + for attrname in obj: if attrname == 'dn': continue @@ -1054,6 +1098,30 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) if str(attrname).lower() == 'objectclass': got_objectclass = True + if str(attrname).lower() == "name": + if len(obj[attrname]) != 1: + error_count += 1 + self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" % + (len(obj[attrname]), attrname, str(obj.dn))) + else: + name_val = obj[attrname][0] + + if str(attrname).lower() == str(rdn1_attr).lower(): + rdn2_attr = attrname + if len(obj[attrname]) != 1: + error_count += 1 + self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" % + (len(obj[attrname]), attrname, str(obj.dn))) + else: + rdn2_val = obj[attrname][0] + + if str(attrname).lower() == 'isdeleted': + if obj[attrname][0] != "FALSE": + isDeleted = True + + if str(attrname).lower() == 'systemflags': + systemFlags = int(obj[attrname][0]) + if str(attrname).lower() == 'replpropertymetadata': if self.has_replmetadata_zero_invocationid(dn, obj[attrname]): error_count += 1 @@ -1145,6 +1213,33 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) error_count += 1 self.err_missing_objectclass(dn) + if ("*" in attrs or "name" in map(str.lower, attrs)): + if name_val is None: + error_count += 1 + self.report("ERROR: Not fixing missing 'name' on '%s'" % (str(obj.dn))) + if rdn2_attr is None: + error_count += 1 + self.report("ERROR: Not fixing missing '%s' on '%s'" % (rdn1_attr, str(obj.dn))) + + if name_val is not None: + parent_dn = None + if isDeleted: + if not (systemFlags & samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE): + parent_dn = deleted_objects_dn + if parent_dn is None: + parent_dn = obj.dn.parent() + expected_dn = ldb.Dn(self.samdb, "%s=%s,%s" % (rdn1_attr, ldb.binary_encode(name_val), parent_dn)) + + if obj.dn == deleted_objects_dn: + expected_dn = obj.dn + + if expected_dn != obj.dn: + error_count += 1 + self.err_wrong_dn(obj, expected_dn, rdn2_attr, rdn2_val, name_val) + elif rdn1_val != rdn2_val: + error_count += 1 + self.report("ERROR: Not fixing %s=%r on '%s'" % (rdn2_attr, rdn2_val, str(obj.dn))) + show_dn = True if got_repl_property_meta_data: if obj.dn == deleted_objects_dn: -- 1.7.9.5 From a9c6d293bd0df9ff1364a2196e1ab98efcf51f72 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 3 Apr 2014 16:03:19 +0200 Subject: [PATCH 3/3] s4:repl_meta_data: fix array assignment in replmd_process_linked_attribute() Signed-off-by: Stefan Metzmacher --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 4e5d8f0..b01c956 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -5397,8 +5397,8 @@ linked_attributes[0]: attrs[0] = attr->lDAPDisplayName; attrs[1] = "isDeleted"; - attrs[1] = "isRecycled"; - attrs[2] = NULL; + attrs[2] = "isRecycled"; + attrs[3] = NULL; /* get the existing message from the db for the object with this GUID, returning attribute being modified. We will then -- 1.7.9.5