dsdb: add vanish links control
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Tue, 5 Jul 2016 23:54:25 +0000 (11:54 +1200)
committerGarming Sam <garming@samba.org>
Fri, 15 Jul 2016 08:01:28 +0000 (10:01 +0200)
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 <douglas.bagnall@catalyst.net.nz>
Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Signed-off-by: Bob Campbell <bobcampbell@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Pair-programmed-with: Andrew Bartlett <abartlet@samba.org>

source4/dsdb/common/util.c
source4/dsdb/common/util.h
source4/dsdb/pydsdb.c
source4/dsdb/samdb/ldb_modules/repl_meta_data.c
source4/dsdb/samdb/samdb.h
source4/setup/schema_samba4.ldif

index bd0b5a33e1946c33c10dddcea19f31c294b67398..0bbf402252395b165520f264cdff56c4aab1e0b3 100644 (file)
@@ -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) {
index 10850736e7dc2a30d3e2d5b82970544a630e0f64..f2867a2e7d76bac9fe00e6cd1d4fc13a19ee40bc 100644 (file)
    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);
 
index faed682c5b1b9a7cd132dc6c63e33fd11d65cc44..efaf66b93cad907fe155063eecc0904f4a02e104 100644 (file)
@@ -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);
 
index efd1932a9ec23d6cc7e21e014e8c41d4503d4d6c..183747a38a584ac854adb570728ebb10a35d9c3b 100644 (file)
@@ -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; i<el->num_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; i<old_el->num_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; i<old_el->num_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));
index 0970948e27044b040e21f8b3ae9d95d4646e878f..438960d4da8d072452c10d35b7c5e32549e1f2cf 100644 (file)
@@ -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;
index ac56f51840ace1a1c269a3d4001314b9aabc3f36..604e1154006024e508deef678127e40f672dd484 100644 (file)
 #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