from samba import dsdb
from samba import common
from samba.dcerpc import misc
+from samba.dcerpc import drsuapi
from samba.ndr import ndr_unpack, ndr_pack
from samba.dcerpc import drsblobs
from samba.common import dsdb_Dn
self.move_to_lost_and_found = False
self.fix_instancetype = False
self.fix_replmetadata_zero_invocationid = False
+ self.fix_replmetadata_unsorted_attid = False
self.fix_deleted_deleted_objects = False
self.fix_dn = False
self.fix_base64_userparameters = False
return 0
def process_metadata(self, val):
- '''Read metadata properties and list attributes in it'''
+ '''Read metadata properties and list attributes in it.
+ raises KeyError if the attid is unknown.'''
list_att = []
+ list_attid = []
repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
obj = repl.ctr
for o in repl.ctr.array:
att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
list_att.append(att.lower())
+ list_attid.append(o.attid)
- return list_att
+ return (list_att, list_attid)
def fix_metadata(self, dn, attr):
nmsg = ldb.Message()
nmsg.dn = dn
nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
- if self.do_modify(nmsg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"],
+ if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
+ "local_oid:1.3.6.1.4.1.7165.4.3.14:0"],
"Failed to fix attribute %s" % attr):
self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
+ def err_replmetadata_unknown_attid(self, dn, attr, repl_meta_data):
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(repl_meta_data))
+ ctr = repl.ctr
+ for o in ctr.array:
+ # Search for an invalid attid
+ try:
+ att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
+ except KeyError:
+ self.report('ERROR: attributeID 0X%0X is not known in our schema, not fixing %s on %s\n' % (o.attid, attr, dn))
+ return
+
+
+ def err_replmetadata_unsorted_attid(self, dn, attr, repl_meta_data):
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(repl_meta_data))
+ ctr = repl.ctr
+ found = False
+
+ self.report('ERROR: unsorted attributeID values in %s on %s\n' % (attr, dn))
+ if not self.confirm_all('Fix %s on %s by sorting the attribute list?'
+ % (attr, dn), 'fix_replmetadata_unsorted_attid'):
+ self.report('Not fixing %s on %s\n' % (attr, dn))
+ return
+
+ # Sort the array, except for the last element
+ ctr.array[:-1] = sorted(ctr.array[:-1], key=lambda o: o.attid)
+
+ replBlob = ndr_pack(repl)
+
+ nmsg = ldb.Message()
+ nmsg.dn = dn
+ nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
+ if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
+ "local_oid:1.3.6.1.4.1.7165.4.3.14:0",
+ "local_oid:1.3.6.1.4.1.7165.4.3.25:0"],
+ "Failed to fix attribute %s" % attr):
+ self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
+
+
def is_deleted_deleted_objects(self, obj):
faulty = False
if "description" not in obj:
# We don't continue, as we may also have other fixes for this attribute
# based on what other attributes we see.
- list_attrs_from_md = self.process_metadata(obj[attrname])
+ try:
+ (list_attrs_from_md, list_attid_from_md) = self.process_metadata(obj[attrname])
+ except KeyError:
+ error_count += 1
+ self.err_replmetadata_unknown_attid(dn, attrname, obj[attrname])
+ continue
+
+ if sorted(list_attid_from_md[:-1]) != list_attid_from_md[:-1]:
+ error_count += 1
+ self.err_replmetadata_unsorted_attid(dn, attrname, obj[attrname])
+
got_repl_property_meta_data = True
continue
msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+ def test_error_replpropertymetadata_nochange(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+ def test_error_replpropertymetadata_allow_sort(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ self.samdb.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0", "local_oid:1.3.6.1.4.1.7165.4.3.25:0"])
+
def test_twoatt_replpropertymetadata(self):
res = self.samdb.search(expression="cn=Administrator",
scope=ldb.SCOPE_SUBTREE,
struct ldb_message_element *objectclass_el;
enum urgent_situation situation;
bool rmd_is_provided;
+ bool rmd_is_just_resorted = false;
if (rename_attrs) {
attrs = rename_attrs;
if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
rmd_is_provided = true;
+ if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
+ rmd_is_just_resorted = true;
+ }
} else {
rmd_is_provided = false;
}
/* In this case the change_replmetadata control was supplied */
/* We check that it's the only attribute that is provided
* (it's a rare case so it's better to keep the code simplier)
- * We also check that the highest local_usn is bigger than
+ * We also check that the highest local_usn is bigger or the same as
* uSNChanged. */
uint64_t db_seq;
if( msg->num_elements != 1 ||
ldb_dn_get_linearized(msg->dn)));
return LDB_ERR_OPERATIONS_ERROR;
}
- *seq_num = find_max_local_usn(omd);
ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
DSDB_FLAG_NEXT_MODULE |
return ret;
}
- db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
- if (*seq_num <= db_seq) {
- DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
- " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
- (long long)*seq_num, (long long)db_seq));
- return LDB_ERR_OPERATIONS_ERROR;
+ if (rmd_is_just_resorted == false) {
+ *seq_num = find_max_local_usn(omd);
+
+ db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
+
+ /*
+ * The test here now allows for a new
+ * replPropertyMetaData with no change, if was
+ * just dbcheck re-sorting the values.
+ */
+ if (*seq_num <= db_seq) {
+ DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
+ " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
+ (long long)*seq_num, (long long)db_seq));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
}
} else {
* replmd_update_rpmd_element has done an update if the
* seq_num is set
*/
- if (*seq_num != 0) {
+ if (*seq_num != 0 || rmd_is_just_resorted == true) {
struct ldb_val *md_value;
struct ldb_message_element *el;
*/
#define DSDB_CONTROL_RESTORE_TOMBSTONE_OID "1.3.6.1.4.1.7165.4.3.24"
+/**
+ OID used to allow the replacement of replPropertyMetaData.
+ It is used when the current replmetadata needs only to be re-sorted, but not edited.
+*/
+#define DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID "1.3.6.1.4.1.7165.4.3.25"
+
#define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1"
struct dsdb_extended_replicated_object {
struct ldb_message *msg;
#Allocated: DSDB_CONTROL_SEC_DESC_PROPAGATION_OID 1.3.6.1.4.1.7165.4.3.21
#Allocated: DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID 1.3.6.1.4.1.7165.4.3.23
#Allocated: DSDB_CONTROL_RESTORE_TOMBSTONE_OID 1.3.6.1.4.1.7165.4.3.24
+#Allocated: DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID 1.3.6.1.4.1.7165.4.3.25
# Extended 1.3.6.1.4.1.7165.4.4.x
#Allocated: DSDB_EXTENDED_REPLICATED_OBJECTS_OID 1.3.6.1.4.1.7165.4.4.1