Convert mtime from a time_t to a struct timespec.
[Samba.git] / source4 / dsdb / samdb / ldb_modules / local_password.c
blobc0e1e9a4bb38d8fd9f76a41db0c96a3e7988e049
1 /*
2 ldb database module
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/>.
23 * Name: ldb
25 * Component: ldb local_password module
27 * Description: correctly update hash values based on changes to userPassword and friends
29 * Author: Andrew Bartlett
32 #include "includes.h"
33 #include "ldb_module.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/ndr/libndr.h"
36 #include "dsdb/samdb/ldb_modules/password_modules.h"
37 #include "dsdb/samdb/ldb_modules/util.h"
38 #include "dsdb/common/util.h"
40 #define PASSWORD_GUID_ATTR "masterGUID"
42 /* This module maintains a local password database, separate from the main LDAP
43 server.
45 This allows the password database to be synchronised in a multi-master
46 fashion, seperate to the more difficult concerns of the main
47 database. (With passwords, the last writer always wins)
49 Each incoming add/modify is split into a remote, and a local request, done
50 in that order.
52 We maintain a list of attributes that are kept locally - perhaps
53 this should use the @KLUDGE_ACL list of passwordAttribute
56 static const char * const password_attrs[] = {
57 "pwdLastSet",
58 DSDB_SECRET_ATTRIBUTES
61 /* And we merge them back into search requests when asked to do so */
63 struct lpdb_reply {
64 struct lpdb_reply *next;
65 struct ldb_reply *remote;
66 struct ldb_dn *local_dn;
69 struct lpdb_context {
71 struct ldb_module *module;
72 struct ldb_request *req;
74 struct ldb_message *local_message;
76 struct lpdb_reply *list;
77 struct lpdb_reply *current;
78 struct ldb_reply *remote_done;
79 struct ldb_reply *remote;
81 bool added_objectGUID;
82 bool added_objectClass;
86 static struct lpdb_context *lpdb_init_context(struct ldb_module *module,
87 struct ldb_request *req)
89 struct ldb_context *ldb;
90 struct lpdb_context *ac;
92 ldb = ldb_module_get_ctx(module);
94 ac = talloc_zero(req, struct lpdb_context);
95 if (ac == NULL) {
96 ldb_set_errstring(ldb, "Out of Memory");
97 return NULL;
100 ac->module = module;
101 ac->req = req;
103 return ac;
106 static int lpdb_local_callback(struct ldb_request *req, struct ldb_reply *ares)
108 struct ldb_context *ldb;
109 struct lpdb_context *ac;
111 ac = talloc_get_type(req->context, struct lpdb_context);
112 ldb = ldb_module_get_ctx(ac->module);
114 if (!ares) {
115 return ldb_module_done(ac->req, NULL, NULL,
116 LDB_ERR_OPERATIONS_ERROR);
118 if (ares->error != LDB_SUCCESS) {
119 return ldb_module_done(ac->req, ares->controls,
120 ares->response, ares->error);
123 if (ares->type != LDB_REPLY_DONE) {
124 ldb_set_errstring(ldb, "Unexpected reply type");
125 talloc_free(ares);
126 return ldb_module_done(ac->req, NULL, NULL,
127 LDB_ERR_OPERATIONS_ERROR);
130 talloc_free(ares);
131 return ldb_module_done(ac->req,
132 ac->remote_done->controls,
133 ac->remote_done->response,
134 ac->remote_done->error);
137 /*****************************************************************************
138 * ADD
139 ****************************************************************************/
141 static int lpdb_add_callback(struct ldb_request *req,
142 struct ldb_reply *ares);
144 static int local_password_add(struct ldb_module *module, struct ldb_request *req)
146 struct ldb_context *ldb;
147 struct ldb_message *remote_message;
148 struct ldb_request *remote_req;
149 struct lpdb_context *ac;
150 struct GUID objectGUID;
151 int ret;
152 unsigned int i;
154 ldb = ldb_module_get_ctx(module);
155 ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_add\n");
157 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
158 return ldb_next_request(module, req);
161 /* If the caller is manipulating the local passwords directly, let them pass */
162 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
163 req->op.add.message->dn) == 0) {
164 return ldb_next_request(module, req);
167 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
168 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
169 break;
173 /* It didn't match any of our password attributes, go on */
174 if (i == ARRAY_SIZE(password_attrs)) {
175 return ldb_next_request(module, req);
178 /* From here, we assume we have password attributes to split off */
179 ac = lpdb_init_context(module, req);
180 if (!ac) {
181 return ldb_operr(ldb);
184 remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message);
185 if (remote_message == NULL) {
186 return ldb_operr(ldb);
189 /* Remove any password attributes from the remote message */
190 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
191 ldb_msg_remove_attr(remote_message, password_attrs[i]);
194 /* Find the objectGUID to use as the key */
195 objectGUID = samdb_result_guid(ac->req->op.add.message, "objectGUID");
197 ac->local_message = ldb_msg_copy_shallow(ac, req->op.add.message);
198 if (ac->local_message == NULL) {
199 return ldb_operr(ldb);
202 /* Remove anything seen in the remote message from the local
203 * message (leaving only password attributes) */
204 for (i=0; i < remote_message->num_elements; i++) {
205 ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
208 /* We must have an objectGUID already, or we don't know where
209 * to add the password. This may be changed to an 'add and
210 * search', to allow the directory to create the objectGUID */
211 if (ldb_msg_find_ldb_val(req->op.add.message, "objectGUID") == NULL) {
212 ldb_set_errstring(ldb,
213 "no objectGUID found in search: "
214 "local_password module must be "
215 "onfigured below objectGUID module!\n");
216 return LDB_ERR_CONSTRAINT_VIOLATION;
219 ac->local_message->dn = ldb_dn_new(ac->local_message,
220 ldb, LOCAL_BASE);
221 if ((ac->local_message->dn == NULL) ||
222 ( ! ldb_dn_add_child_fmt(ac->local_message->dn,
223 PASSWORD_GUID_ATTR "=%s",
224 GUID_string(ac->local_message,
225 &objectGUID)))) {
226 return ldb_operr(ldb);
229 ret = ldb_build_add_req(&remote_req, ldb, ac,
230 remote_message,
231 req->controls,
232 ac, lpdb_add_callback,
233 req);
234 LDB_REQ_SET_LOCATION(remote_req);
235 if (ret != LDB_SUCCESS) {
236 return ret;
239 return ldb_next_request(module, remote_req);
242 /* Add a record, splitting password attributes from the user's main
243 * record */
244 static int lpdb_add_callback(struct ldb_request *req,
245 struct ldb_reply *ares)
247 struct ldb_context *ldb;
248 struct ldb_request *local_req;
249 struct lpdb_context *ac;
250 int ret;
252 ac = talloc_get_type(req->context, struct lpdb_context);
253 ldb = ldb_module_get_ctx(ac->module);
255 if (!ares) {
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(ldb, "Unexpected reply type");
266 talloc_free(ares);
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, ldb, ac,
274 ac->local_message,
275 NULL,
276 ac, lpdb_local_callback,
277 ac->req);
278 LDB_REQ_SET_LOCATION(local_req);
279 if (ret != LDB_SUCCESS) {
280 return ldb_module_done(ac->req, NULL, NULL, ret);
283 ret = ldb_next_request(ac->module, local_req);
284 if (ret != LDB_SUCCESS) {
285 return ldb_module_done(ac->req, NULL, NULL, ret);
287 return LDB_SUCCESS;
290 /*****************************************************************************
291 * MODIFY
292 ****************************************************************************/
294 static int lpdb_modify_callback(struct ldb_request *req,
295 struct ldb_reply *ares);
296 static int lpdb_mod_search_callback(struct ldb_request *req,
297 struct ldb_reply *ares);
299 static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
301 struct ldb_context *ldb;
302 struct lpdb_context *ac;
303 struct ldb_message *remote_message;
304 struct ldb_request *remote_req;
305 int ret;
306 unsigned int i;
308 ldb = ldb_module_get_ctx(module);
309 ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
311 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
312 return ldb_next_request(module, req);
315 /* If the caller is manipulating the local passwords directly, let them pass */
316 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
317 req->op.mod.message->dn) == 0) {
318 return ldb_next_request(module, req);
321 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
322 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
323 break;
327 /* It didn't match any of our password attributes, then we have nothing to do here */
328 if (i == ARRAY_SIZE(password_attrs)) {
329 return ldb_next_request(module, req);
332 /* From here, we assume we have password attributes to split off */
333 ac = lpdb_init_context(module, req);
334 if (!ac) {
335 return ldb_operr(ldb);
338 remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
339 if (remote_message == NULL) {
340 return ldb_operr(ldb);
343 /* Remove any password attributes from the remote message */
344 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
345 ldb_msg_remove_attr(remote_message, password_attrs[i]);
348 ac->local_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
349 if (ac->local_message == NULL) {
350 return ldb_operr(ldb);
353 /* Remove anything seen in the remote message from the local
354 * message (leaving only password attributes) */
355 for (i=0; i < remote_message->num_elements;i++) {
356 ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
359 ret = ldb_build_mod_req(&remote_req, ldb, ac,
360 remote_message,
361 req->controls,
362 ac, lpdb_modify_callback,
363 req);
364 LDB_REQ_SET_LOCATION(remote_req);
365 if (ret != LDB_SUCCESS) {
366 return ret;
369 return ldb_next_request(module, remote_req);
372 /* On a modify, we don't have the objectGUID handy, so we need to
373 * search our DN for it */
374 static int lpdb_modify_callback(struct ldb_request *req,
375 struct ldb_reply *ares)
377 struct ldb_context *ldb;
378 static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
379 struct ldb_request *search_req;
380 struct lpdb_context *ac;
381 int ret;
383 ac = talloc_get_type(req->context, struct lpdb_context);
384 ldb = ldb_module_get_ctx(ac->module);
386 if (!ares) {
387 return ldb_module_done(ac->req, NULL, NULL,
388 LDB_ERR_OPERATIONS_ERROR);
390 if (ares->error != LDB_SUCCESS) {
391 return ldb_module_done(ac->req, ares->controls,
392 ares->response, ares->error);
395 if (ares->type != LDB_REPLY_DONE) {
396 ldb_set_errstring(ldb, "Unexpected reply type");
397 talloc_free(ares);
398 return ldb_module_done(ac->req, NULL, NULL,
399 LDB_ERR_OPERATIONS_ERROR);
402 ac->remote_done = talloc_steal(ac, ares);
404 /* prepare the search operation */
405 ret = ldb_build_search_req(&search_req, ldb, ac,
406 ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
407 "(objectclass=*)", attrs,
408 NULL,
409 ac, lpdb_mod_search_callback,
410 ac->req);
411 LDB_REQ_SET_LOCATION(search_req);
412 if (ret != LDB_SUCCESS) {
413 return ldb_module_done(ac->req, NULL, NULL,
414 LDB_ERR_OPERATIONS_ERROR);
417 ret = ldb_next_request(ac->module, search_req);
418 if (ret != LDB_SUCCESS) {
419 return ldb_module_done(ac->req, NULL, NULL,
420 LDB_ERR_OPERATIONS_ERROR);
422 return LDB_SUCCESS;
425 /* Called when we search for our own entry. Stores the one entry we
426 * expect (as it is a base search) on the context pointer */
427 static int lpdb_mod_search_callback(struct ldb_request *req,
428 struct ldb_reply *ares)
430 struct ldb_context *ldb;
431 struct ldb_request *local_req;
432 struct lpdb_context *ac;
433 struct ldb_dn *local_dn;
434 struct GUID objectGUID;
435 int ret = LDB_SUCCESS;
437 ac = talloc_get_type(req->context, struct lpdb_context);
438 ldb = ldb_module_get_ctx(ac->module);
440 if (!ares) {
441 return ldb_module_done(ac->req, NULL, NULL,
442 LDB_ERR_OPERATIONS_ERROR);
444 if (ares->error != LDB_SUCCESS) {
445 return ldb_module_done(ac->req, ares->controls,
446 ares->response, ares->error);
449 switch (ares->type) {
450 case LDB_REPLY_ENTRY:
451 if (ac->remote != NULL) {
452 ldb_set_errstring(ldb, "Too many results");
453 talloc_free(ares);
454 return ldb_module_done(ac->req, NULL, NULL,
455 LDB_ERR_OPERATIONS_ERROR);
458 ac->remote = talloc_steal(ac, ares);
459 break;
461 case LDB_REPLY_REFERRAL:
463 /* ignore */
464 talloc_free(ares);
465 break;
467 case LDB_REPLY_DONE:
468 /* After we find out the objectGUID for the entry, modify the local
469 * password database as required */
471 talloc_free(ares);
473 /* if it is not an entry of type person this is an error */
474 /* TODO: remove this when sambaPassword will be in schema */
475 if (ac->remote == NULL) {
476 ldb_asprintf_errstring(ldb,
477 "entry just modified (%s) not found!",
478 ldb_dn_get_linearized(req->op.search.base));
479 return ldb_module_done(ac->req, NULL, NULL,
480 LDB_ERR_OPERATIONS_ERROR);
482 if (!ldb_msg_check_string_attribute(ac->remote->message,
483 "objectClass", "person")) {
484 /* Not relevent to us */
485 return ldb_module_done(ac->req,
486 ac->remote_done->controls,
487 ac->remote_done->response,
488 ac->remote_done->error);
491 if (ldb_msg_find_ldb_val(ac->remote->message,
492 "objectGUID") == NULL) {
493 ldb_set_errstring(ldb,
494 "no objectGUID found in search: "
495 "local_password module must be "
496 "configured below objectGUID "
497 "module!\n");
498 return ldb_module_done(ac->req, NULL, NULL,
499 LDB_ERR_OBJECT_CLASS_VIOLATION);
502 objectGUID = samdb_result_guid(ac->remote->message,
503 "objectGUID");
505 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
506 if ((local_dn == NULL) ||
507 ( ! ldb_dn_add_child_fmt(local_dn,
508 PASSWORD_GUID_ATTR "=%s",
509 GUID_string(ac, &objectGUID)))) {
510 return ldb_module_done(ac->req, NULL, NULL,
511 LDB_ERR_OPERATIONS_ERROR);
513 ac->local_message->dn = local_dn;
515 ret = ldb_build_mod_req(&local_req, ldb, ac,
516 ac->local_message,
517 NULL,
518 ac, lpdb_local_callback,
519 ac->req);
520 LDB_REQ_SET_LOCATION(local_req);
521 if (ret != LDB_SUCCESS) {
522 return ldb_module_done(ac->req, NULL, NULL, ret);
525 /* perform the local update */
526 ret = ldb_next_request(ac->module, local_req);
527 if (ret != LDB_SUCCESS) {
528 return ldb_module_done(ac->req, NULL, NULL, ret);
532 return LDB_SUCCESS;
535 /*****************************************************************************
536 * DELETE
537 ****************************************************************************/
539 static int lpdb_delete_callback(struct ldb_request *req,
540 struct ldb_reply *ares);
541 static int lpdb_del_search_callback(struct ldb_request *req,
542 struct ldb_reply *ares);
544 static int local_password_delete(struct ldb_module *module,
545 struct ldb_request *req)
547 struct ldb_context *ldb;
548 struct ldb_request *remote_req;
549 struct lpdb_context *ac;
550 int ret;
552 ldb = ldb_module_get_ctx(module);
553 ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_delete\n");
555 /* do not manipulate our control entries */
556 if (ldb_dn_is_special(req->op.mod.message->dn)) {
557 return ldb_next_request(module, req);
560 /* If the caller is manipulating the local passwords directly,
561 * let them pass */
562 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
563 req->op.del.dn) == 0) {
564 return ldb_next_request(module, req);
567 /* From here, we assume we have password attributes to split off */
568 ac = lpdb_init_context(module, req);
569 if (!ac) {
570 return ldb_operr(ldb);
573 ret = ldb_build_del_req(&remote_req, ldb, ac,
574 req->op.del.dn,
575 req->controls,
576 ac, lpdb_delete_callback,
577 req);
578 LDB_REQ_SET_LOCATION(remote_req);
579 if (ret != LDB_SUCCESS) {
580 return ret;
583 return ldb_next_request(module, remote_req);
586 /* On a modify, we don't have the objectGUID handy, so we need to
587 * search our DN for it */
588 static int lpdb_delete_callback(struct ldb_request *req,
589 struct ldb_reply *ares)
591 struct ldb_context *ldb;
592 static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
593 struct ldb_request *search_req;
594 struct lpdb_context *ac;
595 int ret;
597 ac = talloc_get_type(req->context, struct lpdb_context);
598 ldb = ldb_module_get_ctx(ac->module);
600 if (!ares) {
601 return ldb_module_done(ac->req, NULL, NULL,
602 LDB_ERR_OPERATIONS_ERROR);
604 if (ares->error != LDB_SUCCESS) {
605 return ldb_module_done(ac->req, ares->controls,
606 ares->response, ares->error);
609 if (ares->type != LDB_REPLY_DONE) {
610 ldb_set_errstring(ldb, "Unexpected reply type");
611 talloc_free(ares);
612 return ldb_module_done(ac->req, NULL, NULL,
613 LDB_ERR_OPERATIONS_ERROR);
616 ac->remote_done = talloc_steal(ac, ares);
618 /* prepare the search operation */
619 ret = ldb_build_search_req(&search_req, ldb, ac,
620 ac->req->op.del.dn, LDB_SCOPE_BASE,
621 "(objectclass=*)", attrs,
622 NULL,
623 ac, lpdb_del_search_callback,
624 ac->req);
625 LDB_REQ_SET_LOCATION(search_req);
626 if (ret != LDB_SUCCESS) {
627 return ldb_module_done(ac->req, NULL, NULL,
628 LDB_ERR_OPERATIONS_ERROR);
631 ret = ldb_next_request(ac->module, search_req);
632 if (ret != LDB_SUCCESS) {
633 return ldb_module_done(ac->req, NULL, NULL, ret);
635 return LDB_SUCCESS;
638 /* Called when we search for our own entry. Stores the one entry we
639 * expect (as it is a base search) on the context pointer */
640 static int lpdb_del_search_callback(struct ldb_request *req,
641 struct ldb_reply *ares)
643 struct ldb_context *ldb;
644 struct ldb_request *local_req;
645 struct lpdb_context *ac;
646 struct ldb_dn *local_dn;
647 struct GUID objectGUID;
648 int ret = LDB_SUCCESS;
650 ac = talloc_get_type(req->context, struct lpdb_context);
651 ldb = ldb_module_get_ctx(ac->module);
653 if (!ares) {
654 return ldb_module_done(ac->req, NULL, NULL,
655 LDB_ERR_OPERATIONS_ERROR);
657 if (ares->error != LDB_SUCCESS) {
658 return ldb_module_done(ac->req, ares->controls,
659 ares->response, ares->error);
662 switch (ares->type) {
663 case LDB_REPLY_ENTRY:
664 if (ac->remote != NULL) {
665 ldb_set_errstring(ldb, "Too many results");
666 talloc_free(ares);
667 return ldb_module_done(ac->req, NULL, NULL,
668 LDB_ERR_OPERATIONS_ERROR);
671 ac->remote = talloc_steal(ac, ares);
672 break;
674 case LDB_REPLY_REFERRAL:
676 /* ignore */
677 talloc_free(ares);
678 break;
680 case LDB_REPLY_DONE:
681 /* After we find out the objectGUID for the entry, modify the local
682 * password database as required */
684 talloc_free(ares);
686 /* if it is not an entry of type person this is NOT an error */
687 /* TODO: remove this when sambaPassword will be in schema */
688 if (ac->remote == NULL) {
689 return ldb_module_done(ac->req,
690 ac->remote_done->controls,
691 ac->remote_done->response,
692 ac->remote_done->error);
694 if (!ldb_msg_check_string_attribute(ac->remote->message,
695 "objectClass", "person")) {
696 /* Not relevent to us */
697 return ldb_module_done(ac->req,
698 ac->remote_done->controls,
699 ac->remote_done->response,
700 ac->remote_done->error);
703 if (ldb_msg_find_ldb_val(ac->remote->message,
704 "objectGUID") == NULL) {
705 ldb_set_errstring(ldb,
706 "no objectGUID found in search: "
707 "local_password module must be "
708 "configured below objectGUID "
709 "module!\n");
710 return ldb_module_done(ac->req, NULL, NULL,
711 LDB_ERR_OBJECT_CLASS_VIOLATION);
714 objectGUID = samdb_result_guid(ac->remote->message,
715 "objectGUID");
717 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
718 if ((local_dn == NULL) ||
719 ( ! ldb_dn_add_child_fmt(local_dn,
720 PASSWORD_GUID_ATTR "=%s",
721 GUID_string(ac, &objectGUID)))) {
722 return ldb_module_done(ac->req, NULL, NULL,
723 LDB_ERR_OPERATIONS_ERROR);
726 ret = ldb_build_del_req(&local_req, ldb, ac,
727 local_dn,
728 NULL,
729 ac, lpdb_local_callback,
730 ac->req);
731 LDB_REQ_SET_LOCATION(local_req);
732 if (ret != LDB_SUCCESS) {
733 return ldb_module_done(ac->req, NULL, NULL, ret);
736 /* perform the local update */
737 ret = ldb_next_request(ac->module, local_req);
738 if (ret != LDB_SUCCESS) {
739 return ldb_module_done(ac->req, NULL, NULL, ret);
743 return LDB_SUCCESS;
747 /*****************************************************************************
748 * SEARCH
749 ****************************************************************************/
751 static int lpdb_local_search_callback(struct ldb_request *req,
752 struct ldb_reply *ares);
754 static int lpdb_local_search(struct lpdb_context *ac)
756 struct ldb_context *ldb;
757 struct ldb_request *local_req;
758 int ret;
760 ldb = ldb_module_get_ctx(ac->module);
762 ret = ldb_build_search_req(&local_req, ldb, ac,
763 ac->current->local_dn,
764 LDB_SCOPE_BASE,
765 "(objectclass=*)",
766 ac->req->op.search.attrs,
767 NULL,
768 ac, lpdb_local_search_callback,
769 ac->req);
770 LDB_REQ_SET_LOCATION(local_req);
771 if (ret != LDB_SUCCESS) {
772 return ldb_operr(ldb);
775 return ldb_next_request(ac->module, local_req);
778 static int lpdb_local_search_callback(struct ldb_request *req,
779 struct ldb_reply *ares)
781 struct ldb_context *ldb;
782 struct lpdb_context *ac;
783 struct ldb_reply *merge;
784 struct lpdb_reply *lr;
785 int ret;
786 unsigned int i;
788 ac = talloc_get_type(req->context, struct lpdb_context);
789 ldb = ldb_module_get_ctx(ac->module);
791 if (!ares) {
792 return ldb_module_done(ac->req, NULL, NULL,
793 LDB_ERR_OPERATIONS_ERROR);
795 if (ares->error != LDB_SUCCESS) {
796 return ldb_module_done(ac->req, ares->controls,
797 ares->response, ares->error);
800 lr = ac->current;
802 /* we are interested only in a single reply (base search) */
803 switch (ares->type) {
804 case LDB_REPLY_ENTRY:
806 if (lr->remote == NULL) {
807 ldb_set_errstring(ldb,
808 "Too many results for password entry search!");
809 talloc_free(ares);
810 return ldb_module_done(ac->req, NULL, NULL,
811 LDB_ERR_OPERATIONS_ERROR);
814 merge = lr->remote;
815 lr->remote = NULL;
817 /* steal the local results on the remote results to be
818 * returned all together */
819 talloc_steal(merge, ares->message->elements);
821 /* Make sure never to return the internal key attribute */
822 ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
824 for (i=0; i < ares->message->num_elements; i++) {
825 struct ldb_message_element *el;
827 el = ldb_msg_find_element(merge->message,
828 ares->message->elements[i].name);
829 if (!el) {
830 ret = ldb_msg_add_empty(merge->message,
831 ares->message->elements[i].name,
832 0, &el);
833 if (ret != LDB_SUCCESS) {
834 talloc_free(ares);
835 return ldb_module_done(ac->req,
836 NULL, NULL,
837 LDB_ERR_OPERATIONS_ERROR);
839 *el = ares->message->elements[i];
843 /* free the rest */
844 talloc_free(ares);
846 return ldb_module_send_entry(ac->req, merge->message, merge->controls);
848 case LDB_REPLY_REFERRAL:
849 /* ignore */
850 talloc_free(ares);
851 break;
853 case LDB_REPLY_DONE:
855 talloc_free(ares);
857 /* if this entry was not returned yet, return it now */
858 if (lr->remote) {
859 ret = ldb_module_send_entry(ac->req, ac->remote->message, ac->remote->controls);
860 if (ret != LDB_SUCCESS) {
861 return ldb_module_done(ac->req,
862 NULL, NULL, ret);
864 lr->remote = NULL;
867 if (lr->next->remote->type == LDB_REPLY_DONE) {
868 /* this was the last one */
869 return ldb_module_done(ac->req,
870 lr->next->remote->controls,
871 lr->next->remote->response,
872 lr->next->remote->error);
873 } else {
874 /* next one */
875 ac->current = lr->next;
876 talloc_free(lr);
878 ret = lpdb_local_search(ac);
879 if (ret != LDB_SUCCESS) {
880 return ldb_module_done(ac->req,
881 NULL, NULL, ret);
886 return LDB_SUCCESS;
889 /* For each entry returned in a remote search, do a local base search,
890 * based on the objectGUID we asked for as an additional attribute */
891 static int lpdb_remote_search_callback(struct ldb_request *req,
892 struct ldb_reply *ares)
894 struct ldb_context *ldb;
895 struct lpdb_context *ac;
896 struct ldb_dn *local_dn;
897 struct GUID objectGUID;
898 struct lpdb_reply *lr;
899 int ret;
901 ac = talloc_get_type(req->context, struct lpdb_context);
902 ldb = ldb_module_get_ctx(ac->module);
904 if (!ares) {
905 return ldb_module_done(ac->req, NULL, NULL,
906 LDB_ERR_OPERATIONS_ERROR);
908 if (ares->error != LDB_SUCCESS) {
909 return ldb_module_done(ac->req, ares->controls,
910 ares->response, ares->error);
913 switch (ares->type) {
914 case LDB_REPLY_ENTRY:
915 /* No point searching further if it's not a 'person' entry */
916 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
918 /* Make sure to remove anything we added */
919 if (ac->added_objectGUID) {
920 ldb_msg_remove_attr(ares->message, "objectGUID");
923 if (ac->added_objectClass) {
924 ldb_msg_remove_attr(ares->message, "objectClass");
927 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
930 if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
931 ldb_set_errstring(ldb,
932 "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
933 return ldb_module_done(ac->req, NULL, NULL,
934 LDB_ERR_OPERATIONS_ERROR);
937 objectGUID = samdb_result_guid(ares->message, "objectGUID");
939 if (ac->added_objectGUID) {
940 ldb_msg_remove_attr(ares->message, "objectGUID");
943 if (ac->added_objectClass) {
944 ldb_msg_remove_attr(ares->message, "objectClass");
947 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
948 if ((local_dn == NULL) ||
949 (! ldb_dn_add_child_fmt(local_dn,
950 PASSWORD_GUID_ATTR "=%s",
951 GUID_string(ac, &objectGUID)))) {
952 return ldb_module_done(ac->req, NULL, NULL,
953 LDB_ERR_OPERATIONS_ERROR);
956 lr = talloc_zero(ac, struct lpdb_reply);
957 if (lr == NULL) {
958 return ldb_module_done(ac->req, NULL, NULL,
959 LDB_ERR_OPERATIONS_ERROR);
961 lr->local_dn = talloc_steal(lr, local_dn);
962 lr->remote = talloc_steal(lr, ares);
964 if (ac->list) {
965 ac->current->next = lr;
966 } else {
967 ac->list = lr;
969 ac->current= lr;
971 break;
973 case LDB_REPLY_REFERRAL:
975 return ldb_module_send_referral(ac->req, ares->referral);
977 case LDB_REPLY_DONE:
979 if (ac->list == NULL) {
980 /* found nothing */
981 return ldb_module_done(ac->req, ares->controls,
982 ares->response, ares->error);
985 lr = talloc_zero(ac, struct lpdb_reply);
986 if (lr == NULL) {
987 return ldb_module_done(ac->req, NULL, NULL,
988 LDB_ERR_OPERATIONS_ERROR);
990 lr->remote = talloc_steal(lr, ares);
992 ac->current->next = lr;
994 /* rewind current and start local searches */
995 ac->current= ac->list;
997 ret = lpdb_local_search(ac);
998 if (ret != LDB_SUCCESS) {
999 return ldb_module_done(ac->req, NULL, NULL, ret);
1003 return LDB_SUCCESS;
1006 /* Search for passwords and other attributes. The passwords are
1007 * local, but the other attributes are remote, and we need to glue the
1008 * two search spaces back togeather */
1010 static int local_password_search(struct ldb_module *module, struct ldb_request *req)
1012 struct ldb_context *ldb;
1013 struct ldb_request *remote_req;
1014 struct lpdb_context *ac;
1015 unsigned int i;
1016 int ret;
1017 const char * const *search_attrs = NULL;
1019 ldb = ldb_module_get_ctx(module);
1020 ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_search\n");
1022 if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
1023 return ldb_next_request(module, req);
1026 search_attrs = NULL;
1028 /* If the caller is searching for the local passwords directly, let them pass */
1029 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1030 req->op.search.base) == 0) {
1031 return ldb_next_request(module, req);
1034 if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
1035 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
1036 if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
1037 break;
1041 /* It didn't match any of our password attributes, go on */
1042 if (i == ARRAY_SIZE(password_attrs)) {
1043 return ldb_next_request(module, req);
1047 ac = lpdb_init_context(module, req);
1048 if (!ac) {
1049 return ldb_operr(ldb);
1052 /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
1053 if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
1054 if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
1055 search_attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectGUID");
1056 ac->added_objectGUID = true;
1057 if (!search_attrs) {
1058 return ldb_operr(ldb);
1060 } else {
1061 search_attrs = req->op.search.attrs;
1063 if (!ldb_attr_in_list(search_attrs, "objectClass")) {
1064 search_attrs = ldb_attr_list_copy_add(ac, search_attrs, "objectClass");
1065 ac->added_objectClass = true;
1066 if (!search_attrs) {
1067 return ldb_operr(ldb);
1070 } else {
1071 search_attrs = req->op.search.attrs;
1074 ret = ldb_build_search_req_ex(&remote_req, ldb, ac,
1075 req->op.search.base,
1076 req->op.search.scope,
1077 req->op.search.tree,
1078 search_attrs,
1079 req->controls,
1080 ac, lpdb_remote_search_callback,
1081 req);
1082 LDB_REQ_SET_LOCATION(remote_req);
1083 if (ret != LDB_SUCCESS) {
1084 return ret;
1087 /* perform the search */
1088 return ldb_next_request(module, remote_req);
1091 static const struct ldb_module_ops ldb_local_password_module_ops = {
1092 .name = "local_password",
1093 .add = local_password_add,
1094 .modify = local_password_modify,
1095 .del = local_password_delete,
1096 .search = local_password_search
1099 int ldb_local_password_module_init(const char *version)
1101 LDB_MODULE_CHECK_VERSION(version);
1102 return ldb_register_module(&ldb_local_password_module_ops);