4 Copyright (C) Simo Sorce 2004-2006
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * Component: ldb local_password module
27 * Description: correctly update hash values based on changes to sambaPassword and friends
29 * Author: Andrew Bartlett
33 #include "libcli/ldap/ldap.h"
34 #include "ldb/include/ldb_errors.h"
35 #include "ldb/include/ldb_private.h"
36 #include "dsdb/samdb/samdb.h"
37 #include "librpc/ndr/libndr.h"
38 #include "dsdb/samdb/ldb_modules/password_modules.h"
40 #define PASSWORD_GUID_ATTR "masterGUID"
42 /* This module maintains a local password database, seperate from the main LDAP server.
44 This allows the password database to be syncronised in a multi-master
45 fashion, seperate to the more difficult concerns of the main
46 database. (With passwords, the last writer always wins)
48 Each incoming add/modify is split into a remote, and a local request, done in that order.
50 We maintain a list of attributes that are kept locally:
53 static const char * const password_attrs[] = {
54 "supplementalCredentials",
59 "msDS-KeyVersionNumber",
63 /* And we merge them back into search requests when asked to do so */
67 enum lpdb_type {LPDB_ADD, LPDB_MOD, LPDB_SEARCH} type;
68 enum lpdb_step {LPDB_ADD_REMOTE, LPDB_MOD_REMOTE, LPDB_MOD_SEARCH_SELF, LPDB_LOCAL, LPDB_SEARCH_REMOTE} step;
70 struct ldb_module *module;
71 struct ldb_request *orig_req;
72 struct ldb_request *remote_req;
73 struct ldb_request *search_req;
74 struct ldb_request *local_req;
76 struct ldb_message *local_message;
78 BOOL added_objectGUID;
79 BOOL added_objectClass;
81 struct ldb_reply *search_res;
84 struct lpdb_local_search_context {
85 struct lpdb_context *ac;
86 struct ldb_reply *remote_res;
87 struct ldb_reply *local_res;
90 static struct ldb_handle *lpdb_init_handle(struct ldb_request *req, struct ldb_module *module, enum lpdb_type type)
92 struct lpdb_context *ac;
95 h = talloc_zero(req, struct ldb_handle);
97 ldb_set_errstring(module->ldb, "Out of Memory");
103 ac = talloc_zero(h, struct lpdb_context);
105 ldb_set_errstring(module->ldb, "Out of Memory");
110 h->private_data = (void *)ac;
112 h->state = LDB_ASYNC_INIT;
113 h->status = LDB_SUCCESS;
122 /* Add a record, splitting password attributes from the user's main
125 static int local_password_add(struct ldb_module *module, struct ldb_request *req)
127 struct ldb_handle *h;
128 struct lpdb_context *ac;
129 struct ldb_message *remote_message;
130 struct ldb_message *local_message;
131 struct GUID objectGUID;
134 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_add\n");
136 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
137 return ldb_next_request(module, req);
140 /* If the caller is manipulating the local passwords directly, let them pass */
141 if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
142 req->op.add.message->dn) == 0) {
143 return ldb_next_request(module, req);
146 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
147 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
152 /* It didn't match any of our password attributes, go on */
153 if (i == ARRAY_SIZE(password_attrs)) {
154 return ldb_next_request(module, req);
157 /* TODO: remove this when sambaPassword will be in schema */
158 if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
159 ldb_asprintf_errstring(module->ldb,
160 "Cannot relocate a password on entry: %s, does not have objectClass 'person'",
161 ldb_dn_get_linearized(req->op.add.message->dn));
162 return LDB_ERR_OBJECT_CLASS_VIOLATION;
165 /* From here, we assume we have password attributes to split off */
166 h = lpdb_init_handle(req, module, LPDB_ADD);
168 return LDB_ERR_OPERATIONS_ERROR;
170 ac = talloc_get_type(h->private_data, struct lpdb_context);
174 ac->remote_req = talloc(ac, struct ldb_request);
175 if (ac->remote_req == NULL) {
176 return LDB_ERR_OPERATIONS_ERROR;
179 *(ac->remote_req) = *(ac->orig_req);
181 remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.add.message);
182 if (remote_message == NULL) {
183 return LDB_ERR_OPERATIONS_ERROR;
186 /* Remove any password attributes from the remote message */
187 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
188 ldb_msg_remove_attr(remote_message, password_attrs[i]);
191 ac->remote_req->op.add.message = remote_message;
193 ac->remote_req->context = NULL;
194 ac->remote_req->callback = NULL;
196 ac->local_req = talloc(ac, struct ldb_request);
197 if (ac->local_req == NULL) {
198 return LDB_ERR_OPERATIONS_ERROR;
201 *(ac->local_req) = *(ac->orig_req);
202 local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.add.message);
203 if (local_message == NULL) {
204 return LDB_ERR_OPERATIONS_ERROR;
207 /* Remove anything seen in the remote message from the local
208 * message (leaving only password attributes) */
209 for (i=0;i<ac->remote_req->op.add.message->num_elements;i++) {
210 ldb_msg_remove_attr(local_message, ac->remote_req->op.add.message->elements[i].name);
213 /* We must have an objectGUID already, or we don't know where
214 * to add the password. This may be changed to an 'add and
215 * search', to allow the directory to create the objectGUID */
216 if (ldb_msg_find_ldb_val(ac->orig_req->op.add.message, "objectGUID") == NULL) {
217 ldb_set_errstring(module->ldb,
218 "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
219 return LDB_ERR_CONSTRAINT_VIOLATION;
222 /* Find the objectGUID to use as the key */
223 objectGUID = samdb_result_guid(ac->orig_req->op.add.message, "objectGUID");
225 local_message->dn = ldb_dn_new(local_message, module->ldb, LOCAL_BASE);
226 ldb_dn_add_child_fmt(local_message->dn, PASSWORD_GUID_ATTR "=%s", GUID_string(local_message, &objectGUID));
228 ac->local_req->op.add.message = local_message;
230 ac->local_req->context = NULL;
231 ac->local_req->callback = NULL;
233 ac->step = LPDB_ADD_REMOTE;
235 /* Return our own handle do deal with this call */
238 return ldb_next_request(module, ac->remote_req);
241 /* After adding the remote entry, add the local one */
242 static int local_password_add_local(struct ldb_handle *h) {
244 struct lpdb_context *ac;
245 ac = talloc_get_type(h->private_data, struct lpdb_context);
247 h->state = LDB_ASYNC_INIT;
248 h->status = LDB_SUCCESS;
250 ac->step = LPDB_LOCAL;
252 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
254 /* perform the local add */
255 return ldb_next_request(ac->module, ac->local_req);
258 static int local_password_mod_search_self(struct ldb_handle *h);
260 static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
262 struct ldb_handle *h;
263 struct lpdb_context *ac;
264 struct ldb_message *remote_message;
265 struct ldb_message *local_message;
268 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
270 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
271 return ldb_next_request(module, req);
274 /* If the caller is manipulating the local passwords directly, let them pass */
275 if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
276 req->op.mod.message->dn) == 0) {
277 return ldb_next_request(module, req);
280 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
281 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
286 /* It didn't match any of our password attributes, then we have nothing to do here */
287 if (i == ARRAY_SIZE(password_attrs)) {
288 return ldb_next_request(module, req);
291 /* From here, we assume we have password attributes to split off */
292 h = lpdb_init_handle(req, module, LPDB_MOD);
294 return LDB_ERR_OPERATIONS_ERROR;
296 ac = talloc_get_type(h->private_data, struct lpdb_context);
300 ac->remote_req = talloc(ac, struct ldb_request);
301 if (ac->remote_req == NULL) {
302 return LDB_ERR_OPERATIONS_ERROR;
305 *(ac->remote_req) = *(ac->orig_req);
306 remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.mod.message);
307 if (remote_message == NULL) {
308 return LDB_ERR_OPERATIONS_ERROR;
311 /* Remove any password attributes from the remote message */
312 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
313 ldb_msg_remove_attr(remote_message, password_attrs[i]);
316 ac->remote_req->op.mod.message = remote_message;
318 ac->remote_req->context = NULL;
319 ac->remote_req->callback = NULL;
321 ac->local_req = talloc(ac, struct ldb_request);
322 if (ac->local_req == NULL) {
323 return LDB_ERR_OPERATIONS_ERROR;
326 *(ac->local_req) = *(ac->orig_req);
327 local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.mod.message);
328 if (local_message == NULL) {
329 return LDB_ERR_OPERATIONS_ERROR;
332 /* Remove anything seen in the remote message from the local
333 * message (leaving only password attributes) */
334 for (i=0;i<ac->remote_req->op.mod.message->num_elements;i++) {
335 ldb_msg_remove_attr(local_message, ac->remote_req->op.mod.message->elements[i].name);
338 ac->local_req->op.mod.message = local_message;
339 ac->local_message = local_message;
341 ac->local_req->context = NULL;
342 ac->local_req->callback = NULL;
344 ac->step = LPDB_MOD_REMOTE;
346 /* Return our own handle do deal with this call */
349 return ldb_next_request(module, ac->remote_req);
352 /* Called when we search for our oen entry. Stores the one entry we
353 * expect (as it is a base search) on the context pointer */
354 static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
356 struct lpdb_context *ac;
358 if (!context || !ares) {
359 ldb_set_errstring(ldb, "NULL Context or Result in callback");
360 return LDB_ERR_OPERATIONS_ERROR;
363 ac = talloc_get_type(context, struct lpdb_context);
365 /* we are interested only in the single reply (base search) we receive here */
366 if (ares->type == LDB_REPLY_ENTRY) {
367 if (ac->search_res != NULL) {
368 ldb_set_errstring(ldb, "Too many results");
370 return LDB_ERR_OPERATIONS_ERROR;
373 ac->search_res = talloc_steal(ac, ares);
381 /* On a modify, we don't have the objectGUID handy, so we need to
382 * search our DN for it */
383 static int local_password_mod_search_self(struct ldb_handle *h) {
385 struct lpdb_context *ac;
386 static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
388 ac = talloc_get_type(h->private_data, struct lpdb_context);
390 /* prepare the search operation */
391 ac->search_req = talloc_zero(ac, struct ldb_request);
392 if (ac->search_req == NULL) {
393 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
394 return LDB_ERR_OPERATIONS_ERROR;
397 ac->search_req->operation = LDB_SEARCH;
398 ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
399 ac->search_req->op.search.scope = LDB_SCOPE_BASE;
400 ac->search_req->op.search.tree = ldb_parse_tree(ac->orig_req, NULL);
401 if (ac->search_req->op.search.tree == NULL) {
402 ldb_set_errstring(ac->module->ldb, "Invalid search filter");
403 return LDB_ERR_OPERATIONS_ERROR;
405 ac->search_req->op.search.attrs = attrs;
406 ac->search_req->controls = NULL;
407 ac->search_req->context = ac;
408 ac->search_req->callback = get_self_callback;
409 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
411 ac->step = LPDB_MOD_SEARCH_SELF;
413 return ldb_next_request(ac->module, ac->search_req);
416 /* After we find out the objectGUID for the entry, modify the local
417 * password database as required */
418 static int local_password_mod_local(struct ldb_handle *h) {
420 struct lpdb_context *ac;
421 struct GUID objectGUID;
422 ac = talloc_get_type(h->private_data, struct lpdb_context);
424 /* if it is not an entry of type person this is an error */
425 /* TODO: remove this when sambaPassword will be in schema */
426 if (!ac->search_res) {
427 ldb_asprintf_errstring(ac->module->ldb,
428 "entry just modified (%s) not found!",
429 ldb_dn_get_linearized(ac->remote_req->op.mod.message->dn));
430 return LDB_ERR_OPERATIONS_ERROR;
432 if (!ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "person")) {
433 /* Not relevent to us */
437 if (ldb_msg_find_ldb_val(ac->search_res->message, "objectGUID") == NULL) {
438 ldb_set_errstring(ac->module->ldb,
439 "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
440 return LDB_ERR_OBJECT_CLASS_VIOLATION;
443 objectGUID = samdb_result_guid(ac->search_res->message, "objectGUID");
445 ac->local_message->dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
446 ldb_dn_add_child_fmt(ac->local_message->dn, PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID));
448 h->state = LDB_ASYNC_INIT;
449 h->status = LDB_SUCCESS;
451 ac->step = LPDB_LOCAL;
453 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
455 /* perform the local update */
456 return ldb_next_request(ac->module, ac->local_req);
460 static int lpdb_local_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
462 struct lpdb_local_search_context *local_context;
464 if (!context || !ares) {
465 ldb_set_errstring(ldb, "NULL Context or Result in callback");
466 return LDB_ERR_OPERATIONS_ERROR;
469 local_context = talloc_get_type(context, struct lpdb_local_search_context);
471 /* we are interested only in the single reply (base search) we receive here */
472 switch (ares->type) {
473 case LDB_REPLY_ENTRY:
476 if (local_context->local_res != NULL) {
477 ldb_set_errstring(ldb, "Too many results to base search for password entry!");
479 return LDB_ERR_OPERATIONS_ERROR;
482 local_context->local_res = ares;
484 /* Make sure never to return the internal key attribute to the caller */
485 ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
487 talloc_steal(local_context->remote_res->message->elements, ares->message->elements);
488 for (i=0; i < ares->message->num_elements; i++) {
489 struct ldb_message_element *el;
491 el = ldb_msg_find_element(local_context->remote_res->message,
492 ares->message->elements[i].name);
494 if (ldb_msg_add_empty(local_context->remote_res->message,
495 ares->message->elements[i].name, 0, &el) != LDB_SUCCESS) {
497 return LDB_ERR_OPERATIONS_ERROR;
499 *el = ares->message->elements[i];
502 return local_context->ac->orig_req->callback(ldb,
503 local_context->ac->orig_req->context,
504 local_context->remote_res);
508 /* Fire off the callback if there was no local entry, so we get the rest returned */
509 if (local_context->local_res == NULL) {
510 return local_context->ac->orig_req->callback(ldb,
511 local_context->ac->orig_req->context,
512 local_context->remote_res);
520 ldb_set_errstring(ldb, "Unexpected result type in base search for password entry!");
521 return LDB_ERR_OPERATIONS_ERROR;
526 /* For each entry returned in a remote search, do a local base search,
527 * based on the objectGUID we asked for as an additional attribute */
528 static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
530 struct lpdb_context *ac;
532 if (!context || !ares) {
533 ldb_set_errstring(ldb, "NULL Context or Result in callback");
537 ac = talloc_get_type(context, struct lpdb_context);
539 if (ares->type == LDB_REPLY_ENTRY) {
540 struct ldb_request *req;
541 struct lpdb_local_search_context *local_context;
542 struct GUID objectGUID;
544 /* No point searching further if it's not a 'person' entry */
545 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
547 /* Make sure to remove anything we added */
548 if (ac->added_objectGUID) {
549 ldb_msg_remove_attr(ares->message, "objectGUID");
552 if (ac->added_objectClass) {
553 ldb_msg_remove_attr(ares->message, "objectClass");
556 return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
559 if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
560 ldb_set_errstring(ac->module->ldb,
561 "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
562 return LDB_ERR_OPERATIONS_ERROR;
565 objectGUID = samdb_result_guid(ares->message, "objectGUID");
567 if (ac->added_objectGUID) {
568 ldb_msg_remove_attr(ares->message, "objectGUID");
571 if (ac->added_objectClass) {
572 ldb_msg_remove_attr(ares->message, "objectClass");
575 req = talloc_zero(ac, struct ldb_request);
577 return LDB_ERR_OPERATIONS_ERROR;
580 local_context = talloc(ac, struct lpdb_local_search_context);
581 if (!local_context) {
582 return LDB_ERR_OPERATIONS_ERROR;
584 local_context->ac = ac;
585 local_context->remote_res = ares;
586 local_context->local_res = NULL;
588 req->op.search.base = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
589 if ( ! ldb_dn_add_child_fmt(req->op.search.base, PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID))) {
590 return LDB_ERR_OPERATIONS_ERROR;
592 req->operation = LDB_SEARCH;
593 req->op.search.scope = LDB_SCOPE_BASE;
594 req->op.search.tree = ldb_parse_tree(req, NULL);
595 if (req->op.search.tree == NULL) {
596 ldb_set_errstring(ac->module->ldb, "Out of Memory");
597 return LDB_ERR_OPERATIONS_ERROR;
599 req->op.search.attrs = ac->orig_req->op.search.attrs;
600 req->controls = NULL;
602 req->callback = get_self_callback;
604 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req);
606 req->context = local_context;
607 req->callback = lpdb_local_search_callback;
609 return ldb_next_request(ac->module, req);
611 return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
615 return LDB_ERR_OPERATIONS_ERROR;
618 /* Search for passwords and other attributes. The passwords are
619 * local, but the other attributes are remote, and we need to glue the
620 * two search spaces back togeather */
622 static int local_password_search(struct ldb_module *module, struct ldb_request *req)
624 struct ldb_handle *h;
625 struct lpdb_context *ac;
628 const char * const *search_attrs = NULL;
630 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_search\n");
632 if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
633 return ldb_next_request(module, req);
636 /* If the caller is searching for the local passwords directly, let them pass */
637 if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
638 req->op.search.base) == 0) {
639 return ldb_next_request(module, req);
642 if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
643 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
644 if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
649 /* It didn't match any of our password attributes, go on */
650 if (i == ARRAY_SIZE(password_attrs)) {
651 return ldb_next_request(module, req);
655 h = lpdb_init_handle(req, module, LPDB_SEARCH);
657 return LDB_ERR_OPERATIONS_ERROR;
660 ac = talloc_get_type(h->private_data, struct lpdb_context);
664 ac->remote_req = talloc(ac, struct ldb_request);
665 if (ac->remote_req == NULL) {
666 return LDB_ERR_OPERATIONS_ERROR;
669 /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
670 *(ac->remote_req) = *(ac->orig_req);
672 /* Return our own handle do deal with this call */
673 ac->remote_req->handle = h;
675 ac->remote_req->context = ac;
676 ac->remote_req->callback = lpdb_remote_search_callback;
678 if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
679 if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
680 search_attrs = ldb_attr_list_copy_add(req, req->op.search.attrs, "objectGUID");
681 ac->added_objectGUID = True;
683 return LDB_ERR_OPERATIONS_ERROR;
686 search_attrs = req->op.search.attrs;
688 if (!ldb_attr_in_list(search_attrs, "objectClass")) {
689 search_attrs = ldb_attr_list_copy_add(req, search_attrs, "objectClass");
690 ac->added_objectClass = True;
692 return LDB_ERR_OPERATIONS_ERROR;
696 search_attrs = req->op.search.attrs;
699 ac->remote_req->op.search.attrs = search_attrs;
701 ldb_set_timeout_from_prev_req(module->ldb, ac->orig_req, ac->remote_req);
703 h->state = LDB_ASYNC_INIT;
704 h->status = LDB_SUCCESS;
706 ac->step = LPDB_SEARCH_REMOTE;
708 /* perform the search */
709 ret = ldb_next_request(module, ac->remote_req);
711 if (ret == LDB_SUCCESS) {
712 req->handle = ac->remote_req->handle;
718 static int lpdb_wait(struct ldb_handle *handle) {
719 struct lpdb_context *ac;
722 if (!handle || !handle->private_data) {
723 return LDB_ERR_OPERATIONS_ERROR;
726 if (handle->state == LDB_ASYNC_DONE) {
727 return handle->status;
730 handle->state = LDB_ASYNC_PENDING;
731 handle->status = LDB_SUCCESS;
733 ac = talloc_get_type(handle->private_data, struct lpdb_context);
736 case LPDB_ADD_REMOTE:
737 ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE);
739 if (ret != LDB_SUCCESS) {
740 handle->status = ret;
743 if (ac->remote_req->handle->status != LDB_SUCCESS) {
744 handle->status = ac->remote_req->handle->status;
748 if (ac->remote_req->handle->state != LDB_ASYNC_DONE) {
752 /* original request done, go on */
753 return local_password_add_local(handle);
755 case LPDB_MOD_REMOTE:
756 ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE);
758 if (ret != LDB_SUCCESS) {
759 handle->status = ret;
762 if (ac->remote_req->handle->status != LDB_SUCCESS) {
763 handle->status = ac->remote_req->handle->status;
767 if (ac->remote_req->handle->state != LDB_ASYNC_DONE) {
771 /* original request done, go on */
772 return local_password_mod_search_self(handle);
774 case LPDB_MOD_SEARCH_SELF:
775 ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
777 if (ret != LDB_SUCCESS) {
778 handle->status = ret;
781 if (ac->search_req->handle->status != LDB_SUCCESS) {
782 handle->status = ac->search_req->handle->status;
786 if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
790 /* original request done, go on */
791 return local_password_mod_local(handle);
794 ret = ldb_wait(ac->local_req->handle, LDB_WAIT_NONE);
796 if (ret != LDB_SUCCESS) {
797 handle->status = ret;
800 if (ac->local_req->handle->status != LDB_SUCCESS) {
801 handle->status = ac->local_req->handle->status;
805 if (ac->local_req->handle->state != LDB_ASYNC_DONE) {
811 case LPDB_SEARCH_REMOTE:
812 ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE);
814 if (ret != LDB_SUCCESS) {
815 handle->status = ret;
818 if (ac->remote_req->handle->status != LDB_SUCCESS) {
819 handle->status = ac->remote_req->handle->status;
823 if (ac->remote_req->handle->state != LDB_ASYNC_DONE) {
830 ret = LDB_ERR_OPERATIONS_ERROR;
837 handle->state = LDB_ASYNC_DONE;
841 static int lpdb_wait_all(struct ldb_handle *handle) {
845 while (handle->state != LDB_ASYNC_DONE) {
846 ret = lpdb_wait(handle);
847 if (ret != LDB_SUCCESS) {
852 return handle->status;
855 static int local_password_wait(struct ldb_handle *handle, enum ldb_wait_type type)
857 if (type == LDB_WAIT_ALL) {
858 return lpdb_wait_all(handle);
860 return lpdb_wait(handle);
864 static const struct ldb_module_ops local_password_ops = {
865 .name = "local_password",
866 .add = local_password_add,
867 .modify = local_password_modify,
868 .search = local_password_search,
869 .wait = local_password_wait
873 int local_password_module_init(void)
875 return ldb_register_module(&local_password_ops);