s4-dsdb: added tagging of requests in dsdb modules
[mat/samba.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
39 #define PASSWORD_GUID_ATTR "masterGUID"
40
41 /* This module maintains a local password database, seperate from the main LDAP server.
42
43    This allows the password database to be syncronised in a multi-master
44    fashion, seperate to the more difficult concerns of the main
45    database.  (With passwords, the last writer always wins)
46
47    Each incoming add/modify is split into a remote, and a local request, done in that order.
48
49    We maintain a list of attributes that are kept locally - perhaps
50    this should use the @KLUDGE_ACL list of passwordAttribute
51  */
52
53 static const char * const password_attrs[] = {
54         "supplementalCredentials",
55         "unicodePwd",
56         "dBCSPwd",
57         "lmPwdHistory", 
58         "ntPwdHistory", 
59         "msDS-KeyVersionNumber",
60         "pwdLastSet"
61 };
62
63 /* And we merge them back into search requests when asked to do so */
64
65 struct lpdb_reply {
66         struct lpdb_reply *next;
67         struct ldb_reply *remote;
68         struct ldb_dn *local_dn;
69 };
70
71 struct lpdb_context {
72
73         struct ldb_module *module;
74         struct ldb_request *req;
75
76         struct ldb_message *local_message;
77
78         struct lpdb_reply *list;
79         struct lpdb_reply *current;
80         struct ldb_reply *remote_done;
81         struct ldb_reply *remote;
82
83         bool added_objectGUID;
84         bool added_objectClass;
85
86 };
87
88 static struct lpdb_context *lpdb_init_context(struct ldb_module *module,
89                                               struct ldb_request *req)
90 {
91         struct ldb_context *ldb;
92         struct lpdb_context *ac;
93
94         ldb = ldb_module_get_ctx(module);
95
96         ac = talloc_zero(req, struct lpdb_context);
97         if (ac == NULL) {
98                 ldb_set_errstring(ldb, "Out of Memory");
99                 return NULL;
100         }
101
102         ac->module = module;
103         ac->req = req;
104
105         return ac;
106 }
107
108 static int lpdb_local_callback(struct ldb_request *req, struct ldb_reply *ares)
109 {
110         struct ldb_context *ldb;
111         struct lpdb_context *ac;
112
113         ac = talloc_get_type(req->context, struct lpdb_context);
114         ldb = ldb_module_get_ctx(ac->module);
115
116         if (!ares) {
117                 return ldb_module_done(ac->req, NULL, NULL,
118                                         LDB_ERR_OPERATIONS_ERROR);
119         }
120         if (ares->error != LDB_SUCCESS) {
121                 return ldb_module_done(ac->req, ares->controls,
122                                         ares->response, ares->error);
123         }
124
125         if (ares->type != LDB_REPLY_DONE) {
126                 ldb_set_errstring(ldb, "Unexpected reply type");
127                 talloc_free(ares);
128                 return ldb_module_done(ac->req, NULL, NULL,
129                                         LDB_ERR_OPERATIONS_ERROR);
130         }
131
132         talloc_free(ares);
133         return ldb_module_done(ac->req,
134                                 ac->remote_done->controls,
135                                 ac->remote_done->response,
136                                 ac->remote_done->error);
137 }
138
139 /*****************************************************************************
140  * ADD
141  ****************************************************************************/
142
143 static int lpdb_add_callback(struct ldb_request *req,
144                                 struct ldb_reply *ares);
145
146 static int local_password_add(struct ldb_module *module, struct ldb_request *req)
147 {
148         struct ldb_context *ldb;
149         struct ldb_message *remote_message;
150         struct ldb_request *remote_req;
151         struct lpdb_context *ac;
152         struct GUID objectGUID;
153         int ret;
154         unsigned int i;
155
156         ldb = ldb_module_get_ctx(module);
157         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_add\n");
158
159         if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
160                 return ldb_next_request(module, req);
161         }
162
163         /* If the caller is manipulating the local passwords directly, let them pass */
164         if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
165                                 req->op.add.message->dn) == 0) {
166                 return ldb_next_request(module, req);
167         }
168
169         for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
170                 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
171                         break;
172                 }
173         }
174
175         /* It didn't match any of our password attributes, go on */
176         if (i == ARRAY_SIZE(password_attrs)) {
177                 return ldb_next_request(module, req);
178         }
179
180         /* TODO: remove this when userPassword will be in schema */
181         if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
182                 ldb_asprintf_errstring(ldb,
183                                         "Cannot relocate a password on entry: %s, does not have objectClass 'person'",
184                                         ldb_dn_get_linearized(req->op.add.message->dn));
185                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
186         }
187
188         /* From here, we assume we have password attributes to split off */
189         ac = lpdb_init_context(module, req);
190         if (!ac) {
191                 return ldb_operr(ldb);
192         }
193
194         remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message);
195         if (remote_message == NULL) {
196                 return ldb_operr(ldb);
197         }
198
199         /* Remove any password attributes from the remote message */
200         for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
201                 ldb_msg_remove_attr(remote_message, password_attrs[i]);
202         }
203
204         /* Find the objectGUID to use as the key */
205         objectGUID = samdb_result_guid(ac->req->op.add.message, "objectGUID");
206
207         ac->local_message = ldb_msg_copy_shallow(ac, req->op.add.message);
208         if (ac->local_message == NULL) {
209                 return ldb_operr(ldb);
210         }
211
212         /* Remove anything seen in the remote message from the local
213          * message (leaving only password attributes) */
214         for (i=0; i < remote_message->num_elements; i++) {
215                 ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
216         }
217
218         /* We must have an objectGUID already, or we don't know where
219          * to add the password.  This may be changed to an 'add and
220          * search', to allow the directory to create the objectGUID */
221         if (ldb_msg_find_ldb_val(req->op.add.message, "objectGUID") == NULL) {
222                 ldb_set_errstring(ldb,
223                                   "no objectGUID found in search: "
224                                   "local_password module must be "
225                                   "onfigured below objectGUID module!\n");
226                 return LDB_ERR_CONSTRAINT_VIOLATION;
227         }
228
229         ac->local_message->dn = ldb_dn_new(ac->local_message,
230                                            ldb, LOCAL_BASE);
231         if ((ac->local_message->dn == NULL) ||
232             ( ! ldb_dn_add_child_fmt(ac->local_message->dn,
233                                      PASSWORD_GUID_ATTR "=%s",
234                                      GUID_string(ac->local_message,
235                                                         &objectGUID)))) {
236                 return ldb_operr(ldb);
237         }
238
239         ret = ldb_build_add_req(&remote_req, ldb, ac,
240                                 remote_message,
241                                 req->controls,
242                                 ac, lpdb_add_callback,
243                                 req);
244         if (ret != LDB_SUCCESS) {
245                 return ret;
246         }
247
248         return ldb_next_request(module, remote_req);
249 }
250
251 /* Add a record, splitting password attributes from the user's main
252  * record */
253 static int lpdb_add_callback(struct ldb_request *req,
254                                 struct ldb_reply *ares)
255 {
256         struct ldb_context *ldb;
257         struct ldb_request *local_req;
258         struct lpdb_context *ac;
259         int ret;
260
261         ac = talloc_get_type(req->context, struct lpdb_context);
262         ldb = ldb_module_get_ctx(ac->module);
263
264         if (!ares) {
265                 return ldb_module_done(ac->req, NULL, NULL,
266                                         LDB_ERR_OPERATIONS_ERROR);
267         }
268         if (ares->error != LDB_SUCCESS) {
269                 return ldb_module_done(ac->req, ares->controls,
270                                         ares->response, ares->error);
271         }
272
273         if (ares->type != LDB_REPLY_DONE) {
274                 ldb_set_errstring(ldb, "Unexpected reply type");
275                 talloc_free(ares);
276                 return ldb_module_done(ac->req, NULL, NULL,
277                                         LDB_ERR_OPERATIONS_ERROR);
278         }
279
280         ac->remote_done = talloc_steal(ac, ares);
281
282         ret = ldb_build_add_req(&local_req, ldb, ac,
283                                 ac->local_message,
284                                 NULL,
285                                 ac, lpdb_local_callback,
286                                 ac->req);
287         if (ret != LDB_SUCCESS) {
288                 return ldb_module_done(ac->req, NULL, NULL, ret);
289         }
290
291         ret = ldb_next_request(ac->module, local_req);
292         if (ret != LDB_SUCCESS) {
293                 return ldb_module_done(ac->req, NULL, NULL, ret);
294         }
295         return LDB_SUCCESS;
296 }
297
298 /*****************************************************************************
299  * MODIFY
300  ****************************************************************************/
301
302 static int lpdb_modify_callabck(struct ldb_request *req,
303                                 struct ldb_reply *ares);
304 static int lpdb_mod_search_callback(struct ldb_request *req,
305                                     struct ldb_reply *ares);
306
307 static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
308 {
309         struct ldb_context *ldb;
310         struct lpdb_context *ac;
311         struct ldb_message *remote_message;
312         struct ldb_request *remote_req;
313         int ret;
314         unsigned int i;
315
316         ldb = ldb_module_get_ctx(module);
317         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
318
319         if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
320                 return ldb_next_request(module, req);
321         }
322
323         /* If the caller is manipulating the local passwords directly, let them pass */
324         if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
325                                 req->op.mod.message->dn) == 0) {
326                 return ldb_next_request(module, req);
327         }
328
329         for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
330                 if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
331                         break;
332                 }
333         }
334
335         /* It didn't match any of our password attributes, then we have nothing to do here */
336         if (i == ARRAY_SIZE(password_attrs)) {
337                 return ldb_next_request(module, req);
338         }
339
340         /* From here, we assume we have password attributes to split off */
341         ac = lpdb_init_context(module, req);
342         if (!ac) {
343                 return ldb_operr(ldb);
344         }
345
346         remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
347         if (remote_message == NULL) {
348                 return ldb_operr(ldb);
349         }
350
351         /* Remove any password attributes from the remote message */
352         for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
353                 ldb_msg_remove_attr(remote_message, password_attrs[i]);
354         }
355
356         ac->local_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
357         if (ac->local_message == NULL) {
358                 return ldb_operr(ldb);
359         }
360
361         /* Remove anything seen in the remote message from the local
362          * message (leaving only password attributes) */
363         for (i=0; i < remote_message->num_elements;i++) {
364                 ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
365         }
366
367         ret = ldb_build_mod_req(&remote_req, ldb, ac,
368                                 remote_message,
369                                 req->controls,
370                                 ac, lpdb_modify_callabck,
371                                 req);
372         if (ret != LDB_SUCCESS) {
373                 return ret;
374         }
375
376         return ldb_next_request(module, remote_req);
377 }
378
379 /* On a modify, we don't have the objectGUID handy, so we need to
380  * search our DN for it */
381 static int lpdb_modify_callabck(struct ldb_request *req,
382                                 struct ldb_reply *ares)
383 {
384         struct ldb_context *ldb;
385         static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
386         struct ldb_request *search_req;
387         struct lpdb_context *ac;
388         int ret;
389
390         ac = talloc_get_type(req->context, struct lpdb_context);
391         ldb = ldb_module_get_ctx(ac->module);
392
393         if (!ares) {
394                 return ldb_module_done(ac->req, NULL, NULL,
395                                         LDB_ERR_OPERATIONS_ERROR);
396         }
397         if (ares->error != LDB_SUCCESS) {
398                 return ldb_module_done(ac->req, ares->controls,
399                                         ares->response, ares->error);
400         }
401
402         if (ares->type != LDB_REPLY_DONE) {
403                 ldb_set_errstring(ldb, "Unexpected reply type");
404                 talloc_free(ares);
405                 return ldb_module_done(ac->req, NULL, NULL,
406                                         LDB_ERR_OPERATIONS_ERROR);
407         }
408
409         ac->remote_done = talloc_steal(ac, ares);
410
411         /* prepare the search operation */
412         ret = ldb_build_search_req(&search_req, ldb, ac,
413                                    ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
414                                    "(objectclass=*)", attrs,
415                                    NULL,
416                                    ac, lpdb_mod_search_callback,
417                                    ac->req);
418         if (ret != LDB_SUCCESS) {
419                 return ldb_module_done(ac->req, NULL, NULL,
420                                         LDB_ERR_OPERATIONS_ERROR);
421         }
422
423         ret = ldb_next_request(ac->module, search_req);
424         if (ret != LDB_SUCCESS) {
425                 return ldb_module_done(ac->req, NULL, NULL,
426                                         LDB_ERR_OPERATIONS_ERROR);
427         }
428         return LDB_SUCCESS;
429 }
430
431 /* Called when we search for our own entry.  Stores the one entry we
432  * expect (as it is a base search) on the context pointer */
433 static int lpdb_mod_search_callback(struct ldb_request *req,
434                                     struct ldb_reply *ares)
435 {
436         struct ldb_context *ldb;
437         struct ldb_request *local_req;
438         struct lpdb_context *ac;
439         struct ldb_dn *local_dn;
440         struct GUID objectGUID;
441         int ret = LDB_SUCCESS;
442
443         ac = talloc_get_type(req->context, struct lpdb_context);
444         ldb = ldb_module_get_ctx(ac->module);
445
446         if (!ares) {
447                 return ldb_module_done(ac->req, NULL, NULL,
448                                         LDB_ERR_OPERATIONS_ERROR);
449         }
450         if (ares->error != LDB_SUCCESS) {
451                 return ldb_module_done(ac->req, ares->controls,
452                                         ares->response, ares->error);
453         }
454
455         switch (ares->type) {
456         case LDB_REPLY_ENTRY:
457                 if (ac->remote != NULL) {
458                         ldb_set_errstring(ldb, "Too many results");
459                         talloc_free(ares);
460                         return ldb_module_done(ac->req, NULL, NULL,
461                                                 LDB_ERR_OPERATIONS_ERROR);
462                 }
463
464                 ac->remote = talloc_steal(ac, ares);
465                 break;
466
467         case LDB_REPLY_REFERRAL:
468
469                 /* ignore */
470                 talloc_free(ares);
471                 break;
472
473         case LDB_REPLY_DONE:
474                 /* After we find out the objectGUID for the entry, modify the local
475                  * password database as required */
476
477                 talloc_free(ares);
478
479                 /* if it is not an entry of type person this is an error */
480                 /* TODO: remove this when sambaPassword will be in schema */
481                 if (ac->remote == NULL) {
482                         ldb_asprintf_errstring(ldb,
483                                 "entry just modified (%s) not found!",
484                                 ldb_dn_get_linearized(req->op.search.base));
485                         return ldb_module_done(ac->req, NULL, NULL,
486                                                 LDB_ERR_OPERATIONS_ERROR);
487                 }
488                 if (!ldb_msg_check_string_attribute(ac->remote->message,
489                                                     "objectClass", "person")) {
490                         /* Not relevent to us */
491                         return ldb_module_done(ac->req,
492                                                 ac->remote_done->controls,
493                                                 ac->remote_done->response,
494                                                 ac->remote_done->error);
495                 }
496
497                 if (ldb_msg_find_ldb_val(ac->remote->message,
498                                          "objectGUID") == NULL) {
499                         ldb_set_errstring(ldb,
500                                           "no objectGUID found in search: "
501                                           "local_password module must be "
502                                           "configured below objectGUID "
503                                           "module!\n");
504                         return ldb_module_done(ac->req, NULL, NULL,
505                                         LDB_ERR_OBJECT_CLASS_VIOLATION);
506                 }
507
508                 objectGUID = samdb_result_guid(ac->remote->message,
509                                                 "objectGUID");
510
511                 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
512                 if ((local_dn == NULL) ||
513                     ( ! ldb_dn_add_child_fmt(local_dn,
514                                             PASSWORD_GUID_ATTR "=%s",
515                                             GUID_string(ac, &objectGUID)))) {
516                         return ldb_module_done(ac->req, NULL, NULL,
517                                                 LDB_ERR_OPERATIONS_ERROR);
518                 }
519                 ac->local_message->dn = local_dn;
520
521                 ret = ldb_build_mod_req(&local_req, ldb, ac,
522                                         ac->local_message,
523                                         NULL,
524                                         ac, lpdb_local_callback,
525                                         ac->req);
526                 if (ret != LDB_SUCCESS) {
527                         return ldb_module_done(ac->req, NULL, NULL, ret);
528                 }
529
530                 /* perform the local update */
531                 ret = ldb_next_request(ac->module, local_req);
532                 if (ret != LDB_SUCCESS) {
533                         return ldb_module_done(ac->req, NULL, NULL, ret);
534                 }
535         }
536
537         return LDB_SUCCESS;
538 }
539
540 /*****************************************************************************
541  * DELETE
542  ****************************************************************************/
543
544 static int lpdb_delete_callabck(struct ldb_request *req,
545                                 struct ldb_reply *ares);
546 static int lpdb_del_search_callback(struct ldb_request *req,
547                                     struct ldb_reply *ares);
548
549 static int local_password_delete(struct ldb_module *module,
550                                  struct ldb_request *req)
551 {
552         struct ldb_context *ldb;
553         struct ldb_request *remote_req;
554         struct lpdb_context *ac;
555         int ret;
556
557         ldb = ldb_module_get_ctx(module);
558         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_delete\n");
559
560         /* do not manipulate our control entries */
561         if (ldb_dn_is_special(req->op.mod.message->dn)) {
562                 return ldb_next_request(module, req);
563         }
564
565         /* If the caller is manipulating the local passwords directly,
566          * let them pass */
567         if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
568                                 req->op.del.dn) == 0) {
569                 return ldb_next_request(module, req);
570         }
571
572         /* From here, we assume we have password attributes to split off */
573         ac = lpdb_init_context(module, req);
574         if (!ac) {
575                 return ldb_operr(ldb);
576         }
577
578         ret = ldb_build_del_req(&remote_req, ldb, ac,
579                                 req->op.del.dn,
580                                 req->controls,
581                                 ac, lpdb_delete_callabck,
582                                 req);
583         if (ret != LDB_SUCCESS) {
584                 return ret;
585         }
586
587         return ldb_next_request(module, remote_req);
588 }
589
590 /* On a modify, we don't have the objectGUID handy, so we need to
591  * search our DN for it */
592 static int lpdb_delete_callabck(struct ldb_request *req,
593                                 struct ldb_reply *ares)
594 {
595         struct ldb_context *ldb;
596         static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
597         struct ldb_request *search_req;
598         struct lpdb_context *ac;
599         int ret;
600
601         ac = talloc_get_type(req->context, struct lpdb_context);
602         ldb = ldb_module_get_ctx(ac->module);
603
604         if (!ares) {
605                 return ldb_module_done(ac->req, NULL, NULL,
606                                         LDB_ERR_OPERATIONS_ERROR);
607         }
608         if (ares->error != LDB_SUCCESS) {
609                 return ldb_module_done(ac->req, ares->controls,
610                                         ares->response, ares->error);
611         }
612
613         if (ares->type != LDB_REPLY_DONE) {
614                 ldb_set_errstring(ldb, "Unexpected reply type");
615                 talloc_free(ares);
616                 return ldb_module_done(ac->req, NULL, NULL,
617                                         LDB_ERR_OPERATIONS_ERROR);
618         }
619
620         ac->remote_done = talloc_steal(ac, ares);
621
622         /* prepare the search operation */
623         ret = ldb_build_search_req(&search_req, ldb, ac,
624                                    ac->req->op.del.dn, LDB_SCOPE_BASE,
625                                    "(objectclass=*)", attrs,
626                                    NULL,
627                                    ac, lpdb_del_search_callback,
628                                    ac->req);
629         if (ret != LDB_SUCCESS) {
630                 return ldb_module_done(ac->req, NULL, NULL,
631                                         LDB_ERR_OPERATIONS_ERROR);
632         }
633
634         ret = ldb_next_request(ac->module, search_req);
635         if (ret != LDB_SUCCESS) {
636                 return ldb_module_done(ac->req, NULL, NULL, ret);
637         }
638         return LDB_SUCCESS;
639 }
640
641 /* Called when we search for our own entry.  Stores the one entry we
642  * expect (as it is a base search) on the context pointer */
643 static int lpdb_del_search_callback(struct ldb_request *req,
644                                     struct ldb_reply *ares)
645 {
646         struct ldb_context *ldb;
647         struct ldb_request *local_req;
648         struct lpdb_context *ac;
649         struct ldb_dn *local_dn;
650         struct GUID objectGUID;
651         int ret = LDB_SUCCESS;
652
653         ac = talloc_get_type(req->context, struct lpdb_context);
654         ldb = ldb_module_get_ctx(ac->module);
655
656         if (!ares) {
657                 return ldb_module_done(ac->req, NULL, NULL,
658                                         LDB_ERR_OPERATIONS_ERROR);
659         }
660         if (ares->error != LDB_SUCCESS) {
661                 return ldb_module_done(ac->req, ares->controls,
662                                         ares->response, ares->error);
663         }
664
665         switch (ares->type) {
666         case LDB_REPLY_ENTRY:
667                 if (ac->remote != NULL) {
668                         ldb_set_errstring(ldb, "Too many results");
669                         talloc_free(ares);
670                         return ldb_module_done(ac->req, NULL, NULL,
671                                                 LDB_ERR_OPERATIONS_ERROR);
672                 }
673
674                 ac->remote = talloc_steal(ac, ares);
675                 break;
676
677         case LDB_REPLY_REFERRAL:
678
679                 /* ignore */
680                 talloc_free(ares);
681                 break;
682
683         case LDB_REPLY_DONE:
684                 /* After we find out the objectGUID for the entry, modify the local
685                  * password database as required */
686
687                 talloc_free(ares);
688
689                 /* if it is not an entry of type person this is NOT an error */
690                 /* TODO: remove this when sambaPassword will be in schema */
691                 if (ac->remote == NULL) {
692                         return ldb_module_done(ac->req,
693                                                 ac->remote_done->controls,
694                                                 ac->remote_done->response,
695                                                 ac->remote_done->error);
696                 }
697                 if (!ldb_msg_check_string_attribute(ac->remote->message,
698                                                     "objectClass", "person")) {
699                         /* Not relevent to us */
700                         return ldb_module_done(ac->req,
701                                                 ac->remote_done->controls,
702                                                 ac->remote_done->response,
703                                                 ac->remote_done->error);
704                 }
705
706                 if (ldb_msg_find_ldb_val(ac->remote->message,
707                                          "objectGUID") == NULL) {
708                         ldb_set_errstring(ldb,
709                                           "no objectGUID found in search: "
710                                           "local_password module must be "
711                                           "configured below objectGUID "
712                                           "module!\n");
713                         return ldb_module_done(ac->req, NULL, NULL,
714                                         LDB_ERR_OBJECT_CLASS_VIOLATION);
715                 }
716
717                 objectGUID = samdb_result_guid(ac->remote->message,
718                                                 "objectGUID");
719
720                 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
721                 if ((local_dn == NULL) ||
722                     ( ! ldb_dn_add_child_fmt(local_dn,
723                                             PASSWORD_GUID_ATTR "=%s",
724                                             GUID_string(ac, &objectGUID)))) {
725                         return ldb_module_done(ac->req, NULL, NULL,
726                                                 LDB_ERR_OPERATIONS_ERROR);
727                 }
728
729                 ret = ldb_build_del_req(&local_req, ldb, ac,
730                                         local_dn,
731                                         NULL,
732                                         ac, lpdb_local_callback,
733                                         ac->req);
734                 if (ret != LDB_SUCCESS) {
735                         return ldb_module_done(ac->req, NULL, NULL, ret);
736                 }
737
738                 /* perform the local update */
739                 ret = ldb_next_request(ac->module, local_req);
740                 if (ret != LDB_SUCCESS) {
741                         return ldb_module_done(ac->req, NULL, NULL, ret);
742                 }
743         }
744
745         return LDB_SUCCESS;
746 }
747
748
749 /*****************************************************************************
750  * SEARCH
751  ****************************************************************************/
752
753 static int lpdb_local_search_callback(struct ldb_request *req,
754                                         struct ldb_reply *ares);
755
756 static int lpdb_local_search(struct lpdb_context *ac)
757 {
758         struct ldb_context *ldb;
759         struct ldb_request *local_req;
760         int ret;
761
762         ldb = ldb_module_get_ctx(ac->module);
763
764         ret = ldb_build_search_req(&local_req, ldb, ac,
765                                    ac->current->local_dn,
766                                    LDB_SCOPE_BASE,
767                                    "(objectclass=*)",
768                                    ac->req->op.search.attrs,
769                                    NULL,
770                                    ac, lpdb_local_search_callback,
771                                    ac->req);
772         if (ret != LDB_SUCCESS) {
773                 return ldb_operr(ldb);
774         }
775
776         return ldb_next_request(ac->module, local_req);
777 }
778
779 static int lpdb_local_search_callback(struct ldb_request *req,
780                                         struct ldb_reply *ares)
781 {
782         struct ldb_context *ldb;
783         struct lpdb_context *ac;
784         struct ldb_reply *merge;
785         struct lpdb_reply *lr;
786         int ret;
787         unsigned int i;
788
789         ac = talloc_get_type(req->context, struct lpdb_context);
790         ldb = ldb_module_get_ctx(ac->module);
791
792         if (!ares) {
793                 return ldb_module_done(ac->req, NULL, NULL,
794                                         LDB_ERR_OPERATIONS_ERROR);
795         }
796         if (ares->error != LDB_SUCCESS) {
797                 return ldb_module_done(ac->req, ares->controls,
798                                         ares->response, ares->error);
799         }
800
801         lr = ac->current;
802
803         /* we are interested only in a single reply (base search) */
804         switch (ares->type) {
805         case LDB_REPLY_ENTRY:
806
807                 if (lr->remote == NULL) {
808                         ldb_set_errstring(ldb,
809                                 "Too many results for password entry search!");
810                         talloc_free(ares);
811                         return ldb_module_done(ac->req, NULL, NULL,
812                                                 LDB_ERR_OPERATIONS_ERROR);
813                 }
814
815                 merge = lr->remote;
816                 lr->remote = NULL;
817
818                 /* steal the local results on the remote results to be
819                  * returned all together */
820                 talloc_steal(merge, ares->message->elements);
821
822                 /* Make sure never to return the internal key attribute */
823                 ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
824
825                 for (i=0; i < ares->message->num_elements; i++) {
826                         struct ldb_message_element *el;
827                         
828                         el = ldb_msg_find_element(merge->message,
829                                                   ares->message->elements[i].name);
830                         if (!el) {
831                                 ret = ldb_msg_add_empty(merge->message,
832                                                         ares->message->elements[i].name,
833                                                         0, &el);
834                                 if (ret != LDB_SUCCESS) {
835                                         talloc_free(ares);
836                                         return ldb_module_done(ac->req,
837                                                                 NULL, NULL,
838                                                                 LDB_ERR_OPERATIONS_ERROR);
839                                 }
840                                 *el = ares->message->elements[i];
841                         }
842                 }
843
844                 /* free the rest */
845                 talloc_free(ares);
846
847                 return ldb_module_send_entry(ac->req, merge->message, merge->controls);
848
849         case LDB_REPLY_REFERRAL:
850                 /* ignore */
851                 talloc_free(ares);
852                 break;
853
854         case LDB_REPLY_DONE:
855
856                 talloc_free(ares);
857
858                 /* if this entry was not returned yet, return it now */
859                 if (lr->remote) {
860                         ret = ldb_module_send_entry(ac->req, ac->remote->message, ac->remote->controls);
861                         if (ret != LDB_SUCCESS) {
862                                 return ldb_module_done(ac->req,
863                                                         NULL, NULL, ret);
864                         }
865                         lr->remote = NULL;
866                 }
867
868                 if (lr->next->remote->type == LDB_REPLY_DONE) {
869                         /* this was the last one */
870                         return ldb_module_done(ac->req,
871                                                 lr->next->remote->controls,
872                                                 lr->next->remote->response,
873                                                 lr->next->remote->error);
874                 } else {
875                         /* next one */
876                         ac->current = lr->next;
877                         talloc_free(lr);
878
879                         ret = lpdb_local_search(ac);
880                         if (ret != LDB_SUCCESS) {
881                                 return ldb_module_done(ac->req,
882                                                         NULL, NULL, ret);
883                         }
884                 }
885         }
886
887         return LDB_SUCCESS;
888 }
889
890 /* For each entry returned in a remote search, do a local base search,
891  * based on the objectGUID we asked for as an additional attribute */
892 static int lpdb_remote_search_callback(struct ldb_request *req,
893                                         struct ldb_reply *ares)
894 {
895         struct ldb_context *ldb;
896         struct lpdb_context *ac;
897         struct ldb_dn *local_dn;
898         struct GUID objectGUID;
899         struct lpdb_reply *lr;
900         int ret;
901
902         ac = talloc_get_type(req->context, struct lpdb_context);
903         ldb = ldb_module_get_ctx(ac->module);
904
905         if (!ares) {
906                 return ldb_module_done(ac->req, NULL, NULL,
907                                         LDB_ERR_OPERATIONS_ERROR);
908         }
909         if (ares->error != LDB_SUCCESS) {
910                 return ldb_module_done(ac->req, ares->controls,
911                                         ares->response, ares->error);
912         }
913
914         switch (ares->type) {
915         case LDB_REPLY_ENTRY:
916                 /* No point searching further if it's not a 'person' entry */
917                 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
918
919                         /* Make sure to remove anything we added */
920                         if (ac->added_objectGUID) {
921                                 ldb_msg_remove_attr(ares->message, "objectGUID");
922                         }
923                         
924                         if (ac->added_objectClass) {
925                                 ldb_msg_remove_attr(ares->message, "objectClass");
926                         }
927                         
928                         return ldb_module_send_entry(ac->req, ares->message, ares->controls);
929                 }
930
931                 if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
932                         ldb_set_errstring(ldb, 
933                                           "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
934                         return ldb_module_done(ac->req, NULL, NULL,
935                                                 LDB_ERR_OPERATIONS_ERROR);
936                 }
937         
938                 objectGUID = samdb_result_guid(ares->message, "objectGUID");
939
940                 if (ac->added_objectGUID) {
941                         ldb_msg_remove_attr(ares->message, "objectGUID");
942                 }
943
944                 if (ac->added_objectClass) {
945                         ldb_msg_remove_attr(ares->message, "objectClass");
946                 }
947
948                 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
949                 if ((local_dn == NULL) ||
950                     (! ldb_dn_add_child_fmt(local_dn,
951                                             PASSWORD_GUID_ATTR "=%s",
952                                             GUID_string(ac, &objectGUID)))) {
953                         return ldb_module_done(ac->req, NULL, NULL,
954                                                 LDB_ERR_OPERATIONS_ERROR);
955                 }
956
957                 lr = talloc_zero(ac, struct lpdb_reply);
958                 if (lr == NULL) {
959                         return ldb_module_done(ac->req, NULL, NULL,
960                                                 LDB_ERR_OPERATIONS_ERROR);
961                 }
962                 lr->local_dn = talloc_steal(lr, local_dn);
963                 lr->remote = talloc_steal(lr, ares);
964
965                 if (ac->list) {
966                         ac->current->next = lr;
967                 } else {
968                         ac->list = lr;
969                 }
970                 ac->current= lr;
971
972                 break;
973
974         case LDB_REPLY_REFERRAL:
975
976                 return ldb_module_send_referral(ac->req, ares->referral);
977
978         case LDB_REPLY_DONE:
979
980                 if (ac->list == NULL) {
981                         /* found nothing */
982                         return ldb_module_done(ac->req, ares->controls,
983                                                 ares->response, ares->error);
984                 }
985
986                 lr = talloc_zero(ac, struct lpdb_reply);
987                 if (lr == NULL) {
988                         return ldb_module_done(ac->req, NULL, NULL,
989                                                 LDB_ERR_OPERATIONS_ERROR);
990                 }
991                 lr->remote = talloc_steal(lr, ares);
992
993                 ac->current->next = lr;
994
995                 /* rewind current and start local searches */
996                 ac->current= ac->list;
997
998                 ret = lpdb_local_search(ac);
999                 if (ret != LDB_SUCCESS) {
1000                         return ldb_module_done(ac->req, NULL, NULL, ret);
1001                 }
1002         }
1003
1004         return LDB_SUCCESS;
1005 }
1006
1007 /* Search for passwords and other attributes.  The passwords are
1008  * local, but the other attributes are remote, and we need to glue the
1009  * two search spaces back togeather */
1010
1011 static int local_password_search(struct ldb_module *module, struct ldb_request *req)
1012 {
1013         struct ldb_context *ldb;
1014         struct ldb_request *remote_req;
1015         struct lpdb_context *ac;
1016         unsigned int i;
1017         int ret;
1018         const char * const *search_attrs = NULL;
1019
1020         ldb = ldb_module_get_ctx(module);
1021         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_search\n");
1022
1023         if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
1024                 return ldb_next_request(module, req);
1025         }
1026
1027         search_attrs = NULL;
1028
1029         /* If the caller is searching for the local passwords directly, let them pass */
1030         if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1031                                 req->op.search.base) == 0) {
1032                 return ldb_next_request(module, req);
1033         }
1034
1035         if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
1036                 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
1037                         if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
1038                                 break;
1039                         }
1040                 }
1041                 
1042                 /* It didn't match any of our password attributes, go on */
1043                 if (i == ARRAY_SIZE(password_attrs)) {
1044                         return ldb_next_request(module, req);
1045                 }
1046         }
1047
1048         ac = lpdb_init_context(module, req);
1049         if (!ac) {
1050                 return ldb_operr(ldb);
1051         }
1052
1053         /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
1054         if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
1055                 if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
1056                         search_attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectGUID");
1057                         ac->added_objectGUID = true;
1058                         if (!search_attrs) {
1059                                 return ldb_operr(ldb);
1060                         }
1061                 } else {
1062                         search_attrs = req->op.search.attrs;
1063                 }
1064                 if (!ldb_attr_in_list(search_attrs, "objectClass")) {
1065                         search_attrs = ldb_attr_list_copy_add(ac, search_attrs, "objectClass");
1066                         ac->added_objectClass = true;
1067                         if (!search_attrs) {
1068                                 return ldb_operr(ldb);
1069                         }
1070                 }
1071         } else {
1072                 search_attrs = req->op.search.attrs;
1073         }
1074
1075         ret = ldb_build_search_req_ex(&remote_req, ldb, ac,
1076                                         req->op.search.base,
1077                                         req->op.search.scope,
1078                                         req->op.search.tree,
1079                                         search_attrs,
1080                                         req->controls,
1081                                         ac, lpdb_remote_search_callback,
1082                                         req);
1083         LDB_REQ_SET_LOCATION(remote_req);
1084         if (ret != LDB_SUCCESS) {
1085                 return ret;
1086         }
1087
1088         /* perform the search */
1089         return ldb_next_request(module, remote_req);
1090 }
1091
1092 _PUBLIC_ const struct ldb_module_ops ldb_local_password_module_ops = {
1093         .name          = "local_password",
1094         .add           = local_password_add,
1095         .modify        = local_password_modify,
1096         .del           = local_password_delete,
1097         .search        = local_password_search
1098 };