TODO test/sq sort meta data after adding isDeleted
[metze/samba/wip.git] / source4 / dsdb / samdb / ldb_modules / local_password.c
1 /* 
2    ldb database module
3
4    Copyright (C) Simo Sorce  2004-2008
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
6    Copyright (C) Andrew Tridgell 2004
7
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.
12    
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.
17    
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/>.
20 */
21
22 /*
23  *  Name: ldb
24  *
25  *  Component: ldb local_password module
26  *
27  *  Description: correctly update hash values based on changes to userPassword and friends
28  *
29  *  Author: Andrew Bartlett
30  */
31
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"
39
40 #define PASSWORD_GUID_ATTR "masterGUID"
41
42 /* This module maintains a local password database, separate from the main LDAP
43    server.
44
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)
48
49    Each incoming add/modify is split into a remote, and a local request, done
50    in that order.
51
52    We maintain a list of attributes that are kept locally - perhaps
53    this should use the @KLUDGE_ACL list of passwordAttribute
54  */
55
56 static const char * const password_attrs[] = {
57         "pwdLastSet",
58         DSDB_SECRET_ATTRIBUTES
59 };
60
61 /* And we merge them back into search requests when asked to do so */
62
63 struct lpdb_reply {
64         struct lpdb_reply *next;
65         struct ldb_reply *remote;
66         struct ldb_dn *local_dn;
67 };
68
69 struct lpdb_context {
70
71         struct ldb_module *module;
72         struct ldb_request *req;
73
74         struct ldb_message *local_message;
75
76         struct lpdb_reply *list;
77         struct lpdb_reply *current;
78         struct ldb_reply *remote_done;
79         struct ldb_reply *remote;
80
81         bool added_objectGUID;
82         bool added_objectClass;
83
84 };
85
86 static struct lpdb_context *lpdb_init_context(struct ldb_module *module,
87                                               struct ldb_request *req)
88 {
89         struct ldb_context *ldb;
90         struct lpdb_context *ac;
91
92         ldb = ldb_module_get_ctx(module);
93
94         ac = talloc_zero(req, struct lpdb_context);
95         if (ac == NULL) {
96                 ldb_set_errstring(ldb, "Out of Memory");
97                 return NULL;
98         }
99
100         ac->module = module;
101         ac->req = req;
102
103         return ac;
104 }
105
106 static int lpdb_local_callback(struct ldb_request *req, struct ldb_reply *ares)
107 {
108         struct ldb_context *ldb;
109         struct lpdb_context *ac;
110
111         ac = talloc_get_type(req->context, struct lpdb_context);
112         ldb = ldb_module_get_ctx(ac->module);
113
114         if (!ares) {
115                 return ldb_module_done(ac->req, NULL, NULL,
116                                         LDB_ERR_OPERATIONS_ERROR);
117         }
118         if (ares->error != LDB_SUCCESS) {
119                 return ldb_module_done(ac->req, ares->controls,
120                                         ares->response, ares->error);
121         }
122
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);
128         }
129
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);
135 }
136
137 /*****************************************************************************
138  * ADD
139  ****************************************************************************/
140
141 static int lpdb_add_callback(struct ldb_request *req,
142                                 struct ldb_reply *ares);
143
144 static int local_password_add(struct ldb_module *module, struct ldb_request *req)
145 {
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;
153
154         ldb = ldb_module_get_ctx(module);
155         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_add\n");
156
157         if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
158                 return ldb_next_request(module, req);
159         }
160
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);
165         }
166
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;
170                 }
171         }
172
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);
176         }
177
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);
182         }
183
184         remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message);
185         if (remote_message == NULL) {
186                 return ldb_operr(ldb);
187         }
188
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]);
192         }
193
194         /* Find the objectGUID to use as the key */
195         objectGUID = samdb_result_guid(ac->req->op.add.message, "objectGUID");
196
197         ac->local_message = ldb_msg_copy_shallow(ac, req->op.add.message);
198         if (ac->local_message == NULL) {
199                 return ldb_operr(ldb);
200         }
201
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);
206         }
207
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;
217         }
218
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);
227         }
228
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;
237         }
238
239         return ldb_next_request(module, remote_req);
240 }
241
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)
246 {
247         struct ldb_context *ldb;
248         struct ldb_request *local_req;
249         struct lpdb_context *ac;
250         int ret;
251
252         ac = talloc_get_type(req->context, struct lpdb_context);
253         ldb = ldb_module_get_ctx(ac->module);
254
255         if (!ares) {
256                 return ldb_module_done(ac->req, NULL, NULL,
257                                         LDB_ERR_OPERATIONS_ERROR);
258         }
259         if (ares->error != LDB_SUCCESS) {
260                 return ldb_module_done(ac->req, ares->controls,
261                                         ares->response, ares->error);
262         }
263
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);
269         }
270
271         ac->remote_done = talloc_steal(ac, ares);
272
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);
281         }
282
283         ret = ldb_next_request(ac->module, local_req);
284         if (ret != LDB_SUCCESS) {
285                 return ldb_module_done(ac->req, NULL, NULL, ret);
286         }
287         return LDB_SUCCESS;
288 }
289
290 /*****************************************************************************
291  * MODIFY
292  ****************************************************************************/
293
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);
298
299 static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
300 {
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;
307
308         ldb = ldb_module_get_ctx(module);
309         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
310
311         if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
312                 return ldb_next_request(module, req);
313         }
314
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);
319         }
320
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;
324                 }
325         }
326
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);
330         }
331
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);
336         }
337
338         remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
339         if (remote_message == NULL) {
340                 return ldb_operr(ldb);
341         }
342
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]);
346         }
347
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);
351         }
352
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);
357         }
358
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;
367         }
368
369         return ldb_next_request(module, remote_req);
370 }
371
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)
376 {
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;
382
383         ac = talloc_get_type(req->context, struct lpdb_context);
384         ldb = ldb_module_get_ctx(ac->module);
385
386         if (!ares) {
387                 return ldb_module_done(ac->req, NULL, NULL,
388                                         LDB_ERR_OPERATIONS_ERROR);
389         }
390         if (ares->error != LDB_SUCCESS) {
391                 return ldb_module_done(ac->req, ares->controls,
392                                         ares->response, ares->error);
393         }
394
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);
400         }
401
402         ac->remote_done = talloc_steal(ac, ares);
403
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);
415         }
416
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);
421         }
422         return LDB_SUCCESS;
423 }
424
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)
429 {
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;
436
437         ac = talloc_get_type(req->context, struct lpdb_context);
438         ldb = ldb_module_get_ctx(ac->module);
439
440         if (!ares) {
441                 return ldb_module_done(ac->req, NULL, NULL,
442                                         LDB_ERR_OPERATIONS_ERROR);
443         }
444         if (ares->error != LDB_SUCCESS) {
445                 return ldb_module_done(ac->req, ares->controls,
446                                         ares->response, ares->error);
447         }
448
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);
456                 }
457
458                 ac->remote = talloc_steal(ac, ares);
459                 break;
460
461         case LDB_REPLY_REFERRAL:
462
463                 /* ignore */
464                 talloc_free(ares);
465                 break;
466
467         case LDB_REPLY_DONE:
468                 /* After we find out the objectGUID for the entry, modify the local
469                  * password database as required */
470
471                 talloc_free(ares);
472
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);
481                 }
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);
489                 }
490
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);
500                 }
501
502                 objectGUID = samdb_result_guid(ac->remote->message,
503                                                 "objectGUID");
504
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);
512                 }
513                 ac->local_message->dn = local_dn;
514
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);
523                 }
524
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);
529                 }
530         }
531
532         return LDB_SUCCESS;
533 }
534
535 /*****************************************************************************
536  * DELETE
537  ****************************************************************************/
538
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);
543
544 static int local_password_delete(struct ldb_module *module,
545                                  struct ldb_request *req)
546 {
547         struct ldb_context *ldb;
548         struct ldb_request *remote_req;
549         struct lpdb_context *ac;
550         int ret;
551
552         ldb = ldb_module_get_ctx(module);
553         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_delete\n");
554
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);
558         }
559
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);
565         }
566
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);
571         }
572
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;
581         }
582
583         return ldb_next_request(module, remote_req);
584 }
585
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)
590 {
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;
596
597         ac = talloc_get_type(req->context, struct lpdb_context);
598         ldb = ldb_module_get_ctx(ac->module);
599
600         if (!ares) {
601                 return ldb_module_done(ac->req, NULL, NULL,
602                                         LDB_ERR_OPERATIONS_ERROR);
603         }
604         if (ares->error != LDB_SUCCESS) {
605                 return ldb_module_done(ac->req, ares->controls,
606                                         ares->response, ares->error);
607         }
608
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);
614         }
615
616         ac->remote_done = talloc_steal(ac, ares);
617
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);
629         }
630
631         ret = ldb_next_request(ac->module, search_req);
632         if (ret != LDB_SUCCESS) {
633                 return ldb_module_done(ac->req, NULL, NULL, ret);
634         }
635         return LDB_SUCCESS;
636 }
637
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)
642 {
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;
649
650         ac = talloc_get_type(req->context, struct lpdb_context);
651         ldb = ldb_module_get_ctx(ac->module);
652
653         if (!ares) {
654                 return ldb_module_done(ac->req, NULL, NULL,
655                                         LDB_ERR_OPERATIONS_ERROR);
656         }
657         if (ares->error != LDB_SUCCESS) {
658                 return ldb_module_done(ac->req, ares->controls,
659                                         ares->response, ares->error);
660         }
661
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);
669                 }
670
671                 ac->remote = talloc_steal(ac, ares);
672                 break;
673
674         case LDB_REPLY_REFERRAL:
675
676                 /* ignore */
677                 talloc_free(ares);
678                 break;
679
680         case LDB_REPLY_DONE:
681                 /* After we find out the objectGUID for the entry, modify the local
682                  * password database as required */
683
684                 talloc_free(ares);
685
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);
693                 }
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);
701                 }
702
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);
712                 }
713
714                 objectGUID = samdb_result_guid(ac->remote->message,
715                                                 "objectGUID");
716
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);
724                 }
725
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);
734                 }
735
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);
740                 }
741         }
742
743         return LDB_SUCCESS;
744 }
745
746
747 /*****************************************************************************
748  * SEARCH
749  ****************************************************************************/
750
751 static int lpdb_local_search_callback(struct ldb_request *req,
752                                         struct ldb_reply *ares);
753
754 static int lpdb_local_search(struct lpdb_context *ac)
755 {
756         struct ldb_context *ldb;
757         struct ldb_request *local_req;
758         int ret;
759
760         ldb = ldb_module_get_ctx(ac->module);
761
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);
773         }
774
775         return ldb_next_request(ac->module, local_req);
776 }
777
778 static int lpdb_local_search_callback(struct ldb_request *req,
779                                         struct ldb_reply *ares)
780 {
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;
787
788         ac = talloc_get_type(req->context, struct lpdb_context);
789         ldb = ldb_module_get_ctx(ac->module);
790
791         if (!ares) {
792                 return ldb_module_done(ac->req, NULL, NULL,
793                                         LDB_ERR_OPERATIONS_ERROR);
794         }
795         if (ares->error != LDB_SUCCESS) {
796                 return ldb_module_done(ac->req, ares->controls,
797                                         ares->response, ares->error);
798         }
799
800         lr = ac->current;
801
802         /* we are interested only in a single reply (base search) */
803         switch (ares->type) {
804         case LDB_REPLY_ENTRY:
805
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);
812                 }
813
814                 merge = lr->remote;
815                 lr->remote = NULL;
816
817                 /* steal the local results on the remote results to be
818                  * returned all together */
819                 talloc_steal(merge, ares->message->elements);
820
821                 /* Make sure never to return the internal key attribute */
822                 ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
823
824                 for (i=0; i < ares->message->num_elements; i++) {
825                         struct ldb_message_element *el;
826                         
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);
838                                 }
839                                 *el = ares->message->elements[i];
840                         }
841                 }
842
843                 /* free the rest */
844                 talloc_free(ares);
845
846                 return ldb_module_send_entry(ac->req, merge->message, merge->controls);
847
848         case LDB_REPLY_REFERRAL:
849                 /* ignore */
850                 talloc_free(ares);
851                 break;
852
853         case LDB_REPLY_DONE:
854
855                 talloc_free(ares);
856
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);
863                         }
864                         lr->remote = NULL;
865                 }
866
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);
877
878                         ret = lpdb_local_search(ac);
879                         if (ret != LDB_SUCCESS) {
880                                 return ldb_module_done(ac->req,
881                                                         NULL, NULL, ret);
882                         }
883                 }
884         }
885
886         return LDB_SUCCESS;
887 }
888
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)
893 {
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;
900
901         ac = talloc_get_type(req->context, struct lpdb_context);
902         ldb = ldb_module_get_ctx(ac->module);
903
904         if (!ares) {
905                 return ldb_module_done(ac->req, NULL, NULL,
906                                         LDB_ERR_OPERATIONS_ERROR);
907         }
908         if (ares->error != LDB_SUCCESS) {
909                 return ldb_module_done(ac->req, ares->controls,
910                                         ares->response, ares->error);
911         }
912
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")) {
917
918                         /* Make sure to remove anything we added */
919                         if (ac->added_objectGUID) {
920                                 ldb_msg_remove_attr(ares->message, "objectGUID");
921                         }
922                         
923                         if (ac->added_objectClass) {
924                                 ldb_msg_remove_attr(ares->message, "objectClass");
925                         }
926                         
927                         return ldb_module_send_entry(ac->req, ares->message, ares->controls);
928                 }
929
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);
935                 }
936         
937                 objectGUID = samdb_result_guid(ares->message, "objectGUID");
938
939                 if (ac->added_objectGUID) {
940                         ldb_msg_remove_attr(ares->message, "objectGUID");
941                 }
942
943                 if (ac->added_objectClass) {
944                         ldb_msg_remove_attr(ares->message, "objectClass");
945                 }
946
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);
954                 }
955
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);
960                 }
961                 lr->local_dn = talloc_steal(lr, local_dn);
962                 lr->remote = talloc_steal(lr, ares);
963
964                 if (ac->list) {
965                         ac->current->next = lr;
966                 } else {
967                         ac->list = lr;
968                 }
969                 ac->current= lr;
970
971                 break;
972
973         case LDB_REPLY_REFERRAL:
974
975                 return ldb_module_send_referral(ac->req, ares->referral);
976
977         case LDB_REPLY_DONE:
978
979                 if (ac->list == NULL) {
980                         /* found nothing */
981                         return ldb_module_done(ac->req, ares->controls,
982                                                 ares->response, ares->error);
983                 }
984
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);
989                 }
990                 lr->remote = talloc_steal(lr, ares);
991
992                 ac->current->next = lr;
993
994                 /* rewind current and start local searches */
995                 ac->current= ac->list;
996
997                 ret = lpdb_local_search(ac);
998                 if (ret != LDB_SUCCESS) {
999                         return ldb_module_done(ac->req, NULL, NULL, ret);
1000                 }
1001         }
1002
1003         return LDB_SUCCESS;
1004 }
1005
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 */
1009
1010 static int local_password_search(struct ldb_module *module, struct ldb_request *req)
1011 {
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;
1018
1019         ldb = ldb_module_get_ctx(module);
1020         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_search\n");
1021
1022         if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
1023                 return ldb_next_request(module, req);
1024         }
1025
1026         search_attrs = NULL;
1027
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);
1032         }
1033
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;
1038                         }
1039                 }
1040                 
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);
1044                 }
1045         }
1046
1047         ac = lpdb_init_context(module, req);
1048         if (!ac) {
1049                 return ldb_operr(ldb);
1050         }
1051
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);
1059                         }
1060                 } else {
1061                         search_attrs = req->op.search.attrs;
1062                 }
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);
1068                         }
1069                 }
1070         } else {
1071                 search_attrs = req->op.search.attrs;
1072         }
1073
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;
1085         }
1086
1087         /* perform the search */
1088         return ldb_next_request(module, remote_req);
1089 }
1090
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
1097 };
1098
1099 int ldb_local_password_module_init(const char *version)
1100 {
1101         LDB_MODULE_CHECK_VERSION(version);
1102         return ldb_register_module(&ldb_local_password_module_ops);
1103 }