ldb:rdn_name: add support for LDB_CONTROL_RECALCULATE_RDN_OID on ldb_modify()
authorStefan Metzmacher <metze@samba.org>
Thu, 10 Nov 2016 17:03:26 +0000 (18:03 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 1 Dec 2016 04:54:23 +0000 (05:54 +0100)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12399

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
lib/ldb/modules/rdn_name.c

index e8d5d8ae9bd150a63541d4f9a6414930ca56f6bd..469778227eddf6173d8e504471d430e6689daaed 100644 (file)
@@ -377,11 +377,20 @@ static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
        return ldb_next_request(module, down_req);
 }
 
+static int rdn_recalculate_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct ldb_request *up_req = talloc_get_type(req->context, struct ldb_request);
+
+       talloc_steal(up_req, req);
+       return up_req->callback(up_req, ares);
+}
+
 static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_context *ldb;
        const struct ldb_val *rdn_val_p;
        struct ldb_message_element *e = NULL;
+       struct ldb_control *recalculate_rdn_control = NULL;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -390,6 +399,138 @@ static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
+       recalculate_rdn_control = ldb_request_get_control(req,
+                                       LDB_CONTROL_RECALCULATE_RDN_OID);
+       if (recalculate_rdn_control != NULL) {
+               struct ldb_message *msg = NULL;
+               const char *rdn_name = NULL;
+               struct ldb_val rdn_val;
+               const struct ldb_schema_attribute *a = NULL;
+               struct ldb_request *mod_req = NULL;
+               int ret;
+               struct ldb_message_element *rdn_del = NULL;
+               struct ldb_message_element *name_del = NULL;
+
+               recalculate_rdn_control->critical = false;
+
+               msg = ldb_msg_copy_shallow(req, req->op.mod.message);
+               if (msg == NULL) {
+                       return ldb_module_oom(module);
+               }
+
+               /*
+                * The caller must pass a dummy 'name' attribute
+                * in order to bypass some high level checks.
+                *
+                * We just remove it and check nothing is left.
+                */
+               ldb_msg_remove_attr(msg, "name");
+
+               if (msg->num_elements != 0) {
+                       return ldb_module_operr(module);
+               }
+
+               rdn_name = ldb_dn_get_rdn_name(msg->dn);
+               if (rdn_name == NULL) {
+                       return ldb_module_oom(module);
+               }
+
+               a = ldb_schema_attribute_by_name(ldb, rdn_name);
+               if (a == NULL) {
+                       return ldb_module_operr(module);
+               }
+
+               if (a->name != NULL) {
+                       rdn_name = a->name;
+               }
+
+               rdn_val_p = ldb_dn_get_rdn_val(msg->dn);
+               if (rdn_val_p == NULL) {
+                       return ldb_module_oom(module);
+               }
+               rdn_val = ldb_val_dup(msg, rdn_val_p);
+               if (rdn_val.length == 0) {
+                       return ldb_module_oom(module);
+               }
+
+               /*
+                * This is a bit tricky:
+                *
+                * We want _DELETE elements (as "rdn_del" and "name_del" without
+                * values) first, followed by _ADD (with the real names)
+                * elements (with values). Then we fix up the "rdn_del" and
+                * "name_del" attributes.
+                */
+
+               ret = ldb_msg_add_empty(msg, "rdn_del", LDB_FLAG_MOD_DELETE, NULL);
+               if (ret != 0) {
+                       return ldb_module_oom(module);
+               }
+               ret = ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_ADD, NULL);
+               if (ret != 0) {
+                       return ldb_module_oom(module);
+               }
+               ret = ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL);
+               if (ret != 0) {
+                       return ldb_module_oom(module);
+               }
+
+               ret = ldb_msg_add_empty(msg, "name_del", LDB_FLAG_MOD_DELETE, NULL);
+               if (ret != 0) {
+                       return ldb_module_oom(module);
+               }
+               ret = ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_ADD, NULL);
+               if (ret != 0) {
+                       return ldb_module_oom(module);
+               }
+               ret = ldb_msg_add_value(msg, "name", &rdn_val, NULL);
+               if (ret != 0) {
+                       return ldb_module_oom(module);
+               }
+
+               rdn_del = ldb_msg_find_element(msg, "rdn_del");
+               if (rdn_del == NULL) {
+                       return ldb_module_operr(module);
+               }
+               rdn_del->name = talloc_strdup(msg->elements, rdn_name);
+               if (rdn_del->name == NULL) {
+                       return ldb_module_oom(module);
+               }
+               name_del = ldb_msg_find_element(msg, "name_del");
+               if (name_del == NULL) {
+                       return ldb_module_operr(module);
+               }
+               name_del->name = talloc_strdup(msg->elements, "name");
+               if (name_del->name == NULL) {
+                       return ldb_module_oom(module);
+               }
+
+               ret = ldb_build_mod_req(&mod_req, ldb,
+                                       req, msg, NULL,
+                                       req, rdn_recalculate_callback,
+                                       req);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(req, NULL, NULL, ret);
+               }
+               talloc_steal(mod_req, msg);
+
+               ret = ldb_request_add_control(mod_req,
+                                             LDB_CONTROL_RECALCULATE_RDN_OID,
+                                             false, NULL);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(req, NULL, NULL, ret);
+               }
+               ret = ldb_request_add_control(mod_req,
+                                             LDB_CONTROL_PERMISSIVE_MODIFY_OID,
+                                             false, NULL);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(req, NULL, NULL, ret);
+               }
+
+               /* go on with the call chain */
+               return ldb_next_request(module, mod_req);
+       }
+
        rdn_val_p = ldb_dn_get_rdn_val(req->op.mod.message->dn);
        if (rdn_val_p == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;