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
#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"
#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 {
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)
{
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;
- }
-
- 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;
+ ldb_asprintf_errstring(ldb,
+ "objectclass: the objectclass '%s' seems to be unrelated to the entry!",
+ tmp_class->lDAPDisplayName);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
}
- /* 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)
#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);
}
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 };
instanceType = ldb_msg_find_attr_as_uint(req->op.add.message,
"instanceType", 0);
if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
+ char *referral_uri;
/* When we are trying to readd the root basedn then
* this is denied, but with an interesting mechanism:
* there is generated a referral with the last
if (val == NULL) {
return ldb_operr(ldb);
}
- value = talloc_asprintf(req, "ldap://%s/%s", val->data,
- ldb_dn_get_linearized(req->op.add.message->dn));
- if (value == NULL) {
+ referral_uri = talloc_asprintf(req, "ldap://%s/%s", val->data,
+ ldb_dn_get_linearized(req->op.add.message->dn));
+ if (referral_uri == NULL) {
return ldb_module_oom(module);
}
- return ldb_module_send_referral(req, value);
+ return ldb_module_send_referral(req, referral_uri);
}
}
{
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);
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;
/* 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));
}
}
- 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);
- 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!",
- ldb_dn_get_linearized(msg->dn));
- talloc_free(mem_ctx);
- return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ mem_ctx = talloc_new(ac);
+ if (mem_ctx == NULL) {
+ return ldb_module_oom(ac->module);
}
- /* We must completely replace the existing objectClass entry,
- * because we need it sorted. */
- ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
+ /* Now do the sorting */
+ ret = dsdb_sort_objectClass_attr(ldb, ac->schema, mem_ctx,
+ objectclass_element, msg,
+ 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) {
+ /*
+ * 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));
return LDB_ERR_UNWILLING_TO_PERFORM;
}
+ ret = check_unrelated_objectclasses(ac->module, ac->schema,
+ objectclass,
+ objectclass_element);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
rdn_name = ldb_dn_get_rdn_name(msg->dn);
if (rdn_name == NULL) {
return ldb_operr(ldb);
} 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) {
}
}
- 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,
}
/* 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;
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,
{
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);
++(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;
}
}
- /* 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, mem_ctx,
+ 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!");
+ talloc_free(mem_ctx);
+ 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) {
+ talloc_free(mem_ctx);
+ 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 */
ret = ldb_build_mod_req(&mod_req, ldb, ac,
}
/* 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=*)",
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,
/* 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);
{
static const char * const attrs[] = { "nCName", "objectClass",
"systemFlags",
+ "isDeleted",
"isCriticalSystemObject", NULL };
struct ldb_context *ldb;
struct ldb_request *search_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);
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) {
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;
+ }
}
}