summaryrefslogtreecommitdiffstats
path: root/lib/ldb-samba
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /lib/ldb-samba
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.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 'lib/ldb-samba')
-rw-r--r--lib/ldb-samba/README7
-rw-r--r--lib/ldb-samba/ldb_ildap.c899
-rw-r--r--lib/ldb-samba/ldb_matching_rules.c636
-rw-r--r--lib/ldb-samba/ldb_matching_rules.h30
-rw-r--r--lib/ldb-samba/ldb_wrap.c377
-rw-r--r--lib/ldb-samba/ldb_wrap.h70
-rw-r--r--lib/ldb-samba/ldif_handlers.c1836
-rw-r--r--lib/ldb-samba/ldif_handlers.h30
-rw-r--r--lib/ldb-samba/pyldb.c320
-rw-r--r--lib/ldb-samba/samba_extensions.c169
-rw-r--r--lib/ldb-samba/tests/index.py192
-rwxr-xr-xlib/ldb-samba/tests/match_rules.py1797
-rwxr-xr-xlib/ldb-samba/tests/match_rules_remote.py104
-rw-r--r--lib/ldb-samba/wscript_build44
14 files changed, 6511 insertions, 0 deletions
diff --git a/lib/ldb-samba/README b/lib/ldb-samba/README
new file mode 100644
index 0000000..3fa4715
--- /dev/null
+++ b/lib/ldb-samba/README
@@ -0,0 +1,7 @@
+This directory contains Samba specific extensions to ldb. It also
+serves as example code on how to extend ldb for your own application.
+
+The main extension Samba uses is to provide ldif encode/decode
+routines for specific attributes, so users can get nice pretty
+printing of attributes in ldbedit, while the attributes are stored in
+the standard NDR format in the database.
diff --git a/lib/ldb-samba/ldb_ildap.c b/lib/ldb-samba/ldb_ildap.c
new file mode 100644
index 0000000..b15f38b
--- /dev/null
+++ b/lib/ldb-samba/ldb_ildap.c
@@ -0,0 +1,899 @@
+/*
+ ldb database library - ildap backend
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Simo Sorce 2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ 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 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
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb_ildap
+ *
+ * Component: ldb ildap backend
+ *
+ * Description: This is a ldb backend for the internal ldap
+ * client library in Samba4. By using this backend we are
+ * independent of a system ldap library
+ *
+ * Author: Andrew Tridgell
+ *
+ * Modifications:
+ *
+ * - description: make the module use asynchronous calls
+ * date: Feb 2006
+ * author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "util/dlinklist.h"
+
+#include "libcli/ldap/libcli_ldap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "dsdb/common/util.h"
+
+struct ildb_private {
+ struct ldap_connection *ldap;
+ struct tevent_context *event_ctx;
+};
+
+struct ildb_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ildb_private *ildb;
+ struct ldap_request *ireq;
+
+ /* indicate we are already processing
+ * the ldap_request in ildb_callback() */
+ bool in_ildb_callback;
+
+ bool done;
+
+ struct ildb_destructor_ctx *dc;
+};
+
+static void ildb_request_done(struct ildb_context *ctx,
+ struct ldb_control **ctrls, int error)
+{
+ struct ldb_context *ldb;
+ struct ldb_reply *ares;
+
+ ldb = ldb_module_get_ctx(ctx->module);
+
+ ctx->done = true;
+
+ if (ctx->req == NULL) {
+ /* if the req has been freed already just return */
+ return;
+ }
+
+ ares = talloc_zero(ctx->req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(ldb);
+ ctx->req->callback(ctx->req, NULL);
+ return;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->controls = talloc_steal(ares, ctrls);
+ ares->error = error;
+
+ ctx->req->callback(ctx->req, ares);
+}
+
+static void ildb_auto_done_callback(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct ildb_context *ac;
+
+ ac = talloc_get_type(private_data, struct ildb_context);
+ ildb_request_done(ac, NULL, LDB_SUCCESS);
+}
+
+/*
+ convert a ldb_message structure to a list of ldap_mod structures
+ ready for ildap_add() or ildap_modify()
+*/
+static struct ldap_mod **ildb_msg_to_mods(void *mem_ctx, unsigned int *num_mods,
+ const struct ldb_message *msg,
+ int use_flags)
+{
+ struct ldap_mod **mods;
+ unsigned int i;
+ unsigned int n = 0;
+
+ /* allocate maximum number of elements needed */
+ mods = talloc_array(mem_ctx, struct ldap_mod *, msg->num_elements+1);
+ if (!mods) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ mods[0] = NULL;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ const struct ldb_message_element *el = &msg->elements[i];
+
+ mods[n] = talloc(mods, struct ldap_mod);
+ if (!mods[n]) {
+ goto failed;
+ }
+ mods[n + 1] = NULL;
+ mods[n]->type = 0;
+ mods[n]->attrib = *el;
+ if (use_flags) {
+ switch (el->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_ADD:
+ mods[n]->type = LDAP_MODIFY_ADD;
+ break;
+ case LDB_FLAG_MOD_DELETE:
+ mods[n]->type = LDAP_MODIFY_DELETE;
+ break;
+ case LDB_FLAG_MOD_REPLACE:
+ mods[n]->type = LDAP_MODIFY_REPLACE;
+ break;
+ }
+ }
+ n++;
+ }
+
+ *num_mods = n;
+ return mods;
+
+failed:
+ talloc_free(mods);
+ return NULL;
+}
+
+
+/*
+ map an ildap NTSTATUS to a ldb error code
+*/
+static int ildb_map_error(struct ldb_module *module, NTSTATUS status)
+{
+ struct ildb_private *ildb;
+ struct ldb_context *ldb;
+ TALLOC_CTX *mem_ctx;
+
+ ildb = talloc_get_type(ldb_module_get_private(module), struct ildb_private);
+ ldb = ldb_module_get_ctx(module);
+
+ if (NT_STATUS_IS_OK(status)) {
+ return LDB_SUCCESS;
+ }
+
+ mem_ctx = talloc_new(ildb);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ldb_set_errstring(ldb,
+ ldap_errstr(ildb->ldap, mem_ctx, status));
+ talloc_free(mem_ctx);
+ if (NT_STATUS_IS_LDAP(status)) {
+ return NT_STATUS_LDAP_CODE(status);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static void ildb_request_timeout(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ildb_context *ac = talloc_get_type(private_data, struct ildb_context);
+
+ if (ac->ireq->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(ac->ireq->conn->pending, ac->ireq);
+ }
+
+ ildb_request_done(ac, NULL, LDB_ERR_TIME_LIMIT_EXCEEDED);
+}
+
+static void ildb_callback(struct ldap_request *req)
+{
+ struct ldb_context *ldb;
+ struct ildb_context *ac;
+ NTSTATUS status;
+ struct ldap_SearchResEntry *search;
+ struct ldap_message *msg;
+ struct ldb_control **controls;
+ struct ldb_message *ldbmsg;
+ char *referral;
+ bool callback_failed;
+ bool request_done;
+ int ret;
+ int i;
+
+ ac = talloc_get_type(req->async.private_data, struct ildb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+ callback_failed = false;
+ request_done = false;
+ controls = NULL;
+
+ /* check if we are already processing this request */
+ if (ac->in_ildb_callback) {
+ return;
+ }
+ /* mark the request as being in process */
+ ac->in_ildb_callback = true;
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ ret = ildb_map_error(ac->module, req->status);
+ ildb_request_done(ac, NULL, ret);
+ return;
+ }
+
+ if (req->num_replies < 1) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ ildb_request_done(ac, NULL, ret);
+ return;
+ }
+
+ switch (req->type) {
+
+ case LDAP_TAG_ModifyRequest:
+ if (req->replies[0]->type != LDAP_TAG_ModifyResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_AddRequest:
+ if (req->replies[0]->type != LDAP_TAG_AddResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ return;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_DelRequest:
+ if (req->replies[0]->type != LDAP_TAG_DelResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ return;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_ModifyDNRequest:
+ if (req->replies[0]->type != LDAP_TAG_ModifyDNResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ return;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_SearchRequest:
+ /* loop over all messages */
+ for (i = 0; i < req->num_replies; i++) {
+
+ msg = req->replies[i];
+ switch (msg->type) {
+
+ case LDAP_TAG_SearchResultDone:
+
+ status = ldap_check_response(ac->ireq->conn, &msg->r.GeneralResult);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = ildb_map_error(ac->module, status);
+ break;
+ }
+
+ controls = talloc_steal(ac, msg->controls);
+ if (msg->r.SearchResultDone.resultcode) {
+ if (msg->r.SearchResultDone.errormessage) {
+ ldb_set_errstring(ldb, msg->r.SearchResultDone.errormessage);
+ }
+ }
+
+ ret = msg->r.SearchResultDone.resultcode;
+ request_done = true;
+ break;
+
+ case LDAP_TAG_SearchResultEntry:
+
+ ldbmsg = ldb_msg_new(ac);
+ if (!ldbmsg) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ search = &(msg->r.SearchResultEntry);
+
+ ldbmsg->dn = ldb_dn_new(ldbmsg, ldb, search->dn);
+ if ( ! ldb_dn_validate(ldbmsg->dn)) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+ ldbmsg->num_elements = search->num_attributes;
+ ldbmsg->elements = talloc_move(ldbmsg, &search->attributes);
+
+ controls = talloc_steal(ac, msg->controls);
+
+ ret = ldb_module_send_entry(ac->req, ldbmsg, controls);
+ if (ret != LDB_SUCCESS) {
+ callback_failed = true;
+ }
+
+ break;
+
+ case LDAP_TAG_SearchResultReference:
+
+ referral = talloc_strdup(ac, msg->r.SearchResultReference.referral);
+
+ ret = ldb_module_send_referral(ac->req, referral);
+ if (ret != LDB_SUCCESS) {
+ callback_failed = true;
+ }
+
+ break;
+
+ default:
+ /* TAG not handled, fail ! */
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+ }
+
+ talloc_free(req->replies);
+ req->replies = NULL;
+ req->num_replies = 0;
+
+ break;
+
+ default:
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+
+ /* if the callback failed the caller will have freed the
+ * request. Just return and don't try to use it */
+ if ( ! callback_failed) {
+ request_done = true;
+ }
+ }
+
+ /* mark the request as not being in progress */
+ ac->in_ildb_callback = false;
+
+ if (request_done) {
+ ildb_request_done(ac, controls, ret);
+ }
+
+ return;
+}
+
+static int ildb_request_send(struct ildb_context *ac, struct ldap_message *msg)
+{
+ struct ldb_context *ldb;
+ struct ldap_request *req;
+
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ldb_request_set_state(ac->req, LDB_ASYNC_PENDING);
+
+ req = ldap_request_send(ac->ildb->ldap, msg);
+ if (req == NULL) {
+ ldb_set_errstring(ldb, "async send request failed");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->ireq = talloc_reparent(ac->ildb->ldap, ac, req);
+
+ if (!ac->ireq->conn) {
+ ldb_set_errstring(ldb, "connection to remote LDAP server dropped?");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ TALLOC_FREE(req->time_event);
+ if (ac->req->timeout > 0) {
+ struct timeval tv = {
+ .tv_sec = ac->req->starttime + ac->req->timeout,
+ };
+
+ req->time_event = tevent_add_timer(ac->ildb->event_ctx, ac, tv,
+ ildb_request_timeout, ac);
+ }
+
+ req->async.fn = ildb_callback;
+ req->async.private_data = ac;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ search for matching records using an asynchronous function
+ */
+static int ildb_search(struct ildb_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+ int n;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!req->callback || !req->context) {
+ ldb_set_errstring(ldb, "Async interface called with NULL callback function or NULL context");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->op.search.tree == NULL) {
+ ldb_set_errstring(ldb, "Invalid expression parse tree");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_SearchRequest;
+
+ if (req->op.search.base == NULL) {
+ msg->r.SearchRequest.basedn = talloc_strdup(msg, "");
+ } else {
+ msg->r.SearchRequest.basedn = ldb_dn_get_extended_linearized(msg, req->op.search.base, 0);
+ }
+ if (msg->r.SearchRequest.basedn == NULL) {
+ ldb_set_errstring(ldb, "Unable to determine baseDN");
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->op.search.scope == LDB_SCOPE_DEFAULT) {
+ msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_SUB;
+ } else {
+ msg->r.SearchRequest.scope = req->op.search.scope;
+ }
+
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = 0;
+ msg->r.SearchRequest.tree = discard_const(req->op.search.tree);
+
+ for (n = 0; req->op.search.attrs && req->op.search.attrs[n]; n++) /* noop */ ;
+ msg->r.SearchRequest.num_attributes = n;
+ msg->r.SearchRequest.attributes = req->op.search.attrs;
+ msg->controls = req->controls;
+
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ add a record
+*/
+static int ildb_add(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+ struct ldap_mod **mods;
+ unsigned int i,n;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_AddRequest;
+
+ msg->r.AddRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.add.message->dn, 0);
+ if (msg->r.AddRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ mods = ildb_msg_to_mods(msg, &n, req->op.add.message, 0);
+ if (mods == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->r.AddRequest.num_attributes = n;
+ msg->r.AddRequest.attributes = talloc_array(msg, struct ldb_message_element, n);
+ if (msg->r.AddRequest.attributes == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < n; i++) {
+ msg->r.AddRequest.attributes[i] = mods[i]->attrib;
+ }
+ msg->controls = req->controls;
+
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ modify a record
+*/
+static int ildb_modify(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+ struct ldap_mod **mods;
+ unsigned int i,n;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_ModifyRequest;
+
+ msg->r.ModifyRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.mod.message->dn, 0);
+ if (msg->r.ModifyRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ mods = ildb_msg_to_mods(msg, &n, req->op.mod.message, 1);
+ if (mods == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->r.ModifyRequest.num_mods = n;
+ msg->r.ModifyRequest.mods = talloc_array(msg, struct ldap_mod, n);
+ if (msg->r.ModifyRequest.mods == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < n; i++) {
+ msg->r.ModifyRequest.mods[i] = *mods[i];
+ }
+ msg->controls = req->controls;
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ delete a record
+*/
+static int ildb_delete(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_DelRequest;
+
+ msg->r.DelRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.del.dn, 0);
+ if (msg->r.DelRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ msg->controls = req->controls;
+
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ rename a record
+*/
+static int ildb_rename(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+ const char *rdn_name;
+ const struct ldb_val *rdn_val;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_ModifyDNRequest;
+ msg->r.ModifyDNRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.rename.olddn, 0);
+ if (msg->r.ModifyDNRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ rdn_name = ldb_dn_get_rdn_name(req->op.rename.newdn);
+ rdn_val = ldb_dn_get_rdn_val(req->op.rename.newdn);
+
+ if ((rdn_name != NULL) && (rdn_val != NULL)) {
+ msg->r.ModifyDNRequest.newrdn =
+ talloc_asprintf(msg, "%s=%s", rdn_name,
+ rdn_val->length > 0 ? ldb_dn_escape_value(msg, *rdn_val) : "");
+ } else {
+ msg->r.ModifyDNRequest.newrdn = talloc_strdup(msg, "");
+ }
+ if (msg->r.ModifyDNRequest.newrdn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->r.ModifyDNRequest.newsuperior =
+ ldb_dn_alloc_linearized(msg, ldb_dn_get_parent(msg, req->op.rename.newdn));
+ if (msg->r.ModifyDNRequest.newsuperior == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ msg->r.ModifyDNRequest.deleteolddn = true;
+ msg->controls = req->controls;
+
+ return ildb_request_send(ac, msg);
+}
+
+static int ildb_start_trans(struct ldb_module *module)
+{
+ /* TODO implement a local locking mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static int ildb_end_trans(struct ldb_module *module)
+{
+ /* TODO implement a local transaction mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static int ildb_del_trans(struct ldb_module *module)
+{
+ /* TODO implement a local locking mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static bool ildb_dn_is_special(struct ldb_request *req)
+{
+ struct ldb_dn *dn = NULL;
+
+ switch (req->operation) {
+ case LDB_SEARCH:
+ dn = req->op.search.base;
+ break;
+ case LDB_ADD:
+ dn = req->op.add.message->dn;
+ break;
+ case LDB_MODIFY:
+ dn = req->op.mod.message->dn;
+ break;
+ case LDB_DELETE:
+ dn = req->op.del.dn;
+ break;
+ case LDB_RENAME:
+ dn = req->op.rename.olddn;
+ break;
+ default:
+ break;
+ }
+
+ if (dn && ldb_dn_is_special(dn)) {
+ return true;
+ }
+ return false;
+}
+
+static int ildb_handle_request(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ildb_private *ildb;
+ struct ildb_context *ac;
+ struct tevent_timer *te;
+ int ret;
+
+ ildb = talloc_get_type(ldb_module_get_private(module), struct ildb_private);
+ ldb = ldb_module_get_ctx(module);
+
+ if (req->starttime == 0 || req->timeout == 0) {
+ ldb_set_errstring(ldb, "Invalid timeout settings");
+ return LDB_ERR_TIME_LIMIT_EXCEEDED;
+ }
+
+ ac = talloc_zero(req, struct ildb_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->ildb = ildb;
+
+ if (ildb_dn_is_special(req)) {
+
+ te = tevent_add_timer(ac->ildb->event_ctx,
+ ac, timeval_zero(),
+ ildb_auto_done_callback, ac);
+ if (NULL == te) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+ }
+
+ switch (ac->req->operation) {
+ case LDB_SEARCH:
+ ret = ildb_search(ac);
+ break;
+ case LDB_ADD:
+ ret = ildb_add(ac);
+ break;
+ case LDB_MODIFY:
+ ret = ildb_modify(ac);
+ break;
+ case LDB_DELETE:
+ ret = ildb_delete(ac);
+ break;
+ case LDB_RENAME:
+ ret = ildb_rename(ac);
+ break;
+ default:
+ /* no other op supported */
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct ldb_module_ops ildb_ops = {
+ .name = "ldap",
+ .search = ildb_handle_request,
+ .add = ildb_handle_request,
+ .modify = ildb_handle_request,
+ .del = ildb_handle_request,
+ .rename = ildb_handle_request,
+/* .request = ildb_handle_request, */
+ .start_transaction = ildb_start_trans,
+ .end_transaction = ildb_end_trans,
+ .del_transaction = ildb_del_trans,
+};
+
+/*
+ connect to the database
+*/
+static int ildb_connect(struct ldb_context *ldb, const char *url,
+ unsigned int flags, const char *options[],
+ struct ldb_module **_module)
+{
+ struct ldb_module *module;
+ struct ildb_private *ildb;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct cli_credentials *creds;
+ struct loadparm_context *lp_ctx;
+
+ module = ldb_module_new(ldb, ldb, "ldb_ildap backend", &ildb_ops);
+ if (!module) return LDB_ERR_OPERATIONS_ERROR;
+
+ ildb = talloc(module, struct ildb_private);
+ if (!ildb) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+ ldb_module_set_private(module, ildb);
+
+ ildb->event_ctx = ldb_get_event_context(ldb);
+
+ lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+ struct loadparm_context);
+
+ ildb->ldap = ldap4_new_connection(ildb, lp_ctx,
+ ildb->event_ctx);
+ if (!ildb->ldap) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+
+ if (flags & LDB_FLG_RECONNECT) {
+ ldap_set_reconn_params(ildb->ldap, 10);
+ }
+
+ status = ldap_connect(ildb->ldap, url);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to ldap URL '%s' - %s",
+ url, ldap_errstr(ildb->ldap, module, status));
+ goto failed;
+ }
+
+ /* caller can optionally setup credentials using the opaque token 'credentials' */
+ creds = talloc_get_type(ldb_get_opaque(ldb, "credentials"), struct cli_credentials);
+ if (creds == NULL) {
+ struct auth_session_info *session_info = talloc_get_type(
+ ldb_get_opaque(ldb, DSDB_SESSION_INFO),
+ struct auth_session_info);
+ if (session_info) {
+ creds = session_info->credentials;
+ }
+ }
+
+ if (creds != NULL && cli_credentials_authentication_requested(creds)) {
+ const char *bind_dn = cli_credentials_get_bind_dn(creds);
+ if (bind_dn) {
+ const char *password = cli_credentials_get_password(creds);
+ status = ldap_bind_simple(ildb->ldap, bind_dn, password);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s",
+ ldap_errstr(ildb->ldap, module, status));
+ goto failed;
+ }
+ } else {
+ status = ldap_bind_sasl(ildb->ldap, creds, lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s",
+ ldap_errstr(ildb->ldap, module, status));
+ goto failed;
+ }
+ }
+ }
+
+ *_module = module;
+ return LDB_SUCCESS;
+
+failed:
+ if (ildb != NULL && ildb->ldap != NULL) {
+ ldb_set_errstring(ldb, ldap_errstr(ildb->ldap, module, status));
+ }
+ talloc_free(module);
+ if (NT_STATUS_IS_LDAP(status)) {
+ return NT_STATUS_LDAP_CODE(status);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)
+ || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
+ || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
+ || NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
+ return LDB_ERR_INVALID_CREDENTIALS;
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/*
+ initialise the module
+ */
+_PUBLIC_ int ldb_ildap_init(const char *ldb_version)
+{
+ int ret, i;
+ const char *names[] = { "ldap", "ldaps", "ldapi", NULL };
+ for (i=0; names[i]; i++) {
+ ret = ldb_register_backend(names[i], ildb_connect, true);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
diff --git a/lib/ldb-samba/ldb_matching_rules.c b/lib/ldb-samba/ldb_matching_rules.c
new file mode 100644
index 0000000..59d1385
--- /dev/null
+++ b/lib/ldb-samba/ldb_matching_rules.c
@@ -0,0 +1,636 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ ldb database library - Extended match rules
+
+ Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
+ Copyright (C) Andrew Bartlett <abartlet@samba.org>
+
+ 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/>.
+*/
+
+#include "includes.h"
+#include <ldb_module.h>
+#include "dsdb/samdb/samdb.h"
+#include "ldb_matching_rules.h"
+#include "libcli/security/security.h"
+#include "dsdb/common/util.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "lib/util/smb_strtox.h"
+
+#undef strcasecmp
+
+static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *attr,
+ const struct dsdb_dn *dn_to_match,
+ const char *dn_oid,
+ struct dsdb_dn *to_visit,
+ struct dsdb_dn ***visited,
+ unsigned int *visited_count,
+ bool *matched)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret, i, j;
+ struct ldb_result *res;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ const char *attrs[] = { attr, NULL };
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * Fetch the entry to_visit
+ *
+ * NOTE: This is a new LDB search from the TOP of the module
+ * stack. This means that this search runs the whole stack
+ * from top to bottom.
+ *
+ * This may seem to be in-efficient, but it is also the only
+ * way to ensure that the ACLs for this search are applied
+ * correctly.
+ *
+ * Note also that we don't have the original request
+ * here, so we can not apply controls or timeouts here.
+ */
+ ret = dsdb_search_dn(ldb,
+ tmp_ctx,
+ &res,
+ to_visit->dn,
+ attrs,
+ DSDB_MARK_REQ_UNTRUSTED);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ if (res->count != 1) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg = res->msgs[0];
+
+ /* Fetch the attribute to match from the entry being visited */
+ el = ldb_msg_find_element(msg, attr);
+ if (el == NULL) {
+ /* This entry does not have the attribute to match */
+ talloc_free(tmp_ctx);
+ *matched = false;
+ return LDB_SUCCESS;
+ }
+
+ /*
+ * If the value to match is present in the attribute values of the
+ * current entry being visited, set matched to true and return OK
+ */
+ for (i=0; i<el->num_values; i++) {
+ struct dsdb_dn *dn;
+ dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
+ if (dn == NULL) {
+ talloc_free(tmp_ctx);
+ *matched = false;
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) {
+ talloc_free(tmp_ctx);
+ *matched = true;
+ return LDB_SUCCESS;
+ }
+ }
+
+ /*
+ * If arrived here, the value to match is not in the values of the
+ * entry being visited. Add the entry being visited (to_visit)
+ * to the visited array. The array is (re)allocated in the parent
+ * memory context.
+ */
+ if (visited == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ } else if (*visited == NULL) {
+ *visited = talloc_array(mem_ctx, struct dsdb_dn *, 1);
+ if (*visited == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ (*visited)[0] = to_visit;
+ (*visited_count) = 1;
+ } else {
+ *visited = talloc_realloc(mem_ctx, *visited, struct dsdb_dn *,
+ (*visited_count) + 1);
+ if (*visited == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ (*visited)[(*visited_count)] = to_visit;
+ (*visited_count)++;
+ }
+
+ /*
+ * steal to_visit into visited array context, as it has to live until
+ * the array is freed.
+ */
+ talloc_steal(*visited, to_visit);
+
+ /*
+ * Iterate over the values of the attribute of the entry being
+ * visited (to_visit) and follow them, calling this function
+ * recursively.
+ * If the value is in the visited array, skip it.
+ * Otherwise, follow the link and visit it.
+ */
+ for (i=0; i<el->num_values; i++) {
+ struct dsdb_dn *next_to_visit;
+ bool skip = false;
+
+ next_to_visit = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
+ if (next_to_visit == NULL) {
+ talloc_free(tmp_ctx);
+ *matched = false;
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ /*
+ * If the value is already in the visited array, skip it.
+ * Note the last element of the array is ignored because it is
+ * the current entry DN.
+ */
+ for (j=0; j < (*visited_count) - 1; j++) {
+ struct dsdb_dn *visited_dn = (*visited)[j];
+ if (ldb_dn_compare(visited_dn->dn,
+ next_to_visit->dn) == 0) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ talloc_free(next_to_visit);
+ continue;
+ }
+
+ /* If the value is not in the visited array, evaluate it */
+ ret = ldb_eval_transitive_filter_helper(tmp_ctx, ldb, attr,
+ dn_to_match, dn_oid,
+ next_to_visit,
+ visited, visited_count,
+ matched);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ if (*matched) {
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ *matched = false;
+ return LDB_SUCCESS;
+}
+
+/*
+ * This function parses the linked attribute value to match, whose syntax
+ * will be one of the different DN syntaxes, into a ldb_dn struct.
+ */
+static int ldb_eval_transitive_filter(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *attr,
+ const struct ldb_val *value_to_match,
+ struct dsdb_dn *current_object_dn,
+ bool *matched)
+{
+ const struct dsdb_schema *schema;
+ const struct dsdb_attribute *schema_attr;
+ struct dsdb_dn *dn_to_match;
+ const char *dn_oid;
+ unsigned int count;
+ struct dsdb_dn **visited = NULL;
+
+ schema = dsdb_get_schema(ldb, mem_ctx);
+ if (schema == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attr);
+ if (schema_attr == NULL) {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* This is the DN syntax of the attribute being matched */
+ dn_oid = schema_attr->syntax->ldap_oid;
+
+ /*
+ * Build a ldb_dn struct holding the value to match, which is the
+ * value entered in the search filter
+ */
+ dn_to_match = dsdb_dn_parse(mem_ctx, ldb, value_to_match, dn_oid);
+ if (dn_to_match == NULL) {
+ *matched = false;
+ return LDB_SUCCESS;
+ }
+
+ return ldb_eval_transitive_filter_helper(mem_ctx, ldb, attr,
+ dn_to_match, dn_oid,
+ current_object_dn,
+ &visited, &count, matched);
+}
+
+/*
+ * This rule provides recursive search of a link attribute
+ *
+ * Documented in [MS-ADTS] section 3.1.1.3.4.4.3 LDAP_MATCHING_RULE_TRANSITIVE_EVAL
+ * This allows a search filter such as:
+ *
+ * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
+ *
+ * This searches not only the member attribute, but also any member
+ * attributes that point at an object with this member in them. All the
+ * various DN syntax types are supported, not just plain DNs.
+ *
+ */
+static int ldb_comparator_trans(struct ldb_context *ldb,
+ const char *oid,
+ const struct ldb_message *msg,
+ const char *attribute_to_match,
+ const struct ldb_val *value_to_match,
+ bool *matched)
+{
+ const struct dsdb_schema *schema;
+ const struct dsdb_attribute *schema_attr;
+ struct ldb_dn *msg_dn;
+ struct dsdb_dn *dsdb_msg_dn;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * If the target attribute to match is not a linked attribute, then
+ * the filter evaluates to undefined
+ */
+ schema = dsdb_get_schema(ldb, tmp_ctx);
+ if (schema == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
+ if (schema_attr == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ /*
+ * This extended match filter is only valid for linked attributes,
+ * following the MS definition (the schema attribute has a linkID
+ * defined). See dochelp request 114111212024789 on cifs-protocols
+ * mailing list.
+ */
+ if (schema_attr->linkID == 0) {
+ *matched = false;
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
+
+ /* Duplicate original msg dn as the msg must not be modified */
+ msg_dn = ldb_dn_copy(tmp_ctx, msg->dn);
+ if (msg_dn == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * Build a dsdb dn from the message copied DN, which should be a plain
+ * DN syntax.
+ */
+ dsdb_msg_dn = dsdb_dn_construct(tmp_ctx, msg_dn, data_blob_null,
+ LDB_SYNTAX_DN);
+ if (dsdb_msg_dn == NULL) {
+ *matched = false;
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ ret = ldb_eval_transitive_filter(tmp_ctx, ldb,
+ attribute_to_match,
+ value_to_match,
+ dsdb_msg_dn, matched);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+/*
+ * This rule provides match of a dns object with expired records.
+ *
+ * This allows a search filter such as:
+ *
+ * dnsRecord:1.3.6.1.4.1.7165.4.5.3:=3694869
+ *
+ * where the value is a number of hours since the start of 1601.
+ *
+ * This allows the caller to find records that should become a DNS
+ * tomestone, despite that information being deep within an NDR packed
+ * object
+ */
+static int dsdb_match_for_dns_to_tombstone_time(struct ldb_context *ldb,
+ const char *oid,
+ const struct ldb_message *msg,
+ const char *attribute_to_match,
+ const struct ldb_val *value_to_match,
+ bool *matched)
+{
+ TALLOC_CTX *tmp_ctx;
+ unsigned int i;
+ struct ldb_message_element *el = NULL;
+ struct auth_session_info *session_info = NULL;
+ uint64_t tombstone_time;
+ struct dnsp_DnssrvRpcRecord *rec = NULL;
+ enum ndr_err_code err;
+ *matched = false;
+
+ /* Needs to be dnsRecord, no match otherwise */
+ if (ldb_attr_cmp(attribute_to_match, "dnsRecord") != 0) {
+ return LDB_SUCCESS;
+ }
+
+ el = ldb_msg_find_element(msg, attribute_to_match);
+ if (el == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ if (ldb_msg_element_is_inaccessible(el)) {
+ *matched = false;
+ return LDB_SUCCESS;
+ }
+
+ session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"),
+ struct auth_session_info);
+ if (session_info == NULL) {
+ return ldb_oom(ldb);
+ }
+ if (security_session_user_level(session_info, NULL)
+ != SECURITY_SYSTEM) {
+
+ DBG_ERR("unauthorised access\n");
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ }
+
+ /* We only expect uint32_t <= 10 digits */
+ if (value_to_match->length >= 12) {
+ DBG_ERR("Invalid timestamp passed\n");
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ } else {
+ int error = 0;
+ char s[12];
+
+ memcpy(s, value_to_match->data, value_to_match->length);
+ s[value_to_match->length] = 0;
+ if (s[0] == '\0') {
+ DBG_ERR("Empty timestamp passed\n");
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ tombstone_time = smb_strtoull(s,
+ NULL,
+ 10,
+ &error,
+ SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ DBG_ERR("Invalid timestamp string passed\n");
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ rec = talloc_zero(tmp_ctx, struct dnsp_DnssrvRpcRecord);
+ if (rec == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return ldb_oom(ldb);
+ }
+ err = ndr_pull_struct_blob(
+ &(el->values[i]),
+ tmp_ctx,
+ rec,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)){
+ DBG_ERR("Failed to pull dns rec blob.\n");
+ TALLOC_FREE(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (rec->wType == DNS_TYPE_SOA || rec->wType == DNS_TYPE_NS) {
+ TALLOC_FREE(rec);
+ continue;
+ }
+
+ if (rec->wType == DNS_TYPE_TOMBSTONE) {
+ TALLOC_FREE(rec);
+ continue;
+ }
+ if (rec->dwTimeStamp == 0) {
+ TALLOC_FREE(rec);
+ continue;
+ }
+ if (rec->dwTimeStamp > tombstone_time) {
+ TALLOC_FREE(rec);
+ continue;
+ }
+
+ *matched = true;
+ break;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+
+/*
+ * This rule provides match of a link attribute against a 'should be expunged' criteria
+ *
+ * This allows a search filter such as:
+ *
+ * member:1.3.6.1.4.1.7165.4.5.2:=131139216000000000
+ *
+ * This searches the member attribute, but also any member attributes
+ * that are deleted and should be expunged after the specified NTTIME
+ * time.
+ *
+ */
+static int dsdb_match_for_expunge(struct ldb_context *ldb,
+ const char *oid,
+ const struct ldb_message *msg,
+ const char *attribute_to_match,
+ const struct ldb_val *value_to_match,
+ bool *matched)
+{
+ const struct dsdb_schema *schema;
+ const struct dsdb_attribute *schema_attr;
+ TALLOC_CTX *tmp_ctx;
+ unsigned int i;
+ struct ldb_message_element *el;
+ struct auth_session_info *session_info;
+ uint64_t tombstone_time;
+ *matched = false;
+
+ el = ldb_msg_find_element(msg, attribute_to_match);
+ if (el == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ if (ldb_msg_element_is_inaccessible(el)) {
+ *matched = false;
+ return LDB_SUCCESS;
+ }
+
+ session_info
+ = talloc_get_type(ldb_get_opaque(ldb, DSDB_SESSION_INFO),
+ struct auth_session_info);
+ if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ }
+
+ /*
+ * If the target attribute to match is not a linked attribute, then
+ * the filter evaluates to undefined
+ */
+ schema = dsdb_get_schema(ldb, NULL);
+ if (schema == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* TODO this is O(log n) per attribute */
+ schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
+ if (schema_attr == NULL) {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ /*
+ * This extended match filter is only valid for forward linked attributes.
+ */
+ if (schema_attr->linkID == 0 || (schema_attr->linkID & 1) == 1) {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* Just check we don't allow the caller to fill our stack */
+ if (value_to_match->length >=64) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ } else {
+ int error = 0;
+ char s[value_to_match->length+1];
+
+ memcpy(s, value_to_match->data, value_to_match->length);
+ s[value_to_match->length] = 0;
+ if (s[0] == '\0' || s[0] == '-') {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ tombstone_time = smb_strtoull(s,
+ NULL,
+ 10,
+ &error,
+ SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ NTSTATUS status;
+ struct dsdb_dn *dn;
+ uint64_t rmd_changetime;
+ if (dsdb_dn_is_deleted_val(&el->values[i]) == false) {
+ continue;
+ }
+
+ dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
+ schema_attr->syntax->ldap_oid);
+ if (dn == NULL) {
+ DEBUG(1, ("Error: Failed to parse linked attribute blob of %s.\n", el->name));
+ continue;
+ }
+
+ status = dsdb_get_extended_dn_uint64(dn->dn, &rmd_changetime,
+ "RMD_CHANGETIME");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error: RMD_CHANGETIME is missing on a forward link.\n"));
+ continue;
+ }
+
+ if (rmd_changetime > tombstone_time) {
+ continue;
+ }
+
+ *matched = true;
+ break;
+ }
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+
+int ldb_register_samba_matching_rules(struct ldb_context *ldb)
+{
+ struct ldb_extended_match_rule *transitive_eval = NULL,
+ *match_for_expunge = NULL,
+ *match_for_dns_to_tombstone_time = NULL;
+ int ret;
+
+ transitive_eval = talloc_zero(ldb, struct ldb_extended_match_rule);
+ transitive_eval->oid = SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL;
+ transitive_eval->callback = ldb_comparator_trans;
+ ret = ldb_register_extended_match_rule(ldb, transitive_eval);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(transitive_eval);
+ return ret;
+ }
+
+ match_for_expunge = talloc_zero(ldb, struct ldb_extended_match_rule);
+ match_for_expunge->oid = DSDB_MATCH_FOR_EXPUNGE;
+ match_for_expunge->callback = dsdb_match_for_expunge;
+ ret = ldb_register_extended_match_rule(ldb, match_for_expunge);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(match_for_expunge);
+ return ret;
+ }
+
+ match_for_dns_to_tombstone_time = talloc_zero(
+ ldb,
+ struct ldb_extended_match_rule);
+ match_for_dns_to_tombstone_time->oid = DSDB_MATCH_FOR_DNS_TO_TOMBSTONE_TIME;
+ match_for_dns_to_tombstone_time->callback
+ = dsdb_match_for_dns_to_tombstone_time;
+ ret = ldb_register_extended_match_rule(ldb,
+ match_for_dns_to_tombstone_time);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(match_for_dns_to_tombstone_time);
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/lib/ldb-samba/ldb_matching_rules.h b/lib/ldb-samba/ldb_matching_rules.h
new file mode 100644
index 0000000..28c4e3d
--- /dev/null
+++ b/lib/ldb-samba/ldb_matching_rules.h
@@ -0,0 +1,30 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ ldb database library - Extended match rules
+
+ Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
+
+ 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/>.
+*/
+
+#ifndef _LDB_MATCHING_RULES_H_
+#define _LDB_MATCHING_RULES_H_
+
+/* This rule provides recursive search of a link attribute */
+#define SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL "1.2.840.113556.1.4.1941"
+#define DSDB_MATCH_FOR_EXPUNGE "1.3.6.1.4.1.7165.4.5.2"
+#define DSDB_MATCH_FOR_DNS_TO_TOMBSTONE_TIME "1.3.6.1.4.1.7165.4.5.3"
+
+#endif /* _LDB_MATCHING_RULES_H_ */
diff --git a/lib/ldb-samba/ldb_wrap.c b/lib/ldb-samba/ldb_wrap.c
new file mode 100644
index 0000000..cfc8732
--- /dev/null
+++ b/lib/ldb-samba/ldb_wrap.c
@@ -0,0 +1,377 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ LDB wrap functions
+
+ Copyright (C) Andrew Tridgell 2004-2009
+
+ 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/>.
+*/
+
+/*
+ the stupidity of the unix fcntl locking design forces us to never
+ allow a database file to be opened twice in the same process. These
+ wrappers provide convenient access to a tdb or ldb, taking advantage
+ of talloc destructors to ensure that only a single open is done
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "lib/ldb-samba/ldif_handlers.h"
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "param/param.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/util/util_paths.h"
+#include <tdb.h>
+#include <unistd.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LDB
+
+/*
+ this is used to catch debug messages from ldb
+*/
+static void ldb_wrap_debug(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+
+static void ldb_wrap_debug(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap)
+{
+ int samba_level = -1;
+ switch (level) {
+ case LDB_DEBUG_FATAL:
+ samba_level = DBGLVL_ERR;
+ break;
+ case LDB_DEBUG_ERROR:
+ samba_level = DBGLVL_WARNING;
+ break;
+ case LDB_DEBUG_WARNING:
+ samba_level = DBGLVL_NOTICE;
+ break;
+ case LDB_DEBUG_TRACE:
+ samba_level = DBGLVL_DEBUG + 1;
+ break;
+
+ };
+ if (CHECK_DEBUGLVL(samba_level)) {
+ char *s = NULL;
+ int ret;
+
+ ret = vasprintf(&s, fmt, ap);
+ if (ret == -1) {
+ return;
+ }
+ DEBUG(samba_level, ("ldb: %s\n", s));
+ free(s);
+ }
+}
+
+
+/*
+ connecting to a ldb can be a relatively expensive operation because
+ of the schema and partition loads. We keep a list of open ldb
+ contexts here, and try to re-use when possible.
+
+ This means callers of ldb_wrap_connect() must use talloc_unlink() or
+ the free of a parent to destroy the context
+ */
+static struct ldb_wrap {
+ struct ldb_wrap *next, *prev;
+ struct ldb_wrap_context {
+ /* the context is what we use to tell if two ldb
+ * connections are exactly equivalent
+ */
+ pid_t pid; /* We want to re-open in a new PID due to
+ * the LMDB backend */
+ const char *url;
+ struct tevent_context *ev;
+ struct loadparm_context *lp_ctx;
+ struct auth_session_info *session_info;
+ struct cli_credentials *credentials;
+ unsigned int flags;
+ } context;
+ struct ldb_context *ldb;
+} *ldb_wrap_list;
+
+/*
+ free a ldb_wrap structure
+ */
+static int ldb_wrap_destructor(struct ldb_wrap *w)
+{
+ DLIST_REMOVE(ldb_wrap_list, w);
+ return 0;
+}
+
+/*
+ * The casefolder for s4's LDB databases - Unicode-safe
+ */
+char *wrap_casefold(void *context, void *mem_ctx, const char *s, size_t n)
+{
+ return strupper_talloc_n(mem_ctx, s, n);
+}
+
+
+ struct ldb_context *samba_ldb_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_init(mem_ctx, ev);
+ if (ldb == NULL) {
+ return NULL;
+ }
+
+ ldb_set_modules_dir(ldb, modules_path(ldb, "ldb"));
+
+ ldb_set_debug(ldb, ldb_wrap_debug, NULL);
+
+ ldb_set_utf8_fns(ldb, NULL, wrap_casefold);
+
+ if (session_info) {
+ if (ldb_set_opaque(ldb, DSDB_SESSION_INFO, session_info)) {
+ talloc_free(ldb);
+ return NULL;
+ }
+ }
+
+ if (credentials) {
+ if (ldb_set_opaque(ldb, "credentials", credentials)) {
+ talloc_free(ldb);
+ return NULL;
+ }
+ }
+
+ if (ldb_set_opaque(ldb, "loadparm", lp_ctx)) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ /* This must be done before we load the schema, as these
+ * handlers for objectSid and objectGUID etc must take
+ * precedence over the 'binary attribute' declaration in the
+ * schema */
+ ret = ldb_register_samba_handlers(ldb);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ /* we usually want Samba databases to be private. If we later
+ find we need one public, we will need to add a parameter to
+ ldb_wrap_connect() */
+ ldb_set_create_perms(ldb, 0600);
+
+ return ldb;
+}
+
+ struct ldb_context *ldb_wrap_find(const char *url,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags)
+{
+ pid_t pid = getpid();
+ struct ldb_wrap *w;
+ /* see if we can re-use an existing ldb */
+ for (w=ldb_wrap_list; w; w=w->next) {
+ if (w->context.pid == pid &&
+ w->context.ev == ev &&
+ w->context.lp_ctx == lp_ctx &&
+ w->context.session_info == session_info &&
+ w->context.credentials == credentials &&
+ w->context.flags == flags &&
+ (w->context.url == url || strcmp(w->context.url, url) == 0))
+ return w->ldb;
+ }
+
+ return NULL;
+}
+
+int samba_ldb_connect(struct ldb_context *ldb, struct loadparm_context *lp_ctx,
+ const char *url, unsigned int flags)
+{
+ int ret;
+ char *real_url = NULL;
+
+ /* allow admins to force non-sync ldb for all databases */
+ if (lpcfg_parm_bool(lp_ctx, NULL, "ldb", "nosync", false)) {
+ flags |= LDB_FLG_NOSYNC;
+ }
+
+ if (DEBUGLVL(10)) {
+ flags |= LDB_FLG_ENABLE_TRACING;
+ }
+
+ real_url = lpcfg_private_path(ldb, lp_ctx, url);
+ if (real_url == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_connect(ldb, real_url, flags, NULL);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* setup for leak detection */
+ ldb_set_opaque(ldb, "wrap_url", real_url);
+
+ return LDB_SUCCESS;
+}
+
+ bool ldb_wrap_add(const char *url, struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags,
+ struct ldb_context *ldb)
+{
+ struct ldb_wrap *w;
+ struct ldb_wrap_context c;
+
+ /* add to the list of open ldb contexts */
+ w = talloc(ldb, struct ldb_wrap);
+ if (w == NULL) {
+ return false;
+ }
+
+ c.pid = getpid();
+ c.url = url;
+ c.ev = ev;
+ c.lp_ctx = lp_ctx;
+ c.session_info = session_info;
+ c.credentials = credentials;
+ c.flags = flags;
+
+ w->context = c;
+ w->context.url = talloc_strdup(w, url);
+ if (w->context.url == NULL) {
+ return false;
+ }
+
+ if (session_info) {
+ /* take a reference to the session_info, as it is
+ * possible for the ldb to live longer than the
+ * session_info. This happens when a DRS DsBind call
+ * reuses a handle, but the original connection is
+ * shutdown. The token for the new connection is still
+ * valid, so we need the session_info to remain valid for
+ * ldb modules to use
+ */
+ if (talloc_reference(w, session_info) == NULL) {
+ return false;
+ }
+ }
+
+ w->ldb = ldb;
+
+ DLIST_ADD(ldb_wrap_list, w);
+
+ talloc_set_destructor(w, ldb_wrap_destructor);
+
+ return true;
+}
+
+
+/*
+ wrapped connection to a ldb database
+ to close just talloc_free() the returned ldb_context
+
+ TODO: We need an error_string parameter
+ */
+ struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *url,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ /*
+ * Unlike samdb_connect_url() do not try and cache the LDB
+ * handle, get a new one each time. Only sam.ldb is
+ * punitively expensive to open and helpful caches like this
+ * cause challenges (such as if the value for 'private dir'
+ * changes).
+ */
+
+ ldb = samba_ldb_init(mem_ctx, ev, lp_ctx, session_info, credentials);
+
+ if (ldb == NULL)
+ return NULL;
+
+ ret = samba_ldb_connect(ldb, lp_ctx, url, flags);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ DEBUG(3,("ldb_wrap open of %s\n", url));
+
+ return ldb;
+}
+
+/*
+ call tdb_reopen_all() in case there is a TDB open so we are
+ not blocked from re-opening it inside ldb_tdb.
+*/
+ void ldb_wrap_fork_hook(void)
+{
+ if (tdb_reopen_all(1) != 0) {
+ smb_panic("tdb_reopen_all failed\n");
+ }
+}
+
+ char *ldb_relative_path(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const char *name)
+{
+ const char *base_url =
+ (const char *)ldb_get_opaque(ldb, "ldb_url");
+ char *path, *p, *full_name;
+ if (name == NULL) {
+ return NULL;
+ }
+ if (strncmp("tdb://", base_url, 6) == 0) {
+ base_url = base_url+6;
+ } else if (strncmp("mdb://", base_url, 6) == 0) {
+ base_url = base_url+6;
+ } else if (strncmp("ldb://", base_url, 6) == 0) {
+ base_url = base_url+6;
+ }
+ path = talloc_strdup(mem_ctx, base_url);
+ if (path == NULL) {
+ return NULL;
+ }
+ if ( (p = strrchr(path, '/')) != NULL) {
+ p[0] = '\0';
+ full_name = talloc_asprintf(mem_ctx, "%s/%s", path, name);
+ } else {
+ full_name = talloc_asprintf(mem_ctx, "./%s", name);
+ }
+ talloc_free(path);
+ return full_name;
+}
diff --git a/lib/ldb-samba/ldb_wrap.h b/lib/ldb-samba/ldb_wrap.h
new file mode 100644
index 0000000..aa7ccb3
--- /dev/null
+++ b/lib/ldb-samba/ldb_wrap.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ database wrap headers
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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/>.
+*/
+
+#ifndef _LDB_WRAP_H_
+#define _LDB_WRAP_H_
+
+#include <talloc.h>
+
+struct auth_session_info;
+struct ldb_message;
+struct ldb_dn;
+struct cli_credentials;
+struct loadparm_context;
+struct tevent_context;
+
+char *wrap_casefold(void *context, void *mem_ctx, const char *s, size_t n);
+
+struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *url,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags);
+
+void ldb_wrap_fork_hook(void);
+
+struct ldb_context *samba_ldb_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials);
+struct ldb_context *ldb_wrap_find(const char *url,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags);
+bool ldb_wrap_add(const char *url, struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags,
+ struct ldb_context *ldb);
+char *ldb_relative_path(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const char *name);
+
+int samba_ldb_connect(struct ldb_context *ldb, struct loadparm_context *lp_ctx,
+ const char *url, unsigned int flags);
+
+#endif /* _LDB_WRAP_H_ */
diff --git a/lib/ldb-samba/ldif_handlers.c b/lib/ldb-samba/ldif_handlers.c
new file mode 100644
index 0000000..78a4337
--- /dev/null
+++ b/lib/ldb-samba/ldif_handlers.c
@@ -0,0 +1,1836 @@
+/*
+ ldb database library - ldif handlers for Samba
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Andrew Bartlett 2006-2009
+ Copyright (C) Matthias Dieter Wallnöfer 2009
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ 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 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
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <ldb.h>
+#include <ldb_module.h>
+#include "ldb_handlers.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "librpc/ndr/libndr.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "../lib/util/asn1.h"
+#include "lib/util/smb_strtox.h"
+
+/*
+ use ndr_print_* to convert a NDR formatted blob to a ldif formatted blob
+
+ If mask_errors is true, then function succeeds but out data
+ is set to "<Unable to decode binary data>" message
+
+ \return 0 on success; -1 on error
+*/
+static int ldif_write_NDR(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out,
+ size_t struct_size,
+ ndr_pull_flags_fn_t pull_fn,
+ ndr_print_fn_t print_fn,
+ bool mask_errors)
+{
+ uint8_t *p;
+ enum ndr_err_code err;
+ if (!(ldb_get_flags(ldb) & LDB_FLG_SHOW_BINARY)) {
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+ p = talloc_size(mem_ctx, struct_size);
+ err = ndr_pull_struct_blob(in, mem_ctx,
+ p, pull_fn);
+ if (err != NDR_ERR_SUCCESS) {
+ /* fail in not in mask_error mode */
+ if (!mask_errors) {
+ return -1;
+ }
+ talloc_free(p);
+ out->data = (uint8_t *)talloc_strdup(mem_ctx, "<Unable to decode binary data>");
+ out->length = strlen((const char *)out->data);
+ return 0;
+ }
+ out->data = (uint8_t *)ndr_print_struct_string(mem_ctx, print_fn, "NDR", p);
+ talloc_free(p);
+ if (out->data == NULL) {
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+ out->length = strlen((char *)out->data);
+ return 0;
+}
+
+/*
+ convert a ldif formatted objectSid to a NDR formatted blob
+*/
+static int ldif_read_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ bool ret;
+ enum ndr_err_code ndr_err;
+ struct dom_sid sid;
+ if (in->length > DOM_SID_STR_BUFLEN) {
+ return -1;
+ } else {
+ char p[in->length+1];
+ memcpy(p, in->data, in->length);
+ p[in->length] = '\0';
+
+ ret = dom_sid_parse(p, &sid);
+ if (ret == false) {
+ return -1;
+ }
+
+ *out = data_blob_talloc(mem_ctx, NULL,
+ ndr_size_dom_sid(&sid, 0));
+ if (out->data == NULL) {
+ return -1;
+ }
+
+ ndr_err = ndr_push_struct_into_fixed_blob(out, &sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted objectSid
+*/
+int ldif_write_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct dom_sid sid;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob_all_noalloc(in, &sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ *out = data_blob_string_const(dom_sid_string(mem_ctx, &sid));
+ if (out->data == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+bool ldif_comparision_objectSid_isString(const struct ldb_val *v)
+{
+ if (v->length < 3) {
+ return false;
+ }
+
+ if (strncmp("S-", (const char *)v->data, 2) != 0) return false;
+
+ return true;
+}
+
+/*
+ compare two objectSids
+*/
+static int ldif_comparison_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ if (ldif_comparision_objectSid_isString(v1) && ldif_comparision_objectSid_isString(v2)) {
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ } else if (ldif_comparision_objectSid_isString(v1)
+ && !ldif_comparision_objectSid_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectSid(ldb, mem_ctx, v1, &v) != 0) {
+ /* Perhaps not a string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
+ talloc_free(v.data);
+ return ret;
+ } else if (!ldif_comparision_objectSid_isString(v1)
+ && ldif_comparision_objectSid_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectSid(ldb, mem_ctx, v2, &v) != 0) {
+ /* Perhaps not a string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
+ talloc_free(v.data);
+ return ret;
+ }
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+}
+
+/*
+ canonicalise a objectSid
+*/
+static int ldif_canonicalise_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ if (ldif_comparision_objectSid_isString(in)) {
+ if (ldif_read_objectSid(ldb, mem_ctx, in, out) != 0) {
+ /* Perhaps not a string after all */
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+ return 0;
+ }
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+static int extended_dn_read_SID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct dom_sid sid;
+ enum ndr_err_code ndr_err;
+ if (ldif_comparision_objectSid_isString(in)) {
+ if (ldif_read_objectSid(ldb, mem_ctx, in, out) == 0) {
+ return 0;
+ }
+ }
+
+ /* Perhaps not a string after all */
+ *out = data_blob_talloc(mem_ctx, NULL, in->length/2+1);
+
+ if (!out->data) {
+ return -1;
+ }
+
+ (*out).length = strhex_to_str((char *)out->data, out->length,
+ (const char *)in->data, in->length);
+
+ /* Check it looks like a SID */
+ ndr_err = ndr_pull_struct_blob_all_noalloc(out, &sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a ldif formatted objectGUID to a NDR formatted blob
+*/
+static int ldif_read_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct GUID guid;
+ NTSTATUS status;
+
+ status = GUID_from_data_blob(in, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ status = GUID_to_ndr_blob(&guid, mem_ctx, out);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted objectGUID
+*/
+static int ldif_write_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct GUID guid;
+ NTSTATUS status;
+
+ status = GUID_from_ndr_blob(in, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ out->data = (uint8_t *)GUID_string(mem_ctx, &guid);
+ if (out->data == NULL) {
+ return -1;
+ }
+ out->length = strlen((const char *)out->data);
+ return 0;
+}
+
+static bool ldif_comparision_objectGUID_isString(const struct ldb_val *v)
+{
+ if (v->length != 36 && v->length != 38) return false;
+
+ /* Might be a GUID string, can't be a binary GUID (fixed 16 bytes) */
+ return true;
+}
+
+static int extended_dn_read_GUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+
+ if (in->length == 36 && ldif_read_objectGUID(ldb, mem_ctx, in, out) == 0) {
+ return 0;
+ }
+
+ /* Try as 'hex' form */
+ if (in->length != 32) {
+ return -1;
+ }
+
+ *out = data_blob_talloc(mem_ctx, NULL, in->length/2+1);
+
+ if (!out->data) {
+ return -1;
+ }
+
+ (*out).length = strhex_to_str((char *)out->data, out->length,
+ (const char *)in->data, in->length);
+
+ /* Check it looks like a GUID */
+ if ((*out).length != 16) {
+ data_blob_free(out);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ compare two objectGUIDs
+*/
+static int ldif_comparison_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ if (ldif_comparision_objectGUID_isString(v1) && ldif_comparision_objectGUID_isString(v2)) {
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ } else if (ldif_comparision_objectGUID_isString(v1)
+ && !ldif_comparision_objectGUID_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectGUID(ldb, mem_ctx, v1, &v) != 0) {
+ /* Perhaps it wasn't a valid string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
+ talloc_free(v.data);
+ return ret;
+ } else if (!ldif_comparision_objectGUID_isString(v1)
+ && ldif_comparision_objectGUID_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectGUID(ldb, mem_ctx, v2, &v) != 0) {
+ /* Perhaps it wasn't a valid string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
+ talloc_free(v.data);
+ return ret;
+ }
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+}
+
+/*
+ canonicalise a objectGUID
+*/
+static int ldif_canonicalise_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ if (ldif_comparision_objectGUID_isString(in)) {
+ if (ldif_read_objectGUID(ldb, mem_ctx, in, out) != 0) {
+ /* Perhaps it wasn't a valid string after all */
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+ return 0;
+ }
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+
+/*
+ convert a ldif (SDDL) formatted ntSecurityDescriptor to a NDR formatted blob
+*/
+static int ldif_read_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct security_descriptor *sd;
+ enum ndr_err_code ndr_err;
+
+ sd = talloc(mem_ctx, struct security_descriptor);
+ if (sd == NULL) {
+ return -1;
+ }
+
+ ndr_err = ndr_pull_struct_blob(in, sd, sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* If this does not parse, then it is probably SDDL, and we should try it that way */
+
+ const struct dom_sid *sid = samdb_domain_sid(ldb);
+ talloc_free(sd);
+ sd = sddl_decode(mem_ctx, (const char *)in->data, sid);
+ if (sd == NULL) {
+ return -1;
+ }
+ }
+
+ ndr_err = ndr_push_struct_blob(out, mem_ctx, sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ talloc_free(sd);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted ntSecurityDescriptor (SDDL format)
+*/
+static int ldif_write_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct security_descriptor *sd;
+ enum ndr_err_code ndr_err;
+
+ if (ldb_get_flags(ldb) & LDB_FLG_SHOW_BINARY) {
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct security_descriptor),
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor,
+ (ndr_print_fn_t)ndr_print_security_descriptor,
+ true);
+
+ }
+
+ sd = talloc(mem_ctx, struct security_descriptor);
+ if (sd == NULL) {
+ return -1;
+ }
+ /* We can't use ndr_pull_struct_blob_all because this contains relative pointers */
+ ndr_err = ndr_pull_struct_blob(in, sd, sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(sd);
+ return -1;
+ }
+ out->data = (uint8_t *)sddl_encode(mem_ctx, sd, samdb_domain_sid_cache_only(ldb));
+ talloc_free(sd);
+ if (out->data == NULL) {
+ return -1;
+ }
+ out->length = strlen((const char *)out->data);
+ return 0;
+}
+
+/*
+ convert a string formatted SDDL to a ldif formatted ntSecurityDescriptor (SDDL format)
+*/
+static int ldif_write_sddlSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ if (ldb_get_flags(ldb) & LDB_FLG_SHOW_BINARY) {
+ struct security_descriptor *sd;
+ const struct dom_sid *sid = samdb_domain_sid(ldb);
+
+ sd = sddl_decode(mem_ctx, (const char *)in->data, sid);
+ out->data = (uint8_t *)ndr_print_struct_string(mem_ctx,
+ (ndr_print_fn_t)ndr_print_security_descriptor,
+ "SDDL", sd);
+ out->length = strlen((const char *)out->data);
+ talloc_free(sd);
+ return 0;
+ }
+
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+/*
+ canonicalise an objectCategory. We use the long form as the canonical form:
+ 'person' becomes cn=Person,cn=Schema,cn=Configuration,<basedn>
+
+ Also any short name of an objectClass that points to a different
+ class (such as user) has the canonical form of the class it's
+ defaultObjectCategory points to (eg
+ cn=Person,cn=Schema,cn=Configuration,<basedn>)
+*/
+
+static int ldif_canonicalise_objectCategory(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct ldb_dn *dn1 = NULL;
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
+ const struct dsdb_class *sclass;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!schema) {
+ talloc_free(tmp_ctx);
+ *out = data_blob_talloc(mem_ctx, in->data, in->length);
+ if (in->data && !out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ }
+ dn1 = ldb_dn_from_ldb_val(tmp_ctx, ldb, in);
+ if ( ! ldb_dn_validate(dn1)) {
+ const char *lDAPDisplayName = talloc_strndup(tmp_ctx, (char *)in->data, in->length);
+ sclass = dsdb_class_by_lDAPDisplayName(schema, lDAPDisplayName);
+ if (sclass) {
+ struct ldb_dn *dn = ldb_dn_new(tmp_ctx, ldb,
+ sclass->defaultObjectCategory);
+ if (dn == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *out = data_blob_string_const(ldb_dn_alloc_casefold(mem_ctx, dn));
+ talloc_free(tmp_ctx);
+
+ if (!out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ } else {
+ *out = data_blob_talloc(mem_ctx, in->data, in->length);
+ talloc_free(tmp_ctx);
+
+ if (in->data && !out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ }
+ }
+ *out = data_blob_string_const(ldb_dn_alloc_casefold(mem_ctx, dn1));
+ talloc_free(tmp_ctx);
+
+ if (!out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+}
+
+static int ldif_comparison_objectCategory(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1,
+ const struct ldb_val *v2)
+{
+ return ldb_any_comparison(ldb, mem_ctx, ldif_canonicalise_objectCategory,
+ v1, v2);
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted schemaInfo
+*/
+static int ldif_write_schemaInfo(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct repsFromToBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_schemaInfoBlob,
+ (ndr_print_fn_t)ndr_print_schemaInfoBlob,
+ true);
+}
+
+/*
+ convert a ldif formatted prefixMap to a NDR formatted blob
+*/
+static int ldif_read_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct prefixMapBlob *blob;
+ enum ndr_err_code ndr_err;
+ char *string, *line, *p, *oid;
+ DATA_BLOB oid_blob;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ blob = talloc_zero(tmp_ctx, struct prefixMapBlob);
+ if (blob == NULL) {
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ /* use the switch value to detect if this is in the binary
+ * format
+ */
+ if (in->length >= 4 && IVAL(in->data, 0) == PREFIX_MAP_VERSION_DSDB) {
+ ndr_err = ndr_pull_struct_blob(in, tmp_ctx, blob,
+ (ndr_pull_flags_fn_t)ndr_pull_prefixMapBlob);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ndr_err = ndr_push_struct_blob(out, mem_ctx,
+ blob,
+ (ndr_push_flags_fn_t)ndr_push_prefixMapBlob);
+ talloc_free(tmp_ctx);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ /* If this does not parse, then it is probably the text version, and we should try it that way */
+ blob->version = PREFIX_MAP_VERSION_DSDB;
+
+ string = talloc_strndup(mem_ctx, (const char *)in->data, in->length);
+ if (string == NULL) {
+ talloc_free(blob);
+ return -1;
+ }
+
+ line = string;
+ while (line && line[0]) {
+ int error = 0;
+
+ p=strchr(line, ';');
+ if (p) {
+ p[0] = '\0';
+ } else {
+ p=strchr(line, '\n');
+ if (p) {
+ p[0] = '\0';
+ }
+ }
+ /* allow a trailing separator */
+ if (line == p) {
+ break;
+ }
+
+ blob->ctr.dsdb.mappings = talloc_realloc(blob,
+ blob->ctr.dsdb.mappings,
+ struct drsuapi_DsReplicaOIDMapping,
+ blob->ctr.dsdb.num_mappings+1);
+ if (!blob->ctr.dsdb.mappings) {
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ blob->ctr.dsdb.mappings[blob->ctr.dsdb.num_mappings].id_prefix =
+ smb_strtoul(line, &oid, 10, &error, SMB_STR_STANDARD);
+
+ if (oid[0] != ':' || error != 0) {
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ /* we know there must be at least ":" */
+ oid++;
+
+ if (!ber_write_partial_OID_String(blob->ctr.dsdb.mappings, &oid_blob, oid)) {
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+ blob->ctr.dsdb.mappings[blob->ctr.dsdb.num_mappings].oid.length = oid_blob.length;
+ blob->ctr.dsdb.mappings[blob->ctr.dsdb.num_mappings].oid.binary_oid = oid_blob.data;
+
+ blob->ctr.dsdb.num_mappings++;
+
+ /* Now look past the terminator we added above */
+ if (p) {
+ line = p + 1;
+ } else {
+ line = NULL;
+ }
+ }
+
+ ndr_err = ndr_push_struct_blob(out, mem_ctx,
+ blob,
+ (ndr_push_flags_fn_t)ndr_push_prefixMapBlob);
+ talloc_free(tmp_ctx);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted prefixMap
+*/
+static int ldif_write_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct prefixMapBlob *blob;
+ enum ndr_err_code ndr_err;
+ char *string;
+ uint32_t i;
+
+ if (ldb_get_flags(ldb) & LDB_FLG_SHOW_BINARY) {
+ int err;
+ /* try to decode the blob as S4 prefixMap */
+ err = ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct prefixMapBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_prefixMapBlob,
+ (ndr_print_fn_t)ndr_print_prefixMapBlob,
+ false);
+ if (0 == err) {
+ return err;
+ }
+ /* try parsing it as Windows PrefixMap value */
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct drsuapi_MSPrefixMap_Ctr),
+ (ndr_pull_flags_fn_t)ndr_pull_drsuapi_MSPrefixMap_Ctr,
+ (ndr_print_fn_t)ndr_print_drsuapi_MSPrefixMap_Ctr,
+ true);
+ }
+
+ blob = talloc(mem_ctx, struct prefixMapBlob);
+ if (blob == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_pull_struct_blob_all(in, blob,
+ blob,
+ (ndr_pull_flags_fn_t)ndr_pull_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ goto failed;
+ }
+ if (blob->version != PREFIX_MAP_VERSION_DSDB) {
+ goto failed;
+ }
+ string = talloc_strdup(mem_ctx, "");
+ if (string == NULL) {
+ goto failed;
+ }
+
+ for (i=0; i < blob->ctr.dsdb.num_mappings; i++) {
+ DATA_BLOB oid_blob;
+ char *partial_oid = NULL;
+
+ if (i > 0) {
+ string = talloc_asprintf_append(string, ";");
+ }
+
+ oid_blob = data_blob_const(blob->ctr.dsdb.mappings[i].oid.binary_oid,
+ blob->ctr.dsdb.mappings[i].oid.length);
+ if (!ber_read_partial_OID_String(blob, oid_blob, &partial_oid)) {
+ DEBUG(0, ("ber_read_partial_OID failed on prefixMap item with id: 0x%X",
+ blob->ctr.dsdb.mappings[i].id_prefix));
+ goto failed;
+ }
+ string = talloc_asprintf_append(string, "%u:%s",
+ blob->ctr.dsdb.mappings[i].id_prefix,
+ partial_oid);
+ talloc_free(discard_const(partial_oid));
+ if (string == NULL) {
+ goto failed;
+ }
+ }
+
+ talloc_free(blob);
+ *out = data_blob_string_const(string);
+ return 0;
+
+failed:
+ talloc_free(blob);
+ return -1;
+}
+
+static bool ldif_comparision_prefixMap_isString(const struct ldb_val *v)
+{
+ if (v->length < 4) {
+ return true;
+ }
+
+ if (IVAL(v->data, 0) == PREFIX_MAP_VERSION_DSDB) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ canonicalise a prefixMap
+*/
+static int ldif_canonicalise_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ if (ldif_comparision_prefixMap_isString(in)) {
+ return ldif_read_prefixMap(ldb, mem_ctx, in, out);
+ }
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+static int ldif_comparison_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1,
+ const struct ldb_val *v2)
+{
+ return ldb_any_comparison(ldb, mem_ctx, ldif_canonicalise_prefixMap,
+ v1, v2);
+}
+
+/* length limited conversion of a ldb_val to a int32_t */
+static int val_to_int32(const struct ldb_val *in, int32_t *v)
+{
+ char *end;
+ char buf[64];
+
+ /* make sure we don't read past the end of the data */
+ if (in->length > sizeof(buf)-1) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ strncpy(buf, (char *)in->data, in->length);
+ buf[in->length] = 0;
+
+ /* We've to use "strtoll" here to have the intended overflows.
+ * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
+ *v = (int32_t) strtoll(buf, &end, 0);
+ if (*end != 0) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ return LDB_SUCCESS;
+}
+
+/* length limited conversion of a ldb_val to a int64_t */
+static int val_to_int64(const struct ldb_val *in, int64_t *v)
+{
+ char *end;
+ char buf[64];
+
+ /* make sure we don't read past the end of the data */
+ if (in->length > sizeof(buf)-1) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ strncpy(buf, (char *)in->data, in->length);
+ buf[in->length] = 0;
+
+ *v = (int64_t) strtoll(buf, &end, 0);
+ if (*end != 0) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ return LDB_SUCCESS;
+}
+
+/* Canonicalisation of two 32-bit integers */
+static int ldif_canonicalise_int32(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ int32_t i;
+ int ret;
+
+ ret = val_to_int32(in, &i);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%d", i);
+ if (out->data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ out->length = strlen((char *)out->data);
+ return 0;
+}
+
+/*
+ * Lexicographically sorted representation for a 32-bit integer
+ *
+ * [ INT32_MIN ... -3, -2, -1 | 0 | +1, +2, +3 ... INT32_MAX ]
+ * n o p
+ *
+ * Refer to the comment in lib/ldb/common/attrib_handlers.c for the
+ * corresponding documentation for 64-bit integers.
+ *
+ * The same rules apply but use INT32_MIN and INT32_MAX.
+ *
+ * String representation padding is done to 10 characters.
+ *
+ * INT32_MAX = 2^31 - 1 = 2147483647 (10 characters long)
+ *
+ */
+static int ldif_index_format_int32(struct ldb_context *ldb,
+ void *mem_ctx,
+ const struct ldb_val *in,
+ struct ldb_val *out)
+{
+ int32_t i;
+ int ret;
+ char prefix;
+ size_t len;
+
+ ret = val_to_int32(in, &i);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (i < 0) {
+ /*
+ * i is negative, so this is subtraction rather than
+ * wrap-around.
+ */
+ prefix = 'n';
+ i = INT32_MAX + i + 1;
+ } else if (i > 0) {
+ prefix = 'p';
+ } else {
+ prefix = 'o';
+ }
+
+ out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%c%010ld", prefix, (long)i);
+ if (out->data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ len = talloc_array_length(out->data) - 1;
+ if (len != 11) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ __location__ ": expected index format str %s to"
+ " have length 11 but got %zu",
+ (char*)out->data, len);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ out->length = 11;
+ return 0;
+}
+
+/* Comparison of two 32-bit integers */
+static int ldif_comparison_int32(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ int32_t i1=0, i2=0;
+ val_to_int32(v1, &i1);
+ val_to_int32(v2, &i2);
+ if (i1 == i2) return 0;
+ return i1 > i2? 1 : -1;
+}
+
+/* Canonicalisation of two 64-bit integers */
+static int ldif_canonicalise_int64(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ int64_t i;
+ int ret;
+
+ ret = val_to_int64(in, &i);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%lld", (long long)i);
+ if (out->data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ out->length = strlen((char *)out->data);
+ return 0;
+}
+
+/* Comparison of two 64-bit integers */
+static int ldif_comparison_int64(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ int64_t i1=0, i2=0;
+ val_to_int64(v1, &i1);
+ val_to_int64(v2, &i2);
+ if (i1 == i2) return 0;
+ return i1 > i2? 1 : -1;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted repsFromTo
+*/
+static int ldif_write_repsFromTo(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct repsFromToBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob,
+ (ndr_print_fn_t)ndr_print_repsFromToBlob,
+ true);
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted replPropertyMetaData
+*/
+static int ldif_write_replPropertyMetaData(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct replPropertyMetaDataBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob,
+ (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
+ true);
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted replUpToDateVector
+*/
+static int ldif_write_replUpToDateVector(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct replUpToDateVectorBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob,
+ (ndr_print_fn_t)ndr_print_replUpToDateVectorBlob,
+ true);
+}
+
+static int ldif_write_dn_binary_NDR(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out,
+ size_t struct_size,
+ ndr_pull_flags_fn_t pull_fn,
+ ndr_print_fn_t print_fn,
+ bool mask_errors)
+{
+ uint8_t *p = NULL;
+ enum ndr_err_code err;
+ struct dsdb_dn *dsdb_dn = NULL;
+ char *dn_str = NULL;
+ char *str = NULL;
+
+ if (!(ldb_get_flags(ldb) & LDB_FLG_SHOW_BINARY)) {
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+
+ dsdb_dn = dsdb_dn_parse(mem_ctx, ldb, in, DSDB_SYNTAX_BINARY_DN);
+ if (dsdb_dn == NULL) {
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+
+ p = talloc_size(dsdb_dn, struct_size);
+ if (p == NULL) {
+ TALLOC_FREE(dsdb_dn);
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+
+ err = ndr_pull_struct_blob(&dsdb_dn->extra_part, p, p, pull_fn);
+ if (err != NDR_ERR_SUCCESS) {
+ /* fail in not in mask_error mode */
+ if (!mask_errors) {
+ return -1;
+ }
+ TALLOC_FREE(dsdb_dn);
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+
+ dn_str = ldb_dn_get_extended_linearized(dsdb_dn, dsdb_dn->dn, 1);
+ if (dn_str == NULL) {
+ TALLOC_FREE(dsdb_dn);
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+
+ str = ndr_print_struct_string(mem_ctx, print_fn, dn_str, p);
+ TALLOC_FREE(dsdb_dn);
+ if (str == NULL) {
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+
+ *out = data_blob_string_const(str);
+ return 0;
+}
+
+static int ldif_write_msDS_RevealedUsers(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_dn_binary_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct replPropertyMetaData1),
+ (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1,
+ (ndr_print_fn_t)ndr_print_replPropertyMetaData1,
+ true);
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted dnsRecord
+*/
+static int ldif_write_dnsRecord(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct dnsp_DnssrvRpcRecord),
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord,
+ (ndr_print_fn_t)ndr_print_dnsp_DnssrvRpcRecord,
+ true);
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted dnsProperty
+*/
+static int ldif_write_dnsProperty(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct dnsp_DnsProperty),
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty,
+ (ndr_print_fn_t)ndr_print_dnsp_DnsProperty,
+ true);
+}
+
+/*
+ convert a NDR formatted blob of a supplementalCredentials into text
+*/
+static int ldif_write_supplementalCredentialsBlob(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct supplementalCredentialsBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob,
+ (ndr_print_fn_t)ndr_print_supplementalCredentialsBlob,
+ true);
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted trustAuthInOutBlob
+*/
+static int ldif_write_trustAuthInOutBlob(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct trustAuthInOutBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob,
+ (ndr_print_fn_t)ndr_print_trustAuthInOutBlob,
+ true);
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted msDS-TrustForestTrustInfo
+*/
+static int ldif_write_ForestTrustInfo(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct ForestTrustInfo),
+ (ndr_pull_flags_fn_t)ndr_pull_ForestTrustInfo,
+ (ndr_print_fn_t)ndr_print_ForestTrustInfo,
+ true);
+}
+/*
+ convert a NDR formatted blob of a partialAttributeSet into text
+*/
+static int ldif_write_partialAttributeSet(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ return ldif_write_NDR(ldb, mem_ctx, in, out,
+ sizeof(struct partialAttributeSetBlob),
+ (ndr_pull_flags_fn_t)ndr_pull_partialAttributeSetBlob,
+ (ndr_print_fn_t)ndr_print_partialAttributeSetBlob,
+ true);
+}
+
+
+static int extended_dn_write_hex(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ *out = data_blob_string_const(data_blob_hex_string_lower(mem_ctx, in));
+ if (!out->data) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ compare two dns
+*/
+static int samba_ldb_dn_link_comparison(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ struct ldb_dn *dn1 = NULL, *dn2 = NULL;
+ int ret;
+
+ if (dsdb_dn_is_deleted_val(v1)) {
+ /* If the DN is deleted, then we can't search for it */
+ return -1;
+ }
+
+ if (dsdb_dn_is_deleted_val(v2)) {
+ /* If the DN is deleted, then we can't search for it */
+ return -1;
+ }
+
+ dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1);
+ if ( ! ldb_dn_validate(dn1)) return -1;
+
+ dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2);
+ if ( ! ldb_dn_validate(dn2)) {
+ talloc_free(dn1);
+ return -1;
+ }
+
+ ret = ldb_dn_compare(dn1, dn2);
+
+ talloc_free(dn1);
+ talloc_free(dn2);
+ return ret;
+}
+
+static int samba_ldb_dn_link_canonicalise(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct ldb_dn *dn;
+ int ret = -1;
+
+ out->length = 0;
+ out->data = NULL;
+
+ dn = ldb_dn_from_ldb_val(mem_ctx, ldb, in);
+ if ( ! ldb_dn_validate(dn)) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ /* By including the RMD_FLAGS of a deleted DN, we ensure it
+ * does not casually match a not deleted DN */
+ if (dsdb_dn_is_deleted_val(in)) {
+ out->data = (uint8_t *)talloc_asprintf(mem_ctx,
+ "<RMD_FLAGS=%u>%s",
+ dsdb_dn_val_rmd_flags(in),
+ ldb_dn_get_casefold(dn));
+ } else {
+ out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
+ }
+
+ if (out->data == NULL) {
+ goto done;
+ }
+ out->length = strlen((char *)out->data);
+
+ ret = 0;
+
+done:
+ talloc_free(dn);
+
+ return ret;
+}
+
+
+/*
+ write a 64 bit 2-part range
+*/
+static int ldif_write_range64(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ int64_t v;
+ int ret;
+ ret = val_to_int64(in, &v);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lu-%lu",
+ (unsigned long)(v&0xFFFFFFFF),
+ (unsigned long)(v>>32));
+ if (out->data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ out->length = strlen((char *)out->data);
+ return LDB_SUCCESS;
+}
+
+/*
+ read a 64 bit 2-part range
+*/
+static int ldif_read_range64(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ unsigned long high, low;
+ char buf[64];
+
+ if (memchr(in->data, '-', in->length) == NULL) {
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+
+ if (in->length > sizeof(buf)-1) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ strncpy(buf, (const char *)in->data, in->length);
+ buf[in->length] = 0;
+
+ if (sscanf(buf, "%lu-%lu", &low, &high) != 2) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%llu",
+ (unsigned long long)(((uint64_t)high)<<32) | (low));
+
+ if (out->data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ out->length = strlen((char *)out->data);
+ return LDB_SUCCESS;
+}
+
+/*
+ when this operator_fn is set for a syntax, the backend calls is in
+ preference to the comparison function. We are told the exact
+ comparison operation that is needed, and we can return errors
+ */
+static int samba_syntax_operator_fn(struct ldb_context *ldb, enum ldb_parse_op operation,
+ const struct ldb_schema_attribute *a,
+ const struct ldb_val *v1, const struct ldb_val *v2, bool *matched)
+{
+ switch (operation) {
+ case LDB_OP_AND:
+ case LDB_OP_OR:
+ case LDB_OP_NOT:
+ case LDB_OP_SUBSTRING:
+ case LDB_OP_APPROX:
+ case LDB_OP_EXTENDED:
+ /* handled in the backends */
+ return LDB_ERR_INAPPROPRIATE_MATCHING;
+
+ case LDB_OP_GREATER:
+ case LDB_OP_LESS:
+ case LDB_OP_EQUALITY:
+ {
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ int ret;
+ if (tmp_ctx == NULL) {
+ return ldb_oom(ldb);
+ }
+ ret = a->syntax->comparison_fn(ldb, tmp_ctx, v1, v2);
+ talloc_free(tmp_ctx);
+ if (operation == LDB_OP_GREATER) {
+ *matched = (ret >= 0);
+ } else if (operation == LDB_OP_LESS) {
+ *matched = (ret <= 0);
+ } else {
+ *matched = (ret == 0);
+ }
+ return LDB_SUCCESS;
+ }
+
+ case LDB_OP_PRESENT:
+ *matched = true;
+ return LDB_SUCCESS;
+ }
+
+ /* we shouldn't get here */
+ return LDB_ERR_INAPPROPRIATE_MATCHING;
+}
+
+/*
+ compare two binary objects. This is correct for sorting as the sort order is:
+
+ a
+ aa
+ b
+ bb
+
+ rather than ldb_comparison_binary() which is:
+
+ a
+ b
+ aa
+ bb
+
+*/
+static int samba_ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ return data_blob_cmp(v1, v2);
+}
+
+/*
+ when this operator_fn is set for a syntax, the backend calls is in
+ preference to the comparison function. We are told the exact
+ comparison operation that is needed, and we can return errors.
+
+ This mode optimises for ldb_comparison_binary() if we need equality,
+ as this should be faster as it can do a length-check first.
+ */
+static int samba_syntax_binary_operator_fn(struct ldb_context *ldb, enum ldb_parse_op operation,
+ const struct ldb_schema_attribute *a,
+ const struct ldb_val *v1, const struct ldb_val *v2, bool *matched)
+{
+ if (operation == LDB_OP_EQUALITY) {
+ *matched = (ldb_comparison_binary(ldb, NULL, v1, v2) == 0);
+ return LDB_SUCCESS;
+ }
+ return samba_syntax_operator_fn(ldb, operation, a, v1, v2, matched);
+}
+
+/*
+ see if two DNs match, comparing first by GUID, then by SID, and
+ finally by string components
+ */
+static int samba_dn_extended_match(struct ldb_context *ldb,
+ const struct ldb_val *v1,
+ const struct ldb_val *v2,
+ bool *matched)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn1, *dn2;
+ const struct ldb_val *guid1, *guid2, *sid1, *sid2;
+ uint32_t rmd_flags1, rmd_flags2;
+
+ tmp_ctx = talloc_new(ldb);
+
+ dn1 = ldb_dn_from_ldb_val(tmp_ctx, ldb, v1);
+ dn2 = ldb_dn_from_ldb_val(tmp_ctx, ldb, v2);
+ if (!dn1 || !dn2) {
+ /* couldn't parse as DN's */
+ talloc_free(tmp_ctx);
+ (*matched) = false;
+ return LDB_SUCCESS;
+ }
+
+ rmd_flags1 = dsdb_dn_rmd_flags(dn1);
+ rmd_flags2 = dsdb_dn_rmd_flags(dn2);
+
+ if ((rmd_flags1 & DSDB_RMD_FLAG_DELETED) !=
+ (rmd_flags2 & DSDB_RMD_FLAG_DELETED)) {
+ /* only match if they have the same deletion status */
+ talloc_free(tmp_ctx);
+ (*matched) = false;
+ return LDB_SUCCESS;
+ }
+
+
+ guid1 = ldb_dn_get_extended_component(dn1, "GUID");
+ guid2 = ldb_dn_get_extended_component(dn2, "GUID");
+ if (guid1 && guid2) {
+ (*matched) = (data_blob_cmp(guid1, guid2) == 0);
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
+
+ sid1 = ldb_dn_get_extended_component(dn1, "SID");
+ sid2 = ldb_dn_get_extended_component(dn2, "SID");
+ if (sid1 && sid2) {
+ (*matched) = (data_blob_cmp(sid1, sid2) == 0);
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
+
+ (*matched) = (ldb_dn_compare(dn1, dn2) == 0);
+
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+/*
+ special operation for DNs, to take account of the RMD_FLAGS deleted bit
+ */
+static int samba_syntax_operator_dn(struct ldb_context *ldb, enum ldb_parse_op operation,
+ const struct ldb_schema_attribute *a,
+ const struct ldb_val *v1, const struct ldb_val *v2, bool *matched)
+{
+ if (operation == LDB_OP_PRESENT && dsdb_dn_is_deleted_val(v1)) {
+ /* If the DN is deleted, then we can't search for it */
+
+ /* should this be for equality too? */
+ *matched = false;
+ return LDB_SUCCESS;
+ }
+
+ if (operation == LDB_OP_EQUALITY &&
+ samba_dn_extended_match(ldb, v1, v2, matched) == LDB_SUCCESS) {
+ return LDB_SUCCESS;
+ }
+
+ return samba_syntax_operator_fn(ldb, operation, a, v1, v2, matched);
+}
+
+
+static const struct ldb_schema_syntax samba_syntaxes[] = {
+ {
+ .name = LDB_SYNTAX_SAMBA_SID,
+ .ldif_read_fn = ldif_read_objectSid,
+ .ldif_write_fn = ldif_write_objectSid,
+ .canonicalise_fn = ldif_canonicalise_objectSid,
+ .comparison_fn = ldif_comparison_objectSid,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR,
+ .ldif_read_fn = ldif_read_ntSecurityDescriptor,
+ .ldif_write_fn = ldif_write_ntSecurityDescriptor,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_SDDL_SECURITY_DESCRIPTOR,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_sddlSecurityDescriptor,
+ .canonicalise_fn = ldb_handler_fold,
+ .comparison_fn = ldb_comparison_fold,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_GUID,
+ .ldif_read_fn = ldif_read_objectGUID,
+ .ldif_write_fn = ldif_write_objectGUID,
+ .canonicalise_fn = ldif_canonicalise_objectGUID,
+ .comparison_fn = ldif_comparison_objectGUID,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_OBJECT_CATEGORY,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldif_canonicalise_objectCategory,
+ .comparison_fn = ldif_comparison_objectCategory,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_SCHEMAINFO,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_schemaInfo,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_PREFIX_MAP,
+ .ldif_read_fn = ldif_read_prefixMap,
+ .ldif_write_fn = ldif_write_prefixMap,
+ .canonicalise_fn = ldif_canonicalise_prefixMap,
+ .comparison_fn = ldif_comparison_prefixMap,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_INT32,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldif_canonicalise_int32,
+ .index_format_fn = ldif_index_format_int32,
+ .comparison_fn = ldif_comparison_int32,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_REPSFROMTO,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_repsFromTo,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_REPLPROPERTYMETADATA,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_replPropertyMetaData,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_REPLUPTODATEVECTOR,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_replUpToDateVector,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_REVEALEDUSERS,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_msDS_RevealedUsers,
+ .canonicalise_fn = dsdb_dn_binary_canonicalise,
+ .comparison_fn = dsdb_dn_binary_comparison,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_TRUSTAUTHINOUTBLOB,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_trustAuthInOutBlob,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_FORESTTRUSTINFO,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_ForestTrustInfo,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = DSDB_SYNTAX_BINARY_DN,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = dsdb_dn_binary_canonicalise,
+ .comparison_fn = dsdb_dn_binary_comparison,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = DSDB_SYNTAX_STRING_DN,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = dsdb_dn_string_canonicalise,
+ .comparison_fn = dsdb_dn_string_comparison,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_DN,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = samba_ldb_dn_link_canonicalise,
+ .comparison_fn = samba_ldb_dn_link_comparison,
+ .operator_fn = samba_syntax_operator_dn
+ },{
+ .name = LDB_SYNTAX_SAMBA_RANGE64,
+ .ldif_read_fn = ldif_read_range64,
+ .ldif_write_fn = ldif_write_range64,
+ .canonicalise_fn = ldif_canonicalise_int64,
+ .comparison_fn = ldif_comparison_int64,
+ .operator_fn = samba_syntax_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_DNSRECORD,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_dnsRecord,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_DNSPROPERTY,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_dnsProperty,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_SUPPLEMENTALCREDENTIALS,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_supplementalCredentialsBlob,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_PARTIALATTRIBUTESET,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldif_write_partialAttributeSet,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ },{
+ .name = LDB_SYNTAX_SAMBA_OCTET_STRING,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = samba_ldb_comparison_binary,
+ .operator_fn = samba_syntax_binary_operator_fn
+ }
+};
+
+static const struct ldb_dn_extended_syntax samba_dn_syntax[] = {
+ {
+ .name = "SID",
+ .read_fn = extended_dn_read_SID,
+ .write_clear_fn = ldif_write_objectSid,
+ .write_hex_fn = extended_dn_write_hex
+ },{
+ .name = "GUID",
+ .read_fn = extended_dn_read_GUID,
+ .write_clear_fn = ldif_write_objectGUID,
+ .write_hex_fn = extended_dn_write_hex
+ },{
+ .name = "WKGUID",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ },{
+ .name = "RMD_INVOCID",
+ .read_fn = extended_dn_read_GUID,
+ .write_clear_fn = ldif_write_objectGUID,
+ .write_hex_fn = extended_dn_write_hex
+ },{
+ .name = "RMD_FLAGS",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ },{
+ .name = "RMD_ADDTIME",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ },{
+ .name = "RMD_CHANGETIME",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ },{
+ .name = "RMD_LOCAL_USN",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ },{
+ .name = "RMD_ORIGINATING_USN",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ },{
+ .name = "RMD_VERSION",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ }
+};
+
+/* TODO: Should be dynamic at some point */
+static const struct {
+ const char *name;
+ const char *syntax;
+} samba_attributes[] = {
+ { "ntSecurityDescriptor", LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR },
+ { "oMSyntax", LDB_SYNTAX_SAMBA_INT32 },
+ { "objectCategory", LDB_SYNTAX_SAMBA_OBJECT_CATEGORY },
+ { "schemaInfo", LDB_SYNTAX_SAMBA_SCHEMAINFO },
+ { "prefixMap", LDB_SYNTAX_SAMBA_PREFIX_MAP },
+ { "repsFrom", LDB_SYNTAX_SAMBA_REPSFROMTO },
+ { "repsTo", LDB_SYNTAX_SAMBA_REPSFROMTO },
+ { "replPropertyMetaData", LDB_SYNTAX_SAMBA_REPLPROPERTYMETADATA },
+ { "replUpToDateVector", LDB_SYNTAX_SAMBA_REPLUPTODATEVECTOR },
+ { "msDS-RevealedUsers", LDB_SYNTAX_SAMBA_REVEALEDUSERS },
+ { "trustAuthIncoming", LDB_SYNTAX_SAMBA_TRUSTAUTHINOUTBLOB },
+ { "trustAuthOutgoing", LDB_SYNTAX_SAMBA_TRUSTAUTHINOUTBLOB },
+ { "msDS-TrustForestTrustInfo", LDB_SYNTAX_SAMBA_FORESTTRUSTINFO },
+ { "rIDAllocationPool", LDB_SYNTAX_SAMBA_RANGE64 },
+ { "rIDPreviousAllocationPool", LDB_SYNTAX_SAMBA_RANGE64 },
+ { "rIDAvailablePool", LDB_SYNTAX_SAMBA_RANGE64 },
+ { "defaultSecurityDescriptor", LDB_SYNTAX_SAMBA_SDDL_SECURITY_DESCRIPTOR },
+
+ /*
+ * these are extracted by searching
+ * (&(attributeSyntax=2.5.5.17)(omSyntax=4))
+ *
+ * Except: msAuthz-CentralAccessPolicyID as it might be a GUID see:
+ * adminDescription: For a Central Access Policy, this attribute defines a GUID t
+ * hat can be used to identify the set of policies when applied to a resource.
+ * Until we see a msAuthz-CentralAccessPolicyID value on a windows
+ * server, we ignore it here.
+ */
+ { "mS-DS-CreatorSID", LDB_SYNTAX_SAMBA_SID },
+ { "msDS-QuotaTrustee", LDB_SYNTAX_SAMBA_SID },
+ { "objectSid", LDB_SYNTAX_SAMBA_SID },
+ { "tokenGroups", LDB_SYNTAX_SAMBA_SID },
+ { "tokenGroupsGlobalAndUniversal", LDB_SYNTAX_SAMBA_SID },
+ { "tokenGroupsNoGCAcceptable", LDB_SYNTAX_SAMBA_SID },
+ { "securityIdentifier", LDB_SYNTAX_SAMBA_SID },
+ { "sIDHistory", LDB_SYNTAX_SAMBA_SID },
+ { "syncWithSID", LDB_SYNTAX_SAMBA_SID },
+
+ /*
+ * these are extracted by searching
+ * (&(attributeSyntax=2.5.5.10)(rangeLower=16)(rangeUpper=16)(omSyntax=4))
+ */
+ { "attributeSecurityGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "categoryId", LDB_SYNTAX_SAMBA_GUID },
+ { "controlAccessRights", LDB_SYNTAX_SAMBA_GUID },
+ { "currMachineId", LDB_SYNTAX_SAMBA_GUID },
+ { "fRSReplicaSetGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "fRSVersionGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "implementedCategories", LDB_SYNTAX_SAMBA_GUID },
+ { "msDS-AzObjectGuid", LDB_SYNTAX_SAMBA_GUID },
+ { "msDS-GenerationId", LDB_SYNTAX_SAMBA_GUID },
+ { "msDS-OptionalFeatureGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "msDFSR-ContentSetGuid", LDB_SYNTAX_SAMBA_GUID },
+ { "msDFSR-ReplicationGroupGuid", LDB_SYNTAX_SAMBA_GUID },
+ { "mSMQDigests", LDB_SYNTAX_SAMBA_GUID },
+ { "mSMQOwnerID", LDB_SYNTAX_SAMBA_GUID },
+ { "mSMQQMID", LDB_SYNTAX_SAMBA_GUID },
+ { "mSMQQueueType", LDB_SYNTAX_SAMBA_GUID },
+ { "mSMQSites", LDB_SYNTAX_SAMBA_GUID },
+ { "netbootGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "objectGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "pKTGuid", LDB_SYNTAX_SAMBA_GUID },
+ { "requiredCategories", LDB_SYNTAX_SAMBA_GUID },
+ { "schemaIDGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "siteGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "msDFS-GenerationGUIDv2", LDB_SYNTAX_SAMBA_GUID },
+ { "msDFS-LinkIdentityGUIDv2", LDB_SYNTAX_SAMBA_GUID },
+ { "msDFS-NamespaceIdentityGUIDv2", LDB_SYNTAX_SAMBA_GUID },
+ { "msSPP-CSVLKSkuId", LDB_SYNTAX_SAMBA_GUID },
+ { "msSPP-KMSIds", LDB_SYNTAX_SAMBA_GUID },
+
+ /*
+ * these are known to be GUIDs
+ */
+ { "invocationId", LDB_SYNTAX_SAMBA_GUID },
+ { "parentGUID", LDB_SYNTAX_SAMBA_GUID },
+
+ /* These NDR encoded things we want to be able to read with --show-binary */
+ { "dnsRecord", LDB_SYNTAX_SAMBA_DNSRECORD },
+ { "dNSProperty", LDB_SYNTAX_SAMBA_DNSPROPERTY },
+ { "supplementalCredentials", LDB_SYNTAX_SAMBA_SUPPLEMENTALCREDENTIALS},
+ { "partialAttributeSet", LDB_SYNTAX_SAMBA_PARTIALATTRIBUTESET}
+};
+
+const struct ldb_schema_syntax *ldb_samba_syntax_by_name(struct ldb_context *ldb, const char *name)
+{
+ unsigned int j;
+ const struct ldb_schema_syntax *s = NULL;
+
+ for (j=0; j < ARRAY_SIZE(samba_syntaxes); j++) {
+ if (strcmp(name, samba_syntaxes[j].name) == 0) {
+ s = &samba_syntaxes[j];
+ break;
+ }
+ }
+ return s;
+}
+
+const struct ldb_schema_syntax *ldb_samba_syntax_by_lDAPDisplayName(struct ldb_context *ldb, const char *name)
+{
+ unsigned int j;
+ const struct ldb_schema_syntax *s = NULL;
+
+ for (j=0; j < ARRAY_SIZE(samba_attributes); j++) {
+ if (strcmp(samba_attributes[j].name, name) == 0) {
+ s = ldb_samba_syntax_by_name(ldb, samba_attributes[j].syntax);
+ break;
+ }
+ }
+
+ return s;
+}
+
+static const char *secret_attributes[] = {DSDB_SECRET_ATTRIBUTES, "secret",
+ "priorSecret", NULL};
+
+/*
+ register the samba ldif handlers
+*/
+int ldb_register_samba_handlers(struct ldb_context *ldb)
+{
+ unsigned int i;
+ int ret;
+
+ if (ldb_get_opaque(ldb, "SAMBA_HANDLERS_REGISTERED") != NULL) {
+ return LDB_SUCCESS;
+ }
+
+ ret = ldb_set_opaque(ldb, LDB_SECRET_ATTRIBUTE_LIST_OPAQUE, discard_const_p(char *, secret_attributes));
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ for (i=0; i < ARRAY_SIZE(samba_attributes); i++) {
+ const struct ldb_schema_syntax *s = NULL;
+
+ s = ldb_samba_syntax_by_name(ldb, samba_attributes[i].syntax);
+
+ if (!s) {
+ s = ldb_standard_syntax_by_name(ldb, samba_attributes[i].syntax);
+ }
+
+ if (!s) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_schema_attribute_add_with_syntax(ldb, samba_attributes[i].name, LDB_ATTR_FLAG_FIXED, s);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ for (i=0; i < ARRAY_SIZE(samba_dn_syntax); i++) {
+ ret = ldb_dn_extended_add_syntax(ldb, LDB_ATTR_FLAG_FIXED, &samba_dn_syntax[i]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ }
+
+ ret = ldb_register_samba_matching_rules(ldb);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ldb);
+ return LDB_SUCCESS;
+ }
+
+ ret = ldb_set_opaque(ldb, "SAMBA_HANDLERS_REGISTERED", (void*)1);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/lib/ldb-samba/ldif_handlers.h b/lib/ldb-samba/ldif_handlers.h
new file mode 100644
index 0000000..8b2ade6
--- /dev/null
+++ b/lib/ldb-samba/ldif_handlers.h
@@ -0,0 +1,30 @@
+#ifndef __LIB_LDB_SAMBA_LDIF_HANDLERS_H__
+#define __LIB_LDB_SAMBA_LDIF_HANDLERS_H__
+
+#define LDB_SYNTAX_SAMBA_SID "LDB_SYNTAX_SAMBA_SID"
+#define LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR "1.2.840.113556.1.4.907"
+#define LDB_SYNTAX_SAMBA_GUID "LDB_SYNTAX_SAMBA_GUID"
+#define LDB_SYNTAX_SAMBA_OBJECT_CATEGORY "LDB_SYNTAX_SAMBA_OBJECT_CATEGORY"
+#define LDB_SYNTAX_SAMBA_SCHEMAINFO "LDB_SYNTAX_SAMBA_SCHEMAINFO"
+#define LDB_SYNTAX_SAMBA_PREFIX_MAP "LDB_SYNTAX_SAMBA_PREFIX_MAP"
+#define LDB_SYNTAX_SAMBA_INT32 "LDB_SYNTAX_SAMBA_INT32"
+#define LDB_SYNTAX_SAMBA_REPSFROMTO "LDB_SYNTAX_SAMBA_REPSFROMTO"
+#define LDB_SYNTAX_SAMBA_REPLPROPERTYMETADATA "LDB_SYNTAX_SAMBA_REPLPROPERTYMETADATA"
+#define LDB_SYNTAX_SAMBA_REPLUPTODATEVECTOR "LDB_SYNTAX_SAMBA_REPLUPTODATEVECTOR"
+#define LDB_SYNTAX_SAMBA_REVEALEDUSERS "LDB_SYNTAX_SAMBA_REVEALEDUSERS"
+#define LDB_SYNTAX_SAMBA_RANGE64 "LDB_SYNTAX_SAMBA_RANGE64"
+#define LDB_SYNTAX_SAMBA_DNSRECORD "LDB_SYNTAX_SAMBA_DNSRECORD"
+#define LDB_SYNTAX_SAMBA_DNSPROPERTY "LDB_SYNTAX_SAMBA_DNSPROPERTY"
+#define LDB_SYNTAX_SAMBA_SUPPLEMENTALCREDENTIALS "LDB_SYNTAX_SAMBA_SUPPLEMENTALCREDENTIALS"
+#define LDB_SYNTAX_SAMBA_SDDL_SECURITY_DESCRIPTOR "LDB_SYNTAX_SAMBA_SDDL"
+#define LDB_SYNTAX_SAMBA_TRUSTAUTHINOUTBLOB "LDB_SYNTAX_SAMBA_TRUSTAUTHINOUTBLOB"
+#define LDB_SYNTAX_SAMBA_FORESTTRUSTINFO "LDB_SYNTAX_SAMBA_FORESTTRUSTINFO"
+#define LDB_SYNTAX_SAMBA_PARTIALATTRIBUTESET "LDB_SYNTAX_SAMBA_PARTIALATTRIBUTESET"
+#define LDB_SYNTAX_SAMBA_OCTET_STRING "LDB_SYNTAX_SAMBA_OCTET_STRING"
+#include "lib/ldb-samba/ldif_handlers_proto.h"
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2)
+
+#endif /* __LIB_LDB_SAMBA_LDIF_HANDLERS_H__ */
+
diff --git a/lib/ldb-samba/pyldb.c b/lib/ldb-samba/pyldb.c
new file mode 100644
index 0000000..bea4894
--- /dev/null
+++ b/lib/ldb-samba/pyldb.c
@@ -0,0 +1,320 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Python interface to ldb, Samba-specific functions
+
+ Copyright (C) 2007-2010 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 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
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include "python/py3compat.h"
+#include "includes.h"
+#include <ldb.h>
+#include <pyldb.h>
+#include "param/pyparam.h"
+#include "auth/credentials/pycredentials.h"
+#include "ldb_wrap.h"
+#include "lib/ldb-samba/ldif_handlers.h"
+#include "auth/pyauth.h"
+#include "source4/dsdb/common/util.h"
+#include "lib/ldb/include/ldb_private.h"
+
+
+static PyObject *pyldb_module;
+static PyObject *py_ldb_error;
+static PyTypeObject PySambaLdb;
+
+static void PyErr_SetLdbError(PyObject *error, int ret, struct ldb_context *ldb_ctx)
+{
+ if (ret == LDB_ERR_PYTHON_EXCEPTION)
+ return; /* Python exception should already be set, just keep that */
+
+ PyErr_SetObject(error,
+ Py_BuildValue(discard_const_p(char, "(i,s)"), ret,
+ ldb_ctx == NULL?ldb_strerror(ret):ldb_errstring(ldb_ctx)));
+}
+
+static PyObject *py_ldb_set_loadparm(PyObject *self, PyObject *args)
+{
+ PyObject *py_lp_ctx;
+ struct loadparm_context *lp_ctx;
+ struct ldb_context *ldb;
+
+ if (!PyArg_ParseTuple(args, "O", &py_lp_ctx))
+ return NULL;
+
+ ldb = pyldb_Ldb_AS_LDBCONTEXT(self);
+
+ lp_ctx = lpcfg_from_py_object(ldb, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm object");
+ return NULL;
+ }
+
+ ldb_set_opaque(ldb, "loadparm", lp_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_set_credentials(PyObject *self, PyObject *args)
+{
+ PyObject *py_creds;
+ struct cli_credentials *creds;
+ struct ldb_context *ldb;
+
+ if (!PyArg_ParseTuple(args, "O", &py_creds))
+ return NULL;
+
+ creds = cli_credentials_from_py_object(py_creds);
+ if (creds == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials object");
+ return NULL;
+ }
+
+ ldb = pyldb_Ldb_AS_LDBCONTEXT(self);
+
+ ldb_set_opaque(ldb, "credentials", creds);
+
+ Py_RETURN_NONE;
+}
+
+/* XXX: This function really should be in libldb's pyldb.c */
+static PyObject *py_ldb_set_opaque_integer(PyObject *self, PyObject *args)
+{
+ int value;
+ int *old_val, *new_val;
+ char *py_opaque_name, *opaque_name_talloc;
+ struct ldb_context *ldb;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+
+ if (!PyArg_ParseTuple(args, "si", &py_opaque_name, &value))
+ return NULL;
+
+ ldb = pyldb_Ldb_AS_LDBCONTEXT(self);
+
+ /* see if we have a cached copy */
+ old_val = (int *)ldb_get_opaque(ldb, py_opaque_name);
+ /* XXX: We shouldn't just blindly assume that the value that is
+ * already present has the size of an int and is not shared
+ * with other code that may rely on it not changing.
+ * JRV 20100403 */
+
+ if (old_val) {
+ *old_val = value;
+ Py_RETURN_NONE;
+ }
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ new_val = talloc(tmp_ctx, int);
+ if (new_val == NULL) {
+ talloc_free(tmp_ctx);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ opaque_name_talloc = talloc_strdup(tmp_ctx, py_opaque_name);
+ if (opaque_name_talloc == NULL) {
+ talloc_free(tmp_ctx);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ *new_val = value;
+
+ /* cache the domain_sid in the ldb */
+ ret = ldb_set_opaque(ldb, opaque_name_talloc, new_val);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ PyErr_SetLdbError(py_ldb_error, ret, ldb);
+ return NULL;
+ }
+
+ talloc_steal(ldb, new_val);
+ talloc_steal(ldb, opaque_name_talloc);
+ talloc_free(tmp_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_set_utf8_casefold(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ struct ldb_context *ldb;
+
+ ldb = pyldb_Ldb_AS_LDBCONTEXT(self);
+
+ ldb_set_utf8_fns(ldb, NULL, wrap_casefold);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_set_session_info(PyObject *self, PyObject *args)
+{
+ PyObject *py_session_info;
+ struct auth_session_info *info;
+ struct ldb_context *ldb;
+ PyObject *mod_samba_auth;
+ PyObject *PyAuthSession_Type;
+ bool ret;
+
+ mod_samba_auth = PyImport_ImportModule("samba.dcerpc.auth");
+ if (mod_samba_auth == NULL)
+ return NULL;
+
+ PyAuthSession_Type = PyObject_GetAttrString(mod_samba_auth, "session_info");
+ if (PyAuthSession_Type == NULL) {
+ Py_CLEAR(mod_samba_auth);
+ return NULL;
+ }
+
+ ret = PyArg_ParseTuple(args, "O!", PyAuthSession_Type, &py_session_info);
+
+ Py_DECREF(PyAuthSession_Type);
+ Py_DECREF(mod_samba_auth);
+
+ if (!ret)
+ return NULL;
+
+ ldb = pyldb_Ldb_AS_LDBCONTEXT(self);
+
+ info = PyAuthSession_AsSession(py_session_info);
+
+ ldb_set_opaque(ldb, DSDB_SESSION_INFO, info);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_samba_schema_attribute_add(PyLdbObject *self,
+ PyObject *args)
+{
+ char *attribute, *syntax;
+ const struct ldb_schema_syntax *s;
+ unsigned int flags;
+ int ret;
+ struct ldb_context *ldb_ctx;
+
+ if (!PyArg_ParseTuple(args, "sIs", &attribute, &flags, &syntax))
+ return NULL;
+
+ ldb_ctx = pyldb_Ldb_AsLdbContext((PyObject *)self);
+
+ s = ldb_samba_syntax_by_name(ldb_ctx, syntax);
+ ret = ldb_schema_attribute_add_with_syntax(ldb_ctx, attribute,
+ flags, s);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_error, ret, ldb_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_register_samba_handlers(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ /* XXX: Perhaps call this from PySambaLdb's init function ? */
+
+ ldb = pyldb_Ldb_AS_LDBCONTEXT(self);
+ ret = ldb_register_samba_handlers(ldb);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_error, ret, ldb);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_samba_ldb_methods[] = {
+ { "set_loadparm", (PyCFunction)py_ldb_set_loadparm, METH_VARARGS,
+ "ldb_set_loadparm(session_info)\n"
+ "Set loadparm context to use when connecting." },
+ { "set_credentials", (PyCFunction)py_ldb_set_credentials, METH_VARARGS,
+ "ldb_set_credentials(credentials)\n"
+ "Set credentials to use when connecting." },
+ { "set_opaque_integer", (PyCFunction)py_ldb_set_opaque_integer,
+ METH_VARARGS, NULL },
+ { "set_utf8_casefold", (PyCFunction)py_ldb_set_utf8_casefold,
+ METH_NOARGS,
+ "ldb_set_utf8_casefold()\n"
+ "Set the right Samba casefolding function for UTF8 charset." },
+ { "register_samba_handlers", (PyCFunction)py_ldb_register_samba_handlers,
+ METH_NOARGS,
+ "register_samba_handlers()\n"
+ "Register Samba-specific LDB modules and schemas." },
+ { "set_session_info", (PyCFunction)py_ldb_set_session_info, METH_VARARGS,
+ "set_session_info(session_info)\n"
+ "Set session info to use when connecting." },
+ { "samba_schema_attribute_add",
+ (PyCFunction)py_ldb_samba_schema_attribute_add,
+ METH_VARARGS, NULL },
+ {0},
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "_ldb",
+ .m_doc = "Samba-specific LDB python bindings",
+ .m_size = -1,
+ .m_methods = py_samba_ldb_methods,
+};
+
+static PyTypeObject PySambaLdb = {
+ .tp_name = "samba._ldb.Ldb",
+ .tp_doc = "Connection to a LDB database.",
+ .tp_methods = py_samba_ldb_methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+};
+
+
+MODULE_INIT_FUNC(_ldb)
+{
+ PyObject *m;
+
+ pyldb_module = PyImport_ImportModule("ldb");
+ if (pyldb_module == NULL)
+ return NULL;
+
+ PySambaLdb.tp_base = (PyTypeObject *)PyObject_GetAttrString(pyldb_module, "Ldb");
+ if (PySambaLdb.tp_base == NULL) {
+ Py_CLEAR(pyldb_module);
+ return NULL;
+ }
+
+ py_ldb_error = PyObject_GetAttrString(pyldb_module, "LdbError");
+
+ Py_CLEAR(pyldb_module);
+
+ if (PyType_Ready(&PySambaLdb) < 0)
+ return NULL;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return NULL;
+
+ Py_INCREF(&PySambaLdb);
+ PyModule_AddObject(m, "Ldb", (PyObject *)&PySambaLdb);
+
+#define ADD_LDB_STRING(val) PyModule_AddStringConstant(m, #val, LDB_## val)
+ ADD_LDB_STRING(SYNTAX_SAMBA_INT32);
+
+ return m;
+}
diff --git a/lib/ldb-samba/samba_extensions.c b/lib/ldb-samba/samba_extensions.c
new file mode 100644
index 0000000..be92d98
--- /dev/null
+++ b/lib/ldb-samba/samba_extensions.c
@@ -0,0 +1,169 @@
+/*
+ ldb database library - samba extensions
+
+ Copyright (C) Andrew Tridgell 2010
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ 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 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
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "lib/cmdline/cmdline.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "ldb_wrap.h"
+#include "popt.h"
+
+
+static bool is_popt_table_end(const struct poptOption *o)
+{
+ if (o->longName == NULL &&
+ o->shortName =='\0' &&
+ o->arg == NULL) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ work out the length of a popt array
+ */
+static size_t calculate_popt_array_length(struct poptOption *opts)
+{
+ size_t i = 0;
+
+ for (i = 0; i < UINT32_MAX; i++) {
+ struct poptOption *o = &(opts[i]);
+
+ if (is_popt_table_end(o)) {
+ break;
+ }
+ }
+
+ return i;
+}
+
+/*
+ called to register additional command line options
+ */
+static int extensions_hook(struct ldb_context *ldb, enum ldb_module_hook_type t)
+{
+ switch (t) {
+ case LDB_MODULE_HOOK_CMDLINE_OPTIONS: {
+ size_t len1, len2;
+ struct poptOption **popt_options = ldb_module_popt_options(ldb);
+ struct poptOption *new_array = NULL;
+ bool ok;
+
+ struct poptOption cmdline_extensions[] = {
+ POPT_COMMON_SAMBA_LDB
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_LEGACY_S4
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ ok = samba_cmdline_init(ldb,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ return ldb_oom(ldb);
+ }
+
+ len1 = calculate_popt_array_length(*popt_options);
+ len2 = calculate_popt_array_length(cmdline_extensions);
+ new_array = talloc_array(ldb,
+ struct poptOption,
+ len1 + len2 + 1);
+ if (NULL == new_array) {
+ return ldb_oom(ldb);
+ }
+
+ memcpy(new_array, *popt_options, len1*sizeof(struct poptOption));
+ memcpy(new_array+len1, cmdline_extensions, (1+len2)*sizeof(struct poptOption));
+
+#ifdef DEVELOPER
+ ok = samba_cmdline_sanity_check(new_array);
+ if (!ok) {
+ talloc_free(new_array);
+ return ldb_error(ldb,
+ LDB_ERR_OPERATIONS_ERROR,
+ "Duplicate cmdline options detected!");
+ }
+#endif
+
+ (*popt_options) = new_array;
+ return LDB_SUCCESS;
+ }
+
+ case LDB_MODULE_HOOK_CMDLINE_PRECONNECT: {
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+
+ int r = ldb_register_samba_handlers(ldb);
+ if (r != LDB_SUCCESS) {
+ return ldb_operr(ldb);
+ }
+ gensec_init();
+
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ creds = samba_cmdline_get_creds();
+
+ if (ldb_set_opaque(
+ ldb,
+ DSDB_SESSION_INFO,
+ system_session(lp_ctx))) {
+
+ return ldb_operr(ldb);
+ }
+ if (ldb_set_opaque(ldb, "credentials", creds)) {
+ return ldb_operr(ldb);
+ }
+ if (ldb_set_opaque(ldb, "loadparm", lp_ctx)) {
+ return ldb_operr(ldb);
+ }
+
+ ldb_set_utf8_fns(ldb, NULL, wrap_casefold);
+ break;
+ }
+
+ case LDB_MODULE_HOOK_CMDLINE_POSTCONNECT:
+ /* get the domain SID into the cache for SDDL processing */
+ samdb_domain_sid(ldb);
+ break;
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ initialise the module
+ */
+_PUBLIC_ int ldb_samba_extensions_init(const char *ldb_version)
+{
+ ldb_register_hook(extensions_hook);
+
+ return LDB_SUCCESS;
+}
diff --git a/lib/ldb-samba/tests/index.py b/lib/ldb-samba/tests/index.py
new file mode 100644
index 0000000..2256e3e
--- /dev/null
+++ b/lib/ldb-samba/tests/index.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python3
+#
+# Tests for comparison expressions on indexed keys
+#
+# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2019
+#
+# 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/>.
+#
+"""Tests for expressions containing comparisons on indexed attributes.
+ Copied from ldb's index.py"""
+
+import os
+from unittest import TestCase
+import sys
+from samba import _ldb
+import shutil
+from ldb import SCOPE_SUBTREE
+from samba.tests.subunitrun import TestProgram
+
+PY3 = sys.version_info > (3, 0)
+
+TDB_PREFIX = "tdb://"
+MDB_PREFIX = "mdb://"
+
+def tempdir():
+ import tempfile
+ try:
+ dir_prefix = os.path.join(os.environ["SELFTEST_PREFIX"], "tmp")
+ except KeyError:
+ dir_prefix = None
+ return tempfile.mkdtemp(dir=dir_prefix)
+
+class LdbBaseTest(TestCase):
+ def setUp(self):
+ super(LdbBaseTest, self).setUp()
+ try:
+ if self.prefix is None:
+ self.prefix = TDB_PREFIX
+ except AttributeError:
+ self.prefix = TDB_PREFIX
+
+ def tearDown(self):
+ super(LdbBaseTest, self).tearDown()
+
+ def url(self):
+ return self.prefix + self.filename
+
+ def flags(self):
+ if self.prefix == MDB_PREFIX:
+ return ldb.FLG_NOSYNC
+ else:
+ return 0
+
+ def options(self):
+ if self.prefix == MDB_PREFIX:
+ return ['disable_full_db_scan_for_self_test:1']
+ else:
+ return None
+
+class LdbTDBIndexedComparisonExpressions(LdbBaseTest):
+ def tearDown(self):
+ shutil.rmtree(self.testdir)
+ super(LdbTDBIndexedComparisonExpressions, self).tearDown()
+
+ # Ensure the LDB is closed now, so we close the FD
+ del(self.l)
+
+ def setUp(self):
+ super(LdbTDBIndexedComparisonExpressions, self).setUp()
+ self.testdir = tempdir()
+ self.filename = os.path.join(self.testdir, "indexedcomptest.ldb")
+ # Note that the maximum key length is set to 54
+ # This accounts for the 4 bytes added by the dn formatting
+ # a leading dn=, and a trailing zero terminator
+ #
+ self.l = _ldb.Ldb(self.url(), options=self.options())
+ self.l.add({"dn": "@ATTRIBUTES"})
+ self.l.add({"dn": "@INDEXLIST",
+ "@IDXATTR": [b"int32attr"],
+ "@IDXONE": [b"1"],
+ "@IDXGUID": [b"objectUUID"],
+ "@IDX_DN_GUID": [b"GUID"]})
+
+ def test_comparison_expression(self):
+ self.l.samba_schema_attribute_add("int32attr", 0,
+ _ldb.SYNTAX_SAMBA_INT32)
+
+ int32_max = 2**31-1
+ int32_min = -2**31
+ test_nums = list(range(-5, 5))
+ test_nums += list(range(int32_max-5, int32_max+1))
+ test_nums += list(range(int32_min, int32_min+5))
+ test_nums = sorted(test_nums)
+
+ for i in test_nums:
+ ouuid = 0x0123456789abcdef + i
+ ouuid_s = bytes(('0' + hex(ouuid)[2:]).encode())
+ self.l.add({"dn": "OU=COMPTESTOU{},DC=SAMBA,DC=ORG".format(i),
+ "objectUUID": ouuid_s,
+ "int32attr": str(i)})
+
+ def assert_int32_expr(expr, py_expr=None):
+ res = self.l.search(base="DC=SAMBA,DC=ORG",
+ scope=SCOPE_SUBTREE,
+ expression="(int32attr%s)" % (expr))
+
+ if not py_expr:
+ py_expr = expr
+ expect = [n for n in test_nums if eval(str(n) + py_expr)]
+ vals = sorted([int(r.get("int32attr")[0]) for r in res])
+ self.assertEqual(len(res), len(expect))
+ self.assertEqual(set(vals), set(expect))
+ self.assertEqual(expect, vals)
+
+ assert_int32_expr(">=-2")
+ assert_int32_expr("<=2")
+ assert_int32_expr(">=" + str(int32_min))
+ assert_int32_expr("<=" + str(int32_min))
+ assert_int32_expr("<=" + str(int32_min+1))
+ assert_int32_expr("<=" + str(int32_max))
+ assert_int32_expr(">=" + str(int32_max))
+ assert_int32_expr(">=" + str(int32_max-1))
+ assert_int32_expr("=10", "==10")
+
+ def test_comparison_expression_duplicates(self):
+ self.l.samba_schema_attribute_add("int32attr", 0,
+ _ldb.SYNTAX_SAMBA_INT32)
+
+ int32_max = 2**31-1
+ int32_min = -2**31
+
+ test_nums = list(range(-5, 5)) * 3
+ test_nums += list(range(-20, 20, 5)) * 2
+ test_nums += list(range(-50, 50, 15))
+ test_nums = sorted(test_nums)
+
+ for i, n in enumerate(test_nums):
+ ouuid = 0x0123456789abcdef + i
+ ouuid_s = bytes(('0' + hex(ouuid)[2:]).encode())
+ self.l.add({"dn": "OU=COMPTESTOU{},DC=SAMBA,DC=ORG".format(i),
+ "objectUUID": ouuid_s,
+ "int32attr": str(n)})
+
+ def assert_int32_expr(expr, py_expr=None):
+ res = self.l.search(base="DC=SAMBA,DC=ORG",
+ scope=SCOPE_SUBTREE,
+ expression="(int32attr%s)" % (expr))
+
+ if not py_expr:
+ py_expr = expr
+ expect = [n for n in test_nums if eval(str(n) + py_expr)]
+ vals = sorted([int(r.get("int32attr")[0]) for r in res])
+ self.assertEqual(len(res), len(expect))
+ self.assertEqual(set(vals), set(expect))
+ self.assertEqual(expect, vals)
+
+ assert_int32_expr(">=-2")
+ assert_int32_expr("<=2")
+ assert_int32_expr(">=" + str(int32_min))
+ assert_int32_expr("<=" + str(int32_min))
+ assert_int32_expr("<=" + str(int32_min+1))
+ assert_int32_expr("<=" + str(int32_max))
+ assert_int32_expr(">=" + str(int32_max))
+ assert_int32_expr(">=" + str(int32_max-1))
+ assert_int32_expr("=-5", "==-5")
+ assert_int32_expr("=5", "==5")
+
+# Run the same tests against an lmdb backend
+class LdbLMDBIndexedComparisonExpressions(LdbTDBIndexedComparisonExpressions):
+
+ def setUp(self):
+ if os.environ.get('HAVE_LMDB', '1') == '0':
+ self.skipTest("No lmdb backend")
+ self.prefix = MDB_PREFIX
+ super(LdbLMDBIndexedComparisonExpressions, self).setUp()
+
+ def tearDown(self):
+ super(LdbLMDBIndexedComparisonExpressions, self).tearDown()
+
+
+TestProgram(module=__name__, opts=[])
diff --git a/lib/ldb-samba/tests/match_rules.py b/lib/ldb-samba/tests/match_rules.py
new file mode 100755
index 0000000..2fe6c3e
--- /dev/null
+++ b/lib/ldb-samba/tests/match_rules.py
@@ -0,0 +1,1797 @@
+#!/usr/bin/env python3
+
+import optparse
+import sys
+import os
+import samba
+import samba.getopt as options
+
+from samba.tests.subunitrun import SubunitOptions, TestProgram
+
+from samba.samdb import SamDB
+from samba.auth import system_session
+from ldb import Message, MessageElement, Dn, LdbError
+from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
+from ldb import SCOPE_BASE, SCOPE_SUBTREE, SCOPE_ONELEVEL
+
+# TODO I'm ignoring case in these tests for now.
+# This should be fixed to work inline with Windows.
+# The literal strings are in the case Windows uses.
+# Windows appear to preserve casing of the RDN and uppercase the other keys.
+
+
+class MatchRulesTestsBase(samba.tests.TestCase):
+ def setUp(self):
+ super().setUp()
+ self.lp = self.sambaopts.get_loadparm()
+ self.creds = self.credopts.get_credentials(self.lp)
+
+ self.ldb = SamDB(self.host, credentials=self.creds,
+ session_info=system_session(self.lp),
+ lp=self.lp)
+ self.base_dn = self.ldb.domain_dn()
+ self.ou_rdn = "OU=matchrulestest"
+ self.ou = self.ou_rdn + "," + self.base_dn
+ self.ou_users = "OU=users,%s" % self.ou
+ self.ou_groups = "OU=groups,%s" % self.ou
+ self.ou_computers = "OU=computers,%s" % self.ou
+
+ try:
+ self.ldb.delete(self.ou, ["tree_delete:1"])
+ except LdbError as e:
+ pass
+
+ # Add a organizational unit to create objects
+ self.ldb.add({
+ "dn": self.ou,
+ "objectclass": "organizationalUnit"})
+
+ self.addCleanup(self.ldb.delete, self.ou, controls=['tree_delete:0'])
+
+
+ # Add the following OU hierarchy and set otherWellKnownObjects,
+ # which has BinaryDN syntax:
+ #
+ # o1
+ # |--> o2
+ # | |--> o3
+ # | | |-->o4
+
+ self.ldb.add({
+ "dn": "OU=o1,%s" % self.ou,
+ "objectclass": "organizationalUnit"})
+ self.ldb.add({
+ "dn": "OU=o2,OU=o1,%s" % self.ou,
+ "objectclass": "organizationalUnit"})
+ self.ldb.add({
+ "dn": "OU=o3,OU=o2,OU=o1,%s" % self.ou,
+ "objectclass": "organizationalUnit"})
+ self.ldb.add({
+ "dn": "OU=o4,OU=o3,OU=o2,OU=o1,%s" % self.ou,
+ "objectclass": "organizationalUnit"})
+
+ m = Message()
+ m.dn = Dn(self.ldb, self.ou)
+ m["otherWellKnownObjects"] = MessageElement("B:32:00000000000000000000000000000001:OU=o1,%s" % self.ou,
+ FLAG_MOD_ADD, "otherWellKnownObjects")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "OU=o1,%s" % self.ou)
+ m["otherWellKnownObjects"] = MessageElement("B:32:00000000000000000000000000000002:OU=o2,OU=o1,%s" % self.ou,
+ FLAG_MOD_ADD, "otherWellKnownObjects")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "OU=o2,OU=o1,%s" % self.ou)
+ m["otherWellKnownObjects"] = MessageElement("B:32:00000000000000000000000000000003:OU=o3,OU=o2,OU=o1,%s" % self.ou,
+ FLAG_MOD_ADD, "otherWellKnownObjects")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "OU=o3,OU=o2,OU=o1,%s" % self.ou)
+ m["otherWellKnownObjects"] = MessageElement("B:32:00000000000000000000000000000004:OU=o4,OU=o3,OU=o2,OU=o1,%s" % self.ou,
+ FLAG_MOD_ADD, "otherWellKnownObjects")
+ self.ldb.modify(m)
+
+ # Create OU for users and groups
+ self.ldb.add({
+ "dn": self.ou_users,
+ "objectclass": "organizationalUnit"})
+ self.ldb.add({
+ "dn": self.ou_groups,
+ "objectclass": "organizationalUnit"})
+ self.ldb.add({
+ "dn": self.ou_computers,
+ "objectclass": "organizationalUnit"})
+
+ # Add four groups
+ self.ldb.add({
+ "dn": "cn=g1,%s" % self.ou_groups,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=g2,%s" % self.ou_groups,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=g4,%s" % self.ou_groups,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=g3,%s" % self.ou_groups,
+ "objectclass": "group"})
+
+ # Add four users
+ self.ldb.add({
+ "dn": "cn=u1,%s" % self.ou_users,
+ "objectclass": "user"})
+ self.ldb.add({
+ "dn": "cn=u2,%s" % self.ou_users,
+ "objectclass": "user"})
+ self.ldb.add({
+ "dn": "cn=u3,%s" % self.ou_users,
+ "objectclass": "user"})
+ self.ldb.add({
+ "dn": "cn=u4,%s" % self.ou_users,
+ "objectclass": "user"})
+
+ # Add computers to test Object(DN-Binary) syntax
+ self.ldb.add({
+ "dn": "cn=c1,%s" % self.ou_computers,
+ "objectclass": "computer",
+ "dNSHostName": "c1.%s" % self.lp.get("realm").lower(),
+ "servicePrincipalName": ["HOST/c1"],
+ "sAMAccountName": "c1$",
+ "userAccountControl": "83890178"})
+
+ self.ldb.add({
+ "dn": "cn=c2,%s" % self.ou_computers,
+ "objectclass": "computer",
+ "dNSHostName": "c2.%s" % self.lp.get("realm").lower(),
+ "servicePrincipalName": ["HOST/c2"],
+ "sAMAccountName": "c2$",
+ "userAccountControl": "83890178"})
+
+ self.ldb.add({
+ "dn": "cn=c3,%s" % self.ou_computers,
+ "objectclass": "computer",
+ "dNSHostName": "c3.%s" % self.lp.get("realm").lower(),
+ "servicePrincipalName": ["HOST/c3"],
+ "sAMAccountName": "c3$",
+ "userAccountControl": "83890178"})
+
+ # Create the following hierarchy:
+ # g4
+ # |--> u4
+ # |--> g3
+ # | |--> u3
+ # | |--> g2
+ # | | |--> u2
+ # | | |--> g1
+ # | | | |--> u1
+
+ # u1 member of g1
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g1,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=u1,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # u2 member of g2
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g2,%s" % self.ou_groups)
+ m["member"] = MessageElement("cn=u2,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # u3 member of g3
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=g3,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=u3,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # u4 member of g4
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=g4,%s" % self.ou_groups)
+ m["member"] = MessageElement("cn=u4,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # g3 member of g4
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g4,%s" % self.ou_groups)
+ m["member"] = MessageElement("cn=g3,%s" % self.ou_groups,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # g2 member of g3
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=g3,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=g2,%s" % self.ou_groups,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # g1 member of g2
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=g2,%s" % self.ou_groups)
+ m["member"] = MessageElement("cn=g1,%s" % self.ou_groups,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # Add a couple of ms-Exch-Configuration-Container to test forward-link
+ # attributes without backward link (addressBookRoots2)
+ # e1
+ # |--> e2
+ # | |--> c1
+ self.ldb.add({
+ "dn": "cn=e1,%s" % self.ou,
+ "objectclass": "msExchConfigurationContainer"})
+ self.ldb.add({
+ "dn": "cn=e2,%s" % self.ou,
+ "objectclass": "msExchConfigurationContainer"})
+
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=e2,%s" % self.ou)
+ m["e1"] = MessageElement("cn=c1,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "addressBookRoots2")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=e1,%s" % self.ou)
+ m["e1"] = MessageElement("cn=e2,%s" % self.ou,
+ FLAG_MOD_ADD, "addressBookRoots2")
+ self.ldb.modify(m)
+
+
+
+class MatchRulesTests(MatchRulesTestsBase):
+ def setUp(self):
+ self.sambaopts = sambaopts
+ self.credopts = credopts
+ self.host = host
+ super().setUp()
+
+ # The msDS-RevealedUsers is owned by system and cannot be modified
+ # directly. Set the schemaUpgradeInProgress flag as workaround
+ # and create this hierarchy:
+ # ou=computers
+ # |-> c1
+ # | |->c2
+ # | | |->u1
+
+ #
+ # While appropriate for this test, this is NOT a good practice
+ # in general. This is only done here because the alternative
+ # is to make a schema modification.
+ #
+ # IF/WHEN Samba protects this attribute better, this
+ # particular part of the test can be removed, as the same code
+ # is covered by the addressBookRoots2 case well enough.
+ #
+ m = Message()
+ m.dn = Dn(self.ldb, "")
+ m["e1"] = MessageElement("1", FLAG_MOD_REPLACE, "schemaUpgradeInProgress")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=c2,%s" % self.ou_computers)
+ m["e1"] = MessageElement("B:8:01010101:cn=c3,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "msDS-RevealedUsers")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "cn=c1,%s" % self.ou_computers)
+ m["e1"] = MessageElement("B:8:01010101:cn=c2,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "msDS-RevealedUsers")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "")
+ m["e1"] = MessageElement("0", FLAG_MOD_REPLACE, "schemaUpgradeInProgress")
+ self.ldb.modify(m)
+
+
+ def test_u1_member_of_g4(self):
+ # Search without transitive match must return 0 results
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=u1,%s" % self.ou_users,
+ scope=SCOPE_BASE,
+ expression="memberOf=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ # Search with transitive match must return 1 results
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search("cn=u1,%s" % self.ou_users,
+ scope=SCOPE_BASE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=u1,%s" % self.ou_users).lower())
+
+ def test_g1_member_of_g4(self):
+ # Search without transitive match must return 0 results
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=g1,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="memberOf=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ # Search with transitive match must return 1 results
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search("cn=g1,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g1,%s" % self.ou_groups).lower())
+
+ def test_u1_groups(self):
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g1,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g1,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ def test_u2_groups(self):
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g2,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ def test_u3_groups(self):
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g3,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ def test_u4_groups(self):
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_users,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ def test_extended_dn_u1(self):
+ res1 = self.ldb.search("cn=u1,%s" % self.ou_users,
+ scope=SCOPE_BASE,
+ expression="objectClass=*",
+ attrs=['objectSid', 'objectGUID'])
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("cn=u1,%s" % self.ou_users).lower())
+
+ sid = self.ldb.schema_format_value("objectSid", res1[0]["objectSid"][0]).decode('utf8')
+ guid = self.ldb.schema_format_value("objectGUID", res1[0]['objectGUID'][0]).decode('utf8')
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g1,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g1,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g1,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g1,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g1,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g1,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ def test_extended_dn_u2(self):
+ res1 = self.ldb.search("cn=u2,%s" % self.ou_users,
+ scope=SCOPE_BASE,
+ expression="objectClass=*",
+ attrs=['objectSid', 'objectGUID'])
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("cn=u2,%s" % self.ou_users).lower())
+
+ sid = self.ldb.schema_format_value("objectSid", res1[0]["objectSid"][0]).decode('utf8')
+ guid = self.ldb.schema_format_value("objectGUID", res1[0]['objectGUID'][0]).decode('utf8')
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g2,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g2,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g2,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ def test_extended_dn_u3(self):
+ res1 = self.ldb.search("cn=u3,%s" % self.ou_users,
+ scope=SCOPE_BASE,
+ expression="objectClass=*",
+ attrs=['objectSid', 'objectGUID'])
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("cn=u3,%s" % self.ou_users).lower())
+
+ sid = self.ldb.schema_format_value("objectSid", res1[0]["objectSid"][0]).decode('utf8')
+ guid = self.ldb.schema_format_value("objectGUID", res1[0]['objectGUID'][0]).decode('utf8')
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g3,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g3,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=g3,%s" % self.ou_groups).lower() in dn_list)
+ self.assertTrue(("CN=g4,%s" % self.ou_groups).lower() in dn_list)
+
+ def test_extended_dn_u4(self):
+ res1 = self.ldb.search("cn=u4,%s" % self.ou_users,
+ scope=SCOPE_BASE,
+ expression="objectClass=*",
+ attrs=['objectSid', 'objectGUID'])
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("cn=u4,%s" % self.ou_users).lower())
+
+ sid = self.ldb.schema_format_value("objectSid", res1[0]["objectSid"][0]).decode('utf8')
+ guid = self.ldb.schema_format_value("objectGUID", res1[0]['objectGUID'][0]).decode('utf8')
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<SID=%s>" % sid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=<GUID=%s>" % guid)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ def test_object_dn_binary(self):
+ res1 = self.ldb.search(self.ou_computers,
+ scope=SCOPE_SUBTREE,
+ expression="msDS-RevealedUsers=B:8:01010101:cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=c2,%s" % self.ou_computers).lower())
+
+ res1 = self.ldb.search(self.ou_computers,
+ scope=SCOPE_ONELEVEL,
+ expression="msDS-RevealedUsers=B:8:01010101:cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=c2,%s" % self.ou_computers).lower())
+
+ res1 = self.ldb.search(self.ou_computers,
+ scope=SCOPE_SUBTREE,
+ expression="msDS-RevealedUsers:1.2.840.113556.1.4.1941:=B:8:01010101:cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=c1,%s" % self.ou_computers).lower() in dn_list)
+ self.assertTrue(("CN=c2,%s" % self.ou_computers).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou_computers,
+ scope=SCOPE_ONELEVEL,
+ expression="msDS-RevealedUsers:1.2.840.113556.1.4.1941:=B:8:01010101:cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=c1,%s" % self.ou_computers).lower() in dn_list)
+ self.assertTrue(("CN=c2,%s" % self.ou_computers).lower() in dn_list)
+
+ def test_one_way_links(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="addressBookRoots2=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=e2,%s" % self.ou).lower())
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_ONELEVEL,
+ expression="addressBookRoots2=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=e2,%s" % self.ou).lower())
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="addressBookRoots2:1.2.840.113556.1.4.1941:=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=e1,%s" % self.ou).lower() in dn_list)
+ self.assertTrue(("CN=e2,%s" % self.ou).lower() in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_ONELEVEL,
+ expression="addressBookRoots2:1.2.840.113556.1.4.1941:=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn).lower() for res in res1]
+ self.assertTrue(("CN=e1,%s" % self.ou).lower() in dn_list)
+ self.assertTrue(("CN=e2,%s" % self.ou).lower() in dn_list)
+
+ def test_not_linked_attrs(self):
+ res1 = self.ldb.search(self.base_dn,
+ scope=SCOPE_BASE,
+ expression="wellKnownObjects=B:32:aa312825768811d1aded00c04fd8d5cd:CN=computers,%s" % self.base_dn)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), self.base_dn.lower())
+
+ def test_invalid_basedn(self):
+ res1 = self.ldb.search(self.base_dn,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=c1,ou=computers,ou=matchrulestest,%sXX" % self.base_dn)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.base_dn,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=XX,ou=computers,ou=matchrulestest,%s" % self.base_dn)
+ self.assertEqual(len(res1), 0)
+
+ def test_subtree(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="otherWellKnownObjects=B:32:00000000000000000000000000000004:OU=o4,OU=o3,OU=o2,OU=o1,%s" % self.ou)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("OU=o3,OU=o2,OU=o1,%s" % self.ou).lower())
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_ONELEVEL,
+ expression="otherWellKnownObjects=B:32:00000000000000000000000000000004:OU=o4,OU=o3,OU=o2,OU=o1,%s" % self.ou)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="otherWellKnownObjects:1.2.840.113556.1.4.1941:=B:32:00000000000000000000000000000004:OU=o4,OU=o3,OU=o2,OU=o1,%s" % self.ou)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_ONELEVEL,
+ expression="otherWellKnownObjects:1.2.840.113556.1.4.1941:=B:32:00000000000000000000000000000004:OU=o4,OU=o3,OU=o2,OU=o1,%s" % self.ou)
+ self.assertEqual(len(res1), 0)
+
+ def test_unknown_oid(self):
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:2.4.681.226012.2.8.3882:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:8.16.8720.1008448.8.32.15528:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.3.4:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ def test_nul_text(self):
+ self.assertRaises((ValueError,TypeError),
+ lambda: self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="\00member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users))
+ self.assertRaises((ValueError,TypeError),
+ lambda: self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840\00.113556.1.4.1941:=cn=u1,%s" % self.ou_users))
+ self.assertRaises((ValueError,TypeError),
+ lambda: self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1\00,%s" % self.ou_users))
+ self.assertRaises(LdbError,
+ lambda: self.ldb.search("cn=\00g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users))
+ self.assertRaises(LdbError,
+ lambda: self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:"))
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=")
+ self.assertEqual(len(res1), 0)
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member=")
+ self.assertEqual(len(res1), 0)
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=nonexistent")
+ self.assertEqual(len(res1), 0)
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member=nonexistent")
+ self.assertEqual(len(res1), 0)
+ self.assertRaises(LdbError,
+ lambda: self.ldb.search("cn=\00g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:cn=u1,%s" % self.ou_users))
+ self.assertRaises(LdbError,
+ lambda: self.ldb.search("cn=\00g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1"))
+ self.assertRaises(LdbError,
+ lambda: self.ldb.search("cn=\00g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn="))
+ self.assertRaises(LdbError,
+ lambda: self.ldb.search("cn=\00g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member::=cn=u1,%s" % self.ou_users))
+
+ def test_misc_matches(self):
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=g1,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=g2,%s" % self.ou_groups)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=g2,%s" % self.ou_groups)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=g1,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="memberOf=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="memberOf=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), ("CN=g3,%s" % self.ou_groups))
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="memberOf=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), ("CN=g3,%s" % self.ou_groups))
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_ONELEVEL,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou_groups,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+
+class MatchRuleConditionTests(samba.tests.TestCase):
+ def setUp(self):
+ super(MatchRuleConditionTests, self).setUp()
+ self.lp = sambaopts.get_loadparm()
+ self.creds = credopts.get_credentials(self.lp)
+
+ self.ldb = SamDB(host, credentials=self.creds,
+ session_info=system_session(self.lp),
+ lp=self.lp)
+ self.base_dn = self.ldb.domain_dn()
+ self.ou = "OU=matchruleconditiontests,%s" % self.base_dn
+ self.ou_users = "OU=users,%s" % self.ou
+ self.ou_groups = "OU=groups,%s" % self.ou
+ self.ou_computers = "OU=computers,%s" % self.ou
+
+ # Add a organizational unit to create objects
+ self.ldb.add({
+ "dn": self.ou,
+ "objectclass": "organizationalUnit"})
+
+ # Create users, groups, and computers
+ self.ldb.add({
+ "dn": self.ou_users,
+ "objectclass": "organizationalUnit"})
+ self.ldb.add({
+ "dn": self.ou_groups,
+ "objectclass": "organizationalUnit"})
+ self.ldb.add({
+ "dn": self.ou_computers,
+ "objectclass": "organizationalUnit"})
+
+ self.ldb.add({
+ "dn": "cn=g1,%s" % self.ou_groups,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=g2,%s" % self.ou_groups,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=g3,%s" % self.ou_groups,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=g4,%s" % self.ou_groups,
+ "objectclass": "group"})
+
+ self.ldb.add({
+ "dn": "cn=u1,%s" % self.ou_users,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=u2,%s" % self.ou_users,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=u3,%s" % self.ou_users,
+ "objectclass": "group"})
+ self.ldb.add({
+ "dn": "cn=u4,%s" % self.ou_users,
+ "objectclass": "group"})
+
+ self.ldb.add({
+ "dn": "cn=c1,%s" % self.ou_computers,
+ "objectclass": "user"})
+
+ self.ldb.add({
+ "dn": "cn=c2,%s" % self.ou_computers,
+ "objectclass": "user"})
+
+ self.ldb.add({
+ "dn": "cn=c3,%s" % self.ou_computers,
+ "objectclass": "user"})
+
+ self.ldb.add({
+ "dn": "cn=c4,%s" % self.ou_computers,
+ "objectclass": "user"})
+
+ # Assign groups according to the following structure:
+ # g1-->g2---->g3 --g4
+ # \ | / | / / |
+ # u1- >u2-- | u3<- | u4
+ # \ \ \ \ |
+ # c1* >c2 ->c3 c4
+ # *c1 is a member of u1, u2, u3, and u4
+
+ # u2 is a member of g1 and g2
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g1,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=u2,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g2,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=u2,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # g2 is a member of g1
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g1,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=g2,%s" % self.ou_groups,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # g3 is a member of g2
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g2,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=g3,%s" % self.ou_groups,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # u3 is a member of g3 and g4
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g3,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=u3,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g4,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=u3,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # u4 is a member of g4
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g4,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=u4,%s" % self.ou_users,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # c1 is a member of u1, u2, u3, and u4
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=u1,%s" % self.ou_users)
+ m["member"] = MessageElement("CN=c1,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=u2,%s" % self.ou_users)
+ m["member"] = MessageElement("CN=c1,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=u3,%s" % self.ou_users)
+ m["member"] = MessageElement("CN=c1,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=u4,%s" % self.ou_users)
+ m["member"] = MessageElement("CN=c1,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # c2 is a member of u1
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=u1,%s" % self.ou_users)
+ m["member"] = MessageElement("CN=c2,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # c3 is a member of u2 and g3
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=u2,%s" % self.ou_users)
+ m["member"] = MessageElement("CN=c3,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g3,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=c3,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ # c4 is a member of u4 and g4
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=u4,%s" % self.ou_users)
+ m["member"] = MessageElement("CN=c4,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ m = Message()
+ m.dn = Dn(self.ldb, "CN=g4,%s" % self.ou_groups)
+ m["member"] = MessageElement("CN=c4,%s" % self.ou_computers,
+ FLAG_MOD_ADD, "member")
+ self.ldb.modify(m)
+
+ def tearDown(self):
+ super(MatchRuleConditionTests, self).tearDown()
+ self.ldb.delete(self.ou, controls=['tree_delete:0'])
+
+ def test_g1_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 6)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g1,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ def test_g2_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=g2,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g2,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 5)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=g2,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=g1,%s" % self.ou_groups)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g2,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=g1,%s" % self.ou_groups)
+
+ def test_g3_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=g3,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g3,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=g3,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=g2,%s" % self.ou_groups)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g3,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+
+ def test_g4_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c4,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c4,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=g4,%s" % self.ou_groups)
+ self.assertEqual(len(res1), 0)
+
+ def test_u1_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c2,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c2,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ def test_u2_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u2,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+
+ def test_u3_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=c1,%s" % self.ou_computers)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=u3,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=c1,%s" % self.ou_computers)
+
+ def test_u4_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=g4,%s" % self.ou_groups)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=g4,%s" % self.ou_groups)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c4,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=u4,%s" % self.ou_users)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c4,%s" % self.ou_computers in dn_list)
+
+ def test_c1_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u1,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 8)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u1,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=c1,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ def test_c2_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=c2,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=u1,%s" % self.ou_users)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=c2,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=u1,%s" % self.ou_users)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=c2,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=c2,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ def test_c3_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 4)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=c3,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ def test_c4_members(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member=cn=c4,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=c4,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf=cn=c4,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression="memberOf:1.2.840.113556.1.4.1941:=cn=c4,%s" % self.ou_computers)
+ self.assertEqual(len(res1), 0)
+
+ def test_or_member_queries(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(member:1.2.840.113556.1.4.1941:=cn=c1,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=c2,%s))") % (
+ self.ou_computers, self.ou_computers))
+ self.assertEqual(len(res1), 8)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u1,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(member:1.2.840.113556.1.4.1941:=cn=c2,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=c3,%s))") % (
+ self.ou_computers, self.ou_computers))
+ self.assertEqual(len(res1), 5)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u1,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(member:1.2.840.113556.1.4.1941:=cn=c2,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=c4,%s))") % (
+ self.ou_computers, self.ou_computers))
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u1,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(member:1.2.840.113556.1.4.1941:=cn=c3,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=c4,%s))") % (
+ self.ou_computers, self.ou_computers))
+ self.assertEqual(len(res1), 6)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(member:1.2.840.113556.1.4.1941:=cn=u1,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=c4,%s))") % (
+ self.ou_users, self.ou_computers))
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g4,%s" % self.ou_groups in dn_list)
+
+ def test_and_member_queries(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(member:1.2.840.113556.1.4.1941:=cn=c1,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=c2,%s))") % (
+ self.ou_computers, self.ou_computers))
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn), "CN=u1,%s" % self.ou_users)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(member:1.2.840.113556.1.4.1941:=cn=c2,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=c3,%s))") % (
+ self.ou_computers, self.ou_computers))
+ self.assertEqual(len(res1), 0)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(member:1.2.840.113556.1.4.1941:=cn=c3,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=u3,%s))") % (
+ self.ou_computers, self.ou_users))
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=g1,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(member:1.2.840.113556.1.4.1941:=cn=c1,%s)"
+ "(member:1.2.840.113556.1.4.1941:=cn=u4,%s))") % (
+ self.ou_computers, self.ou_computers))
+ self.assertEqual(len(res1), 0)
+
+ def test_or_memberOf_queries(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g2,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 6)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g3,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 6)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 8)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g2,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c4,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(memberOf:1.2.840.113556.1.4.1941:=cn=g2,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g3,%s))") %
+ (self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 5)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(memberOf:1.2.840.113556.1.4.1941:=cn=g2,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 7)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c4,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(|(memberOf:1.2.840.113556.1.4.1941:=cn=g3,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 5)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u4,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c4,%s" % self.ou_computers in dn_list)
+
+ def test_and_memberOf_queries(self):
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g2,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 5)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u2,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=g3,%s" % self.ou_groups in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g3,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(memberOf:1.2.840.113556.1.4.1941:=cn=g2,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g3,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 3)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+ self.assertTrue("CN=c3,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(memberOf:1.2.840.113556.1.4.1941:=cn=g2,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(memberOf:1.2.840.113556.1.4.1941:=cn=g3,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=g4,%s))") % (
+ self.ou_groups, self.ou_groups))
+ self.assertEqual(len(res1), 2)
+ dn_list = [str(res.dn) for res in res1]
+ self.assertTrue("CN=u3,%s" % self.ou_users in dn_list)
+ self.assertTrue("CN=c1,%s" % self.ou_computers in dn_list)
+
+ res1 = self.ldb.search(self.ou,
+ scope=SCOPE_SUBTREE,
+ expression=("(&(memberOf:1.2.840.113556.1.4.1941:=cn=g1,%s)"
+ "(memberOf:1.2.840.113556.1.4.1941:=cn=c1,%s))") % (
+ self.ou_groups, self.ou_computers))
+ self.assertEqual(len(res1), 0)
+
+if __name__ == "__main__":
+
+ parser = optparse.OptionParser("match_rules.py [options] <host>")
+ sambaopts = options.SambaOptions(parser)
+ parser.add_option_group(sambaopts)
+ parser.add_option_group(options.VersionOptions(parser))
+
+ # use command line creds if available
+ credopts = options.CredentialsOptions(parser)
+ parser.add_option_group(credopts)
+ opts, args = parser.parse_args()
+ subunitopts = SubunitOptions(parser)
+ parser.add_option_group(subunitopts)
+
+ if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+ host = args[0]
+
+ if "://" not in host:
+ if os.path.isfile(host):
+ host = "tdb://%s" % host
+ else:
+ host = "ldap://%s" % host
+
+ TestProgram(module=__name__, opts=subunitopts)
diff --git a/lib/ldb-samba/tests/match_rules_remote.py b/lib/ldb-samba/tests/match_rules_remote.py
new file mode 100755
index 0000000..122231f
--- /dev/null
+++ b/lib/ldb-samba/tests/match_rules_remote.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+
+import optparse
+import sys
+import os
+import samba
+import samba.getopt as options
+
+from samba.tests.subunitrun import SubunitOptions, TestProgram
+
+from samba.samdb import SamDB
+from samba.auth import system_session
+from samba import sd_utils
+from samba.ndr import ndr_unpack
+from ldb import Message, MessageElement, Dn, LdbError
+from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
+from ldb import SCOPE_BASE, SCOPE_SUBTREE, SCOPE_ONELEVEL
+
+from match_rules import MatchRulesTestsBase
+
+
+class MatchRulesTestsUser(MatchRulesTestsBase):
+ def setUp(self):
+ self.sambaopts = sambaopts
+ self.credopts = credopts
+ self.host = host
+ super().setUp()
+ self.sd_utils = sd_utils.SDUtils(self.ldb)
+
+ self.user_pass = "samba123@"
+ self.match_test_user = "matchtestuser"
+ self.ldb.newuser(self.match_test_user,
+ self.user_pass,
+ userou=self.ou_rdn)
+ user_creds = self.insta_creds(template=self.creds,
+ username=self.match_test_user,
+ userpass=self.user_pass)
+ self.user_ldb = SamDB(host, credentials=user_creds, lp=self.lp)
+ token_res = self.user_ldb.search(scope=SCOPE_BASE,
+ base="",
+ attrs=["tokenGroups"])
+ self.user_sid = ndr_unpack(samba.dcerpc.security.dom_sid,
+ token_res[0]["tokenGroups"][0])
+
+ self.member_attr_guid = "bf9679c0-0de6-11d0-a285-00aa003049e2"
+
+ def test_with_denied_link(self):
+
+ # add an ACE that denies the user Read Property (RP) access to
+ # the member attr (which is similar to making the attribute
+ # confidential)
+ ace = "(OD;;RP;{0};;{1})".format(self.member_attr_guid,
+ self.user_sid)
+ g2_dn = Dn(self.ldb, "CN=g2,%s" % self.ou_groups)
+
+ # add the ACE that denies access to the attr under test
+ self.sd_utils.dacl_add_ace(g2_dn, ace)
+
+ # Search without transitive match must return 0 results
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+ # Search with transitive match must return 1 results
+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 1)
+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower())
+
+ # Search as a user match must return 0 results as the intermediate link can't be seen
+ res1 = self.user_ldb.search("cn=g4,%s" % self.ou_groups,
+ scope=SCOPE_BASE,
+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users)
+ self.assertEqual(len(res1), 0)
+
+
+
+parser = optparse.OptionParser("match_rules_remote.py [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+opts, args = parser.parse_args()
+subunitopts = SubunitOptions(parser)
+parser.add_option_group(subunitopts)
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+host = args[0]
+
+if "://" not in host:
+ if os.path.isfile(host):
+ host = "tdb://%s" % host
+ else:
+ host = "ldap://%s" % host
+
+TestProgram(module=__name__, opts=subunitopts)
diff --git a/lib/ldb-samba/wscript_build b/lib/ldb-samba/wscript_build
new file mode 100644
index 0000000..d02bc95
--- /dev/null
+++ b/lib/ldb-samba/wscript_build
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# LDBSAMBA gets included in the ldb build when we are building ldb_ildap
+# as a built-in module and this delutes the symbols in the ldb library with
+# the symbols of all of ldb_ildap's dependencies.
+
+bld.SAMBA_LIBRARY('ldbsamba',
+ source='ldif_handlers.c ldb_matching_rules.c',
+ autoproto='ldif_handlers_proto.h',
+ public_deps='ldb',
+ deps='samba-security ndr NDR_DRSBLOBS NDR_DNSP ldbwrap samdb-common SAMDB_SCHEMA tdb samba-errors',
+ private_library=True
+ )
+
+bld.SAMBA_SUBSYSTEM('ldbwrap',
+ source='ldb_wrap.c',
+ public_headers='ldb_wrap.h',
+ deps='ldb samba-util ldbsamba samba-hostconfig'
+ )
+
+pyparam_util = bld.pyembed_libname('pyparam_util')
+pyldb_util = bld.pyembed_libname('pyldb-util')
+pyauth = 'pyauth'
+bld.SAMBA_PYTHON('python_samba__ldb', 'pyldb.c',
+ deps='ldbsamba %s ldbwrap %s %s' % (pyparam_util, pyldb_util, pyauth),
+ realname='samba/_ldb.so')
+
+bld.SAMBA_MODULE('ldbsamba_extensions',
+ source='samba_extensions.c',
+ init_function='ldb_samba_extensions_init',
+ module_init_name='ldb_init_module',
+ subsystem='ldb',
+ deps='ldb ldbsamba CMDLINE_S4 gensec',
+ internal_module=False)
+
+
+# the s4-internal ldap backend
+bld.SAMBA_MODULE('ldb_ildap',
+ source='ldb_ildap.c',
+ init_function='ldb_ildap_init',
+ module_init_name='ldb_init_module',
+ deps='talloc cli-ldap samba-credentials auth_system_session',
+ internal_module=False,
+ subsystem='ldb')