dsdb: Fix includes when building against system ldb.
[kamenim/samba.git] / source4 / dsdb / samdb / ldb_modules / objectclass.c
index 475b53a7cb01de341f6ff687e146bd958927db88..2df809701c64df1db3315cb23f0ca198bb2c4f94 100644 (file)
@@ -3,6 +3,7 @@
 
    Copyright (C) Simo Sorce  2006-2008
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
+   Copyright (C) Matthias Dieter Wallnöfer 2010
 
    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
 /*
  *  Name: ldb
  *
- *  Component: objectClass sorting module
+ *  Component: objectClass sorting and constraint checking module
  *
  *  Description: 
  *  - sort the objectClass attribute into the class
- *    hierarchy, 
- *  - fix DNs and attributes into 'standard' case
- *  - Add objectCategory and ntSecurityDescriptor defaults
+ *    hierarchy and perform constraint checks (correct RDN name,
+ *    valid parent),
+ *  - fix DNs into 'standard' case
+ *  - Add objectCategory and some other attribute defaults
  *
  *  Author: Andrew Bartlett
  */
@@ -35,7 +37,7 @@
 
 #include "includes.h"
 #include "ldb_module.h"
-#include "dlinklist.h"
+#include "util/dlinklist.h"
 #include "dsdb/samdb/samdb.h"
 #include "librpc/ndr/libndr.h"
 #include "librpc/gen_ndr/ndr_security.h"
@@ -49,8 +51,10 @@ struct oc_context {
 
        struct ldb_module *module;
        struct ldb_request *req;
+       const struct dsdb_schema *schema;
 
        struct ldb_reply *search_res;
+       struct ldb_reply *search_res2;
 
        int (*step_fn)(struct oc_context *);
 };
@@ -70,12 +74,13 @@ static struct oc_context *oc_init_context(struct ldb_module *module,
 
        ac = talloc_zero(req, struct oc_context);
        if (ac == NULL) {
-               ldb_set_errstring(ldb, "Out of Memory");
+               ldb_oom(ldb);
                return NULL;
        }
 
        ac->module = module;
        ac->req = req;
+       ac->schema = dsdb_get_schema(ldb, ac);
 
        return ac;
 }
@@ -93,7 +98,7 @@ static int objectclass_sort(struct ldb_module *module,
                            struct class_list **sorted_out) 
 {
        struct ldb_context *ldb;
-       int i, lowest;
+       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);
@@ -178,7 +183,7 @@ static int objectclass_sort(struct ldb_module *module,
 
        do
        {
-               lowest = INT_MAX;
+               lowest = UINT_MAX;
                current_lowest = NULL;
                for (current = unsorted; schema && current; current = current->next) {
                        if(current->objectclass->subClass_order < lowest) {
@@ -274,6 +279,11 @@ static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
                return ldb_module_done(ac->req, NULL, NULL,
                                        LDB_ERR_OPERATIONS_ERROR);
        }
+
+       if (ares->type == LDB_REPLY_REFERRAL) {
+               return ldb_module_send_referral(ac->req, ares->referral);
+       }
+
        if (ares->error != LDB_SUCCESS) {
                return ldb_module_done(ac->req, ares->controls,
                                        ares->response, ares->error);
@@ -341,41 +351,6 @@ static int fix_dn(TALLOC_CTX *mem_ctx,
        return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
 }
 
-/* Fix all attribute names to be in the correct case, and check they are all valid per the schema */
-static int fix_check_attributes(struct ldb_context *ldb,
-                               const struct dsdb_schema *schema,
-                               struct ldb_message *msg,
-                               enum ldb_request_type op)
-{
-       unsigned int i;
-       for (i=0; i < msg->num_elements; i++) {
-               const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema, msg->elements[i].name);
-               /* Add in a very special case for 'clearTextPassword',
-                * which is used for internal processing only, and is
-                * not presented in the schema */
-               if (!attribute) {
-                       if (strcasecmp(msg->elements[i].name, "clearTextPassword") != 0) {
-                               ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
-                               /* Apparently Windows sends exactly this behaviour */
-                               return LDB_ERR_NO_SUCH_ATTRIBUTE;
-                       }
-               } else {
-                       msg->elements[i].name = attribute->lDAPDisplayName;
-
-                       /* We have to deny write operations on constructed attributes */
-                       if ((attribute->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
-                               if (op == LDB_ADD) {
-                                       return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
-                               } else {
-                                       return LDB_ERR_CONSTRAINT_VIOLATION;
-                               }
-                       }
-
-               }
-       }
-
-       return LDB_SUCCESS;
-}
 
 static int objectclass_do_add(struct oc_context *ac);
 
@@ -429,7 +404,6 @@ static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
        if (ret != LDB_SUCCESS) {
                return ret;
        }
-       talloc_steal(search_req, parent_dn);
 
        ac->step_fn = objectclass_do_add;
 
@@ -439,7 +413,6 @@ static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
 static int objectclass_do_add(struct oc_context *ac)
 {
        struct ldb_context *ldb;
-       const struct dsdb_schema *schema;
        struct ldb_request *add_req;
        char *value;
        struct ldb_message_element *objectclass_element, *el;
@@ -452,19 +425,19 @@ static int objectclass_do_add(struct oc_context *ac)
        const char *rdn_name = NULL;
 
        ldb = ldb_module_get_ctx(ac->module);
-       schema = dsdb_get_schema(ldb);
 
        mem_ctx = talloc_new(ac);
        if (mem_ctx == NULL) {
+               ldb_oom(ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
 
-       /* Check we have a valid parent */
+       /* Check if we have a valid parent - this check is needed since
+        * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
        if (ac->search_res == NULL) {
-               if (ldb_dn_compare(ldb_get_root_basedn(ldb),
-                                                               msg->dn) == 0) {
+               if (ldb_dn_compare(ldb_get_root_basedn(ldb), msg->dn) == 0) {
                        /* Allow the tree to be started */
                        
                        /* but don't keep any error string, it's meaningless */
@@ -477,27 +450,22 @@ static int objectclass_do_add(struct oc_context *ac)
                }
        } else {
 
-               /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
+               /* Fix up the DN to be in the standard form, taking
+                * particular care to match the parent DN */
                ret = fix_dn(msg, 
                             ac->req->op.add.message->dn,
                             ac->search_res->message->dn,
                             &msg->dn);
 
                if (ret != LDB_SUCCESS) {
-                       ldb_asprintf_errstring(ldb, "Could not munge DN %s into normal form", 
+                       ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
                                               ldb_dn_get_linearized(ac->req->op.add.message->dn));
                        talloc_free(mem_ctx);
                        return ret;
                }
 
        }
-       if (schema) {
-               ret = fix_check_attributes(ldb, schema, msg, ac->req->operation);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
-                       return ret;
-               }
-
+       if (ac->schema != NULL) {
                /* This is now the objectClass list from the database */
                objectclass_element = ldb_msg_find_element(msg, "objectClass");
 
@@ -506,7 +474,8 @@ static int objectclass_do_add(struct oc_context *ac)
                        talloc_free(mem_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-               ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
+               ret = objectclass_sort(ac->module, ac->schema, mem_ctx,
+                                      objectclass_element, &sorted);
                if (ret != LDB_SUCCESS) {
                        talloc_free(mem_ctx);
                        return ret;
@@ -545,12 +514,13 @@ static int objectclass_do_add(struct oc_context *ac)
                objectclass_element = ldb_msg_find_element(msg, "objectClass");
 
                /* Make sure its valid to add an object of this type */
-               objectclass = get_last_structural_class(schema,objectclass_element);
+               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_NAMING_VIOLATION;
+                                              "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);
@@ -567,28 +537,20 @@ static int objectclass_do_add(struct oc_context *ac)
                                = ldb_msg_find_element(ac->search_res->message, "objectClass");
 
                        bool allowed_class = false;
-                       int i, j;
+                       unsigned int i, j;
                        for (i=0; allowed_class == false && oc_el && i < oc_el->num_values; i++) {
                                const struct dsdb_class *sclass;
 
-                               sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
+                               sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
+                                                                              &oc_el->values[i]);
                                if (!sclass) {
                                        /* We don't know this class?  what is going on? */
                                        continue;
                                }
-                               if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
-                                       for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
-                                               if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
-                                                       allowed_class = true;
-                                                       break;
-                                               }
-                                       }
-                               } else {
-                                       for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
-                                               if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
-                                                       allowed_class = true;
-                                                       break;
-                                               }
+                               for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
+                                       if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
+                                               allowed_class = true;
+                                               break;
                                        }
                                }
                        }
@@ -694,12 +656,9 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_message_element *objectclass_element;
        struct ldb_message *msg;
-       const struct dsdb_schema *schema = dsdb_get_schema(ldb);
-       struct class_list *sorted, *current;
        struct ldb_request *down_req;
        struct oc_context *ac;
-       TALLOC_CTX *mem_ctx;
-       char *value;
+       bool oc_changes = false;
        int ret;
 
        ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
@@ -708,11 +667,6 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
        if (ldb_dn_is_special(req->op.mod.message->dn)) {
                return ldb_next_request(module, req);
        }
-       
-       /* Without schema, there isn't much to do here */
-       if (!schema) {
-               return ldb_next_request(module, req);
-       }
 
        /* As with the "real" AD we don't accept empty messages */
        if (req->op.mod.message->num_elements == 0) {
@@ -726,141 +680,29 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* If no part of this touches the objectClass, then we don't
-        * need to make any changes.  */
-       objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
-
-       /* If the only operation is the deletion of the objectClass
-        * then go on with just fixing the attribute case */
-       if (!objectclass_element) {
-               msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
-               if (msg == NULL) {
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               
-               ret = fix_check_attributes(ldb, schema, msg, req->operation);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-
-               ret = ldb_build_mod_req(&down_req, ldb, ac,
-                                       msg,
-                                       req->controls,
-                                       ac, oc_op_callback,
-                                       req);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-
-               /* go on with the call chain */
-               return ldb_next_request(module, down_req);
-       }
-
-       switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
-       case LDB_FLAG_MOD_DELETE:
-               if (objectclass_element->num_values == 0) {
-                       return LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED;
-               }
-               break;
-
-       case LDB_FLAG_MOD_REPLACE:
-               mem_ctx = talloc_new(ac);
-               if (mem_ctx == NULL) {
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-
-               msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
-               if (msg == NULL) {
-                       talloc_free(mem_ctx);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-
-               ret = fix_check_attributes(ldb, schema, msg, req->operation);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
-                       return ret;
-               }
-
-               ret = objectclass_sort(module, schema, mem_ctx, objectclass_element, &sorted);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
-                       return ret;
-               }
-
-               /* We must completely replace the existing objectClass entry,
-                * because we need it sorted */
-               
-               ldb_msg_remove_attr(msg, "objectClass");
-               ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
-               
-               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) {
-                       /* copy the value as this string is on the schema
-                        * context and we can't rely on it not changing
-                        * before the operation is over */
-                       value = talloc_strdup(msg,
-                                       current->objectclass->lDAPDisplayName);
-                       if (value == NULL) {
-                               ldb_oom(ldb);
-                               talloc_free(mem_ctx);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
-                       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);
-
-               ret = ldb_msg_sanity_check(ldb, msg);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-
-               ret = ldb_build_mod_req(&down_req, ldb, ac,
-                                       msg,
-                                       req->controls,
-                                       ac, oc_op_callback,
-                                       req);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-
-               /* go on with the call chain */
-               return ldb_next_request(module, down_req);
+       /* Without schema, there isn't much to do here */
+       if (ac->schema == NULL) {
+               talloc_free(ac);
+               return ldb_next_request(module, req);
        }
 
-       /* This isn't the default branch of the switch, but a 'in any
-        * other case'.  When a delete isn't for all objectClasses for
-        * example
-        */
-
        msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
        if (msg == NULL) {
-               ldb_oom(ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = fix_check_attributes(ldb, schema, msg, req->operation);
-       if (ret != LDB_SUCCESS) {
-               ldb_oom(ldb);
-               return ret;
+       /* For now change everything except the objectclasses */
+
+       objectclass_element = ldb_msg_find_element(msg, "objectClass");
+       if (objectclass_element != NULL) {
+               ldb_msg_remove_attr(msg, "objectClass");
+               oc_changes = true;
        }
 
        ret = ldb_build_mod_req(&down_req, ldb, ac,
                                msg,
-                               req->controls,
-                               ac, oc_modify_callback,
+                               req->controls, ac,
+                               oc_changes ? oc_modify_callback : oc_op_callback,
                                req);
        if (ret != LDB_SUCCESS) {
                return ret;
@@ -871,8 +713,8 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
 
 static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       struct ldb_context *ldb;
        static const char * const attrs[] = { "objectClass", NULL };
+       struct ldb_context *ldb;
        struct ldb_request *search_req;
        struct oc_context *ac;
        int ret;
@@ -884,6 +726,11 @@ static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
                return ldb_module_done(ac->req, NULL, NULL,
                                        LDB_ERR_OPERATIONS_ERROR);
        }
+
+       if (ares->type == LDB_REPLY_REFERRAL) {
+               return ldb_module_send_referral(ac->req, ares->referral);
+       }
+
        if (ares->error != LDB_SUCCESS) {
                return ldb_module_done(ac->req, ares->controls,
                                        ares->response, ares->error);
@@ -897,8 +744,11 @@ static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
 
        talloc_free(ares);
 
-       ret = ldb_build_search_req(&search_req, ldb, ac,
-                                  ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
+       /* this looks up the real existing object for fetching some important
+        * informations (objectclasses) */
+       ret = ldb_build_search_req(&search_req, ldb,
+                                  ac, ac->req->op.mod.message->dn,
+                                  LDB_SCOPE_BASE,
                                   "(objectClass=*)",
                                   attrs, NULL, 
                                   ac, get_search_callback,
@@ -913,75 +763,197 @@ static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
        if (ret != LDB_SUCCESS) {
                return ldb_module_done(ac->req, NULL, NULL, ret);
        }
+
        return LDB_SUCCESS;
 }
 
 static int objectclass_do_mod(struct oc_context *ac)
 {
        struct ldb_context *ldb;
-       const struct dsdb_schema *schema;
        struct ldb_request *mod_req;
        char *value;
-       struct ldb_message_element *objectclass_element;
+       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;
+       bool found, replace = false;
        int ret;
 
        ldb = ldb_module_get_ctx(ac->module);
 
+       /* we should always have a valid entry when we enter here */
        if (ac->search_res == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       schema = dsdb_get_schema(ldb);
 
-       mem_ctx = talloc_new(ac);
-       if (mem_ctx == NULL) {
+       oc_el_entry = ldb_msg_find_element(ac->search_res->message,
+                                          "objectClass");
+       if (oc_el_entry == NULL) {
+               /* existing entry without a valid object class? */
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       oc_el_change = ldb_msg_find_element(ac->req->op.mod.message,
+                                           "objectClass");
+       if (oc_el_change == NULL) {
+               /* we should have an objectclass change operation */
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* use a new message structure */
        msg = ldb_msg_new(ac);
        if (msg == NULL) {
-               ldb_set_errstring(ldb,
-                       "objectclass: could not create new modify msg");
-               talloc_free(mem_ctx);
+               ldb_oom(ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* This is now the objectClass list from the database */
-       objectclass_element = ldb_msg_find_element(ac->search_res->message, 
-                                                  "objectClass");
-       if (!objectclass_element) {
-               /* Where did it go?  bail now... */
-               talloc_free(mem_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       
-       /* modify dn */
        msg->dn = ac->req->op.mod.message->dn;
 
-       ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
-       if (ret != LDB_SUCCESS) {
-               return ret;
+       mem_ctx = talloc_new(ac);
+       if (mem_ctx == NULL) {
+               ldb_oom(ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* We must completely replace the existing objectClass entry.
-        * We could do a constrained add/del, but we are meant to be
-        * in a transaction... */
+       switch (oc_el_change->flags & LDB_FLAG_MOD_MASK) {
+       case LDB_FLAG_MOD_ADD:
+               /* Merge the two message elements */
+               for (i = 0; i < oc_el_change->num_values; i++) {
+                       for (j = 0; j < oc_el_entry->num_values; j++) {
+                               if (strcasecmp((char *)oc_el_change->values[i].data,
+                                              (char *)oc_el_entry->values[j].data) == 0) {
+                                       /* we cannot add an already existing object class */
+                                       talloc_free(mem_ctx);
+                                       return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+                               }
+                       }
+                       /* append the new object class value - code was copied
+                        * from "ldb_msg_add_value" */
+                       vals = talloc_realloc(oc_el_entry, oc_el_entry->values,
+                                             struct ldb_val,
+                                             oc_el_entry->num_values + 1);
+                       if (vals == NULL) {
+                               ldb_oom(ldb);
+                               talloc_free(mem_ctx);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       oc_el_entry->values = vals;
+                       oc_el_entry->values[oc_el_entry->num_values] =
+                               oc_el_change->values[i];
+                       ++(oc_el_entry->num_values);
+               }
+
+               objectclass = get_last_structural_class(ac->schema,
+                                                       oc_el_change);
+               if (objectclass != NULL) {
+                       /* we cannot add a new structural object class */
+                       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;
+
+               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) {
+                       talloc_free(mem_ctx);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               /* Merge the two message elements */
+               for (i = 0; i < oc_el_change->num_values; i++) {
+                       found = false;
+                       for (j = 0; j < oc_el_entry->num_values; j++) {
+                               if (strcasecmp((char *)oc_el_change->values[i].data,
+                                              (char *)oc_el_entry->values[j].data) == 0) {
+                                       found = true;
+                                       /* delete the object class value -
+                                        * code was copied from
+                                        * "ldb_msg_remove_element" */
+                                       if (j != oc_el_entry->num_values - 1) {
+                                               memmove(&oc_el_entry->values[j],
+                                                       &oc_el_entry->values[j+1],
+                                                       ((oc_el_entry->num_values-1) - j)*sizeof(struct ldb_val));
+                                       }
+                                       --(oc_el_entry->num_values);
+                                       break;
+                               }
+                       }
+                       if (!found) {
+                               /* we cannot delete a not existing object class */
+                               talloc_free(mem_ctx);
+                               return LDB_ERR_NO_SUCH_ATTRIBUTE;
+                       }
+               }
+
+               /* Make sure that the top-most structural objectclass wasn't
+                * deleted */
+               found = false;
+               for (i = 0; i < oc_el_entry->num_values; i++) {
+                       if (strcasecmp(objectclass->lDAPDisplayName,
+                           (char *)oc_el_entry->values[i].data) == 0) {
+                               found = true; break;
+                       }
+               }
+               if (!found) {
+                       talloc_free(mem_ctx);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+               }
 
-       ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
+
+               /* 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;
+       }
+
+       ret = ldb_msg_add_empty(msg, "objectClass",
+                               LDB_FLAG_MOD_REPLACE, &oc_el_change);
        if (ret != LDB_SUCCESS) {
-               ldb_set_errstring(ldb, "objectclass: could not clear objectclass in modify msg");
+               ldb_oom(ldb);
                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);
+               value = talloc_strdup(msg,
+                                     current->objectclass->lDAPDisplayName);
                if (value == NULL) {
                        ldb_oom(ldb);
+                       talloc_free(mem_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
                ret = ldb_msg_add_string(msg, "objectClass", value);
@@ -992,9 +964,32 @@ static int objectclass_do_mod(struct oc_context *ac)
                }
        }
 
+       talloc_free(mem_ctx);
+
+       if (replace) {
+               /* Well, on replace we are nearly done: we have to test if
+                * the change and entry message element are identically. 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 */
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                              LDB_SUCCESS);
+               } else {
+                       /* they're not exactly the same */
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+               }
+       }
+
+       /* in the other cases we have the real change left to do */
+
        ret = ldb_msg_sanity_check(ldb, msg);
        if (ret != LDB_SUCCESS) {
-               talloc_free(mem_ctx);
                return ret;
        }
 
@@ -1004,12 +999,9 @@ static int objectclass_do_mod(struct oc_context *ac)
                                ac, oc_op_callback,
                                ac->req);
        if (ret != LDB_SUCCESS) {
-               talloc_free(mem_ctx);
                return ret;
        }
 
-       talloc_free(mem_ctx);
-       /* perform the modify */
        return ldb_next_request(ac->module, mod_req);
 }
 
@@ -1017,7 +1009,7 @@ static int objectclass_do_rename(struct oc_context *ac);
 
 static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
 {
-       static const char * const attrs[] = { NULL };
+       static const char * const attrs[] = { "objectClass", NULL };
        struct ldb_context *ldb;
        struct ldb_request *search_req;
        struct oc_context *ac;
@@ -1028,18 +1020,11 @@ static int objectclass_rename(struct ldb_module *module, struct ldb_request *req
 
        ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
 
-       if (ldb_dn_is_special(req->op.rename.newdn)) { /* do not manipulate our control entries */
+       /* do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.rename.newdn)) {
                return ldb_next_request(module, req);
        }
 
-       /* Firstly ensure we are not trying to rename it to be a child of itself */
-       if ((ldb_dn_compare_base(req->op.rename.olddn, req->op.rename.newdn) == 0) 
-           && (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) != 0)) {
-               ldb_asprintf_errstring(ldb, "Cannot rename %s to be a child of itself",
-                                      ldb_dn_get_linearized(req->op.rename.olddn));
-               return LDB_ERR_UNWILLING_TO_PERFORM;
-       }
-
        ac = oc_init_context(module, req);
        if (ac == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
@@ -1051,10 +1036,8 @@ static int objectclass_rename(struct ldb_module *module, struct ldb_request *req
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /*
-         it makes a search request, looking for the parent DN to fix up the new DN
-         to a standard one, at objectclass_do_rename()
-        */
+       /* this looks up the parent object for fetching some important
+        * informations (objectclasses, DN normalisation...) */
        ret = ldb_build_search_req(&search_req, ldb,
                                   ac, parent_dn, LDB_SCOPE_BASE,
                                   "(objectClass=*)",
@@ -1077,11 +1060,51 @@ static int objectclass_rename(struct ldb_module *module, struct ldb_request *req
        ac->step_fn = objectclass_do_rename;
 
        return ldb_next_request(ac->module, search_req);
+}
+
+static int objectclass_do_rename2(struct oc_context *ac);
+
+static int objectclass_do_rename(struct oc_context *ac)
+{
+       static const char * const attrs[] = { "objectClass", NULL };
+       struct ldb_context *ldb;
+       struct ldb_request *search_req;
+       int ret;
+
+       ldb = ldb_module_get_ctx(ac->module);
+
+       /* Check if we have a valid parent - this check is needed since
+        * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
+       if (ac->search_res == NULL) {
+               ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!",
+                                      ldb_dn_get_linearized(ac->req->op.rename.newdn));
+               return LDB_ERR_OTHER;
+       }
+
+       /* now assign "search_res2" to the parent entry to have "search_res"
+        * free for another lookup */
+       ac->search_res2 = ac->search_res;
+       ac->search_res = NULL;
+
+       /* this looks up the real existing object for fetching some important
+        * informations (objectclasses) */
+       ret = ldb_build_search_req(&search_req, ldb,
+                                  ac, ac->req->op.rename.olddn,
+                                  LDB_SCOPE_BASE,
+                                  "(objectClass=*)",
+                                  attrs, NULL,
+                                  ac, get_search_callback,
+                                  ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
+       ac->step_fn = objectclass_do_rename2;
 
+       return ldb_next_request(ac->module, search_req);
 }
 
-static int objectclass_do_rename(struct oc_context *ac)
+static int objectclass_do_rename2(struct oc_context *ac)
 {
        struct ldb_context *ldb;
        struct ldb_request *rename_req;
@@ -1090,26 +1113,98 @@ static int objectclass_do_rename(struct oc_context *ac)
 
        ldb = ldb_module_get_ctx(ac->module);
 
-       /* Check we have a valid parent */
+       /* Check if we have a valid entry - this check is needed since
+        * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
        if (ac->search_res == NULL) {
-               ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!", 
+               ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, entry does not exist!",
                                       ldb_dn_get_linearized(ac->req->op.rename.newdn));
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+
+       if (ac->schema != NULL) {
+               struct ldb_message_element *oc_el_entry, *oc_el_parent;
+               const struct dsdb_class *objectclass;
+               const char *rdn_name;
+               bool allowed_class = false;
+               unsigned int i, j;
+
+               oc_el_entry = ldb_msg_find_element(ac->search_res->message,
+                                                  "objectClass");
+               if (oc_el_entry == NULL) {
+                       /* existing entry without a valid object class? */
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               objectclass = get_last_structural_class(ac->schema, oc_el_entry);
+               if (objectclass == NULL) {
+                       /* existing entry without a valid object class? */
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
+               if ((objectclass->rDNAttID != NULL) &&
+                   (ldb_attr_cmp(rdn_name, objectclass->rDNAttID) != 0)) {
+                       ldb_asprintf_errstring(ldb,
+                                              "objectclass: RDN %s is not correct for most specific structural objectclass %s, should be %s",
+                                              rdn_name,
+                                              objectclass->lDAPDisplayName,
+                                              objectclass->rDNAttID);
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               }
+
+               oc_el_parent = ldb_msg_find_element(ac->search_res2->message,
+                                                   "objectClass");
+               if (oc_el_parent == NULL) {
+                       /* existing entry without a valid object class? */
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               for (i=0; allowed_class == false && i < oc_el_parent->num_values; i++) {
+                       const struct dsdb_class *sclass;
+
+                       sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
+                                                                      &oc_el_parent->values[i]);
+                       if (!sclass) {
+                               /* We don't know this class?  what is going on? */
+                               continue;
+                       }
+                       for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
+                               if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
+                                       allowed_class = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (!allowed_class) {
+                       ldb_asprintf_errstring(ldb,
+                                              "objectclass: structural objectClass %s is not a valid child class for %s",
+                                              objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res2->message->dn));
+                       return LDB_ERR_NAMING_VIOLATION;
+               }
+       }
+
+       /* Ensure we are not trying to rename it to be a child of itself */
+       if ((ldb_dn_compare_base(ac->req->op.rename.olddn,
+                                ac->req->op.rename.newdn) == 0)  &&
+           (ldb_dn_compare(ac->req->op.rename.olddn,
+                           ac->req->op.rename.newdn) != 0)) {
+               ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s to be a child of itself",
+                                      ldb_dn_get_linearized(ac->req->op.rename.olddn));
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
-       
-       /* Fix up the DN to be in the standard form,
-        * taking particular care to match the parent DN */
+
+       /* Fix up the DN to be in the standard form, taking
+        * particular care to match the parent DN */
        ret = fix_dn(ac,
                     ac->req->op.rename.newdn,
-                    ac->search_res->message->dn,
+                    ac->search_res2->message->dn,
                     &fixed_dn);
        if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
+                                      ldb_dn_get_linearized(ac->req->op.rename.newdn));
                return ret;
-       }
 
-       /* TODO: Check this is a valid child to this parent,
-        * by reading the allowedChildClasses and
-        * allowedChildClasssesEffective attributes */
+       }
 
        ret = ldb_build_rename_req(&rename_req, ldb, ac,
                                   ac->req->op.rename.olddn, fixed_dn,