summaryrefslogtreecommitdiff
path: root/source4/dsdb/samdb/ldb_modules/objectclass.c
diff options
context:
space:
mode:
authorMatthias Dieter Wallnöfer <mdw@samba.org>2010-06-05 23:02:25 +0200
committerMatthias Dieter Wallnöfer <mdw@samba.org>2010-06-07 14:47:25 +0200
commite3c686daec130fb3c4a7457943173f31851a8e7d (patch)
tree1ae45824d713bb82d6b258de2c920ffe54523377 /source4/dsdb/samdb/ldb_modules/objectclass.c
parent17f465a4ac5562bec1b40dc97ac414fb3920175b (diff)
downloadsamba-e3c686daec130fb3c4a7457943173f31851a8e7d.tar.gz
samba-e3c686daec130fb3c4a7457943173f31851a8e7d.tar.bz2
samba-e3c686daec130fb3c4a7457943173f31851a8e7d.zip
s4:objectclass LDB module - rework the code which handles the objectclasses modification
Before it has been very incomplete. We try now to match the Windows Server behaviour as close as possible.
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules/objectclass.c')
-rw-r--r--source4/dsdb/samdb/ldb_modules/objectclass.c340
1 files changed, 191 insertions, 149 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/objectclass.c b/source4/dsdb/samdb/ldb_modules/objectclass.c
index 7f8c620e16..b15dff01f7 100644
--- a/source4/dsdb/samdb/ldb_modules/objectclass.c
+++ b/source4/dsdb/samdb/ldb_modules/objectclass.c
@@ -656,11 +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;
- 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");
@@ -682,131 +680,29 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
return LDB_ERR_OPERATIONS_ERROR;
}
+ /* Without schema, there isn't much to do here */
if (ac->schema == NULL) {
- /* Without schema, there isn't much to do here */
talloc_free(ac);
return ldb_next_request(module, req);
}
- /* 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 = 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);
+ msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
}
- 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;
- }
+ /* For now change everything except the objectclasses */
- ret = objectclass_sort(module, ac->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 */
-
+ objectclass_element = ldb_msg_find_element(msg, "objectClass");
+ if (objectclass_element != NULL) {
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);
- }
-
- /* 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;
+ 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;
@@ -817,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;
@@ -848,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,
@@ -864,6 +763,7 @@ 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;
}
@@ -872,66 +772,188 @@ static int objectclass_do_mod(struct oc_context *ac)
struct ldb_context *ldb;
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;
}
- 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, ac->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;
- ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
+ 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;
+ }
+
+
+ /* 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);
@@ -942,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;
}
@@ -954,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);
}