58c0f1f0d522c63da18d4c43d59feb342e306c88
[nivanova/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 "libcli/ldap/ldap.h"
34 #include "ldb_module.h"
35 #include "dsdb/samdb/samdb.h"
36 #include "librpc/ndr/libndr.h"
37 #include "dsdb/samdb/ldb_modules/password_modules.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         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_ERR_OPERATIONS_ERROR;
192         }
193
194         remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message);
195         if (remote_message == NULL) {
196                 return LDB_ERR_OPERATIONS_ERROR;
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_ERR_OPERATIONS_ERROR;
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_ERR_OPERATIONS_ERROR;
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         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_ERR_OPERATIONS_ERROR;
344         }
345
346         remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
347         if (remote_message == NULL) {
348                 return LDB_ERR_OPERATIONS_ERROR;
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_ERR_OPERATIONS_ERROR;
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_ERR_OPERATIONS_ERROR;
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,
637                                         LDB_ERR_OPERATIONS_ERROR);
638         }
639         return LDB_SUCCESS;
640 }
641
642 /* Called when we search for our own entry.  Stores the one entry we
643  * expect (as it is a base search) on the context pointer */
644 static int lpdb_del_search_callback(struct ldb_request *req,
645                                     struct ldb_reply *ares)
646 {
647         struct ldb_context *ldb;
648         struct ldb_request *local_req;
649         struct lpdb_context *ac;
650         struct ldb_dn *local_dn;
651         struct GUID objectGUID;
652         int ret = LDB_SUCCESS;
653
654         ac = talloc_get_type(req->context, struct lpdb_context);
655         ldb = ldb_module_get_ctx(ac->module);
656
657         if (!ares) {
658                 return ldb_module_done(ac->req, NULL, NULL,
659                                         LDB_ERR_OPERATIONS_ERROR);
660         }
661         if (ares->error != LDB_SUCCESS) {
662                 return ldb_module_done(ac->req, ares->controls,
663                                         ares->response, ares->error);
664         }
665
666         switch (ares->type) {
667         case LDB_REPLY_ENTRY:
668                 if (ac->remote != NULL) {
669                         ldb_set_errstring(ldb, "Too many results");
670                         talloc_free(ares);
671                         return ldb_module_done(ac->req, NULL, NULL,
672                                                 LDB_ERR_OPERATIONS_ERROR);
673                 }
674
675                 ac->remote = talloc_steal(ac, ares);
676                 break;
677
678         case LDB_REPLY_REFERRAL:
679
680                 /* ignore */
681                 talloc_free(ares);
682                 break;
683
684         case LDB_REPLY_DONE:
685                 /* After we find out the objectGUID for the entry, modify the local
686                  * password database as required */
687
688                 talloc_free(ares);
689
690                 /* if it is not an entry of type person this is NOT an error */
691                 /* TODO: remove this when sambaPassword will be in schema */
692                 if (ac->remote == NULL) {
693                         return ldb_module_done(ac->req,
694                                                 ac->remote_done->controls,
695                                                 ac->remote_done->response,
696                                                 ac->remote_done->error);
697                 }
698                 if (!ldb_msg_check_string_attribute(ac->remote->message,
699                                                     "objectClass", "person")) {
700                         /* Not relevent to us */
701                         return ldb_module_done(ac->req,
702                                                 ac->remote_done->controls,
703                                                 ac->remote_done->response,
704                                                 ac->remote_done->error);
705                 }
706
707                 if (ldb_msg_find_ldb_val(ac->remote->message,
708                                          "objectGUID") == NULL) {
709                         ldb_set_errstring(ldb,
710                                           "no objectGUID found in search: "
711                                           "local_password module must be "
712                                           "configured below objectGUID "
713                                           "module!\n");
714                         return ldb_module_done(ac->req, NULL, NULL,
715                                         LDB_ERR_OBJECT_CLASS_VIOLATION);
716                 }
717
718                 objectGUID = samdb_result_guid(ac->remote->message,
719                                                 "objectGUID");
720
721                 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
722                 if ((local_dn == NULL) ||
723                     ( ! ldb_dn_add_child_fmt(local_dn,
724                                             PASSWORD_GUID_ATTR "=%s",
725                                             GUID_string(ac, &objectGUID)))) {
726                         return ldb_module_done(ac->req, NULL, NULL,
727                                                 LDB_ERR_OPERATIONS_ERROR);
728                 }
729
730                 ret = ldb_build_del_req(&local_req, ldb, ac,
731                                         local_dn,
732                                         NULL,
733                                         ac, lpdb_local_callback,
734                                         ac->req);
735                 if (ret != LDB_SUCCESS) {
736                         return ldb_module_done(ac->req, NULL, NULL, ret);
737                 }
738
739                 /* perform the local update */
740                 ret = ldb_next_request(ac->module, local_req);
741                 if (ret != LDB_SUCCESS) {
742                         return ldb_module_done(ac->req, NULL, NULL, ret);
743                 }
744         }
745
746         return LDB_SUCCESS;
747 }
748
749
750 /*****************************************************************************
751  * SEARCH
752  ****************************************************************************/
753
754 static int lpdb_local_search_callback(struct ldb_request *req,
755                                         struct ldb_reply *ares);
756
757 static int lpdb_local_search(struct lpdb_context *ac)
758 {
759         struct ldb_context *ldb;
760         struct ldb_request *local_req;
761         int ret;
762
763         ldb = ldb_module_get_ctx(ac->module);
764
765         ret = ldb_build_search_req(&local_req, ldb, ac,
766                                    ac->current->local_dn,
767                                    LDB_SCOPE_BASE,
768                                    "(objectclass=*)",
769                                    ac->req->op.search.attrs,
770                                    NULL,
771                                    ac, lpdb_local_search_callback,
772                                    ac->req);
773         if (ret != LDB_SUCCESS) {
774                 return LDB_ERR_OPERATIONS_ERROR;
775         }
776
777         return ldb_next_request(ac->module, local_req);
778 }
779
780 static int lpdb_local_search_callback(struct ldb_request *req,
781                                         struct ldb_reply *ares)
782 {
783         struct ldb_context *ldb;
784         struct lpdb_context *ac;
785         struct ldb_reply *merge;
786         struct lpdb_reply *lr;
787         int ret;
788         int i;
789
790         ac = talloc_get_type(req->context, struct lpdb_context);
791         ldb = ldb_module_get_ctx(ac->module);
792
793         if (!ares) {
794                 return ldb_module_done(ac->req, NULL, NULL,
795                                         LDB_ERR_OPERATIONS_ERROR);
796         }
797         if (ares->error != LDB_SUCCESS) {
798                 return ldb_module_done(ac->req, ares->controls,
799                                         ares->response, ares->error);
800         }
801
802         lr = ac->current;
803
804         /* we are interested only in a single reply (base search) */
805         switch (ares->type) {
806         case LDB_REPLY_ENTRY:
807
808                 if (lr->remote == NULL) {
809                         ldb_set_errstring(ldb,
810                                 "Too many results for password entry search!");
811                         talloc_free(ares);
812                         return ldb_module_done(ac->req, NULL, NULL,
813                                                 LDB_ERR_OPERATIONS_ERROR);
814                 }
815
816                 merge = lr->remote;
817                 lr->remote = NULL;
818
819                 /* steal the local results on the remote results to be
820                  * returned all together */
821                 talloc_steal(merge, ares->message->elements);
822
823                 /* Make sure never to return the internal key attribute */
824                 ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
825
826                 for (i=0; i < ares->message->num_elements; i++) {
827                         struct ldb_message_element *el;
828                         
829                         el = ldb_msg_find_element(merge->message,
830                                                   ares->message->elements[i].name);
831                         if (!el) {
832                                 ret = ldb_msg_add_empty(merge->message,
833                                                         ares->message->elements[i].name,
834                                                         0, &el);
835                                 if (ret != LDB_SUCCESS) {
836                                         talloc_free(ares);
837                                         return ldb_module_done(ac->req,
838                                                                 NULL, NULL,
839                                                                 LDB_ERR_OPERATIONS_ERROR);
840                                 }
841                                 *el = ares->message->elements[i];
842                         }
843                 }
844
845                 /* free the rest */
846                 talloc_free(ares);
847
848                 return ldb_module_send_entry(ac->req, merge->message, merge->controls);
849
850         case LDB_REPLY_REFERRAL:
851                 /* ignore */
852                 talloc_free(ares);
853                 break;
854
855         case LDB_REPLY_DONE:
856
857                 talloc_free(ares);
858
859                 /* if this entry was not returned yet, return it now */
860                 if (lr->remote) {
861                         ret = ldb_module_send_entry(ac->req, ac->remote->message, ac->remote->controls);
862                         if (ret != LDB_SUCCESS) {
863                                 return ldb_module_done(ac->req,
864                                                         NULL, NULL, ret);
865                         }
866                         lr->remote = NULL;
867                 }
868
869                 if (lr->next->remote->type == LDB_REPLY_DONE) {
870                         /* this was the last one */
871                         return ldb_module_done(ac->req,
872                                                 lr->next->remote->controls,
873                                                 lr->next->remote->response,
874                                                 lr->next->remote->error);
875                 } else {
876                         /* next one */
877                         ac->current = lr->next;
878                         talloc_free(lr);
879
880                         ret = lpdb_local_search(ac);
881                         if (ret != LDB_SUCCESS) {
882                                 return ldb_module_done(ac->req,
883                                                         NULL, NULL, ret);
884                         }
885                 }
886         }
887
888         return LDB_SUCCESS;
889 }
890
891 /* For each entry returned in a remote search, do a local base search,
892  * based on the objectGUID we asked for as an additional attribute */
893 static int lpdb_remote_search_callback(struct ldb_request *req,
894                                         struct ldb_reply *ares)
895 {
896         struct ldb_context *ldb;
897         struct lpdb_context *ac;
898         struct ldb_dn *local_dn;
899         struct GUID objectGUID;
900         struct lpdb_reply *lr;
901         int ret;
902
903         ac = talloc_get_type(req->context, struct lpdb_context);
904         ldb = ldb_module_get_ctx(ac->module);
905
906         if (!ares) {
907                 return ldb_module_done(ac->req, NULL, NULL,
908                                         LDB_ERR_OPERATIONS_ERROR);
909         }
910         if (ares->error != LDB_SUCCESS) {
911                 return ldb_module_done(ac->req, ares->controls,
912                                         ares->response, ares->error);
913         }
914
915         switch (ares->type) {
916         case LDB_REPLY_ENTRY:
917                 /* No point searching further if it's not a 'person' entry */
918                 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
919
920                         /* Make sure to remove anything we added */
921                         if (ac->added_objectGUID) {
922                                 ldb_msg_remove_attr(ares->message, "objectGUID");
923                         }
924                         
925                         if (ac->added_objectClass) {
926                                 ldb_msg_remove_attr(ares->message, "objectClass");
927                         }
928                         
929                         return ldb_module_send_entry(ac->req, ares->message, ares->controls);
930                 }
931
932                 if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
933                         ldb_set_errstring(ldb, 
934                                           "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
935                         return ldb_module_done(ac->req, NULL, NULL,
936                                                 LDB_ERR_OPERATIONS_ERROR);
937                 }
938         
939                 objectGUID = samdb_result_guid(ares->message, "objectGUID");
940
941                 if (ac->added_objectGUID) {
942                         ldb_msg_remove_attr(ares->message, "objectGUID");
943                 }
944
945                 if (ac->added_objectClass) {
946                         ldb_msg_remove_attr(ares->message, "objectClass");
947                 }
948
949                 local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
950                 if ((local_dn == NULL) ||
951                     (! ldb_dn_add_child_fmt(local_dn,
952                                             PASSWORD_GUID_ATTR "=%s",
953                                             GUID_string(ac, &objectGUID)))) {
954                         return ldb_module_done(ac->req, NULL, NULL,
955                                                 LDB_ERR_OPERATIONS_ERROR);
956                 }
957
958                 lr = talloc_zero(ac, struct lpdb_reply);
959                 if (lr == NULL) {
960                         return ldb_module_done(ac->req, NULL, NULL,
961                                                 LDB_ERR_OPERATIONS_ERROR);
962                 }
963                 lr->local_dn = talloc_steal(lr, local_dn);
964                 lr->remote = talloc_steal(lr, ares);
965
966                 if (ac->list) {
967                         ac->current->next = lr;
968                 } else {
969                         ac->list = lr;
970                 }
971                 ac->current= lr;
972
973                 break;
974
975         case LDB_REPLY_REFERRAL:
976
977                 return ldb_module_send_referral(ac->req, ares->referral);
978
979         case LDB_REPLY_DONE:
980
981                 if (ac->list == NULL) {
982                         /* found nothing */
983                         return ldb_module_done(ac->req, ares->controls,
984                                                 ares->response, ares->error);
985                 }
986
987                 lr = talloc_zero(ac, struct lpdb_reply);
988                 if (lr == NULL) {
989                         return ldb_module_done(ac->req, NULL, NULL,
990                                                 LDB_ERR_OPERATIONS_ERROR);
991                 }
992                 lr->remote = talloc_steal(lr, ares);
993
994                 ac->current->next = lr;
995
996                 /* rewind current and start local searches */
997                 ac->current= ac->list;
998
999                 ret = lpdb_local_search(ac);
1000                 if (ret != LDB_SUCCESS) {
1001                         return ldb_module_done(ac->req, NULL, NULL, ret);
1002                 }
1003         }
1004
1005         return LDB_SUCCESS;
1006 }
1007
1008 /* Search for passwords and other attributes.  The passwords are
1009  * local, but the other attributes are remote, and we need to glue the
1010  * two search spaces back togeather */
1011
1012 static int local_password_search(struct ldb_module *module, struct ldb_request *req)
1013 {
1014         struct ldb_context *ldb;
1015         struct ldb_request *remote_req;
1016         struct lpdb_context *ac;
1017         int i;
1018         int ret;
1019         const char * const *search_attrs = NULL;
1020
1021         ldb = ldb_module_get_ctx(module);
1022         ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_search\n");
1023
1024         if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
1025                 return ldb_next_request(module, req);
1026         }
1027
1028         search_attrs = NULL;
1029
1030         /* If the caller is searching for the local passwords directly, let them pass */
1031         if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1032                                 req->op.search.base) == 0) {
1033                 return ldb_next_request(module, req);
1034         }
1035
1036         if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
1037                 for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
1038                         if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
1039                                 break;
1040                         }
1041                 }
1042                 
1043                 /* It didn't match any of our password attributes, go on */
1044                 if (i == ARRAY_SIZE(password_attrs)) {
1045                         return ldb_next_request(module, req);
1046                 }
1047         }
1048
1049         ac = lpdb_init_context(module, req);
1050         if (!ac) {
1051                 return LDB_ERR_OPERATIONS_ERROR;
1052         }
1053
1054         /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
1055         if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
1056                 if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
1057                         search_attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectGUID");
1058                         ac->added_objectGUID = true;
1059                         if (!search_attrs) {
1060                                 return LDB_ERR_OPERATIONS_ERROR;
1061                         }
1062                 } else {
1063                         search_attrs = req->op.search.attrs;
1064                 }
1065                 if (!ldb_attr_in_list(search_attrs, "objectClass")) {
1066                         search_attrs = ldb_attr_list_copy_add(ac, search_attrs, "objectClass");
1067                         ac->added_objectClass = true;
1068                         if (!search_attrs) {
1069                                 return LDB_ERR_OPERATIONS_ERROR;
1070                         }
1071                 }
1072         } else {
1073                 search_attrs = req->op.search.attrs;
1074         }
1075
1076         ret = ldb_build_search_req_ex(&remote_req, ldb, ac,
1077                                         req->op.search.base,
1078                                         req->op.search.scope,
1079                                         req->op.search.tree,
1080                                         search_attrs,
1081                                         req->controls,
1082                                         ac, lpdb_remote_search_callback,
1083                                         req);
1084         if (ret != LDB_SUCCESS) {
1085                 return LDB_ERR_OPERATIONS_ERROR;
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 };