diff options
Diffstat (limited to 'source4/lib/ldb/modules/ldb_map_outbound.c')
-rw-r--r-- | source4/lib/ldb/modules/ldb_map_outbound.c | 1134 |
1 files changed, 1134 insertions, 0 deletions
diff --git a/source4/lib/ldb/modules/ldb_map_outbound.c b/source4/lib/ldb/modules/ldb_map_outbound.c new file mode 100644 index 0000000000..046c92bb99 --- /dev/null +++ b/source4/lib/ldb/modules/ldb_map_outbound.c @@ -0,0 +1,1134 @@ +/* + ldb database mapping module + + Copyright (C) Jelmer Vernooij 2005 + Copyright (C) Martin Kuehl <mkhl@samba.org> 2006 + + * NOTICE: this module is NOT released under the GNU LGPL license as + * other ldb code. This module is release under the GNU GPL v2 or + * later license. + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "ldb/include/includes.h" + +#include "ldb/modules/ldb_map.h" +#include "ldb/modules/ldb_map_private.h" + + +/* Mapping attributes + * ================== */ + +/* Select attributes that stay in the local partition. */ +static const char **map_attrs_select_local(struct ldb_module *module, void *mem_ctx, const char * const *attrs) +{ + const struct ldb_map_context *data = map_get_context(module); + const char **result; + int i, last; + + if (attrs == NULL) + return NULL; + + last = 0; + result = talloc_array(mem_ctx, const char *, 1); + if (result == NULL) { + goto failed; + } + result[0] = NULL; + + for (i = 0; attrs[i]; i++) { + /* Wildcards and ignored attributes are kept locally */ + if ((ldb_attr_cmp(attrs[i], "*") == 0) || + (!map_attr_check_remote(data, attrs[i]))) { + result = talloc_realloc(mem_ctx, result, const char *, last+2); + if (result == NULL) { + goto failed; + } + + result[last] = talloc_strdup(result, attrs[i]); + result[last+1] = NULL; + last++; + } + } + + return result; + +failed: + talloc_free(result); + map_oom(module); + return NULL; +} + +/* Collect attributes that are mapped into the remote partition. */ +static const char **map_attrs_collect_remote(struct ldb_module *module, void *mem_ctx, const char * const *attrs) +{ + const struct ldb_map_context *data = map_get_context(module); + const char **result; + const struct ldb_map_attribute *map; + const char *name; + int i, j, last; + + if (attrs == NULL) + return NULL; + + last = 0; + result = talloc_array(mem_ctx, const char *, 1); + if (result == NULL) { + goto failed; + } + result[0] = NULL; + + for (i = 0; attrs[i]; i++) { + /* Wildcards are kept remotely, too */ + if (ldb_attr_cmp(attrs[i], "*") == 0) { + name = attrs[i]; + goto named; + } + + /* Add remote names of mapped attrs */ + map = map_attr_find_local(data, attrs[i]); + if (map == NULL) { + continue; + } + + switch (map->type) { + case MAP_IGNORE: + continue; + + case MAP_KEEP: + name = attrs[i]; + goto named; + + case MAP_RENAME: + case MAP_CONVERT: + name = map->u.rename.remote_name; + goto named; + + case MAP_GENERATE: + /* Add all remote names of "generate" attrs */ + for (j = 0; map->u.generate.remote_names[j]; j++) { + result = talloc_realloc(mem_ctx, result, const char *, last+2); + if (result == NULL) { + goto failed; + } + + result[last] = talloc_strdup(result, map->u.generate.remote_names[j]); + result[last+1] = NULL; + last++; + } + continue; + } + + named: /* We found a single remote name, add that */ + result = talloc_realloc(mem_ctx, result, const char *, last+2); + if (result == NULL) { + goto failed; + } + + result[last] = talloc_strdup(result, name); + result[last+1] = NULL; + last++; + } + + return result; + +failed: + talloc_free(result); + map_oom(module); + return NULL; +} + +/* Split attributes that stay in the local partition from those that + * are mapped into the remote partition. */ +static int map_attrs_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *attrs) +{ + *local_attrs = map_attrs_select_local(module, local_ctx, attrs); + *remote_attrs = map_attrs_collect_remote(module, remote_ctx, attrs); + + return 0; +} + +/* Merge two lists of attributes into a single one. */ +static int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs) +{ + int i, j, k; + + for (i = 0; (*attrs)[i]; i++) /* noop */ ; + for (j = 0; more_attrs[j]; j++) /* noop */ ; + + *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1); + if (*attrs == NULL) { + map_oom(module); + return -1; + } + + for (k = 0; k < j; k++) { + (*attrs)[i+k] = more_attrs[k]; + } + + (*attrs)[i+k] = NULL; + + return 0; +} + +/* Mapping ldb values + * ================== */ + +/* Map an ldb value from a parse tree into the remote partition. */ +static struct ldb_val ldb_val_map_subtree(struct ldb_module *module, struct ldb_parse_tree *new, const struct ldb_map_attribute *map, const struct ldb_parse_tree *tree) +{ + struct ldb_val val; + + /* Extract the old value */ + switch (tree->operation) { + case LDB_OP_EQUALITY: + val = tree->u.equality.value; + break; + case LDB_OP_LESS: + case LDB_OP_GREATER: + case LDB_OP_APPROX: + val = tree->u.comparison.value; + break; + case LDB_OP_EXTENDED: + val = tree->u.extended.value; + break; + default: + val.length = 0; + val.data = NULL; + return ldb_val_dup(new, &val); + } + + /* Convert to the new value */ + return ldb_val_map_local(module, new, map, val); +} + +/* Mapping message elements + * ======================== */ + +/* Add an element to a message, overwriting any old identically named elements. */ +static int ldb_msg_replace(struct ldb_message *msg, const struct ldb_message_element *el) +{ + struct ldb_message_element *old; + + old = ldb_msg_find_element(msg, el->name); + + /* no local result, add as new element */ + if (old == NULL) { + if (ldb_msg_add_empty(msg, el->name, 0) != 0) { + return -1; + } + + old = ldb_msg_find_element(msg, el->name); + if (old == NULL) { + return -1; + } + } + + *old = *el; /* copy new element */ + + return 0; +} + +/* Map a message element back into the local partition. */ +static struct ldb_message_element *ldb_msg_el_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old) +{ + struct ldb_message_element *el; + int i; + + el = talloc_zero(mem_ctx, struct ldb_message_element); + if (el == NULL) { + map_oom(module); + return NULL; + } + + el->num_values = old->num_values; + el->values = talloc_array(el, struct ldb_val, el->num_values); + if (el->values == NULL) { + talloc_free(el); + map_oom(module); + return NULL; + } + + el->name = map_attr_map_remote(el, map, old->name); + + for (i = 0; i < el->num_values; i++) { + el->values[i] = ldb_val_map_remote(module, el->values, map, old->values[i]); + } + + return el; +} + +/* Merge a remote message element into a local message. */ +static int ldb_msg_el_merge(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const char *attr_name, const struct ldb_message_element *old) +{ + const struct ldb_map_context *data = map_get_context(module); + const struct ldb_map_attribute *map = map_attr_find_remote(data, attr_name); + struct ldb_message_element *el; + + /* Unknown attribute in remote message: + * skip, attribute was probably auto-generated */ + if (map == NULL) { + ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: " + "Skipping attribute '%s': no mapping found\n", + old->name); + return 0; + } + + switch (map->type) { + case MAP_IGNORE: + return -1; + + case MAP_CONVERT: + if (map->u.convert.convert_remote == NULL) { + ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: " + "Skipping attribute '%s': " + "'convert_remote' not set\n", + old->name); + return 0; + } + /* fall through */ + case MAP_KEEP: + case MAP_RENAME: + el = ldb_msg_el_map_remote(module, local, map, old); + break; + + case MAP_GENERATE: + if (map->u.generate.generate_local == NULL) { + ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: " + "Skipping attribute '%s': " + "'generate_local' not set\n", + old->name); + return 0; + } + + el = map->u.generate.generate_local(module, local, old->name, remote); + break; + } + + if (el == NULL) { + return -1; + } + + return ldb_msg_replace(local, el); +} + +/* Mapping messages + * ================ */ + +/* Merge two local messages into a single one. */ +static int ldb_msg_merge_local(struct ldb_module *module, struct ldb_message *msg1, struct ldb_message *msg2) +{ + int i, ret; + + for (i = 0; i < msg2->num_elements; i++) { + ret = ldb_msg_replace(msg1, &msg2->elements[i]); + if (ret) { + return ret; + } + } + + return 0; +} + +/* Merge a local and a remote message into a single local one. */ +static int ldb_msg_merge_remote(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote) +{ + int i, ret; + + /* Try to map each attribute back; + * Add to local message is possible, + * Overwrite old local attribute if necessary */ + for (i = 0; i < remote->num_elements; i++) { + ret = ldb_msg_el_merge(module, local, remote, remote->elements[i].name, &remote->elements[i]); + if (ret) { + return ret; + } + } + + return 0; +} + +/* Mapping search results + * ====================== */ + +/* Map a search result back into the local partition. */ +static int map_reply_remote(struct ldb_module *module, struct ldb_reply *ares) +{ + struct ldb_message *msg; + struct ldb_dn *dn; + int ret; + + /* There is no result message, skip */ + if (ares->type != LDB_REPLY_ENTRY) { + return 0; + } + + /* Create a new result message */ + msg = ldb_msg_new(ares); + if (msg == NULL) { + map_oom(module); + return -1; + } + + /* Merge remote message into new message */ + ret = ldb_msg_merge_remote(module, msg, ares->message); + if (ret) { + talloc_free(msg); + return ret; + } + + /* Create corresponding local DN */ + dn = ldb_dn_map_rebase_remote(module, msg, ares->message->dn); + if (dn == NULL) { + talloc_free(msg); + return -1; + } + msg->dn = dn; + + /* Store new message with new DN as the result */ + talloc_free(ares->message); + ares->message = msg; + + return 0; +} + +/* Mapping parse trees + * =================== */ + +/* Check whether a parse tree can safely be split in two. */ +static BOOL ldb_parse_tree_check_splittable(const struct ldb_parse_tree *tree) +{ + const struct ldb_parse_tree *subtree = tree; + BOOL negate = False; + + while (subtree) { + switch (subtree->operation) { + case LDB_OP_NOT: + negate = !negate; + subtree = subtree->u.isnot.child; + continue; + + case LDB_OP_AND: + return !negate; /* if negate: False */ + + case LDB_OP_OR: + return negate; /* if negate: True */ + + default: + return True; /* simple parse tree */ + } + } + + return True; /* no parse tree */ +} + +/* Collect a list of attributes required to match a given parse tree. */ +static int ldb_parse_tree_collect_attrs(struct ldb_module *module, void *mem_ctx, const char ***attrs, const struct ldb_parse_tree *tree) +{ + const char **new_attrs; + int i, ret; + + if (tree == NULL) { + return 0; + } + + switch (tree->operation) { + case LDB_OP_OR: + case LDB_OP_AND: /* attributes stored in list of subtrees */ + for (i = 0; i < tree->u.list.num_elements; i++) { + ret = ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.list.elements[i]); + if (ret) { + return ret; + } + } + return 0; + + case LDB_OP_NOT: /* attributes stored in single subtree */ + return ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.isnot.child); + + default: /* single attribute in tree */ + new_attrs = ldb_attr_list_copy_add(mem_ctx, *attrs, tree->u.equality.attr); + talloc_free(*attrs); + *attrs = new_attrs; + return 0; + } + + return -1; +} + +static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree); + +/* Select a negated subtree that queries attributes in the local partition */ +static int map_subtree_select_local_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) +{ + struct ldb_parse_tree *child; + int ret; + + /* Prepare new tree */ + *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree)); + if (*new == NULL) { + map_oom(module); + return -1; + } + + /* Generate new subtree */ + ret = map_subtree_select_local(module, *new, &child, tree->u.isnot.child); + if (ret) { + talloc_free(*new); + return ret; + } + + /* Prune tree without subtree */ + if (child == NULL) { + talloc_free(*new); + *new = NULL; + return 0; + } + + (*new)->u.isnot.child = child; + + return ret; +} + +/* Select a list of subtrees that query attributes in the local partition */ +static int map_subtree_select_local_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) +{ + int i, j, ret; + + /* Prepare new tree */ + *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree)); + if (*new == NULL) { + map_oom(module); + return -1; + } + + /* Prepare list of subtrees */ + (*new)->u.list.num_elements = 0; + (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements); + if ((*new)->u.list.elements == NULL) { + map_oom(module); + talloc_free(*new); + return -1; + } + + /* Generate new list of subtrees */ + j = 0; + for (i = 0; i < tree->u.list.num_elements; i++) { + struct ldb_parse_tree *child; + ret = map_subtree_select_local(module, *new, &child, tree->u.list.elements[i]); + if (ret) { + talloc_free(*new); + return ret; + } + + if (child) { + (*new)->u.list.elements[j] = child; + j++; + } + } + + /* Prune tree without subtrees */ + if (j == 0) { + talloc_free(*new); + *new = NULL; + return 0; + } + + /* Fix subtree list size */ + (*new)->u.list.num_elements = j; + (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements); + + return ret; +} + +/* Select a simple subtree that queries attributes in the local partition */ +static int map_subtree_select_local_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) +{ + /* Prepare new tree */ + *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree)); + if (*new == NULL) { + map_oom(module); + return -1; + } + + return 0; +} + +/* Select subtrees that query attributes in the local partition */ +static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) +{ + const struct ldb_map_context *data = map_get_context(module); + + if (tree == NULL) { + return 0; + } + + if (tree->operation == LDB_OP_NOT) { + return map_subtree_select_local_not(module, mem_ctx, new, tree); + } + + if (tree->operation == LDB_OP_AND || tree->operation == LDB_OP_OR) { + return map_subtree_select_local_list(module, mem_ctx, new, tree); + } + + if (map_attr_check_remote(data, tree->u.equality.attr)) { + *new = NULL; + return 0; + } + + return map_subtree_select_local_simple(module, mem_ctx, new, tree); +} + +static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree); + +/* Collect a negated subtree that queries attributes in the remote partition */ +static int map_subtree_collect_remote_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) +{ + struct ldb_parse_tree *child; + int ret; + + /* Prepare new tree */ + *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree)); + if (*new == NULL) { + map_oom(module); + return -1; + } + + /* Generate new subtree */ + ret = map_subtree_collect_remote(module, *new, &child, tree->u.isnot.child); + if (ret) { + talloc_free(*new); + return ret; + } + + /* Prune tree without subtree */ + if (child == NULL) { + talloc_free(*new); + *new = NULL; + return 0; + } + + (*new)->u.isnot.child = child; + + return ret; +} + +/* Collect a list of subtrees that query attributes in the remote partition */ +static int map_subtree_collect_remote_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) +{ + int i, j, ret; + + /* Prepare new tree */ + *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree)); + if (*new == NULL) { + map_oom(module); + return -1; + } + + /* Prepare list of subtrees */ + (*new)->u.list.num_elements = 0; + (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements); + if ((*new)->u.list.elements == NULL) { + map_oom(module); + talloc_free(*new); + return -1; + } + + /* Generate new list of subtrees */ + j = 0; + for (i = 0; i < tree->u.list.num_elements; i++) { + struct ldb_parse_tree *child; + ret = map_subtree_collect_remote(module, *new, &child, tree->u.list.elements[i]); + if (ret) { + talloc_free(*new); + return ret; + } + + if (child) { + (*new)->u.list.elements[j] = child; + j++; + } + } + + /* Prune tree without subtrees */ + if (j == 0) { + talloc_free(*new); + *new = NULL; + return 0; + } + + /* Fix subtree list size */ + (*new)->u.list.num_elements = j; + (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements); + + return ret; +} + +/* Collect a simple subtree that queries attributes in the remote partition */ +static int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map) +{ + const char *attr; + struct ldb_val val; + + /* Prepare new tree */ + *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree)); + if (*new == NULL) { + map_oom(module); + return -1; + } + + /* Map attribute and value */ + attr = map_attr_map_local(*new, map, tree->u.equality.attr); + if (attr == NULL) { + talloc_free(*new); + *new = NULL; + return 0; + } + val = ldb_val_map_subtree(module, *new, map, tree); + + /* Store attribute and value in new tree */ + switch (tree->operation) { + case LDB_OP_PRESENT: + (*new)->u.present.attr = attr; + break; + case LDB_OP_SUBSTRING: + (*new)->u.substring.attr = attr; + (*new)->u.substring.chunks = NULL; /* FIXME! */ + break; + case LDB_OP_EQUALITY: + (*new)->u.equality.attr = attr; + (*new)->u.equality.value = val; + break; + case LDB_OP_LESS: + case LDB_OP_GREATER: + case LDB_OP_APPROX: + (*new)->u.comparison.attr = attr; + (*new)->u.comparison.value = val; + break; + case LDB_OP_EXTENDED: + (*new)->u.extended.attr = attr; + (*new)->u.extended.value = val; + (*new)->u.extended.rule_id = talloc_strdup(*new, tree->u.extended.rule_id); + break; + default: /* unknown kind of simple subtree */ + talloc_free(*new); + return -1; + } + + return 0; +} + +/* Collect subtrees that query attributes in the remote partition */ +static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) +{ + const struct ldb_map_context *data = map_get_context(module); + const struct ldb_map_attribute *map; + + if (tree == NULL) { + return 0; + } + + if (tree->operation == LDB_OP_NOT) { + return map_subtree_collect_remote_not(module, mem_ctx, new, tree); + } + + if ((tree->operation == LDB_OP_AND) || (tree->operation == LDB_OP_OR)) { + return map_subtree_collect_remote_list(module, mem_ctx, new, tree); + } + + if (!map_attr_check_remote(data, tree->u.equality.attr)) { + *new = NULL; + return 0; + } + + map = map_attr_find_local(data, tree->u.equality.attr); + if (map->convert_operator) { + *new = map->convert_operator(data, mem_ctx, tree); + return 0; + } + + if (map->type == MAP_GENERATE) { + ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: " + "Skipping attribute '%s': " + "'convert_operator' not set\n", + tree->u.equality.attr); + *new = NULL; + return 0; + } + + return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, map); +} + +/* Split subtrees that query attributes in the local partition from + * those that query the remote partition. */ +static int ldb_parse_tree_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, struct ldb_parse_tree **local_tree, struct ldb_parse_tree **remote_tree, const struct ldb_parse_tree *tree) +{ + int ret; + + *local_tree = NULL; + *remote_tree = NULL; + + /* No original tree */ + if (tree == NULL) { + return 0; + } + + /* Generate local tree */ + ret = map_subtree_select_local(module, local_ctx, local_tree, tree); + if (ret) { + return ret; + } + + /* Generate remote tree */ + ret = map_subtree_collect_remote(module, remote_ctx, remote_tree, tree); + if (ret) { + talloc_free(*local_tree); + return ret; + } + + return 0; +} + +/* Collect a list of attributes required either explicitly from a + * given list or implicitly from a given parse tree; split the + * collected list into local and remote parts. */ +static int map_attrs_collect_and_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *search_attrs, const struct ldb_parse_tree *tree) +{ + void *tmp_ctx; + const char **tree_attrs; + int ret; + + /* Clear initial lists of partitioned attributes */ + *local_attrs = NULL; + *remote_attrs = NULL; + + /* There are no searched attributes, just stick to that */ + if (search_attrs == NULL) { + return 0; + } + + /* There is no tree, just partition the searched attributes */ + if (tree == NULL) { + return map_attrs_partition(module, local_ctx, remote_ctx, local_attrs, remote_attrs, search_attrs); + } + + /* Create context for temporary memory */ + tmp_ctx = talloc_new(local_ctx); + if (tmp_ctx == NULL) { + goto oom; + } + + /* Prepare list of attributes from tree */ + tree_attrs = talloc_array(tmp_ctx, const char *, 1); + if (tree_attrs == NULL) { + talloc_free(tmp_ctx); + goto oom; + } + tree_attrs[0] = NULL; + + /* Collect attributes from tree */ + ret = ldb_parse_tree_collect_attrs(module, tmp_ctx, &tree_attrs, tree); + if (ret) { + goto done; + } + + /* Merge attributes from search operation */ + ret = map_attrs_merge(module, tmp_ctx, &tree_attrs, search_attrs); + if (ret) { + goto done; + } + + /* Split local from remote attributes */ + ret = map_attrs_partition(module, local_ctx, remote_ctx, local_attrs, remote_attrs, tree_attrs); + +done: + /* Free temporary memory */ + talloc_free(tmp_ctx); + return ret; + +oom: + map_oom(module); + return -1; +} + + +/* Outbound requests: search + * ========================= */ + +/* Pass a merged search result up the callback chain. */ +int map_up_callback(struct ldb_context *ldb, const struct ldb_request *req, struct ldb_reply *ares) +{ + int i; + + /* No callback registered, stop */ + if (req->callback == NULL) { + return LDB_SUCCESS; + } + + /* Only records need special treatment */ + if (ares->type != LDB_REPLY_ENTRY) { + return req->callback(ldb, req->context, ares); + } + + /* Merged result doesn't match original query, skip */ + if (!ldb_match_msg(ldb, ares->message, req->op.search.tree, req->op.search.base, req->op.search.scope)) { + ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_map: " + "Skipping record '%s': " + "doesn't match original search\n", + ldb_dn_linearize(ldb, ares->message->dn)); + return LDB_SUCCESS; + } + + /* Limit result to requested attrs */ + if ((req->op.search.attrs) && (!ldb_attr_in_list(req->op.search.attrs, "*"))) { + for (i = 0; i < ares->message->num_elements; i++) { + const struct ldb_message_element *el = &ares->message->elements[i]; + if (!ldb_attr_in_list(req->op.search.attrs, el->name)) { + ldb_msg_remove_attr(ares->message, el->name); + } + } + } + + return req->callback(ldb, req->context, ares); +} + +/* Merge the remote and local parts of a search result. */ +int map_local_merge_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +{ + struct map_search_context *sc; + int ret; + + if (context == NULL || ares == NULL) { + ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: " + "NULL Context or Result in `map_local_merge_callback`")); + return LDB_ERR_OPERATIONS_ERROR; + } + + sc = talloc_get_type(context, struct map_search_context); + + switch (ares->type) { + case LDB_REPLY_ENTRY: + /* We have already found a local record */ + if (sc->local_res) { + ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: " + "Too many results to base search for local entry")); + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* Store local result */ + sc->local_res = ares; + + /* Merge remote into local message */ + ret = ldb_msg_merge_local(sc->ac->module, ares->message, sc->remote_res->message); + if (ret) { + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; + } + + return map_up_callback(ldb, sc->ac->orig_req, ares); + + case LDB_REPLY_DONE: + /* No local record found, continue with remote record */ + if (sc->local_res == NULL) { + return map_up_callback(ldb, sc->ac->orig_req, sc->remote_res); + } + return LDB_SUCCESS; + + default: + ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: " + "Unexpected result type in base search for local entry")); + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; + } +} + +/* Search the local part of a remote search result. */ +int map_remote_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +{ + struct map_context *ac; + struct map_search_context *sc; + struct ldb_request *req; + int ret; + + if (context == NULL || ares == NULL) { + ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: " + "NULL Context or Result in `map_remote_search_callback`")); + return LDB_ERR_OPERATIONS_ERROR; + } + + ac = talloc_get_type(context, struct map_context); + + /* It's not a record, stop searching */ + if (ares->type != LDB_REPLY_ENTRY) { + return map_up_callback(ldb, ac->orig_req, ares); + } + + /* Map result record into a local message */ + ret = map_reply_remote(ac->module, ares); + if (ret) { + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* There is no local db, stop searching */ + if (!map_check_local_db(ac->module)) { + return map_up_callback(ldb, ac->orig_req, ares); + } + + /* Prepare local search context */ + sc = map_init_search_context(ac, ares); + if (sc == NULL) { + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* Prepare local search request */ + /* TODO: use GUIDs here instead? */ + + ac->search_reqs = talloc_realloc(ac, ac->search_reqs, struct ldb_request *, ac->num_searches + 2); + if (ac->search_reqs == NULL) { + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; + } + + ac->search_reqs[ac->num_searches] + = req = map_search_base_req(ac, ares->message->dn, + NULL, NULL, sc, map_local_merge_callback); + if (req == NULL) { + talloc_free(sc); + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; + } + ac->num_searches++; + ac->search_reqs[ac->num_searches] = NULL; + + return ldb_next_request(ac->module, req); +} + +/* Search a record. */ +int map_search(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_handle *h; + struct map_context *ac; + struct ldb_parse_tree *local_tree, *remote_tree; + const char **local_attrs, **remote_attrs; + int ret; + + /* Do not manipulate our control entries */ + if (ldb_dn_is_special(req->op.search.base)) + return ldb_next_request(module, req); + + /* No mapping requested, skip to next module */ + if ((req->op.search.base) && (!ldb_dn_check_local(module, req->op.search.base))) { + return ldb_next_request(module, req); + } + + /* TODO: How can we be sure about which partition we are + * targetting when there is no search base? */ + + /* Prepare context and handle */ + h = map_init_handle(req, module); + if (h == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + ac = talloc_get_type(h->private_data, struct map_context); + + ac->search_reqs = talloc_array(ac, struct ldb_request *, 2); + if (ac->search_reqs == NULL) { + talloc_free(h); + return LDB_ERR_OPERATIONS_ERROR; + } + ac->num_searches = 1; + ac->search_reqs[1] = NULL; + + /* Prepare the remote operation */ + ac->search_reqs[0] = talloc(ac, struct ldb_request); + if (ac->search_reqs[0] == NULL) { + goto oom; + } + + *(ac->search_reqs[0]) = *req; /* copy the request */ + + ac->search_reqs[0]->handle = h; /* return our own handle to deal with this call */ + + ac->search_reqs[0]->context = ac; + ac->search_reqs[0]->callback = map_remote_search_callback; + + /* Split local from remote attrs */ + ret = map_attrs_collect_and_partition(module, ac, ac->search_reqs[0], &local_attrs, &remote_attrs, req->op.search.attrs, req->op.search.tree); + if (ret) { + goto failed; + } + + ac->local_attrs = local_attrs; + ac->search_reqs[0]->op.search.attrs = remote_attrs; + + /* Split local from remote tree */ + ret = ldb_parse_tree_partition(module, ac, ac->search_reqs[0], &local_tree, &remote_tree, req->op.search.tree); + if (ret) { + goto failed; + } + + if (((local_tree == NULL) ^ (remote_tree == NULL)) && + (!ldb_parse_tree_check_splittable(req->op.search.tree))) { + /* The query can't safely be split, enumerate the remote partition */ + local_tree = NULL; + remote_tree = NULL; + } + + if (local_tree == NULL) { + /* Construct default local parse tree */ + local_tree = talloc_zero(ac, struct ldb_parse_tree); + if (local_tree == NULL) { + map_oom(ac->module); + goto failed; + } + + local_tree->operation = LDB_OP_PRESENT; + local_tree->u.present.attr = talloc_strdup(local_tree, IS_MAPPED); + } + if (remote_tree == NULL) { + /* Construct default remote parse tree */ + remote_tree = ldb_parse_tree(ac->search_reqs[0], NULL); + if (remote_tree == NULL) { + goto failed; + } + } + + ac->local_tree = local_tree; + ac->search_reqs[0]->op.search.tree = remote_tree; + + ldb_set_timeout_from_prev_req(module->ldb, req, ac->search_reqs[0]); + + h->state = LDB_ASYNC_INIT; + h->status = LDB_SUCCESS; + + ac->step = MAP_SEARCH_REMOTE; + + ret = ldb_next_remote_request(module, ac->search_reqs[0]); + if (ret == LDB_SUCCESS) { + req->handle = h; + } + return ret; + +oom: + map_oom(module); +failed: + talloc_free(h); + return LDB_ERR_OPERATIONS_ERROR; +} |