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