root/source4/dsdb/samdb/ldb_modules/local_password.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lpdb_init_context
  2. lpdb_local_callback
  3. local_password_add
  4. lpdb_add_callback
  5. local_password_modify
  6. lpdb_modify_callabck
  7. lpdb_mod_search_callback
  8. local_password_delete
  9. lpdb_delete_callabck
  10. lpdb_del_search_callback
  11. lpdb_local_search
  12. lpdb_local_search_callback
  13. lpdb_remote_search_callback
  14. local_password_search

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

/* [<][>][^][v][top][bottom][index][help] */