summaryrefslogtreecommitdiff
path: root/source4/dsdb
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dsdb')
-rw-r--r--source4/dsdb/samdb/ldb_modules/config.mk16
-rw-r--r--source4/dsdb/samdb/ldb_modules/local_password.c888
-rw-r--r--source4/dsdb/samdb/ldb_modules/password_modules.h3
3 files changed, 904 insertions, 3 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/config.mk b/source4/dsdb/samdb/ldb_modules/config.mk
index ce4f12bcfe..b9fc15fc02 100644
--- a/source4/dsdb/samdb/ldb_modules/config.mk
+++ b/source4/dsdb/samdb/ldb_modules/config.mk
@@ -65,11 +65,21 @@ OBJ_FILES = password_hash.o
PUBLIC_DEPENDENCIES = HEIMDAL_KRB5
PRIVATE_DEPENDENCIES = HEIMDAL_HDB_KEYS
#
-# End MODULE ldb_rootdse
+# End MODULE ldb_password_hash
+################################################
+
################################################
+# Start MODULE ldb_password_sync
+[MODULE::ldb_password_sync]
+SUBSYSTEM = ldb
+INIT_FUNCTION = password_sync_module_init
+OBJ_FILES = password_sync.o
+# End MODULE ldb_password_sync
################################################
-# Start MODULE ldb_cludge_acl
+
+################################################
+# Start MODULE ldb_kludge_acl
[MODULE::ldb_kludge_acl]
SUBSYSTEM = ldb
INIT_FUNCTION = ldb_kludge_acl_init
@@ -78,7 +88,7 @@ OBJ_FILES = \
PUBLIC_DEPENDENCIES = \
LIBSECURITY
#
-# End MODULE ldb_rootdse
+# End MODULE ldb_kludge_acl
################################################
################################################
diff --git a/source4/dsdb/samdb/ldb_modules/local_password.c b/source4/dsdb/samdb/ldb_modules/local_password.c
new file mode 100644
index 0000000000..a570e7c83d
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/local_password.c
@@ -0,0 +1,888 @@
+/*
+ ldb database module
+
+ Copyright (C) Simo Sorce 2004-2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb local_password module
+ *
+ * Description: correctly update hash values based on changes to sambaPassword and friends
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/ndr/libndr.h"
+#include "dsdb/samdb/ldb_modules/password_modules.h"
+
+#define PASSWORD_GUID_ATTR "masterGUID"
+
+/* This module maintains a local password database, seperate from the main LDAP server.
+
+ This allows the password database to be syncronised in a multi-master
+ fashion, seperate to the more difficult concerns of the main
+ database. (With passwords, the last writer always wins)
+
+ Each incoming add/modify is split into a remote, and a local request, done in that order.
+
+ We maintain a list of attributes that are kept locally:
+ */
+
+static const char * const password_attrs[] = {
+ "sambaPassword",
+ "krb5Key",
+ "ntPwdHash",
+ "lmPwdHash",
+ "sambaLMPwdHistory",
+ "sambaNTPwdHistory",
+ "msDS-KeyVersionNumber",
+ "pwdLastSet"
+};
+
+/* And we merge them back into search requests when asked to do so */
+
+struct lpdb_async_context {
+
+ enum lpdb_type {LPDB_ADD, LPDB_MOD, LPDB_SEARCH} type;
+ enum lpdb_step {LPDB_ADD_REMOTE, LPDB_MOD_REMOTE, LPDB_MOD_SEARCH_SELF, LPDB_LOCAL, LPDB_SEARCH_REMOTE} step;
+
+ struct ldb_module *module;
+ struct ldb_request *orig_req;
+ struct ldb_request *remote_req;
+ struct ldb_request *search_req;
+ struct ldb_request *local_req;
+
+ struct ldb_message *local_message;
+
+ BOOL added_objectGUID;
+ BOOL added_objectClass;
+
+ struct ldb_async_result *search_res;
+};
+
+struct lpdb_async_local_search_context {
+ struct lpdb_async_context *ac;
+ struct ldb_async_result *remote_res;
+ struct ldb_async_result *local_res;
+};
+
+static struct ldb_async_handle *lpdb_init_handle(struct ldb_request *req, struct ldb_module *module, enum lpdb_type type)
+{
+ struct lpdb_async_context *ac;
+ struct ldb_async_handle *h;
+
+ h = talloc_zero(req, struct ldb_async_handle);
+ if (h == NULL) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct lpdb_async_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->type = type;
+ ac->module = module;
+ ac->orig_req = req;
+
+ return h;
+}
+
+/* Add a record, splitting password attributes from the user's main
+ * record */
+
+static int local_password_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_async_handle *h;
+ struct lpdb_async_context *ac;
+ struct ldb_message *remote_message;
+ struct ldb_message *local_message;
+ struct GUID objectGUID;
+ int i;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_add\n");
+
+ if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is manipulating the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(module->ldb,
+ ldb_dn_explode(req, LOCAL_BASE),
+ req->op.add.message->dn) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
+ break;
+ }
+ }
+
+ /* It didn't match any of our password attributes, go on */
+ if (i == ARRAY_SIZE(password_attrs)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* TODO: remove this when sambaPassword will be in schema */
+ if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(module, "Cannot relocate a password on entry: %s, does not have objectClass 'person'",
+ ldb_dn_linearize(req, req->op.add.message->dn)));
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* From here, we assume we have password attributes to split off */
+ h = lpdb_init_handle(req, module, LPDB_ADD);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct lpdb_async_context);
+
+ ac->orig_req = req;
+
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *(ac->remote_req) = *(ac->orig_req);
+
+ remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.add.message);
+ if (remote_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove any password attributes from the remote message */
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ ldb_msg_remove_attr(remote_message, password_attrs[i]);
+ }
+
+ ac->remote_req->op.add.message = remote_message;
+
+ ac->remote_req->async.context = NULL;
+ ac->remote_req->async.callback = NULL;
+
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *(ac->local_req) = *(ac->orig_req);
+ local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.add.message);
+ if (local_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove anything seen in the remote message from the local
+ * message (leaving only password attributes) */
+ for (i=0;i<ac->remote_req->op.add.message->num_elements;i++) {
+ ldb_msg_remove_attr(local_message, ac->remote_req->op.add.message->elements[i].name);
+ }
+
+ /* We must have an objectGUID already, or we don't know where
+ * to add the password. This may be changed to an 'add and
+ * search', to allow the directory to create the objectGUID */
+ if (ldb_msg_find_ldb_val(ac->orig_req->op.add.message, "objectGUID") == NULL) {
+ ldb_set_errstring(module->ldb,
+ talloc_asprintf(req,
+ "no objectGUID found in search: local_password module must be configured below objectGUID module!\n"));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ /* Find the objectGUID to use as the key */
+ objectGUID = samdb_result_guid(ac->orig_req->op.add.message, "objectGUID");
+
+ local_message->dn = ldb_dn_string_compose(local_message,
+ ldb_dn_explode(local_message, LOCAL_BASE),
+ PASSWORD_GUID_ATTR "=%s", GUID_string(local_message, &objectGUID));
+
+ ac->local_req->op.add.message = local_message;
+
+ ac->local_req->async.context = NULL;
+ ac->local_req->async.callback = NULL;
+
+ ac->step = LPDB_ADD_REMOTE;
+
+ /* Return our own handle do deal with this call */
+ req->async.handle = h;
+
+ return ldb_next_request(module, ac->remote_req);
+}
+
+/* After adding the remote entry, add the local one */
+static int local_password_add_local(struct ldb_async_handle *h) {
+
+ struct lpdb_async_context *ac;
+ ac = talloc_get_type(h->private_data, struct lpdb_async_context);
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = LPDB_LOCAL;
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ /* perform the local add */
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+static int local_password_mod_search_self(struct ldb_async_handle *h);
+
+static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_async_handle *h;
+ struct lpdb_async_context *ac;
+ struct ldb_message *remote_message;
+ struct ldb_message *local_message;
+ int i;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is manipulating the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(module->ldb,
+ ldb_dn_explode(req, LOCAL_BASE),
+ req->op.mod.message->dn) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
+ break;
+ }
+ }
+
+ /* It didn't match any of our password attributes, then we have nothing to do here */
+ if (i == ARRAY_SIZE(password_attrs)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* From here, we assume we have password attributes to split off */
+ h = lpdb_init_handle(req, module, LPDB_MOD);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct lpdb_async_context);
+
+ ac->orig_req = req;
+
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *(ac->remote_req) = *(ac->orig_req);
+ remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.mod.message);
+ if (remote_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove any password attributes from the remote message */
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ ldb_msg_remove_attr(remote_message, password_attrs[i]);
+ }
+
+ ac->remote_req->op.mod.message = remote_message;
+
+ ac->remote_req->async.context = NULL;
+ ac->remote_req->async.callback = NULL;
+
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *(ac->local_req) = *(ac->orig_req);
+ local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.mod.message);
+ if (local_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove anything seen in the remote message from the local
+ * message (leaving only password attributes) */
+ for (i=0;i<ac->remote_req->op.mod.message->num_elements;i++) {
+ ldb_msg_remove_attr(local_message, ac->remote_req->op.mod.message->elements[i].name);
+ }
+
+ ac->local_req->op.mod.message = local_message;
+ ac->local_message = local_message;
+
+ ac->local_req->async.context = NULL;
+ ac->local_req->async.callback = NULL;
+
+ ac->step = LPDB_MOD_REMOTE;
+
+ /* Return our own handle do deal with this call */
+ req->async.handle = h;
+
+ return ldb_next_request(module, ac->remote_req);
+}
+
+/* Called when we search for our oen entry. Stores the one entry we
+ * expect (as it is a base search) on the context pointer */
+static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
+{
+ struct lpdb_async_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(context, struct lpdb_async_context);
+
+ /* we are interested only in the single reply (base search) we receive here */
+ if (ares->type == LDB_REPLY_ENTRY) {
+ if (ac->search_res != NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_res = talloc_steal(ac, ares);
+ } else {
+ talloc_free(ares);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* On a modify, we don't have the objectGUID handy, so we need to
+ * search our DN for it */
+static int local_password_mod_search_self(struct ldb_async_handle *h) {
+
+ struct lpdb_async_context *ac;
+ static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
+
+ ac = talloc_get_type(h->private_data, struct lpdb_async_context);
+
+ /* prepare the search operation */
+ ac->search_req = talloc_zero(ac, struct ldb_request);
+ if (ac->search_req == NULL) {
+ ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_req->operation = LDB_SEARCH;
+ ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
+ ac->search_req->op.search.scope = LDB_SCOPE_BASE;
+ ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
+ if (ac->search_req->op.search.tree == NULL) {
+ ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->search_req->op.search.attrs = attrs;
+ ac->search_req->controls = NULL;
+ ac->search_req->async.context = ac;
+ ac->search_req->async.callback = get_self_callback;
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
+
+ ac->step = LPDB_MOD_SEARCH_SELF;
+
+ return ldb_next_request(ac->module, ac->search_req);
+}
+
+/* After we find out the objectGUID for the entry, modify the local
+ * password database as required */
+static int local_password_mod_local(struct ldb_async_handle *h) {
+
+ struct lpdb_async_context *ac;
+ struct GUID objectGUID;
+ ac = talloc_get_type(h->private_data, struct lpdb_async_context);
+
+ /* if it is not an entry of type person this is an error */
+ /* TODO: remove this when sambaPassword will be in schema */
+ if (!ac->search_res) {
+ ldb_set_errstring(ac->module->ldb,
+ talloc_asprintf(ac,
+ "entry just modified (%s) not found!",
+ ldb_dn_linearize(ac, ac->remote_req->op.mod.message->dn)));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (!ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "person")) {
+ /* Not relevent to us */
+ return LDB_SUCCESS;
+ }
+
+ if (ldb_msg_find_ldb_val(ac->search_res->message, "objectGUID") == NULL) {
+ ldb_set_errstring(ac->module->ldb,
+ talloc_asprintf(ac,
+ "no objectGUID found in search: local_password module must be configured below objectGUID module!\n"));
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ objectGUID = samdb_result_guid(ac->search_res->message, "objectGUID");
+
+ ac->local_message->dn = ldb_dn_string_compose(ac,
+ ldb_dn_explode(ac, LOCAL_BASE),
+ PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID));
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = LPDB_LOCAL;
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ /* perform the local update */
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+
+static int lpdb_local_search_async_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
+{
+ struct lpdb_async_local_search_context *local_context;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ local_context = talloc_get_type(context, struct lpdb_async_local_search_context);
+
+ /* we are interested only in the single reply (base search) we receive here */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ {
+ int i;
+ if (local_context->local_res != NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results to base search for password entry!"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ local_context->local_res = ares;
+
+ /* Make sure never to return the internal key attribute to the caller */
+ ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
+
+ talloc_steal(local_context->remote_res->message->elements, ares->message->elements);
+ for (i=0; i < ares->message->num_elements; i++) {
+ struct ldb_message_element *el;
+
+ el = ldb_msg_find_element(local_context->remote_res->message,
+ ares->message->elements[i].name);
+ if (!el) {
+ if (ldb_msg_add_empty(local_context->remote_res->message,
+ ares->message->elements[i].name, 0) != LDB_SUCCESS) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ el = ldb_msg_find_element(local_context->remote_res->message,
+ ares->message->elements[i].name);
+ *el = ares->message->elements[i];
+ }
+ }
+ return local_context->ac->orig_req->async.callback(ldb,
+ local_context->ac->orig_req->async.context,
+ local_context->remote_res);
+ }
+ case LDB_REPLY_DONE:
+ {
+ /* Fire off the callback if there was no local entry, so we get the rest returned */
+ if (local_context->local_res == NULL) {
+ return local_context->ac->orig_req->async.callback(ldb,
+ local_context->ac->orig_req->async.context,
+ local_context->remote_res);
+ }
+ }
+ default:
+ {
+ talloc_free(ares);
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "Unexpected result type in base search for password entry!"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+}
+
+/* For each entry returned in a remote search, do a local base search,
+ * based on the objectGUID we asked for as an additional attribute */
+static int lpdb_remote_search_async_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
+{
+ struct lpdb_async_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct lpdb_async_context);
+
+ if (ares->type == LDB_REPLY_ENTRY) {
+ struct ldb_request *req;
+ struct lpdb_async_local_search_context *local_context;
+ struct GUID objectGUID;
+
+ /* No point searching further if it's not a 'person' entry */
+ if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
+
+ /* Make sure to remove anything we added */
+ if (ac->added_objectGUID) {
+ ldb_msg_remove_attr(ares->message, "objectGUID");
+ }
+
+ if (ac->added_objectClass) {
+ ldb_msg_remove_attr(ares->message, "objectClass");
+ }
+
+ return ac->orig_req->async.callback(ldb, ac->orig_req->async.context, ares);
+ }
+
+ if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
+ ldb_set_errstring(ac->module->ldb,
+ talloc_asprintf(ac,
+ "no objectGUID found in search: local_password module must be configured below objectGUID module!\n"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ objectGUID = samdb_result_guid(ares->message, "objectGUID");
+
+ if (ac->added_objectGUID) {
+ ldb_msg_remove_attr(ares->message, "objectGUID");
+ }
+
+ if (ac->added_objectClass) {
+ ldb_msg_remove_attr(ares->message, "objectClass");
+ }
+
+ req = talloc_zero(ac, struct ldb_request);
+ if (!req) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ local_context = talloc(ac, struct lpdb_async_local_search_context);
+ if (!local_context) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ local_context->ac = ac;
+ local_context->remote_res = ares;
+ local_context->local_res = NULL;
+
+ req->op.search.base = ldb_dn_string_compose(ac,
+ ldb_dn_explode(ac, LOCAL_BASE),
+ PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID));
+ if (!req->op.search.base) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ req->operation = LDB_SEARCH;
+ req->op.search.scope = LDB_SCOPE_BASE;
+ req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
+ if (req->op.search.tree == NULL) {
+ ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "out of memory"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ req->op.search.attrs = ac->orig_req->op.search.attrs;
+ req->controls = NULL;
+ req->async.context = ac;
+ req->async.callback = get_self_callback;
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req);
+
+ req->async.context = local_context;
+ req->async.callback = lpdb_local_search_async_callback;
+
+ return ldb_next_request(ac->module, req);
+ } else {
+ return ac->orig_req->async.callback(ldb, ac->orig_req->async.context, ares);
+ }
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Search for passwords and other attributes. The passwords are
+ * local, but the other attributes are remote, and we need to glue the
+ * two search spaces back togeather */
+
+static int local_password_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_async_handle *h;
+ struct lpdb_async_context *ac;
+ int i;
+ int ret;
+ const char * const *search_attrs = NULL;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_search\n");
+
+ if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is searching for the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(module->ldb,
+ ldb_dn_explode(req, LOCAL_BASE),
+ req->op.search.base) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
+ break;
+ }
+ }
+
+ /* It didn't match any of our password attributes, go on */
+ if (i == ARRAY_SIZE(password_attrs)) {
+ return ldb_next_request(module, req);
+ }
+ }
+
+ h = lpdb_init_handle(req, module, LPDB_SEARCH);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(h->private_data, struct lpdb_async_context);
+
+ ac->orig_req = req;
+
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
+ *(ac->remote_req) = *(ac->orig_req);
+
+ /* Return our own handle do deal with this call */
+ ac->remote_req->async.handle = h;
+
+ ac->remote_req->async.context = ac;
+ ac->remote_req->async.callback = lpdb_remote_search_async_callback;
+
+ if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
+ if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
+ search_attrs = ldb_attr_list_copy_add(req, req->op.search.attrs, "objectGUID");
+ ac->added_objectGUID = True;
+ if (!search_attrs) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ } else {
+ search_attrs = req->op.search.attrs;
+ }
+ if (!ldb_attr_in_list(search_attrs, "objectClass")) {
+ search_attrs = ldb_attr_list_copy_add(req, search_attrs, "objectClass");
+ ac->added_objectClass = True;
+ if (!search_attrs) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ } else {
+ search_attrs = req->op.search.attrs;
+ }
+
+ ac->remote_req->op.search.attrs = search_attrs;
+
+ ldb_set_timeout_from_prev_req(module->ldb, ac->orig_req, ac->remote_req);
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = LPDB_SEARCH_REMOTE;
+
+ /* perform the search */
+ ret = ldb_next_request(module, ac->remote_req);
+
+ if (ret == LDB_SUCCESS) {
+ req->async.handle = ac->remote_req->async.handle;
+ }
+
+ return ret;
+}
+
+static int lpdb_async_wait(struct ldb_async_handle *handle) {
+ struct lpdb_async_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct lpdb_async_context);
+
+ switch (ac->step) {
+ case LPDB_ADD_REMOTE:
+ ret = ldb_async_wait(ac->remote_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->remote_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->remote_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->remote_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* original request done, go on */
+ return local_password_add_local(handle);
+
+ case LPDB_MOD_REMOTE:
+ ret = ldb_async_wait(ac->remote_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->remote_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->remote_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->remote_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* original request done, go on */
+ return local_password_mod_search_self(handle);
+
+ case LPDB_MOD_SEARCH_SELF:
+ ret = ldb_async_wait(ac->search_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->search_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->search_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* original request done, go on */
+ return local_password_mod_local(handle);
+
+ case LPDB_LOCAL:
+ ret = ldb_async_wait(ac->local_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->local_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->local_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->local_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ break;
+
+ case LPDB_SEARCH_REMOTE:
+ ret = ldb_async_wait(ac->remote_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->remote_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->remote_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->remote_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ break;
+
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+static int lpdb_async_wait_all(struct ldb_async_handle *handle) {
+
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = lpdb_async_wait(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+static int local_password_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return lpdb_async_wait_all(handle);
+ } else {
+ return lpdb_async_wait(handle);
+ }
+}
+
+static const struct ldb_module_ops local_password_ops = {
+ .name = "local_password",
+ .add = local_password_add,
+ .modify = local_password_modify,
+ .search = local_password_search,
+ .async_wait = local_password_async_wait
+};
+
+
+int local_password_module_init(void)
+{
+ return ldb_register_module(&local_password_ops);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/password_modules.h b/source4/dsdb/samdb/ldb_modules/password_modules.h
new file mode 100644
index 0000000000..40d0144416
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/password_modules.h
@@ -0,0 +1,3 @@
+/* We store these passwords under this base DN: */
+
+#define LOCAL_BASE "cn=Passwords"