From 077180191197e257f97dcea5eabb4e226b87e945 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 28 Jul 2006 06:29:09 +0000 Subject: r17287: Add the local_password module to the tree, so it doesn't get lost in ldb API changes. Andrew Bartlett (This used to be commit 44806c67dbabe2952fe355de76d7fa51f772775f) --- source4/dsdb/samdb/ldb_modules/local_password.c | 888 ++++++++++++++++++++++++ 1 file changed, 888 insertions(+) create mode 100644 source4/dsdb/samdb/ldb_modules/local_password.c (limited to 'source4/dsdb/samdb/ldb_modules/local_password.c') 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 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;iremote_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;iremote_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); +} -- cgit