4 Copyright (C) Simo Sorce 2004-2008
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 userPassword 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 */
66 struct lpdb_reply *next;
67 struct ldb_reply *remote;
68 struct ldb_dn *local_dn;
73 struct ldb_module *module;
74 struct ldb_request *req;
76 struct ldb_message *local_message;
78 struct lpdb_reply *list;
79 struct lpdb_reply *current;
80 struct ldb_reply *remote_done;
81 struct ldb_reply *remote;
83 bool added_objectGUID;
84 bool added_objectClass;
88 static struct lpdb_context *lpdb_init_context(struct ldb_module *module,
89 struct ldb_request *req)
91 struct lpdb_context *ac;
93 ac = talloc_zero(req, struct lpdb_context);
95 ldb_set_errstring(module->ldb, "Out of Memory");
105 static int lpdb_local_callback(struct ldb_request *req, struct ldb_reply *ares)
107 struct lpdb_context *ac;
109 ac = talloc_get_type(req->context, struct lpdb_context);
112 return ldb_module_done(ac->req, NULL, NULL,
113 LDB_ERR_OPERATIONS_ERROR);
115 if (ares->error != LDB_SUCCESS) {
116 return ldb_module_done(ac->req, ares->controls,
117 ares->response, ares->error);
120 if (ares->type != LDB_REPLY_DONE) {
121 ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
123 return ldb_module_done(ac->req, NULL, NULL,
124 LDB_ERR_OPERATIONS_ERROR);
128 return ldb_module_done(ac->req,
129 ac->remote_done->controls,
130 ac->remote_done->response,
131 ac->remote_done->error);
134 /*****************************************************************************
136 ****************************************************************************/
138 static int lpdb_add_callback(struct ldb_request *req,
139 struct ldb_reply *ares);
141 static int local_password_add(struct ldb_module *module, struct ldb_request *req)
143 struct ldb_message *remote_message;
144 struct ldb_request *remote_req;
145 struct lpdb_context *ac;
146 struct GUID objectGUID;
150 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_add\n");
152 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
153 return ldb_next_request(module, req);
156 /* If the caller is manipulating the local passwords directly, let them pass */
157 if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
158 req->op.add.message->dn) == 0) {
159 return ldb_next_request(module, req);
162 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
163 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
168 /* It didn't match any of our password attributes, go on */
169 if (i == ARRAY_SIZE(password_attrs)) {
170 return ldb_next_request(module, req);
173 /* TODO: remove this when userPassword will be in schema */
174 if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
175 ldb_asprintf_errstring(module->ldb,
176 "Cannot relocate a password on entry: %s, does not have objectClass 'person'",
177 ldb_dn_get_linearized(req->op.add.message->dn));
178 return LDB_ERR_OBJECT_CLASS_VIOLATION;
181 /* From here, we assume we have password attributes to split off */
182 ac = lpdb_init_context(module, req);
184 return LDB_ERR_OPERATIONS_ERROR;
187 remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message);
188 if (remote_message == NULL) {
189 return LDB_ERR_OPERATIONS_ERROR;
192 /* Remove any password attributes from the remote message */
193 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
194 ldb_msg_remove_attr(remote_message, password_attrs[i]);
197 /* Find the objectGUID to use as the key */
198 objectGUID = samdb_result_guid(ac->req->op.add.message, "objectGUID");
200 ac->local_message = ldb_msg_copy_shallow(ac, req->op.add.message);
201 if (ac->local_message == NULL) {
202 return LDB_ERR_OPERATIONS_ERROR;
205 /* Remove anything seen in the remote message from the local
206 * message (leaving only password attributes) */
207 for (i=0; i < remote_message->num_elements; i++) {
208 ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
211 /* We must have an objectGUID already, or we don't know where
212 * to add the password. This may be changed to an 'add and
213 * search', to allow the directory to create the objectGUID */
214 if (ldb_msg_find_ldb_val(req->op.add.message, "objectGUID") == NULL) {
215 ldb_set_errstring(module->ldb,
216 "no objectGUID found in search: "
217 "local_password module must be "
218 "onfigured below objectGUID module!\n");
219 return LDB_ERR_CONSTRAINT_VIOLATION;
222 ac->local_message->dn = ldb_dn_new(ac->local_message,
223 module->ldb, LOCAL_BASE);
224 if ((ac->local_message->dn == NULL) ||
225 ( ! ldb_dn_add_child_fmt(ac->local_message->dn,
226 PASSWORD_GUID_ATTR "=%s",
227 GUID_string(ac->local_message,
229 return LDB_ERR_OPERATIONS_ERROR;
232 ret = ldb_build_add_req(&remote_req, module->ldb, ac,
235 ac, lpdb_add_callback,
237 if (ret != LDB_SUCCESS) {
241 return ldb_next_request(module, remote_req);
244 /* Add a record, splitting password attributes from the user's main
246 static int lpdb_add_callback(struct ldb_request *req,
247 struct ldb_reply *ares)
249 struct ldb_request *local_req;
250 struct lpdb_context *ac;
253 ac = talloc_get_type(req->context, struct lpdb_context);
256 return ldb_module_done(ac->req, NULL, NULL,
257 LDB_ERR_OPERATIONS_ERROR);
259 if (ares->error != LDB_SUCCESS) {
260 return ldb_module_done(ac->req, ares->controls,
261 ares->response, ares->error);
264 if (ares->type != LDB_REPLY_DONE) {
265 ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
267 return ldb_module_done(ac->req, NULL, NULL,
268 LDB_ERR_OPERATIONS_ERROR);
271 ac->remote_done = talloc_steal(ac, ares);
273 ret = ldb_build_add_req(&local_req, ac->module->ldb, ac,
276 ac, lpdb_local_callback,
278 if (ret != LDB_SUCCESS) {
279 return ldb_module_done(ac->req, NULL, NULL, ret);
282 ret = ldb_next_request(ac->module, local_req);
283 if (ret != LDB_SUCCESS) {
284 return ldb_module_done(ac->req, NULL, NULL, ret);
289 /*****************************************************************************
291 ****************************************************************************/
293 static int lpdb_modify_callabck(struct ldb_request *req,
294 struct ldb_reply *ares);
295 static int lpdb_mod_search_callback(struct ldb_request *req,
296 struct ldb_reply *ares);
298 static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
300 struct lpdb_context *ac;
301 struct ldb_message *remote_message;
302 struct ldb_request *remote_req;
306 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
308 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
309 return ldb_next_request(module, req);
312 /* If the caller is manipulating the local passwords directly, let them pass */
313 if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
314 req->op.mod.message->dn) == 0) {
315 return ldb_next_request(module, req);
318 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
319 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
324 /* It didn't match any of our password attributes, then we have nothing to do here */
325 if (i == ARRAY_SIZE(password_attrs)) {
326 return ldb_next_request(module, req);
329 /* From here, we assume we have password attributes to split off */
330 ac = lpdb_init_context(module, req);
332 return LDB_ERR_OPERATIONS_ERROR;
335 remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
336 if (remote_message == NULL) {
337 return LDB_ERR_OPERATIONS_ERROR;
340 /* Remove any password attributes from the remote message */
341 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
342 ldb_msg_remove_attr(remote_message, password_attrs[i]);
345 ac->local_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
346 if (ac->local_message == NULL) {
347 return LDB_ERR_OPERATIONS_ERROR;
350 /* Remove anything seen in the remote message from the local
351 * message (leaving only password attributes) */
352 for (i=0; i < remote_message->num_elements;i++) {
353 ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
356 ret = ldb_build_mod_req(&remote_req, module->ldb, ac,
359 ac, lpdb_modify_callabck,
361 if (ret != LDB_SUCCESS) {
365 return ldb_next_request(module, remote_req);
368 /* On a modify, we don't have the objectGUID handy, so we need to
369 * search our DN for it */
370 static int lpdb_modify_callabck(struct ldb_request *req,
371 struct ldb_reply *ares)
373 static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
374 struct ldb_request *search_req;
375 struct lpdb_context *ac;
378 ac = talloc_get_type(req->context, struct lpdb_context);
381 return ldb_module_done(ac->req, NULL, NULL,
382 LDB_ERR_OPERATIONS_ERROR);
384 if (ares->error != LDB_SUCCESS) {
385 return ldb_module_done(ac->req, ares->controls,
386 ares->response, ares->error);
389 if (ares->type != LDB_REPLY_DONE) {
390 ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
392 return ldb_module_done(ac->req, NULL, NULL,
393 LDB_ERR_OPERATIONS_ERROR);
396 ac->remote_done = talloc_steal(ac, ares);
398 /* prepare the search operation */
399 ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
400 ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
401 "(objectclass=*)", attrs,
403 ac, lpdb_mod_search_callback,
405 if (ret != LDB_SUCCESS) {
406 return ldb_module_done(ac->req, NULL, NULL,
407 LDB_ERR_OPERATIONS_ERROR);
410 ret = ldb_next_request(ac->module, search_req);
411 if (ret != LDB_SUCCESS) {
412 return ldb_module_done(ac->req, NULL, NULL,
413 LDB_ERR_OPERATIONS_ERROR);
418 /* Called when we search for our own entry. Stores the one entry we
419 * expect (as it is a base search) on the context pointer */
420 static int lpdb_mod_search_callback(struct ldb_request *req,
421 struct ldb_reply *ares)
423 struct ldb_request *local_req;
424 struct lpdb_context *ac;
425 struct ldb_dn *local_dn;
426 struct GUID objectGUID;
427 int ret = LDB_SUCCESS;
429 ac = talloc_get_type(req->context, struct lpdb_context);
432 return ldb_module_done(ac->req, NULL, NULL,
433 LDB_ERR_OPERATIONS_ERROR);
435 if (ares->error != LDB_SUCCESS) {
436 return ldb_module_done(ac->req, ares->controls,
437 ares->response, ares->error);
440 switch (ares->type) {
441 case LDB_REPLY_ENTRY:
442 if (ac->remote != NULL) {
443 ldb_set_errstring(ac->module->ldb, "Too many results");
445 return ldb_module_done(ac->req, NULL, NULL,
446 LDB_ERR_OPERATIONS_ERROR);
449 ac->remote = talloc_steal(ac, ares);
452 case LDB_REPLY_REFERRAL:
459 /* After we find out the objectGUID for the entry, modify the local
460 * password database as required */
464 /* if it is not an entry of type person this is an error */
465 /* TODO: remove this when sambaPassword will be in schema */
466 if (ac->remote == NULL) {
467 ldb_asprintf_errstring(ac->module->ldb,
468 "entry just modified (%s) not found!",
469 ldb_dn_get_linearized(req->op.search.base));
470 return ldb_module_done(ac->req, NULL, NULL,
471 LDB_ERR_OPERATIONS_ERROR);
473 if (!ldb_msg_check_string_attribute(ac->remote->message,
474 "objectClass", "person")) {
475 /* Not relevent to us */
476 return ldb_module_done(ac->req,
477 ac->remote_done->controls,
478 ac->remote_done->response,
479 ac->remote_done->error);
482 if (ldb_msg_find_ldb_val(ac->remote->message,
483 "objectGUID") == NULL) {
484 ldb_set_errstring(ac->module->ldb,
485 "no objectGUID found in search: "
486 "local_password module must be "
487 "configured below objectGUID "
489 return ldb_module_done(ac->req, NULL, NULL,
490 LDB_ERR_OBJECT_CLASS_VIOLATION);
493 objectGUID = samdb_result_guid(ac->remote->message,
496 local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
497 if ((local_dn == NULL) ||
498 ( ! ldb_dn_add_child_fmt(local_dn,
499 PASSWORD_GUID_ATTR "=%s",
500 GUID_string(ac, &objectGUID)))) {
501 return ldb_module_done(ac->req, NULL, NULL,
502 LDB_ERR_OPERATIONS_ERROR);
504 ac->local_message->dn = local_dn;
506 ret = ldb_build_mod_req(&local_req, ac->module->ldb, ac,
509 ac, lpdb_local_callback,
511 if (ret != LDB_SUCCESS) {
512 return ldb_module_done(ac->req, NULL, NULL, ret);
515 /* perform the local update */
516 ret = ldb_next_request(ac->module, local_req);
517 if (ret != LDB_SUCCESS) {
518 return ldb_module_done(ac->req, NULL, NULL, ret);
525 /*****************************************************************************
527 ****************************************************************************/
529 static int lpdb_delete_callabck(struct ldb_request *req,
530 struct ldb_reply *ares);
531 static int lpdb_del_search_callback(struct ldb_request *req,
532 struct ldb_reply *ares);
534 static int local_password_delete(struct ldb_module *module,
535 struct ldb_request *req)
537 struct ldb_request *remote_req;
538 struct lpdb_context *ac;
541 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_delete\n");
543 /* do not manipulate our control entries */
544 if (ldb_dn_is_special(req->op.mod.message->dn)) {
545 return ldb_next_request(module, req);
548 /* If the caller is manipulating the local passwords directly,
550 if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
551 req->op.del.dn) == 0) {
552 return ldb_next_request(module, req);
555 /* From here, we assume we have password attributes to split off */
556 ac = lpdb_init_context(module, req);
558 return LDB_ERR_OPERATIONS_ERROR;
561 ret = ldb_build_del_req(&remote_req, module->ldb, ac,
564 ac, lpdb_delete_callabck,
566 if (ret != LDB_SUCCESS) {
570 return ldb_next_request(module, remote_req);
573 /* On a modify, we don't have the objectGUID handy, so we need to
574 * search our DN for it */
575 static int lpdb_delete_callabck(struct ldb_request *req,
576 struct ldb_reply *ares)
578 static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
579 struct ldb_request *search_req;
580 struct lpdb_context *ac;
583 ac = talloc_get_type(req->context, struct lpdb_context);
586 return ldb_module_done(ac->req, NULL, NULL,
587 LDB_ERR_OPERATIONS_ERROR);
589 if (ares->error != LDB_SUCCESS) {
590 return ldb_module_done(ac->req, ares->controls,
591 ares->response, ares->error);
594 if (ares->type != LDB_REPLY_DONE) {
595 ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
597 return ldb_module_done(ac->req, NULL, NULL,
598 LDB_ERR_OPERATIONS_ERROR);
601 ac->remote_done = talloc_steal(ac, ares);
603 /* prepare the search operation */
604 ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
605 ac->req->op.del.dn, LDB_SCOPE_BASE,
606 "(objectclass=*)", attrs,
608 ac, lpdb_del_search_callback,
610 if (ret != LDB_SUCCESS) {
611 return ldb_module_done(ac->req, NULL, NULL,
612 LDB_ERR_OPERATIONS_ERROR);
615 ret = ldb_next_request(ac->module, search_req);
616 if (ret != LDB_SUCCESS) {
617 return ldb_module_done(ac->req, NULL, NULL,
618 LDB_ERR_OPERATIONS_ERROR);
623 /* Called when we search for our own entry. Stores the one entry we
624 * expect (as it is a base search) on the context pointer */
625 static int lpdb_del_search_callback(struct ldb_request *req,
626 struct ldb_reply *ares)
628 struct ldb_request *local_req;
629 struct lpdb_context *ac;
630 struct ldb_dn *local_dn;
631 struct GUID objectGUID;
632 int ret = LDB_SUCCESS;
634 ac = talloc_get_type(req->context, struct lpdb_context);
637 return ldb_module_done(ac->req, NULL, NULL,
638 LDB_ERR_OPERATIONS_ERROR);
640 if (ares->error != LDB_SUCCESS) {
641 return ldb_module_done(ac->req, ares->controls,
642 ares->response, ares->error);
645 switch (ares->type) {
646 case LDB_REPLY_ENTRY:
647 if (ac->remote != NULL) {
648 ldb_set_errstring(ac->module->ldb, "Too many results");
650 return ldb_module_done(ac->req, NULL, NULL,
651 LDB_ERR_OPERATIONS_ERROR);
654 ac->remote = talloc_steal(ac, ares);
657 case LDB_REPLY_REFERRAL:
664 /* After we find out the objectGUID for the entry, modify the local
665 * password database as required */
669 /* if it is not an entry of type person this is NOT an error */
670 /* TODO: remove this when sambaPassword will be in schema */
671 if (ac->remote == NULL) {
672 return ldb_module_done(ac->req,
673 ac->remote_done->controls,
674 ac->remote_done->response,
675 ac->remote_done->error);
677 if (!ldb_msg_check_string_attribute(ac->remote->message,
678 "objectClass", "person")) {
679 /* Not relevent to us */
680 return ldb_module_done(ac->req,
681 ac->remote_done->controls,
682 ac->remote_done->response,
683 ac->remote_done->error);
686 if (ldb_msg_find_ldb_val(ac->remote->message,
687 "objectGUID") == NULL) {
688 ldb_set_errstring(ac->module->ldb,
689 "no objectGUID found in search: "
690 "local_password module must be "
691 "configured below objectGUID "
693 return ldb_module_done(ac->req, NULL, NULL,
694 LDB_ERR_OBJECT_CLASS_VIOLATION);
697 objectGUID = samdb_result_guid(ac->remote->message,
700 local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
701 if ((local_dn == NULL) ||
702 ( ! ldb_dn_add_child_fmt(local_dn,
703 PASSWORD_GUID_ATTR "=%s",
704 GUID_string(ac, &objectGUID)))) {
705 return ldb_module_done(ac->req, NULL, NULL,
706 LDB_ERR_OPERATIONS_ERROR);
709 ret = ldb_build_del_req(&local_req, ac->module->ldb, ac,
712 ac, lpdb_local_callback,
714 if (ret != LDB_SUCCESS) {
715 return ldb_module_done(ac->req, NULL, NULL, ret);
718 /* perform the local update */
719 ret = ldb_next_request(ac->module, local_req);
720 if (ret != LDB_SUCCESS) {
721 return ldb_module_done(ac->req, NULL, NULL, ret);
729 /*****************************************************************************
731 ****************************************************************************/
733 static int lpdb_local_search_callback(struct ldb_request *req,
734 struct ldb_reply *ares);
736 static int lpdb_local_search(struct lpdb_context *ac)
738 struct ldb_request *local_req;
741 ret = ldb_build_search_req(&local_req, ac->module->ldb, ac,
742 ac->current->local_dn,
745 ac->req->op.search.attrs,
747 ac, lpdb_local_search_callback,
749 if (ret != LDB_SUCCESS) {
750 return LDB_ERR_OPERATIONS_ERROR;
753 return ldb_next_request(ac->module, local_req);
756 static int lpdb_local_search_callback(struct ldb_request *req,
757 struct ldb_reply *ares)
759 struct lpdb_context *ac;
760 struct ldb_reply *merge;
761 struct lpdb_reply *lr;
765 ac = talloc_get_type(req->context, struct lpdb_context);
768 return ldb_module_done(ac->req, NULL, NULL,
769 LDB_ERR_OPERATIONS_ERROR);
771 if (ares->error != LDB_SUCCESS) {
772 return ldb_module_done(ac->req, ares->controls,
773 ares->response, ares->error);
778 /* we are interested only in a single reply (base search) */
779 switch (ares->type) {
780 case LDB_REPLY_ENTRY:
782 if (lr->remote == NULL) {
783 ldb_set_errstring(ac->module->ldb,
784 "Too many results for password entry search!");
786 return ldb_module_done(ac->req, NULL, NULL,
787 LDB_ERR_OPERATIONS_ERROR);
793 /* steal the local results on the remote results to be
794 * returned all together */
795 talloc_steal(merge, ares->message->elements);
797 /* Make sure never to return the internal key attribute */
798 ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
800 for (i=0; i < ares->message->num_elements; i++) {
801 struct ldb_message_element *el;
803 el = ldb_msg_find_element(merge->message,
804 ares->message->elements[i].name);
806 ret = ldb_msg_add_empty(merge->message,
807 ares->message->elements[i].name,
809 if (ret != LDB_SUCCESS) {
811 return ldb_module_done(ac->req,
813 LDB_ERR_OPERATIONS_ERROR);
815 *el = ares->message->elements[i];
822 return ldb_module_send_entry(ac->req, merge->message);
824 case LDB_REPLY_REFERRAL:
833 /* if this entry was not returned yet, return it now */
835 ret = ldb_module_send_entry(ac->req, ac->remote->message);
836 if (ret != LDB_SUCCESS) {
837 return ldb_module_done(ac->req,
843 if (lr->next->remote->type == LDB_REPLY_DONE) {
844 /* this was the last one */
845 return ldb_module_done(ac->req,
846 lr->next->remote->controls,
847 lr->next->remote->response,
848 lr->next->remote->error);
851 ac->current = lr->next;
854 ret = lpdb_local_search(ac);
855 if (ret != LDB_SUCCESS) {
856 return ldb_module_done(ac->req,
865 /* For each entry returned in a remote search, do a local base search,
866 * based on the objectGUID we asked for as an additional attribute */
867 static int lpdb_remote_search_callback(struct ldb_request *req,
868 struct ldb_reply *ares)
870 struct lpdb_context *ac;
871 struct ldb_dn *local_dn;
872 struct GUID objectGUID;
873 struct lpdb_reply *lr;
876 ac = talloc_get_type(req->context, struct lpdb_context);
879 return ldb_module_done(ac->req, NULL, NULL,
880 LDB_ERR_OPERATIONS_ERROR);
882 if (ares->error != LDB_SUCCESS) {
883 return ldb_module_done(ac->req, ares->controls,
884 ares->response, ares->error);
887 switch (ares->type) {
888 case LDB_REPLY_ENTRY:
889 /* No point searching further if it's not a 'person' entry */
890 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
892 /* Make sure to remove anything we added */
893 if (ac->added_objectGUID) {
894 ldb_msg_remove_attr(ares->message, "objectGUID");
897 if (ac->added_objectClass) {
898 ldb_msg_remove_attr(ares->message, "objectClass");
901 return ldb_module_send_entry(ac->req, ares->message);
904 if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
905 ldb_set_errstring(ac->module->ldb,
906 "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
907 return ldb_module_done(ac->req, NULL, NULL,
908 LDB_ERR_OPERATIONS_ERROR);
911 objectGUID = samdb_result_guid(ares->message, "objectGUID");
913 if (ac->added_objectGUID) {
914 ldb_msg_remove_attr(ares->message, "objectGUID");
917 if (ac->added_objectClass) {
918 ldb_msg_remove_attr(ares->message, "objectClass");
921 local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
922 if ((local_dn == NULL) ||
923 (! ldb_dn_add_child_fmt(local_dn,
924 PASSWORD_GUID_ATTR "=%s",
925 GUID_string(ac, &objectGUID)))) {
926 return ldb_module_done(ac->req, NULL, NULL,
927 LDB_ERR_OPERATIONS_ERROR);
930 lr = talloc_zero(ac, struct lpdb_reply);
932 return ldb_module_done(ac->req, NULL, NULL,
933 LDB_ERR_OPERATIONS_ERROR);
935 lr->local_dn = talloc_steal(lr, local_dn);
936 lr->remote = talloc_steal(lr, ares);
939 ac->current->next = lr;
947 case LDB_REPLY_REFERRAL:
949 return ldb_module_send_referral(ac->req, ares->referral);
953 if (ac->list == NULL) {
955 return ldb_module_done(ac->req, ares->controls,
956 ares->response, ares->error);
959 lr = talloc_zero(ac, struct lpdb_reply);
961 return ldb_module_done(ac->req, NULL, NULL,
962 LDB_ERR_OPERATIONS_ERROR);
964 lr->remote = talloc_steal(lr, ares);
966 ac->current->next = lr;
968 /* rewind current and start local searches */
969 ac->current= ac->list;
971 ret = lpdb_local_search(ac);
972 if (ret != LDB_SUCCESS) {
973 return ldb_module_done(ac->req, NULL, NULL, ret);
980 /* Search for passwords and other attributes. The passwords are
981 * local, but the other attributes are remote, and we need to glue the
982 * two search spaces back togeather */
984 static int local_password_search(struct ldb_module *module, struct ldb_request *req)
986 struct ldb_request *remote_req;
987 struct lpdb_context *ac;
990 const char * const *search_attrs = NULL;
992 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_search\n");
994 if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
995 return ldb_next_request(module, req);
1000 /* If the caller is searching for the local passwords directly, let them pass */
1001 if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
1002 req->op.search.base) == 0) {
1003 return ldb_next_request(module, req);
1006 if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
1007 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
1008 if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
1013 /* It didn't match any of our password attributes, go on */
1014 if (i == ARRAY_SIZE(password_attrs)) {
1015 return ldb_next_request(module, req);
1019 ac = lpdb_init_context(module, req);
1021 return LDB_ERR_OPERATIONS_ERROR;
1024 /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
1025 if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
1026 if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
1027 search_attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectGUID");
1028 ac->added_objectGUID = true;
1029 if (!search_attrs) {
1030 return LDB_ERR_OPERATIONS_ERROR;
1033 search_attrs = req->op.search.attrs;
1035 if (!ldb_attr_in_list(search_attrs, "objectClass")) {
1036 search_attrs = ldb_attr_list_copy_add(ac, search_attrs, "objectClass");
1037 ac->added_objectClass = true;
1038 if (!search_attrs) {
1039 return LDB_ERR_OPERATIONS_ERROR;
1043 search_attrs = req->op.search.attrs;
1046 ret = ldb_build_search_req_ex(&remote_req, module->ldb, ac,
1047 req->op.search.base,
1048 req->op.search.scope,
1049 req->op.search.tree,
1052 ac, lpdb_remote_search_callback,
1054 if (ret != LDB_SUCCESS) {
1055 return LDB_ERR_OPERATIONS_ERROR;
1058 /* perform the search */
1059 return ldb_next_request(module, remote_req);
1062 _PUBLIC_ const struct ldb_module_ops ldb_local_password_module_ops = {
1063 .name = "local_password",
1064 .add = local_password_add,
1065 .modify = local_password_modify,
1066 .del = local_password_delete,
1067 .search = local_password_search