From af7658d6dd4ba862ae62f0ed0dd12877617dc534 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 14:01:21 +0100 Subject: [PATCH 01/11] ldb:rdn_name: normalize rdn_name in rdn_rename_callback() We already do that on 'add'. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 Signed-off-by: Stefan Metzmacher --- lib/ldb/modules/rdn_name.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/ldb/modules/rdn_name.c b/lib/ldb/modules/rdn_name.c index f44ea71..e8d5d8a 100644 --- a/lib/ldb/modules/rdn_name.c +++ b/lib/ldb/modules/rdn_name.c @@ -243,6 +243,7 @@ static int rdn_rename_callback(struct ldb_request *req, struct ldb_reply *ares) struct rename_context *ac; struct ldb_request *mod_req; const char *rdn_name; + const struct ldb_schema_attribute *a = NULL; const struct ldb_val *rdn_val_p; struct ldb_val rdn_val; struct ldb_message *msg; @@ -286,6 +287,15 @@ static int rdn_rename_callback(struct ldb_request *req, struct ldb_reply *ares) goto error; } + a = ldb_schema_attribute_by_name(ldb, rdn_name); + if (a == NULL) { + goto error; + } + + if (a->name != NULL) { + rdn_name = a->name; + } + rdn_val_p = ldb_dn_get_rdn_val(msg->dn); if (rdn_val_p == NULL) { goto error; -- 1.9.1 From b11c59b6a8a9857ace9cc0f56d5ac84311f141bd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 18:01:47 +0100 Subject: [PATCH 02/11] ldb:controls: add LDB_CONTROL_RECALCULATE_RDN_OID This will be used by 'samba-tool dbcheck' to fix the rdn attribute name. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 Signed-off-by: Stefan Metzmacher --- lib/ldb/common/ldb_controls.c | 22 ++++++++++++++++++++++ lib/ldb/include/ldb.h | 8 ++++++++ source4/setup/schema_samba4.ldif | 1 + 3 files changed, 31 insertions(+) diff --git a/lib/ldb/common/ldb_controls.c b/lib/ldb/common/ldb_controls.c index a83768a..3b474c2 100644 --- a/lib/ldb/common/ldb_controls.c +++ b/lib/ldb/common/ldb_controls.c @@ -821,6 +821,28 @@ struct ldb_control *ldb_parse_control_from_string(struct ldb_context *ldb, TALLO return ctrl; } + if (LDB_CONTROL_CMP(control_strings, LDB_CONTROL_RECALCULATE_RDN_NAME) == 0) { + const char *p; + int crit, ret; + + p = &(control_strings[sizeof(LDB_CONTROL_RECALCULATE_RDN_NAME)]); + ret = sscanf(p, "%d", &crit); + if ((ret != 1) || (crit < 0) || (crit > 1)) { + ldb_set_errstring(ldb, + "invalid recalculate_rdn control syntax\n" + " syntax: crit(b)\n" + " note: b = boolean"); + talloc_free(ctrl); + return NULL; + } + + ctrl->oid = LDB_CONTROL_RECALCULATE_RDN_OID; + ctrl->critical = crit; + ctrl->data = NULL; + + return ctrl; + } + if (LDB_CONTROL_CMP(control_strings, LDB_CONTROL_DOMAIN_SCOPE_NAME) == 0) { const char *p; int crit, ret; diff --git a/lib/ldb/include/ldb.h b/lib/ldb/include/ldb.h index 7422d46..bddb1fb 100644 --- a/lib/ldb/include/ldb.h +++ b/lib/ldb/include/ldb.h @@ -499,6 +499,14 @@ typedef int (*ldb_qsort_cmp_fn_t) (void *v1, void *v2, void *opaque); #define LDB_CONTROL_BYPASS_OPERATIONAL_NAME "bypassoperational" /** + OID for recalculate RDN (rdn attribute and 'name') control. This control forces + the rdn_name module to the recalculate the rdn and name attributes as if the + object was just created. +*/ +#define LDB_CONTROL_RECALCULATE_RDN_OID "1.3.6.1.4.1.7165.4.3.30" +#define LDB_CONTROL_RECALCULATE_RDN_NAME "recalculate_rdn" + +/** OID for recalculate SD control. This control force the dsdb code to recalculate the SD of the object as if the object was just created. diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif index 04505de..b63bd30 100644 --- a/source4/setup/schema_samba4.ldif +++ b/source4/setup/schema_samba4.ldif @@ -217,6 +217,7 @@ #Allocated: DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID 1.3.6.1.4.1.7165.4.3.27 #Allocated: DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID 1.3.6.1.4.1.7165.4.3.28 #Allocated: DSDB_CONTROL_REPLMD_VANISH_LINKS 1.3.6.1.4.1.7165.4.3.29 +#Allocated: LDB_CONTROL_RECALCULATE_RDN_OID 1.3.6.1.4.1.7165.4.3.30 # 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 -- 1.9.1 From 67f99d9e8bafc845cecaeb583fa376dd292794eb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 18:03:26 +0100 Subject: [PATCH 03/11] ldb:rdn_name: add support for LDB_CONTROL_RECALCULATE_RDN_OID on ldb_modify() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 Signed-off-by: Stefan Metzmacher --- lib/ldb/modules/rdn_name.c | 141 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/lib/ldb/modules/rdn_name.c b/lib/ldb/modules/rdn_name.c index e8d5d8a..4697782 100644 --- a/lib/ldb/modules/rdn_name.c +++ b/lib/ldb/modules/rdn_name.c @@ -377,11 +377,20 @@ static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req) return ldb_next_request(module, down_req); } +static int rdn_recalculate_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct ldb_request *up_req = talloc_get_type(req->context, struct ldb_request); + + talloc_steal(up_req, req); + return up_req->callback(up_req, ares); +} + static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb; const struct ldb_val *rdn_val_p; struct ldb_message_element *e = NULL; + struct ldb_control *recalculate_rdn_control = NULL; ldb = ldb_module_get_ctx(module); @@ -390,6 +399,138 @@ static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req) return ldb_next_request(module, req); } + recalculate_rdn_control = ldb_request_get_control(req, + LDB_CONTROL_RECALCULATE_RDN_OID); + if (recalculate_rdn_control != NULL) { + struct ldb_message *msg = NULL; + const char *rdn_name = NULL; + struct ldb_val rdn_val; + const struct ldb_schema_attribute *a = NULL; + struct ldb_request *mod_req = NULL; + int ret; + struct ldb_message_element *rdn_del = NULL; + struct ldb_message_element *name_del = NULL; + + recalculate_rdn_control->critical = false; + + msg = ldb_msg_copy_shallow(req, req->op.mod.message); + if (msg == NULL) { + return ldb_module_oom(module); + } + + /* + * The caller must pass a dummy 'name' attribute + * in order to bypass some high level checks. + * + * We just remove it and check nothing is left. + */ + ldb_msg_remove_attr(msg, "name"); + + if (msg->num_elements != 0) { + return ldb_module_operr(module); + } + + rdn_name = ldb_dn_get_rdn_name(msg->dn); + if (rdn_name == NULL) { + return ldb_module_oom(module); + } + + a = ldb_schema_attribute_by_name(ldb, rdn_name); + if (a == NULL) { + return ldb_module_operr(module); + } + + if (a->name != NULL) { + rdn_name = a->name; + } + + rdn_val_p = ldb_dn_get_rdn_val(msg->dn); + if (rdn_val_p == NULL) { + return ldb_module_oom(module); + } + rdn_val = ldb_val_dup(msg, rdn_val_p); + if (rdn_val.length == 0) { + return ldb_module_oom(module); + } + + /* + * This is a bit tricky: + * + * We want _DELETE elements (as "rdn_del" and "name_del" without + * values) first, followed by _ADD (with the real names) + * elements (with values). Then we fix up the "rdn_del" and + * "name_del" attributes. + */ + + ret = ldb_msg_add_empty(msg, "rdn_del", LDB_FLAG_MOD_DELETE, NULL); + if (ret != 0) { + return ldb_module_oom(module); + } + ret = ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_ADD, NULL); + if (ret != 0) { + return ldb_module_oom(module); + } + ret = ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL); + if (ret != 0) { + return ldb_module_oom(module); + } + + ret = ldb_msg_add_empty(msg, "name_del", LDB_FLAG_MOD_DELETE, NULL); + if (ret != 0) { + return ldb_module_oom(module); + } + ret = ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_ADD, NULL); + if (ret != 0) { + return ldb_module_oom(module); + } + ret = ldb_msg_add_value(msg, "name", &rdn_val, NULL); + if (ret != 0) { + return ldb_module_oom(module); + } + + rdn_del = ldb_msg_find_element(msg, "rdn_del"); + if (rdn_del == NULL) { + return ldb_module_operr(module); + } + rdn_del->name = talloc_strdup(msg->elements, rdn_name); + if (rdn_del->name == NULL) { + return ldb_module_oom(module); + } + name_del = ldb_msg_find_element(msg, "name_del"); + if (name_del == NULL) { + return ldb_module_operr(module); + } + name_del->name = talloc_strdup(msg->elements, "name"); + if (name_del->name == NULL) { + return ldb_module_oom(module); + } + + ret = ldb_build_mod_req(&mod_req, ldb, + req, msg, NULL, + req, rdn_recalculate_callback, + req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(req, NULL, NULL, ret); + } + talloc_steal(mod_req, msg); + + ret = ldb_request_add_control(mod_req, + LDB_CONTROL_RECALCULATE_RDN_OID, + false, NULL); + if (ret != LDB_SUCCESS) { + return ldb_module_done(req, NULL, NULL, ret); + } + ret = ldb_request_add_control(mod_req, + LDB_CONTROL_PERMISSIVE_MODIFY_OID, + false, NULL); + if (ret != LDB_SUCCESS) { + return ldb_module_done(req, NULL, NULL, ret); + } + + /* go on with the call chain */ + return ldb_next_request(module, mod_req); + } + rdn_val_p = ldb_dn_get_rdn_val(req->op.mod.message->dn); if (rdn_val_p == NULL) { return LDB_ERR_OPERATIONS_ERROR; -- 1.9.1 From 120704c8738a01200687a94020f70b0dbf3bc558 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 30 Oct 2016 23:54:44 +0100 Subject: [PATCH 04/11] s4:dsdb/repl_meta_data: normalize rdn attribute name via the schema BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 Signed-off-by: Stefan Metzmacher --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 10 ++++++++-- 1 file changed, 8 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 4a66697..7d12c23 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -1446,15 +1446,21 @@ static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb, NTTIME now, bool is_schema_nc) { + const char *rdn_name = ldb_dn_get_rdn_name(msg->dn); + const struct dsdb_attribute *rdn_attr = + dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name); + const char *attr_name = rdn_attr != NULL ? + rdn_attr->lDAPDisplayName : + rdn_name; struct ldb_message_element new_el = { .flags = LDB_FLAG_MOD_REPLACE, - .name = ldb_dn_get_rdn_name(msg->dn), + .name = attr_name, .num_values = 1, .values = discard_const_p(struct ldb_val, rdn_new) }; struct ldb_message_element old_el = { .flags = LDB_FLAG_MOD_REPLACE, - .name = ldb_dn_get_rdn_name(msg->dn), + .name = attr_name, .num_values = rdn_old ? 1 : 0, .values = discard_const_p(struct ldb_val, rdn_old) }; -- 1.9.1 From c4c1d9f83ee1380aa0e2ec90df1c1a1d6f8e9082 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 19:01:04 +0100 Subject: [PATCH 05/11] s4:dsdb/repl_meta_data: add more DSDB_FLAG_INTERNAL_*_META_DATA flags These will be required in some cases, e.g. when fixing 'CN: somename' into 'cn: somename' without changing the replication meta data, as the rdn attribute is only replicated implicitly. It's required to run samba-tool dbcheck on each dc and we should avoid forcing a bulk replication storm triggered by each dc. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 Signed-off-by: Stefan Metzmacher --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 11 +++++++++++ source4/dsdb/samdb/samdb.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 7d12c23..e1c6518 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -1306,6 +1306,12 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) { may_skip = true; } + if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_ADD) { + if (el->flags & DSDB_FLAG_INTERNAL_MAY_SKIP_META_DATA) { + el->flags &= ~DSDB_FLAG_INTERNAL_MAY_SKIP_META_DATA; + may_skip = true; + } + } } else if (old_el == NULL && el->num_values == 0) { if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) { may_skip = true; @@ -1326,6 +1332,11 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, may_skip = true; } + if (el->flags & DSDB_FLAG_INTERNAL_SKIP_META_DATA) { + el->flags &= ~DSDB_FLAG_INTERNAL_SKIP_META_DATA; + may_skip = true; + } + if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) { may_skip = false; el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA; diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index 176d065..2f5bca0 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -312,6 +312,11 @@ struct dsdb_extended_sec_desc_propagation_op { * must be in LDB_FLAG_INTERNAL_MASK * see also the values in lib/ldb/include/ldb_module.h */ +/* Force a meta data update. */ #define DSDB_FLAG_INTERNAL_FORCE_META_DATA 0x10000 +/* Never update meta data */ +#define DSDB_FLAG_INTERNAL_SKIP_META_DATA 0x20000 +/* May skip an LDB_FLAG_MOD_ADD is the values did not change */ +#define DSDB_FLAG_INTERNAL_MAY_SKIP_META_DATA 0x20000 #endif /* __SAMDB_H__ */ -- 1.9.1 From 729d729597cd5e3c20b1d2b6697b5510b9aa3e72 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 19:01:04 +0100 Subject: [PATCH 06/11] s4:dsdb/repl_meta_data: add support for LDB_CONTROL_RECALCULATE_RDN_OID to replmd_modify() These will be required in some cases, e.g. when fixing 'CN: somename' into 'cn: somename' without changing the replication meta data, as the rdn attribute is only replicated implicitly. It's required to run samba-tool dbcheck on each dc and we should avoid forcing a bulk replication storm triggered by each dc. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 Signed-off-by: Stefan Metzmacher --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 76 +++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index e1c6518..7d9a6bf 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -2747,6 +2747,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) unsigned int functional_level; const struct ldb_message_element *guid_el = NULL; struct ldb_control *sd_propagation_control; + struct ldb_control *recalculate_rdn_control = NULL; struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module), struct replmd_private); @@ -2796,6 +2797,81 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) return LDB_ERR_OPERATIONS_ERROR; } + recalculate_rdn_control = ldb_request_get_control(req, + LDB_CONTROL_RECALCULATE_RDN_OID); + if (recalculate_rdn_control != NULL) { + struct ldb_message_element *el = NULL; + const char *rdn_name = NULL; + const struct dsdb_attribute *rdn_attr = NULL; + int cmp; + + if (msg->num_elements != 4) { + return ldb_module_operr(module); + } + + rdn_name = ldb_dn_get_rdn_name(msg->dn); + if (rdn_name == NULL) { + return ldb_module_oom(module); + } + rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, + rdn_name); + if (rdn_attr == NULL) { + return ldb_module_operr(module); + } + + el = &msg->elements[0]; + if (el->flags != LDB_FLAG_MOD_DELETE) { + return ldb_module_operr(module); + } + el->flags |= DSDB_FLAG_INTERNAL_SKIP_META_DATA; + cmp = strcmp(el->name, rdn_attr->lDAPDisplayName); + if (cmp != 0) { + /* + * This must be an exact match! + */ + return ldb_module_operr(module); + } + + el = &msg->elements[1]; + if (el->flags != LDB_FLAG_MOD_ADD) { + return ldb_module_operr(module); + } + el->flags |= DSDB_FLAG_INTERNAL_MAY_SKIP_META_DATA; + cmp = strcmp(el->name, rdn_attr->lDAPDisplayName); + if (cmp != 0) { + /* + * This must be an exact match! + */ + return ldb_module_operr(module); + } + + el = &msg->elements[2]; + if (el->flags != LDB_FLAG_MOD_DELETE) { + return ldb_module_operr(module); + } + el->flags |= DSDB_FLAG_INTERNAL_SKIP_META_DATA; + cmp = strcmp(el->name, "name"); + if (cmp != 0) { + /* + * This must be an exact match! + */ + return ldb_module_operr(module); + } + + el = &msg->elements[3]; + if (el->flags != LDB_FLAG_MOD_ADD) { + return ldb_module_operr(module); + } + el->flags |= DSDB_FLAG_INTERNAL_MAY_SKIP_META_DATA; + cmp = strcmp(el->name, "name"); + if (cmp != 0) { + /* + * This must be an exact match! + */ + return ldb_module_operr(module); + } + } + ldb_msg_remove_attr(msg, "whenChanged"); ldb_msg_remove_attr(msg, "uSNChanged"); -- 1.9.1 From e97c690ea2a1a686435f50a304caf795424642f7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 11:32:33 +0100 Subject: [PATCH 07/11] dbcheck: add more checks to validate the RDN attribute names BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 Signed-off-by: Stefan Metzmacher --- python/samba/dbchecker.py | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index 294c320..68ca6bc 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -98,6 +98,7 @@ class dbcheck(object): self.dn_set = set() self.link_id_cache = {} + self.lDAPDisplayName_cache = {} self.name_map = {} try: res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % samdb.domain_dn(), scope=ldb.SCOPE_BASE, @@ -399,6 +400,15 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) self.link_id_cache[attrname] = (linkID, revname) return linkID, revname + def normalize_lDAPDisplayName(self, attrname): + attrname = attrname.lower() + if attrname in self.lDAPDisplayName_cache: + return self.lDAPDisplayName_cache[attrname] + attid = self.samdb_schema.get_attid_from_lDAPDisplayName(attrname) + v = self.samdb_schema.get_lDAPDisplayName_by_attid(attid) + self.lDAPDisplayName_cache[attrname] = v + return v; + def err_empty_attribute(self, dn, attrname): '''fix empty attributes''' self.report("ERROR: Empty attribute %s in %s" % (attrname, dn)) @@ -1496,6 +1506,9 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) if self.verbose: self.report("Checking object %s" % dn) + dn_rdn_name = dn.get_rdn_name().upper() + rdn_lDAPDisplayName = self.normalize_lDAPDisplayName(dn_rdn_name) + # If we modify the pass-by-reference attrs variable, then we get a # replPropertyMetadata for every object that we check. attrs = list(attrs) @@ -1506,7 +1519,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) if str(dn.get_rdn_name()).lower() in map(str.lower, attrs): attrs.append("name") if 'name' in map(str.lower, attrs): - attrs.append(dn.get_rdn_name()) + attrs.append(rdn_lDAPDisplayName) attrs.append("isDeleted") attrs.append("systemFlags") if '*' in attrs: @@ -1578,7 +1591,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) else: name_val = obj[attrname][0] - if str(attrname).lower() == str(obj.dn.get_rdn_name()).lower(): + if str(attrname).lower() == str(rdn_lDAPDisplayName).lower(): object_rdn_attr = attrname if len(obj[attrname]) != 1: error_count += 1 @@ -1777,7 +1790,15 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) self.report("ERROR: Not fixing missing 'name' on '%s'" % (str(obj.dn))) if object_rdn_attr is None: error_count += 1 - self.report("ERROR: Not fixing missing '%s' on '%s'" % (obj.dn.get_rdn_name(), str(obj.dn))) + self.report("ERROR: Not fixing missing '%s' on '%s'" % (rdn_lDAPDisplayName, str(obj.dn))) + elif object_rdn_attr != rdn_lDAPDisplayName: + error_count += 1 + self.report("ERROR: Not fixing attrname '%s' into '%s' on '%s'" % ( + object_rdn_attr, rdn_lDAPDisplayName, str(obj.dn))) + + if obj.dn.get_rdn_name() != dn_rdn_name: + if name_val is None: + name_val = obj.dn.get_rdn_value() if name_val is not None: parent_dn = None @@ -1787,7 +1808,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) if parent_dn is None: parent_dn = obj.dn.parent() expected_dn = ldb.Dn(self.samdb, "RDN=RDN,%s" % (parent_dn)) - expected_dn.set_component(0, obj.dn.get_rdn_name(), name_val) + expected_dn.set_component(0, obj.dn.get_rdn_name().upper(), name_val) if obj.dn == deleted_objects_dn: expected_dn = obj.dn @@ -1795,9 +1816,19 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) if expected_dn != obj.dn: error_count += 1 self.err_wrong_dn(obj, expected_dn, object_rdn_attr, object_rdn_val, name_val) - elif obj.dn.get_rdn_value() != object_rdn_val: - error_count += 1 - self.report("ERROR: Not fixing %s=%r on '%s'" % (object_rdn_attr, object_rdn_val, str(obj.dn))) + else: + if obj.dn.get_rdn_value() != object_rdn_val: + error_count += 1 + self.report("ERROR: Not fixing %s=%r on '%s'" % (object_rdn_attr, object_rdn_val, str(obj.dn))) + + if obj.dn.get_rdn_name() != dn_rdn_name: + error_count += 1 + self.report("ERROR: Not fixing RDN '%s' into '%s' on '%s'" % ( + obj.dn.get_rdn_name(), dn_rdn_name, str(obj.dn))) + elif str(obj.dn) != str(expected_dn): + error_count += 1 + self.report("ERROR: Not fixing DN '%s' into '%s'" % ( + str(obj.dn), str(expected_dn))) show_dn = True if got_repl_property_meta_data: -- 1.9.1 From e905254de5597695ecc0e69e28f519defb2fca58 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 12:21:38 +0100 Subject: [PATCH 08/11] dbcheck: move search controls into a local variable Signed-off-by: Stefan Metzmacher --- python/samba/dbchecker.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index 68ca6bc..36a7da5 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -1527,21 +1527,22 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) else: attrs.append("objectGUID") - try: - sd_flags = 0 - sd_flags |= security.SECINFO_OWNER - sd_flags |= security.SECINFO_GROUP - sd_flags |= security.SECINFO_DACL - sd_flags |= security.SECINFO_SACL + sd_flags = 0 + sd_flags |= security.SECINFO_OWNER + sd_flags |= security.SECINFO_GROUP + sd_flags |= security.SECINFO_DACL + sd_flags |= security.SECINFO_SACL + search_controls = [ + "extended_dn:1:1", + "show_recycled:1", + "show_deleted:1", + "sd_flags:1:%d" % sd_flags, + "reveal_internals:0", + ] + try: res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, - controls=[ - "extended_dn:1:1", - "show_recycled:1", - "show_deleted:1", - "sd_flags:1:%d" % sd_flags, - "reveal_internals:0", - ], + controls=search_controls, attrs=attrs) except ldb.LdbError, (enum, estr): if enum == ldb.ERR_NO_SUCH_OBJECT: -- 1.9.1 From c13cde3f8b9f440325855d724e9ccd6bc6f0a703 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 10 Nov 2016 19:04:21 +0100 Subject: [PATCH 09/11] TODO dbcheck: fixup 'CN: somename' into 'cn: somename' TODO: we should do this fix up after a possible rename via self.err_wrong_dn(), that rename is more important and may already fix the problem... BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399 --- python/samba/dbchecker.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index 36a7da5..8b3c875 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -95,6 +95,7 @@ class dbcheck(object): self.fix_missing_deleted_objects = False self.fix_replica_locations = False self.fix_missing_rid_set_master = False + self.fix_rdn_name = False self.dn_set = set() self.link_id_cache = {} @@ -409,6 +410,26 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) self.lDAPDisplayName_cache[attrname] = v return v; + def err_wrong_rdn_attribute_name(self, obj, wrong_rdn_name, correct_rdn_name, rdn_val): + '''handle a wrong rdn attribute name''' + msg = "RDN attribute '%s' should be '%s' on '%s'" % (wrong_rdn_name, correct_rdn_name, obj.dn) + + self.report("ERROR: wrong %s" % msg) + if not self.confirm_all('Change %s?' % msg, 'fix_rdn_name'): + self.report('Not changing %s' % msg) + return + + new_rdn = ldb.Dn(self.samdb, str(obj.dn)) + new_rdn.remove_base_components(len(obj.dn) - 1) + new_parent = obj.dn.parent() + + + m = ldb.Message() + m.dn = obj.dn + m['value'] = ldb.MessageElement('__ignore_recalculate_rdn__', ldb.FLAG_MOD_REPLACE, 'name') + if self.do_modify(m, ["recalculate_rdn:1"], 'Failed to correct %s' % msg): + self.report('Corrected %s' % msg) + def err_empty_attribute(self, dn, attrname): '''fix empty attributes''' self.report("ERROR: Empty attribute %s in %s" % (attrname, dn)) @@ -1794,9 +1815,22 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) self.report("ERROR: Not fixing missing '%s' on '%s'" % (rdn_lDAPDisplayName, str(obj.dn))) elif object_rdn_attr != rdn_lDAPDisplayName: error_count += 1 - self.report("ERROR: Not fixing attrname '%s' into '%s' on '%s'" % ( + if name_val is not None: + self.err_wrong_rdn_attribute_name(obj, object_rdn_attr, + rdn_lDAPDisplayName, + name_val) + else: + self.report("ERROR: Not fixing attrname '%s' into '%s' on '%s'" % ( object_rdn_attr, rdn_lDAPDisplayName, str(obj.dn))) + res = self.samdb.search(base=obj.dn, scope=ldb.SCOPE_BASE, + controls=search_controls, + attrs=attrs) + obj = res[0] + object_rdn_attr = rdn_lDAPDisplayName + object_rdn_val = obj[object_rdn_attr][0] + name_val = obj["name"][0] + if obj.dn.get_rdn_name() != dn_rdn_name: if name_val is None: name_val = obj.dn.get_rdn_value() -- 1.9.1 From f930cfb50265dca111afbf81a21cb560e507bbb9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 11 Nov 2016 15:39:25 +0100 Subject: [PATCH 10/11] python/samba/dbchecker.py TODO... --- python/samba/dbchecker.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index 8b3c875..68c7cee 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -1608,7 +1608,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) 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'" % + self.report("ERROR: num_values(%d) for '%s' on '%s'" % (len(obj[attrname]), attrname, str(obj.dn))) else: name_val = obj[attrname][0] @@ -1617,7 +1617,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) object_rdn_attr = attrname if len(obj[attrname]) != 1: error_count += 1 - self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" % + self.report("ERROR: num_values(%d) for '%s' on '%s'" % (len(obj[attrname]), attrname, str(obj.dn))) else: object_rdn_val = obj[attrname][0] @@ -1809,10 +1809,18 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) 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))) + self.report("ERROR: missing 'name' on '%s'" % (str(obj.dn))) + name_val = obj.dn.get_rdn_value() if object_rdn_attr is None: error_count += 1 - self.report("ERROR: Not fixing missing '%s' on '%s'" % (rdn_lDAPDisplayName, str(obj.dn))) + self.report("ERROR: missing '%s' on '%s'" % (rdn_lDAPDisplayName, str(obj.dn))) + if object_rdn_val is None: + object_rdn_val = name_val + if object_rdn_val != name_val: + error_count += 1 + self.report("ERROR: value difference %s=[%s] %s[%s] on '%s'" % ( + rdn_lDAPDisplayName, str(obj.dn))) + elif object_rdn_attr != rdn_lDAPDisplayName: error_count += 1 if name_val is not None: -- 1.9.1 From a8ac7ade8e6285f97d9059835f3c9b53e7a66c5e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 11 Nov 2016 15:39:34 +0100 Subject: [PATCH 11/11] Revert "python/samba/dbchecker.py TODO..." This reverts commit 811d00ab9b7096b68c1c3c9f5740ef6438d5261c. --- python/samba/dbchecker.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index 68c7cee..8b3c875 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -1608,7 +1608,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) if str(attrname).lower() == "name": if len(obj[attrname]) != 1: error_count += 1 - self.report("ERROR: num_values(%d) for '%s' on '%s'" % + 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] @@ -1617,7 +1617,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) object_rdn_attr = attrname if len(obj[attrname]) != 1: error_count += 1 - self.report("ERROR: num_values(%d) for '%s' on '%s'" % + self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" % (len(obj[attrname]), attrname, str(obj.dn))) else: object_rdn_val = obj[attrname][0] @@ -1809,18 +1809,10 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) if ("*" in attrs or "name" in map(str.lower, attrs)): if name_val is None: error_count += 1 - self.report("ERROR: missing 'name' on '%s'" % (str(obj.dn))) - name_val = obj.dn.get_rdn_value() + self.report("ERROR: Not fixing missing 'name' on '%s'" % (str(obj.dn))) if object_rdn_attr is None: error_count += 1 - self.report("ERROR: missing '%s' on '%s'" % (rdn_lDAPDisplayName, str(obj.dn))) - if object_rdn_val is None: - object_rdn_val = name_val - if object_rdn_val != name_val: - error_count += 1 - self.report("ERROR: value difference %s=[%s] %s[%s] on '%s'" % ( - rdn_lDAPDisplayName, str(obj.dn))) - + self.report("ERROR: Not fixing missing '%s' on '%s'" % (rdn_lDAPDisplayName, str(obj.dn))) elif object_rdn_attr != rdn_lDAPDisplayName: error_count += 1 if name_val is not None: -- 1.9.1