diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/dsdb/samdb/ldb_modules/ranged_results.c | |
parent | Initial commit. (diff) | |
download | samba-upstream.tar.xz samba-upstream.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules/ranged_results.c')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/ranged_results.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/ranged_results.c b/source4/dsdb/samdb/ldb_modules/ranged_results.c new file mode 100644 index 0000000..b010abb --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/ranged_results.c @@ -0,0 +1,295 @@ +/* + ldb database library + + Copyright (C) Andrew Bartlett 2007 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb ranged results module + * + * Description: munge AD-style 'ranged results' requests into + * requests for all values in an attribute, then return the range to + * the client. + * + * Author: Andrew Bartlett + */ + +#include "includes.h" +#include "ldb_module.h" + +#undef strncasecmp + +struct rr_context { + struct ldb_module *module; + struct ldb_request *req; + bool dirsync_in_use; +}; + +static struct rr_context *rr_init_context(struct ldb_module *module, + struct ldb_request *req) +{ + struct ldb_control *dirsync_control = NULL; + struct rr_context *ac = talloc_zero(req, struct rr_context); + if (ac == NULL) { + ldb_set_errstring(ldb_module_get_ctx(module), "Out of Memory"); + return NULL; + } + + ac->module = module; + ac->req = req; + + /* + * check if there's a dirsync control (as there is an + * interaction between these modules) + */ + dirsync_control = ldb_request_get_control(req, + LDB_CONTROL_DIRSYNC_OID); + if (dirsync_control != NULL) { + ac->dirsync_in_use = true; + } + + return ac; +} + +static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct ldb_context *ldb; + struct rr_context *ac; + unsigned int i, j; + TALLOC_CTX *temp_ctx; + + ac = talloc_get_type(req->context, struct rr_context); + ldb = ldb_module_get_ctx(ac->module); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + + if (ares->type == LDB_REPLY_REFERRAL) { + return ldb_module_send_referral(ac->req, ares->referral); + } + + if (ares->type == LDB_REPLY_DONE) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + + if (ac->dirsync_in_use) { + /* + * We return full attribute values when mixed with + * dirsync + */ + return ldb_module_send_entry(ac->req, + ares->message, + ares->controls); + } + /* LDB_REPLY_ENTRY */ + + temp_ctx = talloc_new(ac->req); + if (!temp_ctx) { + ldb_module_oom(ac->module); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + /* Find those that are range requests from the attribute list */ + for (i = 0; ac->req->op.search.attrs[i]; i++) { + char *p, *new_attr; + const char *end_str; + unsigned int start, end; + struct ldb_message_element *el; + struct ldb_val *orig_values; + + p = strchr(ac->req->op.search.attrs[i], ';'); + if (!p) { + continue; + } + if (strncasecmp(p, ";range=", strlen(";range=")) != 0) { + continue; + } + if (sscanf(p, ";range=%u-%u", &start, &end) != 2) { + if (sscanf(p, ";range=%u-*", &start) == 1) { + end = (unsigned int)-1; + } else { + continue; + } + } + new_attr = talloc_strndup(temp_ctx, + ac->req->op.search.attrs[i], + (size_t)(p - ac->req->op.search.attrs[i])); + + if (!new_attr) { + ldb_oom(ldb); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + el = ldb_msg_find_element(ares->message, new_attr); + talloc_free(new_attr); + if (!el) { + continue; + } + if (end >= (el->num_values - 1)) { + /* Need to leave the requested attribute in + * there (so add an empty one to match) */ + end_str = "*"; + end = el->num_values - 1; + } else { + end_str = talloc_asprintf(temp_ctx, "%u", end); + if (!end_str) { + ldb_oom(ldb); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + } + /* If start is greater then where we are find the end to be */ + if (start > end) { + el->num_values = 0; + el->values = NULL; + } else { + orig_values = el->values; + + if ((start + end < start) || (start + end < end)) { + ldb_asprintf_errstring(ldb, + "range request error: start or end would overflow!"); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_UNWILLING_TO_PERFORM); + } + + el->num_values = 0; + + el->values = talloc_array(ares->message->elements, + struct ldb_val, + (end - start) + 1); + if (!el->values) { + ldb_oom(ldb); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + for (j=start; j <= end; j++) { + el->values[el->num_values] = orig_values[j]; + el->num_values++; + } + } + el->name = talloc_asprintf(ares->message->elements, + "%s;range=%u-%s", el->name, start, + end_str); + if (!el->name) { + ldb_oom(ldb); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + } + + talloc_free(temp_ctx); + + return ldb_module_send_entry(ac->req, ares->message, ares->controls); +} + +/* search */ +static int rr_search(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb; + unsigned int i; + unsigned int start, end; + const char **new_attrs = NULL; + bool found_rr = false; + struct ldb_request *down_req; + struct rr_context *ac; + int ret; + + ldb = ldb_module_get_ctx(module); + + /* Strip the range request from the attribute */ + for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) { + char *p; + size_t range_len = strlen(";range="); + + new_attrs = talloc_realloc(req, new_attrs, const char *, i+2); + new_attrs[i] = req->op.search.attrs[i]; + new_attrs[i+1] = NULL; + p = strchr(new_attrs[i], ';'); + if (!p) { + continue; + } + if (strncasecmp(p, ";range=", range_len) != 0) { + continue; + } + end = (unsigned int)-1; + if (sscanf(p + range_len, "%u-*", &start) != 1) { + if (sscanf(p + range_len, "%u-%u", &start, &end) != 2) { + ldb_asprintf_errstring(ldb, + "range request error: " + "range request malformed"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + } + if (start > end) { + ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + found_rr = true; + new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i], + (size_t)(p - new_attrs[i])); + + if (!new_attrs[i]) { + return ldb_oom(ldb); + } + } + + if (found_rr) { + ac = rr_init_context(module, req); + if (!ac) { + return ldb_operr(ldb); + } + + ret = ldb_build_search_req_ex(&down_req, ldb, ac, + req->op.search.base, + req->op.search.scope, + req->op.search.tree, + new_attrs, + req->controls, + ac, rr_search_callback, + req); + LDB_REQ_SET_LOCATION(down_req); + if (ret != LDB_SUCCESS) { + return ret; + } + return ldb_next_request(module, down_req); + } + + /* No change, just run the original request as if we were never here */ + talloc_free(new_attrs); + return ldb_next_request(module, req); +} + +static const struct ldb_module_ops ldb_ranged_results_module_ops = { + .name = "ranged_results", + .search = rr_search, +}; + +int ldb_ranged_results_module_init(const char *version) +{ + LDB_MODULE_CHECK_VERSION(version); + return ldb_register_module(&ldb_ranged_results_module_ops); +} |