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