source4 dsdb modules: Add new module "unique_object_sids"
authorGary Lockyer <gary@catalyst.net.nz>
Sun, 26 Nov 2017 22:09:49 +0000 (11:09 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Sat, 9 Dec 2017 23:47:29 +0000 (00:47 +0100)
New module that sets the LDB_FLAG_INTERNAL_UNIQUE_VALUE on all local
objectSIDS and ensure it is cleared for any foreign security principals.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13004

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
source4/dsdb/samdb/ldb_modules/tests/test_unique_object_sids.c [new file with mode: 0644]
source4/dsdb/samdb/ldb_modules/unique_object_sids.c [new file with mode: 0644]
source4/dsdb/samdb/ldb_modules/wscript_build
source4/dsdb/samdb/ldb_modules/wscript_build_server
source4/selftest/tests.py

diff --git a/source4/dsdb/samdb/ldb_modules/tests/test_unique_object_sids.c b/source4/dsdb/samdb/ldb_modules/tests/test_unique_object_sids.c
new file mode 100644 (file)
index 0000000..dfc6d49
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+   Unit tests for the unique objectSID code in unique_object_sids.c
+
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
+
+   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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <cmocka.h>
+
+int ldb_unique_object_sids_init(const char *version);
+#include "../unique_object_sids.c"
+
+#include "../libcli/security/dom_sid.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+#define TEST_BE "tdb"
+
+#define DOMAIN_SID  "S-1-5-21-2470180966-3899876309-2637894779"
+#define LOCAL_SID   "S-1-5-21-2470180966-3899876309-2637894779-1000"
+#define FOREIGN_SID "S-1-5-21-2470180966-3899876309-2637894778-1000"
+
+static struct ldb_request *last_request;
+
+/*
+ * ldb_next_request mock, records the request passed in last_request
+ * so it can be examined in the test cases.
+ */
+int ldb_next_request(
+       struct ldb_module *module,
+       struct ldb_request *request)
+{
+       last_request = request;
+       return ldb_module_done(request, NULL, NULL, LDB_SUCCESS);
+}
+
+/*
+ * Test context
+ */
+struct ldbtest_ctx {
+       struct tevent_context *ev;
+       struct ldb_context *ldb;
+       struct ldb_module *module;
+
+       const char *dbfile;
+       const char *lockfile;   /* lockfile is separate */
+
+       const char *dbpath;
+       struct dom_sid *domain_sid;
+};
+
+/*
+ * Remove any database files created by the tests
+ */
+static void unlink_old_db(struct ldbtest_ctx *test_ctx)
+{
+       int ret;
+
+       errno = 0;
+       ret = unlink(test_ctx->lockfile);
+       if (ret == -1 && errno != ENOENT) {
+               fail();
+       }
+
+       errno = 0;
+       ret = unlink(test_ctx->dbfile);
+       if (ret == -1 && errno != ENOENT) {
+               fail();
+       }
+}
+
+/*
+ * Empty module to signal the end of the module list
+ */
+static const struct ldb_module_ops eol_ops = {
+       .name              = "eol",
+       .search            = NULL,
+       .add               = NULL,
+       .modify            = NULL,
+       .del               = NULL,
+       .rename            = NULL,
+       .init_context      = NULL
+};
+
+/*
+ * Test set up
+ */
+static int setup(void **state)
+{
+       struct ldbtest_ctx *test_ctx    = NULL;
+       struct ldb_module *eol          = NULL;
+       int rc;
+
+       test_ctx = talloc_zero(NULL, struct ldbtest_ctx);
+       assert_non_null(test_ctx);
+
+       test_ctx->ev = tevent_context_init(test_ctx);
+       assert_non_null(test_ctx->ev);
+
+       test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev);
+       assert_non_null(test_ctx->ldb);
+
+       test_ctx->domain_sid = talloc_zero(test_ctx, struct dom_sid);
+       assert_non_null(test_ctx->domain_sid);
+       assert_true(string_to_sid(test_ctx->domain_sid, DOMAIN_SID));
+       ldb_set_opaque(test_ctx->ldb, "cache.domain_sid", test_ctx->domain_sid);
+
+        test_ctx->module = ldb_module_new(
+               test_ctx,
+               test_ctx->ldb,
+               "unique_object_sids",
+               &ldb_unique_object_sids_module_ops);
+       assert_non_null(test_ctx->module);
+       eol = ldb_module_new(test_ctx, test_ctx->ldb, "eol", &eol_ops);
+       assert_non_null(eol);
+       ldb_module_set_next(test_ctx->module, eol);
+
+       test_ctx->dbfile = talloc_strdup(test_ctx, "duptest.ldb");
+       assert_non_null(test_ctx->dbfile);
+
+       test_ctx->lockfile = talloc_asprintf(test_ctx, "%s-lock",
+                                            test_ctx->dbfile);
+       assert_non_null(test_ctx->lockfile);
+
+       test_ctx->dbpath = talloc_asprintf(test_ctx,
+                       TEST_BE"://%s", test_ctx->dbfile);
+       assert_non_null(test_ctx->dbpath);
+
+       unlink_old_db(test_ctx);
+
+       rc = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
+       assert_int_equal(rc, LDB_SUCCESS);
+
+       rc = unique_object_sids_init(test_ctx->module);
+       assert_int_equal(rc, LDB_SUCCESS);
+
+       *state = test_ctx;
+
+       last_request = NULL;
+       return 0;
+}
+
+/*
+ * Test clean up
+ */
+static int teardown(void **state)
+{
+       struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                       struct ldbtest_ctx);
+
+       unlink_old_db(test_ctx);
+       talloc_free(test_ctx);
+       return 0;
+}
+
+/*
+ * Add an objectSID in string form to the supplied message
+ *
+ *
+ */
+static void add_sid(
+       struct ldb_message *msg,
+       const char *sid_str)
+{
+       struct ldb_val v;
+       enum ndr_err_code ndr_err;
+       struct dom_sid *sid = NULL;
+
+       sid = talloc_zero(msg, struct dom_sid);
+       assert_non_null(sid);
+       assert_true(string_to_sid(sid, sid_str));
+       ndr_err = ndr_push_struct_blob(&v, msg, sid,
+                                      (ndr_push_flags_fn_t)ndr_push_dom_sid);
+       assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+       assert_int_equal(0, ldb_msg_add_value(msg, "objectSID", &v, NULL));
+}
+
+/*
+ * The object is in the current local domain so it should have
+ * DB_FLAG_INTERNAL_UNIQUE_VALUE set
+ */
+static void test_objectSID_in_domain(void **state)
+{
+       struct ldbtest_ctx *test_ctx =
+               talloc_get_type_abort(*state, struct ldbtest_ctx);
+       struct ldb_context *ldb                 = test_ctx->ldb;
+       struct ldb_message *msg                 = ldb_msg_new(test_ctx);
+       struct ldb_message_element *el          = NULL;
+       struct ldb_request *request             = NULL;
+       struct ldb_request *original_request    = NULL;
+       int rc;
+
+       msg->dn = ldb_dn_new(msg, ldb, "dc=test");
+       add_sid(msg, LOCAL_SID);
+
+       rc = ldb_build_add_req(
+               &request,
+               test_ctx->ldb,
+               test_ctx,
+               msg,
+               NULL,
+               NULL,
+               ldb_op_default_callback,
+               NULL);
+
+       assert_int_equal(rc, LDB_SUCCESS);
+       assert_non_null(request);
+       original_request = request;
+
+       rc = unique_object_sids_add(test_ctx->module, request);
+       assert_int_equal(rc, LDB_SUCCESS);
+
+       /*
+        * Check that a copy of the request was passed to the next module
+        * and not the original request
+        */
+       assert_ptr_not_equal(last_request, original_request);
+
+       /*
+        * Check the flag was set on the request passed to the next
+        * module
+        */
+       el = ldb_msg_find_element(last_request->op.add.message, "objectSID");
+       assert_non_null(el);
+       assert_true(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
+
+       /*
+        * Check the flag was not  set on the original request
+        */
+       el = ldb_msg_find_element(request->op.add.message, "objectSID");
+       assert_non_null(el);
+       assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
+
+}
+
+/*
+ * The object is not in the current local domain so it should NOT have
+ * DB_FLAG_INTERNAL_UNIQUE_VALUE set
+ */
+static void test_objectSID_not_in_domain(void **state)
+{
+       struct ldbtest_ctx *test_ctx =
+               talloc_get_type_abort(*state, struct ldbtest_ctx);
+       struct ldb_context *ldb                 = test_ctx->ldb;
+       struct ldb_message *msg                 = ldb_msg_new(test_ctx);
+       struct ldb_message_element *el          = NULL;
+       struct ldb_request *request             = NULL;
+       struct ldb_request *original_request    = NULL;
+       int rc;
+
+       msg->dn = ldb_dn_new(msg, ldb, "dc=test");
+       add_sid(msg, FOREIGN_SID);
+
+       rc = ldb_build_add_req(
+               &request,
+               test_ctx->ldb,
+               test_ctx,
+               msg,
+               NULL,
+               NULL,
+               ldb_op_default_callback,
+               NULL);
+
+       assert_int_equal(rc, LDB_SUCCESS);
+       assert_non_null(request);
+       original_request = request;
+
+       rc = unique_object_sids_add(test_ctx->module, request);
+       assert_int_equal(rc, LDB_SUCCESS);
+
+       /*
+        * Check that the original request was passed to the next module
+        * and not a copy
+        */
+       assert_ptr_equal(last_request, original_request);
+
+       /*
+        * Check that the flag was not set on the objectSID element
+        */
+       el = ldb_msg_find_element(msg, "objectSID");
+       assert_non_null(el);
+       assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
+}
+
+/*
+ * No objectSID on the record so it should pass through the module untouched
+ *
+ */
+static void test_no_objectSID(void **state)
+{
+       struct ldbtest_ctx *test_ctx =
+               talloc_get_type_abort(*state, struct ldbtest_ctx);
+       struct ldb_context *ldb                 = test_ctx->ldb;
+       struct ldb_message *msg                 = ldb_msg_new(test_ctx);
+       struct ldb_request *request             = NULL;
+       struct ldb_request *original_request    = NULL;
+       int rc;
+
+       msg->dn = ldb_dn_new(msg, ldb, "dc=test");
+       assert_int_equal(LDB_SUCCESS, ldb_msg_add_string(msg, "cn", "test"));
+
+       rc = ldb_build_add_req(
+               &request,
+               test_ctx->ldb,
+               test_ctx,
+               msg,
+               NULL,
+               NULL,
+               ldb_op_default_callback,
+               NULL);
+
+       assert_int_equal(rc, LDB_SUCCESS);
+       assert_non_null(request);
+       original_request = request;
+
+       rc = unique_object_sids_add(test_ctx->module, request);
+       assert_int_equal(rc, LDB_SUCCESS);
+
+       /*
+        * Check that the original request was passed to the next module
+        * and not a copy
+        */
+       assert_ptr_equal(last_request, original_request);
+
+}
+
+/*
+ * Attempt to modify an objectSID DSDB_CONTROL_REPLICATED_UPDATE_OID not set
+ * this should fail with LDB_ERR_UNWILLING_TO_PERFORM
+ */
+static void test_modify_of_objectSID_not_replicated(void **state)
+{
+       struct ldbtest_ctx *test_ctx =
+               talloc_get_type_abort(*state, struct ldbtest_ctx);
+       struct ldb_context *ldb                 = test_ctx->ldb;
+       struct ldb_message *msg                 = ldb_msg_new(test_ctx);
+       struct ldb_request *request             = NULL;
+       int rc;
+
+       msg->dn = ldb_dn_new(msg, ldb, "dc=test");
+       add_sid(msg, LOCAL_SID);
+
+       rc = ldb_build_mod_req(
+               &request,
+               test_ctx->ldb,
+               test_ctx,
+               msg,
+               NULL,
+               NULL,
+               ldb_op_default_callback,
+               NULL);
+
+       assert_int_equal(rc, LDB_SUCCESS);
+       assert_non_null(request);
+
+       rc = unique_object_sids_modify(test_ctx->module, request);
+
+       assert_int_equal(rc, LDB_ERR_UNWILLING_TO_PERFORM);
+}
+
+
+/*
+ * Attempt to modify an objectSID DSDB_CONTROL_REPLICATED_UPDATE_OID set
+ * this should succeed
+ */
+static void test_modify_of_objectSID_replicated(void **state)
+{
+       struct ldbtest_ctx *test_ctx =
+               talloc_get_type_abort(*state, struct ldbtest_ctx);
+       struct ldb_context *ldb                 = test_ctx->ldb;
+       struct ldb_message *msg                 = ldb_msg_new(test_ctx);
+       struct ldb_message_element *el          = NULL;
+       struct ldb_request *request             = NULL;
+       struct ldb_request *original_request    = NULL;
+       int rc;
+
+       msg->dn = ldb_dn_new(msg, ldb, "dc=test");
+       add_sid(msg, LOCAL_SID);
+
+       rc = ldb_build_mod_req(
+               &request,
+               test_ctx->ldb,
+               test_ctx,
+               msg,
+               NULL,
+               NULL,
+               ldb_op_default_callback,
+               NULL);
+       assert_int_equal(rc, LDB_SUCCESS);
+       assert_non_null(request);
+       original_request = request;
+
+       ldb_request_add_control(
+               request,
+               DSDB_CONTROL_REPLICATED_UPDATE_OID,
+               false,
+               NULL);
+
+       rc = unique_object_sids_modify(test_ctx->module, request);
+
+       assert_int_equal(rc, LDB_SUCCESS);
+
+       /*
+        * Check that a copy of the request was passed to the next module
+        * and not the original request
+        */
+       assert_ptr_not_equal(last_request, original_request);
+
+       /*
+        * Check the flag was set on the request passed to the next
+        * module
+        */
+       el = ldb_msg_find_element(last_request->op.add.message, "objectSID");
+       assert_non_null(el);
+       assert_true(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
+
+       /*
+        * Check the flag was not  set on the original request
+        */
+       el = ldb_msg_find_element(request->op.add.message, "objectSID");
+       assert_non_null(el);
+       assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
+
+}
+
+/*
+ * Test the a modify with no object SID is passed through correctly
+ *
+ */
+static void test_modify_no_objectSID(void **state)
+{
+       struct ldbtest_ctx *test_ctx =
+               talloc_get_type_abort(*state, struct ldbtest_ctx);
+       struct ldb_context *ldb                 = test_ctx->ldb;
+       struct ldb_message *msg                 = ldb_msg_new(test_ctx);
+       struct ldb_request *request             = NULL;
+       struct ldb_request *original_request    = NULL;
+       int rc;
+
+       msg->dn = ldb_dn_new(msg, ldb, "dc=test");
+       assert_int_equal(LDB_SUCCESS, ldb_msg_add_string(msg, "cn", "test"));
+
+       rc = ldb_build_mod_req(
+               &request,
+               test_ctx->ldb,
+               test_ctx,
+               msg,
+               NULL,
+               NULL,
+               ldb_op_default_callback,
+               NULL);
+
+       assert_int_equal(rc, LDB_SUCCESS);
+       assert_non_null(request);
+       original_request = request;
+
+       rc = unique_object_sids_modify(test_ctx->module, request);
+       assert_int_equal(rc, LDB_SUCCESS);
+
+       /*
+        * Check that the original request was passed to the next module
+        * and not a copy
+        */
+       assert_ptr_equal(last_request, original_request);
+
+}
+
+int main(void) {
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test_setup_teardown(
+                       test_objectSID_in_domain,
+                       setup,
+                       teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_objectSID_not_in_domain,
+                       setup,
+                       teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_no_objectSID,
+                       setup,
+                       teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_modify_no_objectSID,
+                       setup,
+                       teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_modify_of_objectSID_not_replicated,
+                       setup,
+                       teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_modify_of_objectSID_replicated,
+                       setup,
+                       teardown),
+       };
+
+       cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/unique_object_sids.c b/source4/dsdb/samdb/ldb_modules/unique_object_sids.c
new file mode 100644 (file)
index 0000000..f8427bc
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+   ldb database module to enforce unique local objectSIDs
+
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
+
+   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/>.
+*/
+
+/*
+
+   Duplicate ObjectSIDs are possible on foreign security principals and
+   replication conflict records.  However a duplicate objectSID within
+   the local domainSID is an error.
+
+   As the uniqueness requirement depends on the source domain it is not possible
+   to enforce this with a unique index.
+
+   This module sets the LDB_FLAG_FORCE_UNIQUE_INDEX for objectSIDs in the
+   local domain.
+*/
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/dom_sid.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+
+struct private_data {
+       const struct dom_sid *domain_sid;
+};
+
+
+/*
+ * Does the add request contain a local objectSID
+ */
+static bool message_contains_local_objectSID(
+       struct ldb_module *module,
+       const struct ldb_message *msg)
+{
+       struct dom_sid *objectSID       = NULL;
+
+       struct private_data *data =
+               talloc_get_type(
+                       ldb_module_get_private(module),
+                       struct private_data);
+
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       objectSID = samdb_result_dom_sid(frame, msg, "objectSID");
+       if (objectSID == NULL) {
+               TALLOC_FREE(frame);
+               return false;
+       }
+
+       /*
+        * data->domain_sid can be NULL but dom_sid_in_domain handles this
+        * case correctly. See unique_object_sids_init for more details.
+        */
+       if (!dom_sid_in_domain(data->domain_sid, objectSID)) {
+               TALLOC_FREE(frame);
+               return false;
+       }
+       TALLOC_FREE(frame);
+       return true;
+}
+
+static int flag_objectSID(
+       struct ldb_module *module,
+       struct ldb_request *req,
+       const struct ldb_message *msg,
+       struct ldb_message **new_msg)
+{
+       struct ldb_message_element *el  = NULL;
+
+       *new_msg = ldb_msg_copy_shallow(req, msg);
+       if (!*new_msg) {
+               return ldb_module_oom(module);
+       }
+
+       el = ldb_msg_find_element(*new_msg, "objectSID");
+       if (el == NULL) {
+               struct ldb_context *ldb = NULL;
+               ldb = ldb_module_get_ctx(module);
+               ldb_asprintf_errstring(
+                       ldb,
+                       "Unable to locate objectSID in copied request\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       el->flags |= LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX;
+       return LDB_SUCCESS;
+}
+
+/* add */
+static int unique_object_sids_add(
+       struct ldb_module *module,
+       struct ldb_request *req)
+{
+       const struct ldb_message *msg = req->op.add.message;
+       struct ldb_message *new_msg     = NULL;
+       struct ldb_request *new_req     = NULL;
+       struct ldb_context *ldb         = NULL;
+       int rc;
+
+       if (!message_contains_local_objectSID(module, msg)) {
+               /*
+                * Request does not contain a local objectSID so chain the
+                * next module
+                */
+               return ldb_next_request(module, req);
+       }
+
+       /*
+        * The add request contains an objectSID for the local domain
+        */
+
+       rc = flag_objectSID(module, req, msg, &new_msg);
+       if (rc != LDB_SUCCESS) {
+               return rc;
+       }
+
+       ldb = ldb_module_get_ctx(module);
+       rc = ldb_build_add_req(
+               &new_req,
+               ldb,
+               req,
+               new_msg,
+               req->controls,
+               req,
+               dsdb_next_callback,
+               req);
+       if (rc != LDB_SUCCESS) {
+               return rc;
+       }
+
+       return ldb_next_request(module, new_req);
+}
+
+/* modify */
+static int unique_object_sids_modify(
+       struct ldb_module *module,
+       struct ldb_request *req)
+{
+
+       const struct ldb_message *msg   = req->op.mod.message;
+       struct ldb_message *new_msg     = NULL;
+       struct ldb_request *new_req     = NULL;
+       struct ldb_context *ldb         = NULL;
+       int rc;
+
+       if (!message_contains_local_objectSID(module, msg)) {
+               /*
+                * Request does not contain a local objectSID so chain the
+                * next module
+                */
+               return ldb_next_request(module, req);
+       }
+
+       ldb = ldb_module_get_ctx(module);
+
+       /*
+        * If DSDB_CONTROL_REPLICATED_UPDATE_OID replicated is set we know
+        * that the modify request is well formed and objectSID only appears
+        * once.
+        *
+        * Enforcing this assumption simplifies the subsequent code.
+        *
+        */
+       if(!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
+               ldb_asprintf_errstring(
+                       ldb,
+                       "Modify of %s rejected, "
+                       "as it is modifying an objectSID\n",
+                       ldb_dn_get_linearized(msg->dn));
+               return LDB_ERR_UNWILLING_TO_PERFORM;
+       }
+
+
+       rc = flag_objectSID(module, req, msg, &new_msg);
+       if (rc != LDB_SUCCESS) {
+               return rc;
+       }
+
+       ldb = ldb_module_get_ctx(module);
+       rc = ldb_build_mod_req(
+               &new_req,
+               ldb,
+               req,
+               new_msg,
+               req->controls,
+               req,
+               dsdb_next_callback,
+               req);
+       if (rc != LDB_SUCCESS) {
+               return rc;
+       }
+
+       return ldb_next_request(module, new_req);
+}
+
+/* init */
+static int unique_object_sids_init(
+       struct ldb_module *module)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct private_data *data = NULL;
+       int ret;
+
+       ret = ldb_next_init(module);
+
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       data = talloc_zero(module, struct private_data);
+       if (!data) {
+               return ldb_module_oom(module);
+       }
+
+       data->domain_sid = samdb_domain_sid(ldb);
+       if (data->domain_sid == NULL) {
+               /*
+                * Unable to determine the domainSID, this normally occurs
+                * when provisioning. As there is no easy way to detect
+                * that we are provisioning.  We currently just log this as a
+                * warning.
+                */
+               ldb_debug(
+                       ldb,
+                       LDB_DEBUG_WARNING,
+                       "Unable to determine the DomainSID, "
+                       "can not enforce uniqueness constraint on local "
+                       "domainSIDs\n");
+       }
+
+       ldb_module_set_private(module, data);
+
+       return LDB_SUCCESS;
+}
+
+static const struct ldb_module_ops ldb_unique_object_sids_module_ops = {
+       .name              = "unique_object_sids",
+       .init_context      = unique_object_sids_init,
+       .add               = unique_object_sids_add,
+       .modify            = unique_object_sids_modify,
+};
+
+int ldb_unique_object_sids_init(const char *version)
+{
+       LDB_MODULE_CHECK_VERSION(version);
+       return ldb_register_module(&ldb_unique_object_sids_module_ops);
+}
index 0df5fe03fff65fc9664b74f6eb7ce9c66f97cf40..b0afcf9d8269f9b18154d09af6696e1c4ce0f4ce 100644 (file)
@@ -18,5 +18,16 @@ bld.SAMBA_SUBSYSTEM('DSDB_MODULE_HELPER_RIDALLOC',
        deps='MESSAGING',
        )
 
+# Build the cmocka unit tests
+bld.SAMBA_BINARY('test_unique_object_sids',
+        source='tests/test_unique_object_sids.c',
+        deps='''
+            talloc
+            samdb
+            cmocka
+            DSDB_MODULE_HELPERS
+        ''',
+        install=False)
+
 if bld.AD_DC_BUILD_IS_ENABLED():
     bld.PROCESS_SEPARATE_RULE("server")
index 41b3fe70b3729f99c3f79d8fb100d24ef5efd544..4aac7f2f9f35a826496187a425386465dbbb0696 100644 (file)
@@ -400,3 +400,12 @@ bld.SAMBA_MODULE('ldb_vlv',
        deps='samdb-common',
        subsystem='ldb'
        )
+
+bld.SAMBA_MODULE('ldb_unique_object_sids',
+       'unique_object_sids.c',
+       init_function='ldb_unique_object_sids_init',
+       module_init_name='ldb_init_module',
+       internal_module=False,
+       deps='samdb-common DSDB_MODULE_HELPERS',
+       subsystem='ldb'
+       )
index c132ac90b544d911716d31897cf09e69d5628de9..a9ed46f5c8ec7cf49b0ecac836a51919f780fd2b 100755 (executable)
@@ -990,3 +990,8 @@ for env in ['vampire_dc', 'promoted_dc', 'rodc']:
 # check the databases are all OK. PLEASE LEAVE THIS AS THE LAST TEST
 for env in ["ad_dc_ntvfs", "ad_dc", "fl2000dc", "fl2003dc", "fl2008r2dc", 'vampire_dc', 'promoted_dc']:
     plantestsuite("samba4.blackbox.dbcheck(%s)" % env, env + ":local" , ["PYTHON=%s" % python, os.path.join(bbdir, "dbcheck.sh"), '$PREFIX/provision', configuration])
+
+# cmocka tests not requiring a specific encironment
+#
+plantestsuite("samba4.dsdb.samdb.ldb_modules.unique_object_sids" , "none",
+              [os.path.join(bindir(), "test_unique_object_sids")])