+
/*
Partitions ldb module
Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
* 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
+ * other ldb code. This module is release under the GNU GPL v3 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
+ 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,
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
*/
#include "includes.h"
-#include "ldb/include/includes.h"
+#include "ldb/include/ldb_includes.h"
#include "dsdb/samdb/samdb.h"
struct partition_private_data {
struct partition_context {
struct ldb_module *module;
- struct ldb_handle *handle;
struct ldb_request *orig_req;
struct ldb_request **down_req;
h->private_data = ac;
ac->module = module;
- ac->handle = h;
ac->orig_req = req;
req->handle = h;
struct ldb_module *module)
{
struct ldb_module *current;
- static const struct ldb_module_ops ops; /* zero */
+_PUBLIC_ static const struct ldb_module_ops ops; /* zero */
current = talloc_zero(mem_ctx, struct ldb_module);
if (current == NULL) {
return module;
return NULL;
};
-static struct ldb_module *find_backend(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
-{
- struct dsdb_control_current_partition *partition;
- struct partition_private_data *data = talloc_get_type(module->private_data,
- struct partition_private_data);
-
- /* Skip the lot if 'data' isn't here yet (initialistion) */
- if (!data) {
- return module;
- }
-
- partition = find_partition(data, dn);
- if (!partition) {
- return module;
- }
-
- return make_module_for_next_request(req, module->ldb, partition->module);
-};
-
-
-/*
- fire the caller's callback for every entry, but only send 'done' once.
-*/
+/**
+ * fire the caller's callback for every entry, but only send 'done' once.
+ */
static int partition_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct partition_context *ac;
- if (!context || !ares) {
- ldb_set_errstring(ldb, "partition_search_callback: NULL Context or Result in 'search' callback");
- goto error;
- }
-
ac = talloc_get_type(context, struct partition_context);
if (ares->type == LDB_REPLY_ENTRY) {
return LDB_SUCCESS;
}
}
-error:
- talloc_free(ares);
- return LDB_ERR_OPERATIONS_ERROR;
}
-/*
- only fire the 'last' callback, and only for START-TLS for now
-*/
+/**
+ * only fire the 'last' callback, and only for START-TLS for now
+ */
static int partition_other_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct partition_context *ac;
- if (!context) {
- ldb_set_errstring(ldb, "partition_other_callback: NULL Context in 'other' callback");
- goto error;
- }
-
ac = talloc_get_type(context, struct partition_context);
if (!ac->orig_req->callback) {
return LDB_SUCCESS;
}
ldb_set_errstring(ldb, "partition_other_callback: Unknown reply type, only supports START_TLS");
-error:
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
-static int partition_send_request(struct partition_context *ac, struct ldb_module *partition,
- struct ldb_dn *partition_base_dn)
+static int partition_send_request(struct partition_context *ac,
+ struct dsdb_control_current_partition *partition)
{
int ret;
- struct ldb_module *next = make_module_for_next_request(ac->module, ac->module->ldb, partition);
+ struct ldb_module *backend;
struct ldb_request *req;
+
+ if (partition) {
+ backend = make_module_for_next_request(ac, ac->module->ldb, partition->module);
+ } else {
+ backend = ac->module;
+ }
+
ac->down_req = talloc_realloc(ac, ac->down_req,
struct ldb_request *, ac->num_requests + 1);
if (!ac->down_req) {
- ldb_set_errstring(ac->module->ldb, "Out of Memory");
+ ldb_oom(ac->module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
req = ac->down_req[ac->num_requests] = talloc(ac, struct ldb_request);
if (req == NULL) {
- ldb_set_errstring(ac->module->ldb, "Out of Memory");
+ ldb_oom(ac->module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
- *ac->down_req[ac->num_requests] = *ac->orig_req; /* copy the request */
+ *req = *ac->orig_req; /* copy the request */
+
+ if (req->controls) {
+ req->controls
+ = talloc_memdup(req,
+ ac->orig_req->controls, talloc_get_size(ac->orig_req->controls));
+ if (req->controls == NULL) {
+ ldb_oom(ac->module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
if (req->operation == LDB_SEARCH) {
/* If the search is for 'more' than this partition,
* then change the basedn, so a remote LDAP server
* doesn't object */
- if (ldb_dn_compare_base(partition_base_dn, req->op.search.base) != 0) {
- req->op.search.base = partition_base_dn;
+ if (partition) {
+ if (ldb_dn_compare_base(partition->dn, req->op.search.base) != 0) {
+ req->op.search.base = partition->dn;
+ }
+ } else {
+ req->op.search.base = NULL;
}
req->callback = partition_search_callback;
req->context = ac;
req->context = ac;
}
- /* Spray off search requests to all backends */
- ret = ldb_next_request(next, req);
+ if (partition) {
+ ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ /* Spray off search requests the backend */
+ ret = ldb_next_request(backend, req);
if (ret != LDB_SUCCESS) {
return ret;
}
-
+
ac->num_requests++;
return LDB_SUCCESS;
}
-/* Send a request down to all the partitions */
+/**
+ * Send a request down to all the partitions
+ */
static int partition_send_all(struct ldb_module *module,
- struct partition_context *ac, struct ldb_request *req)
+ struct partition_context *ac,
+ struct ldb_request *req)
{
int i;
struct partition_private_data *data = talloc_get_type(module->private_data,
struct partition_private_data);
- int ret = partition_send_request(ac, module->next, NULL);
+ int ret = partition_send_request(ac, NULL);
if (ret != LDB_SUCCESS) {
return ret;
}
for (i=0; data && data->partitions && data->partitions[i]; i++) {
- ret = partition_send_request(ac, data->partitions[i]->module, data->partitions[i]->dn);
+ ret = partition_send_request(ac, data->partitions[i]);
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_SUCCESS;
}
-/* Figure out which backend a request needs to be aimed at. Some
- * requests must be replicated to all backends */
+/**
+ * Figure out which backend a request needs to be aimed at. Some
+ * requests must be replicated to all backends
+ */
static int partition_replicate(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
{
- int i;
+ unsigned i;
+ int ret;
+ struct dsdb_control_current_partition *partition;
struct ldb_module *backend;
struct partition_private_data *data = talloc_get_type(module->private_data,
struct partition_private_data);
- /* Is this a special DN, we need to replicate to every backend? */
- for (i=0; data->replicate && data->replicate[i]; i++) {
- if (ldb_dn_compare(data->replicate[i],
- dn) == 0) {
- struct partition_context *ac;
-
- ac = partition_init_handle(req, module);
- if (!ac) {
- return LDB_ERR_OPERATIONS_ERROR;
+ if (req->operation != LDB_SEARCH) {
+ /* Is this a special DN, we need to replicate to every backend? */
+ for (i=0; data->replicate && data->replicate[i]; i++) {
+ if (ldb_dn_compare(data->replicate[i],
+ dn) == 0) {
+ struct partition_context *ac;
+
+ ac = partition_init_handle(req, module);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return partition_send_all(module, ac, req);
}
-
- return partition_send_all(module, ac, req);
}
}
- /* Otherwise, we need to find the backend to fire it to */
+ /* Otherwise, we need to find the partition to fire it to */
+
+ /* Find partition */
+ partition = find_partition(data, dn);
+ if (!partition) {
+ /*
+ * if we haven't found a matching partition
+ * pass the request to the main ldb
+ *
+ * TODO: we should maybe return an error here
+ * if it's not a special dn
+ */
+
+ return ldb_next_request(module, req);
+ }
+
+ backend = make_module_for_next_request(req, module->ldb, partition->module);
+ if (!backend) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
- /* Find backend */
- backend = find_backend(module, req, dn);
-
/* issue request */
return ldb_next_request(backend, req);
-
}
/* search */
static int partition_search(struct ldb_module *module, struct ldb_request *req)
{
+ struct ldb_control **saved_controls;
+
/* Find backend */
struct partition_private_data *data = talloc_get_type(module->private_data,
struct partition_private_data);
/* (later) consider if we should be searching multiple
* partitions (for 'invisible' partition behaviour */
- if (ldb_get_opaque(module->ldb, "global_catalog")) {
+ struct ldb_control *search_control = ldb_request_get_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID);
+ struct ldb_control *domain_scope_control = ldb_request_get_control(req, LDB_CONTROL_DOMAIN_SCOPE_OID);
+
+ struct ldb_search_options_control *search_options = NULL;
+ if (search_control) {
+ search_options = talloc_get_type(search_control->data, struct ldb_search_options_control);
+ }
+
+ /* Remove the domain_scope control, so we don't confuse a backend server */
+ if (domain_scope_control && !save_controls(domain_scope_control, req, &saved_controls)) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* TODO:
+ Generate referrals (look for a partition under this DN) if we don't have the above control specified
+ */
+
+ if (search_options && (search_options->search_options & LDB_SEARCH_OPTION_PHANTOM_ROOT)) {
int ret, i;
struct partition_context *ac;
-
+ if ((search_options->search_options & ~LDB_SEARCH_OPTION_PHANTOM_ROOT) == 0) {
+ /* We have processed this flag, so we are done with this control now */
+
+ /* Remove search control, so we don't confuse a backend server */
+ if (search_control && !save_controls(search_control, req, &saved_controls)) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
ac = partition_init_handle(req, module);
if (!ac) {
return LDB_ERR_OPERATIONS_ERROR;
for (i=0; data && data->partitions && data->partitions[i]; i++) {
/* Find all partitions under the search base */
if (ldb_dn_compare_base(req->op.search.base, data->partitions[i]->dn) == 0) {
- ret = partition_send_request(ac, data->partitions[i]->module, data->partitions[i]->dn);
+ ret = partition_send_request(ac, data->partitions[i]);
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_SUCCESS;
} else {
- struct ldb_module *backend = find_backend(module, req, req->op.search.base);
-
- return ldb_next_request(backend, req);
+ /* Handle this like all other requests */
+ if (search_control && (search_options->search_options & ~LDB_SEARCH_OPTION_PHANTOM_ROOT) == 0) {
+ /* We have processed this flag, so we are done with this control now */
+
+ /* Remove search control, so we don't confuse a backend server */
+ if (search_control && !save_controls(search_control, req, &saved_controls)) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ return partition_replicate(module, req, req->op.search.base);
}
}
/* rename */
static int partition_rename(struct ldb_module *module, struct ldb_request *req)
{
+ int i, matched = -1;
/* Find backend */
- struct ldb_module *backend = find_backend(module, req, req->op.rename.olddn);
- struct ldb_module *backend2 = find_backend(module, req, req->op.rename.newdn);
+ struct dsdb_control_current_partition *backend, *backend2;
+
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ if (!data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ backend = find_partition(data, req->op.rename.olddn);
+ backend2 = find_partition(data, req->op.rename.newdn);
+
+ if ((backend && !backend2) || (!backend && backend2)) {
+ return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+ }
+
+ if (backend != backend2) {
+ ldb_asprintf_errstring(module->ldb,
+ "Cannot rename from %s in %s to %s in %s: %s",
+ ldb_dn_get_linearized(req->op.rename.olddn),
+ ldb_dn_get_linearized(backend->dn),
+ ldb_dn_get_linearized(req->op.rename.newdn),
+ ldb_dn_get_linearized(backend2->dn),
+ ldb_strerror(LDB_ERR_AFFECTS_MULTIPLE_DSAS));
+ return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+ }
+
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ if (ldb_dn_compare_base(req->op.rename.olddn, data->partitions[i]->dn) == 0) {
+ matched = i;
+ }
+ }
- if (backend->next != backend2->next) {
+ if (matched > 0) {
+ ldb_asprintf_errstring(module->ldb,
+ "Cannot rename from %s to %s, subtree rename would cross partition %s: %s",
+ ldb_dn_get_linearized(req->op.rename.olddn),
+ ldb_dn_get_linearized(req->op.rename.newdn),
+ ldb_dn_get_linearized(data->partitions[matched]->dn),
+ ldb_strerror(LDB_ERR_AFFECTS_MULTIPLE_DSAS));
return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
}
static int sort_compare(void *void1,
void *void2, void *opaque)
{
- struct dsdb_control_current_partition **pp1 = void1;
- struct dsdb_control_current_partition **pp2 = void2;
+ struct dsdb_control_current_partition **pp1 =
+ (struct dsdb_control_current_partition **)void1;
+ struct dsdb_control_current_partition **pp2 =
+ (struct dsdb_control_current_partition **)void2;
struct dsdb_control_current_partition *partition1 = talloc_get_type(*pp1,
struct dsdb_control_current_partition);
struct dsdb_control_current_partition *partition2 = talloc_get_type(*pp2,
{
int ret, i;
TALLOC_CTX *mem_ctx = talloc_new(module);
- static const char *attrs[] = { "partition", "replicateEntries", "modules", NULL };
+ const char *attrs[] = { "partition", "replicateEntries", "modules", NULL };
struct ldb_result *res;
struct ldb_message *msg;
struct ldb_message_element *partition_attributes;
return LDB_ERR_CONSTRAINT_VIOLATION;
}
- data->partitions[i]->backend = private_path(data->partitions[i], p);
+ data->partitions[i]->backend = samdb_relative_path(module->ldb,
+ data->partitions[i],
+ p);
+ if (!data->partitions[i]->backend) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: unable to determine an relative path for partition: %s", base);
+ talloc_free(mem_ctx);
+ }
ret = ldb_connect_backend(module->ldb, data->partitions[i]->backend, NULL, &data->partitions[i]->module);
if (ret != LDB_SUCCESS) {
talloc_free(mem_ctx);
}
}
-static const struct ldb_module_ops partition_ops = {
+_PUBLIC_ const struct ldb_module_ops ldb_partition_module_ops = {
.name = "partition",
.init_context = partition_init,
.search = partition_search,
.del_transaction = partition_del_trans,
.wait = partition_wait
};
-
-int ldb_partition_init(void)
-{
- return ldb_register_module(&partition_ops);
-}