objectclass AS_SYSTEM
[metze/samba/wip.git] / source4 / dsdb / samdb / ldb_modules / objectclass.c
index 0eceaefce4a15a45824e1e45ac34ec18637552e4..51b7b4c42398eaf275117d3f89b5f67f3966e60a 100644 (file)
@@ -3,7 +3,7 @@
 
    Copyright (C) Simo Sorce  2006-2008
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
-   Copyright (C) Matthias Dieter Wallnöfer 2010
+   Copyright (C) Matthias Dieter Wallnöfer 2010-2011
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -37,7 +37,6 @@
 
 #include "includes.h"
 #include "ldb_module.h"
-#include "util/dlinklist.h"
 #include "dsdb/samdb/samdb.h"
 #include "librpc/ndr/libndr.h"
 #include "librpc/gen_ndr/ndr_security.h"
@@ -45,8 +44,7 @@
 #include "auth/auth.h"
 #include "param/param.h"
 #include "../libds/common/flags.h"
-#include "dsdb/samdb/ldb_modules/schema.h"
-#include "util.h"
+#include "dsdb/samdb/ldb_modules/util.h"
 
 struct oc_context {
 
@@ -60,11 +58,6 @@ struct oc_context {
        int (*step_fn)(struct oc_context *);
 };
 
-struct class_list {
-       struct class_list *prev, *next;
-       const struct dsdb_class *objectclass;
-};
-
 static struct oc_context *oc_init_context(struct ldb_module *module,
                                          struct ldb_request *req)
 {
@@ -88,135 +81,58 @@ static struct oc_context *oc_init_context(struct ldb_module *module,
 
 static int objectclass_do_add(struct oc_context *ac);
 
-/* Sort objectClasses into correct order, and validate that all
- * objectClasses specified actually exist in the schema
+/*
+ * This checks if we have unrelated object classes in our entry's "objectClass"
+ * attribute. That means "unsatisfied" abstract classes (no concrete subclass)
+ * or two or more disjunct structural ones.
+ * If one of these conditions are true, blame.
  */
-
-static int objectclass_sort(struct ldb_module *module,
-                           const struct dsdb_schema *schema,
-                           TALLOC_CTX *mem_ctx,
-                           struct ldb_message_element *objectclass_element,
-                           struct class_list **sorted_out) 
+static int check_unrelated_objectclasses(struct ldb_module *module,
+                                       const struct dsdb_schema *schema,
+                                       const struct dsdb_class *struct_objectclass,
+                                       struct ldb_message_element *objectclass_element)
 {
-       struct ldb_context *ldb;
-       unsigned int i, lowest;
-       struct class_list *unsorted = NULL, *sorted = NULL, *current = NULL, *poss_parent = NULL, *new_parent = NULL, *current_lowest = NULL;
-
-       ldb = ldb_module_get_ctx(module);
-
-       /* DESIGN:
-        *
-        * We work on 4 different 'bins' (implemented here as linked lists):
-        *
-        * * sorted:       the eventual list, in the order we wish to push
-        *                 into the database.  This is the only ordered list.
-        *
-        * * parent_class: The current parent class 'bin' we are
-        *                 trying to find subclasses for
-        *
-        * * subclass:     The subclasses we have found so far
-        *
-        * * unsorted:     The remaining objectClasses
-        *
-        * The process is a matter of filtering objectClasses up from
-        * unsorted into sorted.  Order is irrelevent in the later 3 'bins'.
-        * 
-        * We start with 'top' (found and promoted to parent_class
-        * initially).  Then we find (in unsorted) all the direct
-        * subclasses of 'top'.  parent_classes is concatenated onto
-        * the end of 'sorted', and subclass becomes the list in
-        * parent_class.
-        *
-        * We then repeat, until we find no more subclasses.  Any left
-        * over classes are added to the end.
-        *
-        */
-
-       /* Firstly, dump all the objectClass elements into the
-        * unsorted bin, except for 'top', which is special */
-       for (i=0; i < objectclass_element->num_values; i++) {
-               current = talloc(mem_ctx, struct class_list);
-               if (!current) {
-                       return ldb_oom(ldb);
-               }
-               current->objectclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &objectclass_element->values[i]);
-               if (!current->objectclass) {
-                       ldb_asprintf_errstring(ldb, "objectclass %.*s is not a valid objectClass in schema", 
-                                              (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
-                       /* This looks weird, but windows apparently returns this for invalid objectClass values */
-                       return LDB_ERR_NO_SUCH_ATTRIBUTE;
-               } else if (current->objectclass->isDefunct) {
-                       ldb_asprintf_errstring(ldb, "objectclass %.*s marked as isDefunct objectClass in schema - not valid for new objects", 
-                                              (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
-                       /* This looks weird, but windows apparently returns this for invalid objectClass values */
-                       return LDB_ERR_NO_SUCH_ATTRIBUTE;
-               }
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       unsigned int i;
+       bool found;
 
-               /* Don't add top to list, we will do that later */
-               if (ldb_attr_cmp("top", current->objectclass->lDAPDisplayName) != 0) {
-                       DLIST_ADD_END(unsorted, current, struct class_list *);
-               }
+       if (schema == NULL) {
+               return LDB_SUCCESS;
        }
 
-       /* Add top here, to prevent duplicates */
-       current = talloc(mem_ctx, struct class_list);
-       current->objectclass = dsdb_class_by_lDAPDisplayName(schema, "top");
-       DLIST_ADD_END(sorted, current, struct class_list *);
+       for (i = 0; i < objectclass_element->num_values; i++) {
+               const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema,
+                                                                                          &objectclass_element->values[i]);
+               const struct dsdb_class *tmp_class2 = struct_objectclass;
 
-
-       /* For each object:  find parent chain */
-       for (current = unsorted; schema && current; current = current->next) {
-               for (poss_parent = unsorted; poss_parent; poss_parent = poss_parent->next) {
-                       if (ldb_attr_cmp(poss_parent->objectclass->lDAPDisplayName, current->objectclass->subClassOf) == 0) {
-                               break;
-                       }
-               }
-               /* If we didn't get to the end of the list, we need to add this parent */
-               if (poss_parent || (ldb_attr_cmp("top", current->objectclass->subClassOf) == 0)) {
+               /* Pointer comparison can be used due to the same schema str. */
+               if (tmp_class == NULL ||
+                   tmp_class == struct_objectclass ||
+                   tmp_class->objectClassCategory > 2 ||
+                   ldb_attr_cmp(tmp_class->lDAPDisplayName, "top") == 0) {
                        continue;
                }
 
-               new_parent = talloc(mem_ctx, struct class_list);
-               new_parent->objectclass = dsdb_class_by_lDAPDisplayName(schema, current->objectclass->subClassOf);
-               DLIST_ADD_END(unsorted, new_parent, struct class_list *);
-       }
-
-       do
-       {
-               lowest = UINT_MAX;
-               current_lowest = NULL;
-               for (current = unsorted; schema && current; current = current->next) {
-                       if(current->objectclass->subClass_order < lowest) {
-                               current_lowest = current;
-                               lowest = current->objectclass->subClass_order;
+               found = false;
+               while (!found &&
+                      ldb_attr_cmp(tmp_class2->lDAPDisplayName, "top") != 0) {
+                       tmp_class2 = dsdb_class_by_lDAPDisplayName(schema,
+                                                                  tmp_class2->subClassOf);
+                       if (tmp_class2 == tmp_class) {
+                               found = true;
                        }
                }
-
-               if(current_lowest != NULL) {
-                       DLIST_REMOVE(unsorted,current_lowest);
-                       DLIST_ADD_END(sorted,current_lowest, struct class_list *);
+               if (found) {
+                       continue;
                }
-       } while(unsorted);
-
 
-       if (!unsorted) {
-               *sorted_out = sorted;
-               return LDB_SUCCESS;
+               ldb_asprintf_errstring(ldb,
+                                      "objectclass: the objectclass '%s' seems to be unrelated to the entry!",
+                                      tmp_class->lDAPDisplayName);
+               return LDB_ERR_OBJECT_CLASS_VIOLATION;
        }
 
-       if (!schema) {
-               /* If we don't have schema yet, then just merge the lists again */
-               DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
-               *sorted_out = sorted;
-               return LDB_SUCCESS;
-       }
-
-       /* This shouldn't happen, and would break MMC, perhaps there
-        * was no 'top', a conflict in the objectClasses or some other
-        * schema error?
-        */
-       ldb_asprintf_errstring(ldb, "objectclass %s is not a valid objectClass in objectClass chain", unsorted->objectclass->lDAPDisplayName);
-       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+       return LDB_SUCCESS;
 }
 
 static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
@@ -354,7 +270,7 @@ static int fix_dn(struct ldb_context *ldb,
 #endif
 
 
-       /* And replace it with CN=foo (we need the attribute in upper case */
+       /* And replace it with CN=foo (we need the attribute in upper case) */
        return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
 }
 
@@ -368,7 +284,6 @@ static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
        struct oc_context *ac;
        struct ldb_dn *parent_dn;
        const struct ldb_val *val;
-       char *value;
        int ret;
        static const char * const parent_attrs[] = { "objectClass", NULL };
 
@@ -436,6 +351,14 @@ static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
                return ret;
        }
 
+       ret = dsdb_request_add_controls(search_req,
+                                       DSDB_FLAG_AS_SYSTEM|
+                                       DSDB_SEARCH_SHOW_DELETED|
+                                       DSDB_SEARCH_SHOW_RECYCLED);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
        ac->step_fn = objectclass_do_add;
 
        return ldb_next_request(ac->module, search_req);
@@ -450,7 +373,7 @@ static bool check_rodc_ntdsdsa_add(struct oc_context *ac,
 {
        struct ldb_control *rodc_control;
 
-       if (strcasecmp(objectclass->lDAPDisplayName, "nTDSDSA") != 0) {
+       if (ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") != 0) {
                return false;
        }
        rodc_control = ldb_request_get_control(ac->req, LDB_CONTROL_RODC_DCPROMO_OID);
@@ -468,8 +391,6 @@ static int objectclass_do_add(struct oc_context *ac)
        struct ldb_request *add_req;
        struct ldb_message_element *objectclass_element, *el;
        struct ldb_message *msg;
-       TALLOC_CTX *mem_ctx;
-       struct class_list *sorted, *current;
        const char *rdn_name = NULL;
        char *value;
        const struct dsdb_class *objectclass;
@@ -491,8 +412,8 @@ static int objectclass_do_add(struct oc_context *ac)
 
                /* An add operation on partition DNs without "NC-add" operation
                 * isn't allowed. */
-               instanceType = ldb_msg_find_attr_as_uint(ac->req->op.add.message,
-                                                        "instanceType", 0);
+               instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType",
+                                                        0);
                if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
                        ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!", 
                                               ldb_dn_get_linearized(msg->dn));
@@ -515,100 +436,53 @@ static int objectclass_do_add(struct oc_context *ac)
                }
        }
 
-       mem_ctx = talloc_new(ac);
-       if (mem_ctx == NULL) {
-               return ldb_module_oom(ac->module);
-       }
-
        if (ac->schema != NULL) {
+               /*
+                * Notice: by the normalization function call in "ldb_request()"
+                * case "LDB_ADD" we have always only *one* "objectClass"
+                * attribute at this stage!
+                */
+
                objectclass_element = ldb_msg_find_element(msg, "objectClass");
                if (!objectclass_element) {
                        ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, no objectclass specified!",
                                               ldb_dn_get_linearized(msg->dn));
-                       talloc_free(mem_ctx);
                        return LDB_ERR_OBJECT_CLASS_VIOLATION;
                }
                if (objectclass_element->num_values == 0) {
                        ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, at least one (structural) objectclass has to be specified!",
                                               ldb_dn_get_linearized(msg->dn));
-                       talloc_free(mem_ctx);
                        return LDB_ERR_CONSTRAINT_VIOLATION;
                }
 
-               /* Here we do now get the "objectClass" list from the
-                * database. */
-               ret = objectclass_sort(ac->module, ac->schema, mem_ctx,
-                                      objectclass_element, &sorted);
+               /* Now do the sorting */
+               ret = dsdb_sort_objectClass_attr(ldb, ac->schema,
+                                                objectclass_element, msg,
+                                                objectclass_element);
                if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
                        return ret;
                }
-               
-               ldb_msg_remove_element(msg, objectclass_element);
 
-               /* Well, now we shouldn't find any additional "objectClass"
-                * message element (required by the AD specification). */
-               objectclass_element = ldb_msg_find_element(msg, "objectClass");
-               if (objectclass_element != NULL) {
-                       ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, only one 'objectclass' attribute specification is allowed!",
+               /*
+                * Get the new top-most structural object class and check for
+                * unrelated structural classes
+                */
+               objectclass = dsdb_get_last_structural_class(ac->schema,
+                                                            objectclass_element);
+               if (objectclass == NULL) {
+                       ldb_asprintf_errstring(ldb,
+                                              "Failed to find a structural class for %s",
                                               ldb_dn_get_linearized(msg->dn));
-                       talloc_free(mem_ctx);
-                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
                }
 
-               /* We must completely replace the existing objectClass entry,
-                * because we need it sorted. */
-               ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
+               ret = check_unrelated_objectclasses(ac->module, ac->schema,
+                                                   objectclass,
+                                                   objectclass_element);
                if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
                        return ret;
                }
 
-               /* Move from the linked list back into an ldb msg */
-               for (current = sorted; current; current = current->next) {
-                       value = talloc_strdup(msg,
-                                             current->objectclass->lDAPDisplayName);
-                       if (value == NULL) {
-                               talloc_free(mem_ctx);
-                               return ldb_module_oom(ac->module);
-                       }
-
-                       /* LSA-specific objectclasses per default not allowed */
-                       if (((strcmp(value, "secret") == 0) ||
-                            (strcmp(value, "trustedDomain") == 0)) &&
-                           ldb_req_is_untrusted(ac->req)) {
-                               ldb_asprintf_errstring(ldb,
-                                                      "objectclass: object class '%s' is LSA-specific, rejecting creation of '%s'!",
-                                                      value,
-                                                      ldb_dn_get_linearized(msg->dn));
-                               return LDB_ERR_UNWILLING_TO_PERFORM;
-                       }
-
-                       ret = ldb_msg_add_string(msg, "objectClass", value);
-                       if (ret != LDB_SUCCESS) {
-                               ldb_set_errstring(ldb,
-                                                 "objectclass: could not re-add sorted "
-                                                 "objectclass to modify msg");
-                               talloc_free(mem_ctx);
-                               return ret;
-                       }
-               }
-
-               talloc_free(mem_ctx);
-
-               /* Retrive the message again so get_last_structural_class works */
-               objectclass_element = ldb_msg_find_element(msg, "objectClass");
-
-               /* Make sure its valid to add an object of this type */
-               objectclass = get_last_structural_class(ac->schema,
-                                                       objectclass_element);
-               if(objectclass == NULL) {
-                       ldb_asprintf_errstring(ldb,
-                                              "Failed to find a structural class for %s",
-                                              ldb_dn_get_linearized(msg->dn));
-                       return LDB_ERR_UNWILLING_TO_PERFORM;
-               }
-
                rdn_name = ldb_dn_get_rdn_name(msg->dn);
                if (rdn_name == NULL) {
                        return ldb_operr(ldb);
@@ -753,14 +627,15 @@ static int objectclass_do_add(struct oc_context *ac)
                } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0
                                || ldb_attr_cmp(objectclass->lDAPDisplayName, "serversContainer") == 0
                                || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") == 0) {
+                       if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0)
+                               systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
                        systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
-
                } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLink") == 0
+                               || ldb_attr_cmp(objectclass->lDAPDisplayName, "subnet") == 0
                                || ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLinkBridge") == 0
                                || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
                        systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
                }
-
                /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
 
                if (el || systemFlags != 0) {
@@ -781,11 +656,6 @@ static int objectclass_do_add(struct oc_context *ac)
                }
        }
 
-       ret = ldb_msg_sanity_check(ldb, msg);
-       if (ret != LDB_SUCCESS) {
-               return ret;
-       }
-
        ret = ldb_build_add_req(&add_req, ldb, ac,
                                msg,
                                ac->req->controls,
@@ -853,7 +723,7 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
        }
 
        /* MS-ADTS 3.1.1.5.3.5 - on a forest level < 2003 we do allow updates
-        * only on application NCs - not on the standard DCs */
+        * only on application NCs - not on the default ones */
        if (oc_changes &&
            (dsdb_forest_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
                struct ldb_dn *nc_root;
@@ -922,7 +792,7 @@ static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
        talloc_free(ares);
 
        /* this looks up the real existing object for fetching some important
-        * informations (objectclasses) */
+        * information (objectclasses) */
        ret = ldb_build_search_req(&search_req, ldb,
                                   ac, ac->req->op.mod.message->dn,
                                   LDB_SCOPE_BASE,
@@ -935,6 +805,14 @@ static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
                return ldb_module_done(ac->req, NULL, NULL, ret);
        }
 
+       ret = dsdb_request_add_controls(search_req,
+                                       DSDB_FLAG_AS_SYSTEM|
+                                       DSDB_SEARCH_SHOW_DELETED|
+                                       DSDB_SEARCH_SHOW_RECYCLED);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+
        ac->step_fn = objectclass_do_mod;
 
        ret = ldb_next_request(ac->module, search_req);
@@ -949,15 +827,12 @@ static int objectclass_do_mod(struct oc_context *ac)
 {
        struct ldb_context *ldb;
        struct ldb_request *mod_req;
-       char *value;
        struct ldb_message_element *oc_el_entry, *oc_el_change;
        struct ldb_val *vals;
        struct ldb_message *msg;
-       TALLOC_CTX *mem_ctx;
-       struct class_list *sorted, *current;
        const struct dsdb_class *objectclass;
        unsigned int i, j, k;
-       bool found, replace = false;
+       bool found;
        int ret;
 
        ldb = ldb_module_get_ctx(ac->module);
@@ -982,11 +857,6 @@ static int objectclass_do_mod(struct oc_context *ac)
 
        msg->dn = ac->req->op.mod.message->dn;
 
-       mem_ctx = talloc_new(ac);
-       if (mem_ctx == NULL) {
-               return ldb_module_oom(ac->module);
-       }
-
        /* We've to walk over all "objectClass" message elements */
        for (k = 0; k < ac->req->op.mod.message->num_elements; k++) {
                if (ldb_attr_cmp(ac->req->op.mod.message->elements[k].name,
@@ -1007,7 +877,6 @@ static int objectclass_do_mod(struct oc_context *ac)
                                                                       "objectclass: cannot re-add an existing objectclass: '%.*s'!",
                                                                       (int)oc_el_change->values[i].length,
                                                                       (const char *)oc_el_change->values[i].data);
-                                               talloc_free(mem_ctx);
                                                return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
                                        }
                                }
@@ -1017,7 +886,6 @@ static int objectclass_do_mod(struct oc_context *ac)
                                                      struct ldb_val,
                                                      oc_el_entry->num_values + 1);
                                if (vals == NULL) {
-                                       talloc_free(mem_ctx);
                                        return ldb_module_oom(ac->module);
                                }
                                oc_el_entry->values = vals;
@@ -1026,50 +894,18 @@ static int objectclass_do_mod(struct oc_context *ac)
                                ++(oc_el_entry->num_values);
                        }
 
-                       objectclass = get_last_structural_class(ac->schema,
-                                                               oc_el_change);
-                       if (objectclass != NULL) {
-                               ldb_asprintf_errstring(ldb,
-                                                      "objectclass: cannot add a new top-most structural objectclass '%s'!",
-                                                      objectclass->lDAPDisplayName);
-                               talloc_free(mem_ctx);
-                               return LDB_ERR_OBJECT_CLASS_VIOLATION;
-                       }
-
-                       /* Now do the sorting */
-                       ret = objectclass_sort(ac->module, ac->schema, mem_ctx,
-                                              oc_el_entry, &sorted);
-                       if (ret != LDB_SUCCESS) {
-                               talloc_free(mem_ctx);
-                               return ret;
-                       }
-
                        break;
 
                case LDB_FLAG_MOD_REPLACE:
-                       /* Do the sorting for the change message element */
-                       ret = objectclass_sort(ac->module, ac->schema, mem_ctx,
-                                              oc_el_change, &sorted);
-                       if (ret != LDB_SUCCESS) {
-                               talloc_free(mem_ctx);
-                               return ret;
-                       }
-
-                       /* this is a replace */
-                       replace = true;
+                       /*
+                        * In this case the new "oc_el_entry" is simply
+                        * "oc_el_change"
+                        */
+                       oc_el_entry = oc_el_change;
 
                        break;
 
                case LDB_FLAG_MOD_DELETE:
-                       /* get the actual top-most structural objectclass */
-                       objectclass = get_last_structural_class(ac->schema,
-                                                               oc_el_entry);
-                       if (objectclass == NULL) {
-                               /* no structural objectclass? */
-                               talloc_free(mem_ctx);
-                               return ldb_operr(ldb);
-                       }
-
                        /* Merge the two message elements */
                        for (i = 0; i < oc_el_change->num_values; i++) {
                                found = false;
@@ -1096,100 +932,47 @@ static int objectclass_do_mod(struct oc_context *ac)
                                                               "objectclass: cannot delete this objectclass: '%.*s'!",
                                                               (int)oc_el_change->values[i].length,
                                                               (const char *)oc_el_change->values[i].data);
-                                       talloc_free(mem_ctx);
                                        return LDB_ERR_NO_SUCH_ATTRIBUTE;
                                }
                        }
 
-                       /* Make sure that the top-most structural object class
-                        * hasn't been deleted */
-                       found = false;
-                       for (i = 0; i < oc_el_entry->num_values; i++) {
-                               if (ldb_attr_cmp(objectclass->lDAPDisplayName,
-                                                (char *)oc_el_entry->values[i].data) == 0) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-                       if (!found) {
-                               ldb_asprintf_errstring(ldb,
-                                                      "objectclass: cannot delete the top-most structural objectclass '%s'!",
-                                                      objectclass->lDAPDisplayName);
-                               talloc_free(mem_ctx);
-                               return LDB_ERR_OBJECT_CLASS_VIOLATION;
-                       }
-
-                       /* Now do the sorting */
-                       ret = objectclass_sort(ac->module, ac->schema, mem_ctx,
-                                              oc_el_entry, &sorted);
-                       if (ret != LDB_SUCCESS) {
-                               talloc_free(mem_ctx);
-                               return ret;
-                       }
-
                        break;
                }
 
-               /* (Re)-add an empty "objectClass" attribute on the object
-                * classes change message "msg". */
-               ldb_msg_remove_attr(msg, "objectClass");
-               ret = ldb_msg_add_empty(msg, "objectClass",
-                                       LDB_FLAG_MOD_REPLACE, &oc_el_change);
+               /* Now do the sorting */
+               ret = dsdb_sort_objectClass_attr(ldb, ac->schema, oc_el_entry,
+                                                msg, oc_el_entry);
                if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
                        return ret;
                }
 
-               /* Move from the linked list back into an ldb msg */
-               for (current = sorted; current; current = current->next) {
-                       value = talloc_strdup(msg,
-                                             current->objectclass->lDAPDisplayName);
-                       if (value == NULL) {
-                               talloc_free(mem_ctx);
-                               return ldb_module_oom(ac->module);
-                       }
-                       ret = ldb_msg_add_string(msg, "objectClass", value);
-                       if (ret != LDB_SUCCESS) {
-                               ldb_set_errstring(ldb,
-                                                 "objectclass: could not re-add sorted objectclasses!");
-                               talloc_free(mem_ctx);
-                               return ret;
-                       }
+               /*
+                * Get the new top-most structural object class and check for
+                * unrelated structural classes
+                */
+               objectclass = dsdb_get_last_structural_class(ac->schema,
+                                                            oc_el_entry);
+               if (objectclass == NULL) {
+                       ldb_set_errstring(ldb,
+                                         "objectclass: cannot delete all structural objectclasses!");
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
                }
 
-               if (replace) {
-                       /* Well, on replace we are nearly done: we have to test
-                        * if the change and entry message element are identical
-                        * ly. We can use "ldb_msg_element_compare" since now
-                        * the specified objectclasses match for sure in case.
-                        */
-                       ret = ldb_msg_element_compare(oc_el_entry,
-                                                     oc_el_change);
-                       if (ret == 0) {
-                               ret = ldb_msg_element_compare(oc_el_change,
-                                                             oc_el_entry);
-                       }
-                       if (ret == 0) {
-                               /* they are the same so we are done in this
-                                * case */
-                               talloc_free(mem_ctx);
-                               return ldb_module_done(ac->req, NULL, NULL,
-                                                      LDB_SUCCESS);
-                       } else {
-                               ldb_set_errstring(ldb,
-                                                 "objectclass: the specified objectclasses are not exactly the same as on the entry!");
-                               talloc_free(mem_ctx);
-                               return LDB_ERR_OBJECT_CLASS_VIOLATION;
-                       }
+               /* Check for unrelated objectclasses */
+               ret = check_unrelated_objectclasses(ac->module, ac->schema,
+                                                   objectclass,
+                                                   oc_el_entry);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
                }
-
-               /* Now we've applied all changes from "oc_el_change" to
-                * "oc_el_entry" therefore the new "oc_el_entry" will be
-                * "oc_el_change". */
-               oc_el_entry = oc_el_change;
        }
 
-       talloc_free(mem_ctx);
+       /* Now add the new object class attribute to the change message */
+       ret = ldb_msg_add(msg, oc_el_entry, LDB_FLAG_MOD_REPLACE);
+       if (ret != LDB_SUCCESS) {
+               ldb_module_oom(ac->module);
+               return ret;
+       }
 
        /* Now we have the real and definitive change left to do */
 
@@ -1239,7 +1022,7 @@ static int objectclass_rename(struct ldb_module *module, struct ldb_request *req
        }
 
        /* this looks up the parent object for fetching some important
-        * informations (objectclasses, DN normalisation...) */
+        * information (objectclasses, DN normalisation...) */
        ret = ldb_build_search_req(&search_req, ldb,
                                   ac, parent_dn, LDB_SCOPE_BASE,
                                   "(objectClass=*)",
@@ -1254,9 +1037,10 @@ static int objectclass_rename(struct ldb_module *module, struct ldb_request *req
        /* we have to add the show recycled control, as otherwise DRS
           deletes will be refused as we will think the target parent
           does not exist */
-       ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
-                                     false, NULL);
-
+       ret = dsdb_request_add_controls(search_req,
+                                       DSDB_FLAG_AS_SYSTEM|
+                                       DSDB_SEARCH_SHOW_DELETED|
+                                       DSDB_SEARCH_SHOW_RECYCLED);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -1291,7 +1075,7 @@ static int objectclass_do_rename(struct oc_context *ac)
        ac->search_res = NULL;
 
        /* this looks up the real existing object for fetching some important
-        * informations (objectclasses) */
+        * information (objectclasses) */
        ret = ldb_build_search_req(&search_req, ldb,
                                   ac, ac->req->op.rename.olddn,
                                   LDB_SCOPE_BASE,
@@ -1304,6 +1088,14 @@ static int objectclass_do_rename(struct oc_context *ac)
                return ret;
        }
 
+       ret = dsdb_request_add_controls(search_req,
+                                       DSDB_FLAG_AS_SYSTEM|
+                                       DSDB_SEARCH_SHOW_DELETED|
+                                       DSDB_SEARCH_SHOW_RECYCLED);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
        ac->step_fn = objectclass_do_rename2;
 
        return ldb_next_request(ac->module, search_req);
@@ -1340,7 +1132,8 @@ static int objectclass_do_rename2(struct oc_context *ac)
                        /* existing entry without a valid object class? */
                        return ldb_operr(ldb);
                }
-               objectclass = get_last_structural_class(ac->schema, oc_el_entry);
+               objectclass = dsdb_get_last_structural_class(ac->schema,
+                                                            oc_el_entry);
                if (objectclass == NULL) {
                        /* existing entry without a valid object class? */
                        return ldb_operr(ldb);
@@ -1443,6 +1236,7 @@ static int objectclass_delete(struct ldb_module *module, struct ldb_request *req
 {
        static const char * const attrs[] = { "nCName", "objectClass",
                                              "systemFlags",
+                                             "isDeleted",
                                              "isCriticalSystemObject", NULL };
        struct ldb_context *ldb;
        struct ldb_request *search_req;
@@ -1470,11 +1264,11 @@ static int objectclass_delete(struct ldb_module *module, struct ldb_request *req
        }
 
        /* this looks up the entry object for fetching some important
-        * informations (object classes, system flags...) */
+        * information (object classes, system flags...) */
        ret = ldb_build_search_req(&search_req, ldb,
                                   ac, req->op.del.dn, LDB_SCOPE_BASE,
                                   "(objectClass=*)",
-                                  attrs, NULL,
+                                  attrs, req->controls,
                                   ac, get_search_callback,
                                   req);
        LDB_REQ_SET_LOCATION(search_req);
@@ -1482,6 +1276,14 @@ static int objectclass_delete(struct ldb_module *module, struct ldb_request *req
                return ret;
        }
 
+       ret = dsdb_request_add_controls(search_req,
+                                       DSDB_FLAG_AS_SYSTEM|
+                                       DSDB_SEARCH_SHOW_DELETED|
+                                       DSDB_SEARCH_SHOW_RECYCLED);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
        ac->step_fn = objectclass_do_delete;
 
        return ldb_next_request(ac->module, search_req);
@@ -1506,7 +1308,7 @@ static int objectclass_do_delete(struct oc_context *ac)
        }
 
        /* DC's ntDSDSA object */
-       if (ldb_dn_compare(ac->req->op.del.dn, samdb_ntds_settings_dn(ldb)) == 0) {
+       if (ldb_dn_compare(ac->req->op.del.dn, samdb_ntds_settings_dn(ldb, ac)) == 0) {
                ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's ntDSDSA object!",
                                       ldb_dn_get_linearized(ac->req->op.del.dn));
                return LDB_ERR_UNWILLING_TO_PERFORM;
@@ -1529,6 +1331,17 @@ static int objectclass_do_delete(struct oc_context *ac)
                talloc_free(dn);
        }
 
+       /* Only trusted request from system account are allowed to delete
+        * deleted objects.
+        */
+       if (ldb_msg_check_string_attribute(ac->search_res->message, "isDeleted", "TRUE") &&
+                       (ldb_req_is_untrusted(ac->req) ||
+                               !dsdb_module_am_system(ac->module))) {
+               ldb_asprintf_errstring(ldb, "Delete of '%s' failed",
+                                               ldb_dn_get_linearized(ac->req->op.del.dn));
+               return LDB_ERR_UNWILLING_TO_PERFORM;
+       }
+
        /* crossRef objects regarding config, schema and default domain NCs */
        if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass",
                                 "crossRef") != NULL) {
@@ -1568,10 +1381,28 @@ static int objectclass_do_delete(struct oc_context *ac)
                isCriticalSystemObject = ldb_msg_find_attr_as_bool(ac->search_res->message,
                                                                   "isCriticalSystemObject", false);
                if (isCriticalSystemObject) {
-                       ldb_asprintf_errstring(ldb,
+                       /*
+                        * Following the explaination from Microsoft
+                        * https://lists.samba.org/archive/cifs-protocol/2011-August/002046.html
+                        * "I finished the investigation on this behavior.
+                        * As per MS-ADTS 3.1.5.5.7.2 , when a tree deletion is performed ,
+                        * every object in the tree will be checked to see if it has isCriticalSystemObject
+                        * set to TRUE, including the root node on which the delete operation is performed
+                        * But there is an exception  if the root object is a SAM specific objects(3.1.1.5.2.3 MS-ADTS)
+                        * Its deletion is done through SAM manger and isCriticalSystemObject attribute is not checked
+                        * The root node of the tree delete in your case is CN=ARES,OU=Domain Controllers,DC=w2k8r2,DC=home,DC=matws,DC=net
+                        * which is a SAM object  with  user class.  Therefore the tree deletion is performed without any error
+                        */
+
+                       if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "group") == NULL &&
+                           samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samDomain") == NULL &&
+                           samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samServer") == NULL &&
+                           samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "user") == NULL) {
+                                       ldb_asprintf_errstring(ldb,
                                               "objectclass: Cannot tree-delete %s, it's a critical system object!",
                                               ldb_dn_get_linearized(ac->req->op.del.dn));
                        return LDB_ERR_UNWILLING_TO_PERFORM;
+                       }
                }
        }