/*
ldb database library
- Copyright (C) Simo Sorce 2004
+ Copyright (C) Andrew Bartlett 2005-2009
+ Copyright (C) Simo Sorce 2006-2008
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
+ version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
- * Name: ldb
+ * Name: rdn_name
*
- * Component: ldb objectguid module
+ * Component: ldb rdn name module
*
- * Description: add a unique objectGUID onto every new record
+ * Description: keep a consistent name attribute on objects manpulations
*
- * Author: Simo Sorce
+ * Author: Andrew Bartlett
+ *
+ * Modifications:
+ * - made the module async
+ * Simo Sorce Mar 2006
*/
-#include "includes.h"
-#include "ldb/include/includes.h"
+#include "ldb_includes.h"
+#include "ldb_module.h"
+
+struct rename_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
-static struct ldb_message_element *rdn_name_find_attribute(const struct ldb_message *msg, const char *name)
+ struct ldb_reply *ares;
+};
+
+static int rdn_name_add_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
{
- int i;
+ struct rename_context *ac;
- for (i = 0; i < msg->num_elements; i++) {
- if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
- return &msg->elements[i];
- }
+ ac = talloc_get_type(req->context, struct rename_context);
+
+ if (!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);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
}
- return NULL;
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
}
static int rdn_name_add(struct ldb_module *module, struct ldb_request *req)
{
- const struct ldb_message *msg = req->op.add.message;
- struct ldb_message *msg2;
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ struct rename_context *ac;
+ struct ldb_message *msg;
struct ldb_message_element *attribute;
- struct ldb_dn_component *rdn;
- int i, ret;
+ const struct ldb_schema_attribute *a;
+ const char *rdn_name;
+ struct ldb_val rdn_val;
+ unsigned int i;
+ int ret;
- ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_add_record\n");
+ ldb = ldb_module_get_ctx(module);
/* do not manipulate our control entries */
- if (ldb_dn_is_special(msg->dn)) {
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
return ldb_next_request(module, req);
}
- /* Perhaps someone above us knows better */
- if ((attribute = rdn_name_find_attribute(msg, "name")) != NULL ) {
- return ldb_next_request(module, req);
+ ac = talloc_zero(req, struct rename_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
}
- msg2 = talloc(module, struct ldb_message);
- if (!msg2) {
- return -1;
- }
+ ac->module = module;
+ ac->req = req;
- msg2->dn = msg->dn;
- msg2->num_elements = msg->num_elements;
- msg2->private_data = msg->private_data;
- msg2->elements = talloc_array(msg2, struct ldb_message_element, msg2->num_elements);
- for (i = 0; i < msg2->num_elements; i++) {
- msg2->elements[i] = msg->elements[i];
+ msg = ldb_msg_copy_shallow(req, req->op.add.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
}
- rdn = ldb_dn_get_rdn(msg2, msg2->dn);
- if (!rdn) {
- talloc_free(msg2);
- return -1;
+ rdn_name = ldb_dn_get_rdn_name(msg->dn);
+ if (rdn_name == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
}
- if (ldb_msg_add_value(msg2, "name", &rdn->value) != 0) {
- talloc_free(msg2);
- return -1;
+ rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(msg->dn));
+
+ /* Perhaps someone above us tried to set this? Then ignore it */
+ ldb_msg_remove_attr(msg, "name");
+
+ ret = ldb_msg_add_value(msg, "name", &rdn_val, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
- attribute = rdn_name_find_attribute(msg2, rdn->name);
+ a = ldb_schema_attribute_by_name(ldb, rdn_name);
+ if (a == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ attribute = ldb_msg_find_element(msg, rdn_name);
if (!attribute) {
- if (ldb_msg_add_value(msg2, rdn->name, &rdn->value) != 0) {
- talloc_free(msg2);
- return -1;
+ /* add entry with normalised RDN information if possible */
+ if (a->name != NULL) {
+ ret = ldb_msg_add_value(msg, a->name, &rdn_val, NULL);
+ } else {
+ ret = ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
} else {
- const struct ldb_attrib_handler *handler
- = ldb_attrib_handler(module->ldb, rdn->name);
- for (i=0; i < attribute->num_values; i++) {
- if (handler->comparison_fn(module->ldb, msg2, &rdn->value, &attribute->values[i]) == 0) {
+ /* normalise attribute name if possible */
+ if (a->name != NULL) {
+ attribute->name = a->name;
+ }
+ /* normalise attribute value */
+ for (i = 0; i < attribute->num_values; i++) {
+ bool matched;
+ if (a->syntax->operator_fn) {
+ ret = a->syntax->operator_fn(ldb, LDB_OP_EQUALITY, a,
+ &rdn_val, &attribute->values[i], &matched);
+ if (ret != LDB_SUCCESS) return ret;
+ } else {
+ matched = (a->syntax->comparison_fn(ldb, msg,
+ &rdn_val, &attribute->values[i]) == 0);
+ }
+ if (matched) {
/* overwrite so it matches in case */
- attribute->values[i] = rdn->value;
+ attribute->values[i] = rdn_val;
break;
}
}
if (i == attribute->num_values) {
- ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
- "RDN mismatch on %s: %s",
- ldb_dn_linearize(msg2, msg2->dn), rdn->name);
- talloc_free(msg2);
- return -1;
+ char *rdn_errstring = talloc_asprintf(ac,
+ "RDN mismatch on %s: %s (%.*s) should match one of:",
+ ldb_dn_get_linearized(msg->dn), rdn_name,
+ (int)rdn_val.length, (const char *)rdn_val.data);
+ for (i = 0; i < attribute->num_values; i++) {
+ rdn_errstring = talloc_asprintf_append(
+ rdn_errstring, " (%.*s)",
+ (int)attribute->values[i].length,
+ (const char *)attribute->values[i].data);
+ }
+ ldb_set_errstring(ldb, rdn_errstring);
+ /* Match AD's error here */
+ return LDB_ERR_INVALID_DN_SYNTAX;
}
}
- req->op.add.message = msg2;
- ret = ldb_next_request(module, req);
- req->op.add.message = msg;
+ ret = ldb_build_add_req(&down_req, ldb, req,
+ msg,
+ req->controls,
+ ac, rdn_name_add_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
- talloc_free(msg2);
+ talloc_steal(down_req, msg);
- return ret;
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
}
-static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
+static int rdn_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
{
- const struct ldb_message *msg = req->op.mod.message;
- struct ldb_message *msg2;
- struct ldb_message_element *attribute;
- struct ldb_dn_component *rdn;
- int ret, i;
+ struct rename_context *ac;
- ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_modify_record\n");
+ ac = talloc_get_type(req->context, struct rename_context);
- /* do not manipulate our control entries */
- if (ldb_dn_is_special(msg->dn)) {
- return ldb_next_request(module, req);
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
}
- /* Perhaps someone above us knows better */
- if ((attribute = rdn_name_find_attribute(msg, "name")) != NULL ) {
- return ldb_next_request(module, req);
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ return ldb_module_send_referral(ac->req, ares->referral);
}
- msg2 = talloc(module, struct ldb_message);
- if (!msg2) {
- return -1;
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
}
- msg2->dn = msg->dn;
- msg2->num_elements = msg->num_elements;
- msg2->private_data = msg->private_data;
- msg2->elements = talloc_array(msg2, struct ldb_message_element, msg2->num_elements);
- for (i = 0; i < msg2->num_elements; i++) {
- msg2->elements[i] = msg->elements[i];
+ /* the only supported reply right now is a LDB_REPLY_DONE */
+ if (ares->type != LDB_REPLY_DONE) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
}
-
- rdn = ldb_dn_get_rdn(msg2, msg2->dn);
- if (!rdn) {
- talloc_free(msg2);
- return -1;
+
+ /* send saved controls eventually */
+ return ldb_module_done(ac->req, ac->ares->controls,
+ ac->ares->response, LDB_SUCCESS);
+}
+
+static int rdn_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct rename_context *ac;
+ struct ldb_request *mod_req;
+ const char *rdn_name;
+ struct ldb_val rdn_val;
+ struct ldb_message *msg;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct rename_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ goto error;
}
-
- if (ldb_msg_add_value(msg2, "name", &rdn->value) != 0) {
- talloc_free(msg2);
- return -1;
+
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ return ldb_module_send_referral(ac->req, ares->referral);
}
- attribute = rdn_name_find_attribute(msg2, "name");
- if (!attribute) {
- talloc_free(msg2);
- return -1;
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
}
- attribute->flags = LDB_FLAG_MOD_REPLACE;
+ /* the only supported reply right now is a LDB_REPLY_DONE */
+ if (ares->type != LDB_REPLY_DONE) {
+ goto error;
+ }
+
+ /* save reply for caller */
+ ac->ares = talloc_steal(ac, ares);
+
+ msg = ldb_msg_new(ac);
+ if (msg == NULL) {
+ goto error;
+ }
+ msg->dn = ldb_dn_copy(msg, ac->req->op.rename.newdn);
+ if (msg->dn == NULL) {
+ goto error;
+ }
+ rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
+ if (rdn_name == NULL) {
+ goto error;
+ }
+
+ rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(ac->req->op.rename.newdn));
+
+ if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
+ goto error;
+ }
- req->op.add.message = msg2;
- ret = ldb_next_request(module, req);
- req->op.add.message = msg;
+ ret = ldb_build_mod_req(&mod_req, ldb,
+ ac, msg, NULL,
+ ac, rdn_modify_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ talloc_steal(mod_req, msg);
- talloc_free(msg2);
+ /* go on with the call chain */
+ return ldb_next_request(ac->module, mod_req);
- return ret;
+error:
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
}
-static int rdn_name_request(struct ldb_module *module, struct ldb_request *req)
+static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
{
- switch (req->operation) {
-
- case LDB_REQ_ADD:
- return rdn_name_add(module, req);
+ struct ldb_context *ldb;
+ struct rename_context *ac;
+ struct ldb_request *down_req;
+ int ret;
- case LDB_REQ_MODIFY:
- return rdn_name_modify(module, req);
+ ldb = ldb_module_get_ctx(module);
-
- default:
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.rename.newdn)) {
return ldb_next_request(module, req);
+ }
+ ac = talloc_zero(req, struct rename_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
}
-}
-static const struct ldb_module_ops rdn_name_ops = {
- .name = "rdn_name",
- .request = rdn_name_request
-};
+ ac->module = module;
+ ac->req = req;
+
+ ret = ldb_build_rename_req(&down_req,
+ ldb,
+ ac,
+ req->op.rename.olddn,
+ req->op.rename.newdn,
+ req->controls,
+ ac,
+ rdn_rename_callback,
+ req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ /* rename first, modify "name" if rename is ok */
+ return ldb_next_request(module, down_req);
+}
-/* the init function */
-struct ldb_module *rdn_name_module_init(struct ldb_context *ldb, const char *options[])
+static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
{
- struct ldb_module *ctx;
+ struct ldb_context *ldb;
- ctx = talloc(ldb, struct ldb_module);
- if (!ctx)
- return NULL;
+ ldb = ldb_module_get_ctx(module);
- ctx->private_data = NULL;
- ctx->ldb = ldb;
- ctx->prev = ctx->next = NULL;
- ctx->ops = &rdn_name_ops;
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ return ldb_next_request(module, req);
+ }
- return ctx;
+ if (ldb_msg_find_element(req->op.mod.message, "distinguishedName")) {
+ ldb_asprintf_errstring(ldb, "Modify of 'distinguishedName' on %s not permitted, must use 'rename' operation instead",
+ ldb_dn_get_linearized(req->op.mod.message->dn));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ if (ldb_msg_find_element(req->op.mod.message, "name")) {
+ ldb_asprintf_errstring(ldb, "Modify of 'name' on %s not permitted, must use 'rename' operation instead",
+ ldb_dn_get_linearized(req->op.mod.message->dn));
+ return LDB_ERR_NOT_ALLOWED_ON_RDN;
+ }
+
+ if (ldb_msg_find_element(req->op.mod.message, ldb_dn_get_rdn_name(req->op.mod.message->dn))) {
+ ldb_asprintf_errstring(ldb, "Modify of RDN '%s' on %s not permitted, must use 'rename' operation instead",
+ ldb_dn_get_rdn_name(req->op.mod.message->dn), ldb_dn_get_linearized(req->op.mod.message->dn));
+ return LDB_ERR_NOT_ALLOWED_ON_RDN;
+ }
+
+ /* All OK, they kept their fingers out of the special attributes */
+ return ldb_next_request(module, req);
}
+
+const struct ldb_module_ops ldb_rdn_name_module_ops = {
+ .name = "rdn_name",
+ .add = rdn_name_add,
+ .modify = rdn_name_modify,
+ .rename = rdn_name_rename
+};