From 5ce969d0c70afc1f14a9b223edbaec7a847c64de Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 6 Jul 2016 11:54:25 +1200 Subject: [PATCH] dsdb: add vanish links control Normally linked attributes are deleted by marking them as with RMD flags, but sometimes we want them to vanish without trace. At those times we set the DSDB_CONTROL_REPLMD_VANISH_LINKS control. Signed-off-by: Douglas Bagnall Signed-off-by: Garming Sam Signed-off-by: Bob Campbell Reviewed-by: Andrew Bartlett Pair-programmed-with: Andrew Bartlett --- source4/dsdb/common/util.c | 7 + source4/dsdb/common/util.h | 33 ++--- source4/dsdb/pydsdb.c | 1 + .../dsdb/samdb/ldb_modules/repl_meta_data.c | 135 ++++++++++++++---- source4/dsdb/samdb/samdb.h | 3 + source4/setup/schema_samba4.ldif | 1 + 6 files changed, 138 insertions(+), 42 deletions(-) diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index bd0b5a33e19..0bbf4022523 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -4288,6 +4288,13 @@ int dsdb_request_add_controls(struct ldb_request *req, uint32_t dsdb_flags) } } + if (dsdb_flags & DSDB_REPLMD_VANISH_LINKS) { + ret = ldb_request_add_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS, true, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + if (dsdb_flags & DSDB_MODIFY_PARTIAL_REPLICA) { ret = ldb_request_add_control(req, DSDB_CONTROL_PARTIAL_REPLICA, false, NULL); if (ret != LDB_SUCCESS) { diff --git a/source4/dsdb/common/util.h b/source4/dsdb/common/util.h index 10850736e7d..f2867a2e7d7 100644 --- a/source4/dsdb/common/util.h +++ b/source4/dsdb/common/util.h @@ -26,22 +26,23 @@ flags for dsdb_request_add_controls(). For the module functions, the upper 16 bits are in dsdb/samdb/ldb_modules/util.h */ -#define DSDB_SEARCH_SEARCH_ALL_PARTITIONS 0x0001 -#define DSDB_SEARCH_SHOW_DELETED 0x0002 -#define DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT 0x0004 -#define DSDB_SEARCH_REVEAL_INTERNALS 0x0008 -#define DSDB_SEARCH_SHOW_EXTENDED_DN 0x0010 -#define DSDB_MODIFY_RELAX 0x0020 -#define DSDB_MODIFY_PERMISSIVE 0x0040 -#define DSDB_FLAG_AS_SYSTEM 0x0080 -#define DSDB_TREE_DELETE 0x0100 -#define DSDB_SEARCH_ONE_ONLY 0x0200 /* give an error unless 1 record */ -#define DSDB_SEARCH_SHOW_RECYCLED 0x0400 -#define DSDB_PROVISION 0x0800 -#define DSDB_BYPASS_PASSWORD_HASH 0x1000 -#define DSDB_SEARCH_NO_GLOBAL_CATALOG 0x2000 -#define DSDB_MODIFY_PARTIAL_REPLICA 0x4000 -#define DSDB_PASSWORD_BYPASS_LAST_SET 0x8000 +#define DSDB_SEARCH_SEARCH_ALL_PARTITIONS 0x00001 +#define DSDB_SEARCH_SHOW_DELETED 0x00002 +#define DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT 0x00004 +#define DSDB_SEARCH_REVEAL_INTERNALS 0x00008 +#define DSDB_SEARCH_SHOW_EXTENDED_DN 0x00010 +#define DSDB_MODIFY_RELAX 0x00020 +#define DSDB_MODIFY_PERMISSIVE 0x00040 +#define DSDB_FLAG_AS_SYSTEM 0x00080 +#define DSDB_TREE_DELETE 0x00100 +#define DSDB_SEARCH_ONE_ONLY 0x00200 /* give an error unless 1 record */ +#define DSDB_SEARCH_SHOW_RECYCLED 0x00400 +#define DSDB_PROVISION 0x00800 +#define DSDB_BYPASS_PASSWORD_HASH 0x01000 +#define DSDB_SEARCH_NO_GLOBAL_CATALOG 0x02000 +#define DSDB_MODIFY_PARTIAL_REPLICA 0x04000 +#define DSDB_PASSWORD_BYPASS_LAST_SET 0x08000 +#define DSDB_REPLMD_VANISH_LINKS 0x10000 bool is_attr_in_list(const char * const * attrs, const char *attr); diff --git a/source4/dsdb/pydsdb.c b/source4/dsdb/pydsdb.c index faed682c5b1..efaf66b93ca 100644 --- a/source4/dsdb/pydsdb.c +++ b/source4/dsdb/pydsdb.c @@ -1323,6 +1323,7 @@ void initdsdb(void) ADD_DSDB_STRING(DSDB_SYNTAX_OR_NAME); ADD_DSDB_STRING(DSDB_CONTROL_DBCHECK); ADD_DSDB_STRING(DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA); + ADD_DSDB_STRING(DSDB_CONTROL_REPLMD_VANISH_LINKS); ADD_DSDB_STRING(DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID); ADD_DSDB_STRING(DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID); diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index efd1932a9ec..183747a38a5 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -2177,6 +2177,9 @@ static int replmd_modify_la_delete(struct ldb_module *module, int ret; const struct GUID *invocation_id; struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_control *vanish_links_ctrl = NULL; + bool vanish_links = false; + unsigned int num_to_delete = el->num_values; NTTIME now; unix_to_nt_time(&now, t); @@ -2220,11 +2223,20 @@ static int replmd_modify_la_delete(struct ldb_module *module, return ret; } + if (parent) { + vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS); + if (vanish_links_ctrl) { + vanish_links = true; + vanish_links_ctrl->critical = false; + } + } + + el->num_values = 0; el->values = NULL; /* see if we are being asked to delete any links that don't exist or are already deleted */ - for (i=0; inum_values; i++) { + for (i=0; i < num_to_delete; i++) { struct parsed_dn *p = &dns[i]; struct parsed_dn *p2; uint32_t rmd_flags; @@ -2245,8 +2257,15 @@ static int replmd_modify_la_delete(struct ldb_module *module, rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn); if (rmd_flags & DSDB_RMD_FLAG_DELETED) { struct GUID_txt_buf buf; + const char *guid_str = GUID_buf_string(&p->guid, &buf); + if (vanish_links) { + DEBUG(0, ("Deleting deleted linked attribute %s to %s, " + "because vanish_links control is set\n", + el->name, guid_str)); + continue; + } ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s", - el->name, GUID_buf_string(&p->guid, &buf)); + el->name, guid_str); if (ldb_attr_cmp(el->name, "member") == 0) { talloc_free(tmp_ctx); return LDB_ERR_UNWILLING_TO_PERFORM; @@ -2257,34 +2276,75 @@ static int replmd_modify_la_delete(struct ldb_module *module, } } - /* for each new value, see if it exists already with the same GUID - if it is not already deleted and matches the delete list then delete it - */ - for (i=0; inum_values; i++) { - struct parsed_dn *p = &old_dns[i]; - uint32_t rmd_flags; + if (vanish_links) { + if (num_to_delete == old_el->num_values || num_to_delete == 0) { + el->flags = LDB_FLAG_MOD_REPLACE; - if (el->num_values && parsed_dn_find(dns, el->num_values, &p->guid, NULL) == NULL) { - continue; + for (i = 0; i < old_el->num_values; i++) { + ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + } + talloc_free(tmp_ctx); + return LDB_SUCCESS; + } else { + unsigned int num_values = 0; + unsigned int j = 0; + for (i = 0; i < old_el->num_values; i++) { + if (parsed_dn_find(dns, num_to_delete, &old_dns[i].guid, NULL) != NULL) { + /* The element is in the delete list. mark it dead. */ + ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + old_dns[i].v->length = 0; + } else { + num_values++; + } + } + for (i = 0; i < old_el->num_values; i++) { + if (old_el->values[i].length != 0) { + old_el->values[j] = old_el->values[i]; + j++; + if (j == num_values) { + break; + } + } + } + old_el->num_values = num_values; } + } else { - rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn); - if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue; + /* for each new value, see if it exists already with the same GUID + if it is not already deleted and matches the delete list then delete it + */ + for (i=0; inum_values; i++) { + struct parsed_dn *p = &old_dns[i]; + uint32_t rmd_flags; - ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn, - invocation_id, seq_num, seq_num, now, 0, true); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; - } + if (num_to_delete && parsed_dn_find(dns, num_to_delete, &p->guid, NULL) == NULL) { + continue; + } - ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; + rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn); + if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue; + + ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn, + invocation_id, seq_num, seq_num, now, 0, true); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } } } - el->values = talloc_steal(msg->elements, old_el->values); el->num_values = old_el->num_values; @@ -2476,7 +2536,16 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module, } if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) { - /* don't do anything special for linked attributes */ + /* + * Nothing special is required for modifying or vanishing links + * in fl2000 since they are just strings in a multi-valued + * attribute. + */ + struct ldb_control *ctrl = ldb_request_get_control(parent, + DSDB_CONTROL_REPLMD_VANISH_LINKS); + if (ctrl) { + ctrl->critical = false; + } return LDB_SUCCESS; } @@ -3015,6 +3084,7 @@ static int replmd_delete_remove_link(struct ldb_module *module, const struct dsdb_attribute *target_attr; struct ldb_message_element *el2; struct ldb_val dn_val; + uint32_t dsdb_flags = 0; if (dsdb_dn_is_deleted_val(&el->values[i])) { continue; @@ -3058,7 +3128,13 @@ static int replmd_delete_remove_link(struct ldb_module *module, el2->values = &dn_val; el2->num_values = 1; - ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent); + /* + * Ensure that we tell the modification to vanish any linked + * attributes (not simply mark them as isDeleted = TRUE) + */ + dsdb_flags |= DSDB_REPLMD_VANISH_LINKS; + + ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -3146,6 +3222,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request NULL }; unsigned int i, el_count = 0; + uint32_t dsdb_flags = 0; enum deletion_state deletion_state, next_deletion_state; if (ldb_dn_is_special(req->op.del.dn)) { @@ -3505,6 +3582,12 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) { continue; } + } else { + /* + * Ensure that we tell the modification to vanish any linked + * attributes (not simply mark them as isDeleted = TRUE) + */ + dsdb_flags |= DSDB_REPLMD_VANISH_LINKS; } ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el); if (ret != LDB_SUCCESS) { @@ -3602,7 +3685,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request msg->dn = new_dn; } - ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req); + ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req); if (ret != LDB_SUCCESS) { ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s", ldb_dn_get_linearized(old_dn), ldb_errstring(ldb)); diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index 0970948e270..438960d4da8 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -182,6 +182,9 @@ struct dsdb_control_password_user_account_control { */ #define DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID "1.3.6.1.4.1.7165.4.3.28" +/* passed when we want to thoroughly delete linked attributes */ +#define DSDB_CONTROL_REPLMD_VANISH_LINKS "1.3.6.1.4.1.7165.4.3.29" + #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; diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif index ac56f51840a..604e1154006 100644 --- a/source4/setup/schema_samba4.ldif +++ b/source4/setup/schema_samba4.ldif @@ -214,6 +214,7 @@ #Allocated: DSDB_CONTROL_PASSWORD_DEFAULT_LAST_SET_OID 1.3.6.1.4.1.7165.4.3.26 #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 # 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 -- 2.34.1