r17514: Simplify the way to set ldb errors and add another
[kamenim/samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
index 79c863374ca12fb92759c2dbf6124be46654cbba..93af3ae2600e2d73be52a544dc10d75842596e78 100644 (file)
@@ -1,8 +1,8 @@
 /* 
    ldb database module
 
-   Copyright (C) Simo Sorce  2004
-   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+   Copyright (C) Simo Sorce  2004-2006
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
    Copyright (C) Andrew Tridgell 2004
 
    This program is free software; you can redistribute it and/or modify
@@ -44,6 +44,7 @@
 #include "dsdb/samdb/samdb.h"
 #include "ads.h"
 #include "hdb.h"
+#include "dsdb/samdb/ldb_modules/password_modules.h"
 
 /* If we have decided there is reason to work on this request, then
  * setup all the password hash types correctly.
@@ -65,7 +66,7 @@
  *
  */
 
-struct ph_async_context {
+struct ph_context {
 
        enum ph_type {PH_ADD, PH_MOD} type;
        enum ph_step {PH_ADD_SEARCH_DOM, PH_ADD_DO_ADD, PH_MOD_DO_REQ, PH_MOD_SEARCH_SELF, PH_MOD_SEARCH_DOM, PH_MOD_DO_MOD} step;
@@ -74,14 +75,16 @@ struct ph_async_context {
        struct ldb_request *orig_req;
 
        struct ldb_request *dom_req;
-       struct ldb_async_result *dom_res;
+       struct ldb_reply *dom_res;
 
        struct ldb_request *down_req;
 
        struct ldb_request *search_req;
-       struct ldb_async_result *search_res;
+       struct ldb_reply *search_res;
 
        struct ldb_request *mod_req;
+
+       struct dom_sid *domain_sid;
 };
 
 struct domain_data {
@@ -153,10 +156,10 @@ static int add_krb5_keys_from_password(struct ldb_module *module, struct ldb_mes
                char *name = talloc_strdup(msg, samAccountName);
                char *saltbody;
                if (name == NULL) {
-                       ldb_set_errstring(module->ldb,
-                                         talloc_asprintf(msg, "password_hash_handle: "
-                                                         "generation of new kerberos keys failed: %s is a computer without a samAccountName",
-                                                         ldb_dn_linearize(msg, msg->dn)));
+                       ldb_asprintf_errstring(module->ldb,
+                                               "password_hash_handle: "
+                                               "generation of new kerberos keys failed: %s is a computer without a samAccountName",
+                                               ldb_dn_linearize(msg, msg->dn));
                        return LDB_ERR_OPERATIONS_ERROR;
                }
                if (name[strlen(name)-1] == '$') {
@@ -184,10 +187,10 @@ static int add_krb5_keys_from_password(struct ldb_module *module, struct ldb_mes
                } 
        } else {
                if (!samAccountName) {
-                       ldb_set_errstring(module->ldb,
-                                         talloc_asprintf(msg, "password_hash_handle: "
-                                                         "generation of new kerberos keys failed: %s has no samAccountName",
-                                                         ldb_dn_linearize(msg, msg->dn)));
+                       ldb_asprintf_errstring(module->ldb,
+                                               "password_hash_handle: "
+                                               "generation of new kerberos keys failed: %s has no samAccountName",
+                                               ldb_dn_linearize(msg, msg->dn));
                        return LDB_ERR_OPERATIONS_ERROR;
                }
                krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context,
@@ -197,11 +200,10 @@ static int add_krb5_keys_from_password(struct ldb_module *module, struct ldb_mes
        }
 
        if (krb5_ret) {
-               ldb_set_errstring(module->ldb,
-                                 talloc_asprintf(msg, "password_hash_handle: "
-                                                 "generation of a saltking principal failed: %s",
-                                                 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                                            krb5_ret, msg)));
+               ldb_asprintf_errstring(module->ldb,
+                                       "password_hash_handle: "
+                                       "generation of a saltking principal failed: %s",
+                                       smb_get_krb5_error_message(smb_krb5_context->krb5_context, krb5_ret, msg));
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -211,11 +213,10 @@ static int add_krb5_keys_from_password(struct ldb_module *module, struct ldb_mes
        krb5_free_principal(smb_krb5_context->krb5_context, salt_principal);
 
        if (krb5_ret) {
-               ldb_set_errstring(module->ldb,
-                                 talloc_asprintf(msg, "password_hash_handle: "
-                                                 "generation of new kerberos keys failed: %s",
-                                                 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                                            krb5_ret, msg)));
+               ldb_asprintf_errstring(module->ldb,
+                                       "password_hash_handle: "
+                                       "generation of new kerberos keys failed: %s",
+                                       smb_get_krb5_error_message(smb_krb5_context->krb5_context, krb5_ret, msg));
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -229,7 +230,7 @@ static int add_krb5_keys_from_password(struct ldb_module *module, struct ldb_mes
                struct ldb_val val;
                int ret;
                
-               if (keys[i].key.keytype == ENCTYPE_ARCFOUR_HMAC) {
+               if (keys[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
                        /* We might end up doing this below:
                         * This ensures we get the unicode
                         * conversion right.  This should also
@@ -280,9 +281,9 @@ static int add_krb5_keys_from_NThash(struct ldb_module *module, struct ldb_messa
        }
 
        krb5_ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
-                                ENCTYPE_ARCFOUR_HMAC,
-                                ntPwdHash->hash, sizeof(ntPwdHash->hash), 
-                                &key.key);
+                                     ETYPE_ARCFOUR_HMAC_MD5,
+                                     ntPwdHash->hash, sizeof(ntPwdHash->hash), 
+                                     &key.key);
        if (krb5_ret) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -416,22 +417,22 @@ static int setPwdHistory(struct ldb_module *module, struct ldb_message *msg, str
        return LDB_SUCCESS;
 }
 
-static struct ldb_async_handle *ph_init_handle(struct ldb_request *req, struct ldb_module *module, enum ph_type type)
+static struct ldb_handle *ph_init_handle(struct ldb_request *req, struct ldb_module *module, enum ph_type type)
 {
-       struct ph_async_context *ac;
-       struct ldb_async_handle *h;
+       struct ph_context *ac;
+       struct ldb_handle *h;
 
-       h = talloc_zero(req, struct ldb_async_handle);
+       h = talloc_zero(req, struct ldb_handle);
        if (h == NULL) {
-               ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+               ldb_set_errstring(module->ldb, "Out of Memory");
                return NULL;
        }
 
        h->module = module;
 
-       ac = talloc_zero(h, struct ph_async_context);
+       ac = talloc_zero(h, struct ph_context);
        if (ac == NULL) {
-               ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+               ldb_set_errstring(module->ldb, "Out of Memory");
                talloc_free(h);
                return NULL;
        }
@@ -448,21 +449,21 @@ static struct ldb_async_handle *ph_init_handle(struct ldb_request *req, struct l
        return h;
 }
 
-static int get_domain_data_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
+static int get_domain_data_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
 {
-       struct ph_async_context *ac;
+       struct ph_context *ac;
 
        if (!context || !ares) {
-               ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ac = talloc_get_type(context, struct ph_async_context);
+       ac = talloc_get_type(context, struct ph_context);
 
        /* we are interested only in the single reply (base search) we receive here */
        if (ares->type == LDB_REPLY_ENTRY) {
                if (ac->dom_res != NULL) {
-                       ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
+                       ldb_set_errstring(ldb, "Too many results");
                        talloc_free(ares);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
@@ -474,8 +475,7 @@ static int get_domain_data_callback(struct ldb_context *ldb, void *context, stru
        return LDB_SUCCESS;
 }
 
-static int build_domain_data_request(struct ph_async_context *ac,
-                                    struct dom_sid *sid)
+static int build_domain_data_request(struct ph_context *ac)
 {
        /* attrs[] is returned from this function in
           ac->dom_req->op.search.attrs, so it must be static, as
@@ -489,10 +489,11 @@ static int build_domain_data_request(struct ph_async_context *ac,
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ac->dom_req->operation = LDB_SEARCH;
-       ac->dom_req->op.search.base = NULL;
+       ac->dom_req->op.search.base = samdb_base_dn(ac);
        ac->dom_req->op.search.scope = LDB_SCOPE_SUBTREE;
 
-       filter = talloc_asprintf(ac->dom_req, "(&(objectSid=%s)(objectClass=domain))", dom_sid_string(ac->dom_req, sid));
+       filter = talloc_asprintf(ac->dom_req, "(&(objectSid=%s)(|(objectClass=domain)(objectClass=builtinDomain)))", 
+                                ldap_encode_ndr_dom_sid(ac->dom_req, ac->domain_sid));
        if (filter == NULL) {
                ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
                talloc_free(ac->dom_req);
@@ -501,29 +502,38 @@ static int build_domain_data_request(struct ph_async_context *ac,
 
        ac->dom_req->op.search.tree = ldb_parse_tree(ac->module->ldb, filter);
        if (ac->dom_req->op.search.tree == NULL) {
-               ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
+               ldb_set_errstring(ac->module->ldb, "Invalid search filter");
                talloc_free(ac->dom_req);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ac->dom_req->op.search.attrs = attrs;
        ac->dom_req->controls = NULL;
-       ac->dom_req->async.context = ac;
-       ac->dom_req->async.callback = get_domain_data_callback;
-       ac->dom_req->async.timeout = ac->orig_req->async.timeout;
+       ac->dom_req->context = ac;
+       ac->dom_req->callback = get_domain_data_callback;
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->dom_req);
 
        return LDB_SUCCESS;
 }
 
-static struct domain_data *get_domain_data(struct ldb_module *module, void *mem_ctx, struct ldb_async_result *res)
+static struct domain_data *get_domain_data(struct ldb_module *module, void *ctx, struct ldb_reply *res)
 {
        struct domain_data *data;
        const char *tmp;
+       struct ph_context *ac;
        
-       data = talloc_zero(mem_ctx, struct domain_data);
+       ac = talloc_get_type(ctx, struct ph_context);
+
+       data = talloc_zero(ac, struct domain_data);
        if (data == NULL) {
                return NULL;
        }
 
+       if (res == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Could not find this user's domain: %s!\n", dom_sid_string(data, ac->domain_sid));
+               talloc_free(data);
+               return NULL;
+       }
+
        data->pwdProperties = samdb_result_uint(res->message, "pwdProperties", 0);
        data->pwdHistoryLength = samdb_result_uint(res->message, "pwdHistoryLength", 0);
        tmp = ldb_msg_find_string(res->message, "dnsDomain", NULL);
@@ -534,7 +544,7 @@ static struct domain_data *get_domain_data(struct ldb_module *module, void *mem_
                        ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
                        return NULL;
                }
-               data->realm = strupper_talloc(mem_ctx, tmp);
+               data->realm = strupper_talloc(data, tmp);
                if (data->realm == NULL) {
                        ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
                        return NULL;
@@ -546,10 +556,11 @@ static struct domain_data *get_domain_data(struct ldb_module *module, void *mem_
 
 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ldb_async_handle *h;
-       struct ph_async_context *ac;
-       struct ldb_message_element *attribute;
-       struct dom_sid *domain_sid;
+       struct ldb_handle *h;
+       struct ph_context *ac;
+       struct ldb_message_element *sambaAttr;
+       struct ldb_message_element *ntAttr;
+       struct ldb_message_element *lmAttr;
        int ret;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
@@ -558,66 +569,88 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
+       /* If the caller is manipulating the local passwords directly, let them pass */
+       if (ldb_dn_compare_base(module->ldb, 
+                               ldb_dn_explode(req, LOCAL_BASE),
+                               req->op.add.message->dn) == 0) {
+               return ldb_next_request(module, req);
+       }
+
        /* nobody must touch password Histories */
        if (ldb_msg_find_element(req->op.add.message, "sambaNTPwdHistory") ||
            ldb_msg_find_element(req->op.add.message, "sambaLMPwdHistory")) {
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
-       /* If no part of this touches the sambaPassword, then we don't
-        * need to make any changes.  For password changes/set there should
-        * be a 'delete' or a 'modify' on this attribute. */
-       if ((attribute = ldb_msg_find_element(req->op.add.message, "sambaPassword")) == NULL ) {
+       /* If no part of this ADD touches the sambaPassword, or the NT
+        * or LM hashes, then we don't need to make any changes.  */
+
+       sambaAttr = ldb_msg_find_element(req->op.mod.message, "sambaPassword");
+       ntAttr = ldb_msg_find_element(req->op.mod.message, "ntPwdHash");
+       lmAttr = ldb_msg_find_element(req->op.mod.message, "lmPwdHash");
+
+       if ((!sambaAttr) && (!ntAttr) && (!lmAttr)) {
                return ldb_next_request(module, req);
        }
 
        /* if it is not an entry of type person its an error */
        /* TODO: remove this when sambaPassword will be in schema */
        if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
+               ldb_set_errstring(module->ldb, "Cannot set a password on entry that does not have objectClass 'person'");
                return LDB_ERR_OBJECT_CLASS_VIOLATION;
        }
 
        /* check sambaPassword is single valued here */
        /* TODO: remove this when sambaPassword will be single valued in schema */
-       if (attribute->num_values > 1) {
-               ldb_set_errstring(module->ldb, talloc_asprintf(req,
-                                       "mupltiple values for sambaPassword not allowed!\n"));
+       if (sambaAttr && sambaAttr->num_values > 1) {
+               ldb_set_errstring(module->ldb, "mupltiple values for sambaPassword not allowed!\n");
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
-       /* get user domain data */
-       domain_sid = samdb_result_sid_prefix(req, req->op.add.message, "objectSid");
-       if (domain_sid == NULL) {
-               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (ntAttr && (ntAttr->num_values > 1)) {
+               ldb_set_errstring(module->ldb, "mupltiple values for lmPwdHash not allowed!\n");
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+       if (lmAttr && (lmAttr->num_values > 1)) {
+               ldb_set_errstring(module->ldb, "mupltiple values for lmPwdHash not allowed!\n");
+               return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
        h = ph_init_handle(req, module, PH_ADD);
        if (!h) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ac = talloc_get_type(h->private_data, struct ph_async_context);
+       ac = talloc_get_type(h->private_data, struct ph_context);
 
-       ret = build_domain_data_request(ac, domain_sid);
+       /* get user domain data */
+       ac->domain_sid = samdb_result_sid_prefix(ac, req->op.add.message, "objectSid");
+       if (ac->domain_sid == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = build_domain_data_request(ac);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
        ac->step = PH_ADD_SEARCH_DOM;
 
-       req->async.handle = h;
+       req->handle = h;
 
        return ldb_next_request(module, ac->dom_req);
 }
 
-static int password_hash_add_do_add(struct ldb_async_handle *h) {
+static int password_hash_add_do_add(struct ldb_handle *h) {
 
-       struct ph_async_context *ac;
+       struct ph_context *ac;
        struct domain_data *domain;
        struct smb_krb5_context *smb_krb5_context;
+       struct ldb_message_element *sambaAttr;
        struct ldb_message *msg;
+       int ret;
 
-       ac = talloc_get_type(h->private_data, struct ph_async_context);
+       ac = talloc_get_type(h->private_data, struct ph_context);
 
        domain = get_domain_data(ac->module, ac, ac->dom_res);
        if (domain == NULL) {
@@ -634,36 +667,42 @@ static int password_hash_add_do_add(struct ldb_async_handle *h) {
        if (ac->down_req->op.add.message == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
+
        /* Some operations below require kerberos contexts */
        if (smb_krb5_init_context(ac->down_req, &smb_krb5_context) != 0) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* we can compute new password hashes from the unicode password */
-       if (add_password_hashes(ac->module, msg, 0) != LDB_SUCCESS) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       /* now add krb5 keys based on unicode password */
-       if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
-                                       ldb_msg_find_string(msg, "samAccountName", NULL),
-                                       ldb_msg_find_string(msg, "userPrincipalName", NULL),
-                                       ldb_msg_check_string_attribute(msg, "objectClass", "computer")
-                                      ) != LDB_SUCCESS) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       /* add also kr5 keys based on NT the hash */
-       if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       /* if both the domain properties and the user account controls do not permit
-        * clear text passwords then wipe out the sambaPassword */
-       if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
-           (!(ldb_msg_find_uint(msg, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
-               ldb_msg_remove_attr(msg, "sambaPassword");
+       /* if we have sambaPassword in the original message add the operatio on it here */
+       sambaAttr = ldb_msg_find_element(msg, "sambaPassword");
+       if (sambaAttr) {
+               ret = add_password_hashes(ac->module, msg, 0);
+               /* we can compute new password hashes from the unicode password */
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               
+               /* now add krb5 keys based on unicode password */
+               ret = add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
+                                                 ldb_msg_find_string(msg, "samAccountName", NULL),
+                                                 ldb_msg_find_string(msg, "userPrincipalName", NULL),
+                                                 ldb_msg_check_string_attribute(msg, "objectClass", "computer"));
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               
+               /* add also kr5 keys based on NT the hash */
+               ret = add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               
+               /* if both the domain properties and the user account controls do not permit
+                * clear text passwords then wipe out the sambaPassword */
+               if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
+                   (!(ldb_msg_find_uint(msg, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
+                       ldb_msg_remove_attr(msg, "sambaPassword");
+               }
        }
 
        /* don't touch it if a value is set. It could be an incoming samsync */
@@ -685,19 +724,22 @@ static int password_hash_add_do_add(struct ldb_async_handle *h) {
 
        ac->step = PH_ADD_DO_ADD;
 
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req);
+
        /* perform the operation */
        return ldb_next_request(ac->module, ac->down_req);
 }
 
-static int password_hash_mod_search_self(struct ldb_async_handle *h);
+static int password_hash_mod_search_self(struct ldb_handle *h);
 
 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ldb_async_handle *h;
-       struct ph_async_context *ac;
+       struct ldb_handle *h;
+       struct ph_context *ac;
        struct ldb_message_element *sambaAttr;
        struct ldb_message_element *ntAttr;
        struct ldb_message_element *lmAttr;
+       struct ldb_message *msg;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
 
@@ -705,6 +747,13 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
                return ldb_next_request(module, req);
        }
        
+       /* If the caller is manipulating the local passwords directly, let them pass */
+       if (ldb_dn_compare_base(module->ldb, 
+                               ldb_dn_explode(req, LOCAL_BASE),
+                               req->op.mod.message->dn) == 0) {
+               return ldb_next_request(module, req);
+       }
+
        /* nobody must touch password Histories */
        if (ldb_msg_find_element(req->op.mod.message, "sambaNTPwdHistory") ||
            ldb_msg_find_element(req->op.mod.message, "sambaLMPwdHistory")) {
@@ -742,59 +791,61 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
        if (!h) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ac = talloc_get_type(h->private_data, struct ph_async_context);
+       ac = talloc_get_type(h->private_data, struct ph_context);
 
        /* return or own handle to deal with this call */
-       req->async.handle = h;
+       req->handle = h;
 
        /* prepare the first operation */
        ac->down_req = talloc_zero(ac, struct ldb_request);
        if (ac->down_req == NULL) {
-               ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
+               ldb_set_errstring(module->ldb, "Out of memory!");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        *(ac->down_req) = *req; /* copy the request */
 
        /* use a new message structure so that we can modify it */
-       ac->down_req->op.mod.message = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
+       ac->down_req->op.mod.message = msg = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
 
        /* - remove any imodification to the password from the first commit
         *   we will make the real modification later */
-       if (sambaAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "sambaPassword");
-       if (ntAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "ntPwdHash");
-       if (lmAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "lmPwdHash");
+       if (sambaAttr) ldb_msg_remove_attr(msg, "sambaPassword");
+       if (ntAttr) ldb_msg_remove_attr(msg, "ntPwdHash");
+       if (lmAttr) ldb_msg_remove_attr(msg, "lmPwdHash");
 
        /* if there was nothing else to be modify skip to next step */
-       if (ac->down_req->op.mod.message->num_elements == 0) {
+       if (msg->num_elements == 0) {
                talloc_free(ac->down_req);
                ac->down_req = NULL;
                return password_hash_mod_search_self(h);
        }
        
-       ac->down_req->async.context = NULL;
-       ac->down_req->async.callback = NULL;
+       ac->down_req->context = NULL;
+       ac->down_req->callback = NULL;
 
        ac->step = PH_MOD_DO_REQ;
 
+       ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
+
        return ldb_next_request(module, ac->down_req);
 }
 
-static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
+static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
 {
-       struct ph_async_context *ac;
+       struct ph_context *ac;
 
        if (!context || !ares) {
-               ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ac = talloc_get_type(context, struct ph_async_context);
+       ac = talloc_get_type(context, struct ph_context);
 
        /* we are interested only in the single reply (base search) we receive here */
        if (ares->type == LDB_REPLY_ENTRY) {
                if (ac->search_res != NULL) {
-                       ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
+                       ldb_set_errstring(ldb, "Too many results");
                        talloc_free(ares);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
@@ -802,7 +853,7 @@ static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_
                /* if it is not an entry of type person this is an error */
                /* TODO: remove this when sambaPassword will be in schema */
                if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
-                       ldb_set_errstring(ldb, talloc_asprintf(ldb, "Object class violation"));
+                       ldb_set_errstring(ldb, "Object class violation");
                        talloc_free(ares);
                        return LDB_ERR_OBJECT_CLASS_VIOLATION;
                }
@@ -815,11 +866,18 @@ static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_
        return LDB_SUCCESS;
 }
 
-static int password_hash_mod_search_self(struct ldb_async_handle *h) {
+static int password_hash_mod_search_self(struct ldb_handle *h) {
 
-       struct ph_async_context *ac;
+       struct ph_context *ac;
+       static const char * const attrs[] = { "userAccountControl", "sambaLMPwdHistory", 
+                                             "sambaNTPwdHistory", 
+                                             "objectSid", "msDS-KeyVersionNumber", 
+                                             "objectClass", "userPrincipalName",
+                                             "samAccountName", 
+                                             "lmPwdHash", "ntPwdHash",
+                                             NULL };
 
-       ac = talloc_get_type(h->private_data, struct ph_async_context);
+       ac = talloc_get_type(h->private_data, struct ph_context);
 
        /* prepare the search operation */
        ac->search_req = talloc_zero(ac, struct ldb_request);
@@ -833,37 +891,36 @@ static int password_hash_mod_search_self(struct ldb_async_handle *h) {
        ac->search_req->op.search.scope = LDB_SCOPE_BASE;
        ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
        if (ac->search_req->op.search.tree == NULL) {
-               ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
+               ldb_set_errstring(ac->module->ldb, "Invalid search filter");
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ac->search_req->op.search.attrs = NULL;
+       ac->search_req->op.search.attrs = attrs;
        ac->search_req->controls = NULL;
-       ac->search_req->async.context = ac;
-       ac->search_req->async.callback = get_self_callback;
-       ac->search_req->async.timeout = ac->orig_req->async.timeout;
+       ac->search_req->context = ac;
+       ac->search_req->callback = get_self_callback;
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
 
        ac->step = PH_MOD_SEARCH_SELF;
 
        return ldb_next_request(ac->module, ac->search_req);
 }
 
-static int password_hash_mod_search_dom(struct ldb_async_handle *h) {
+static int password_hash_mod_search_dom(struct ldb_handle *h) {
 
-       struct ph_async_context *ac;
-       struct dom_sid *domain_sid;
+       struct ph_context *ac;
        int ret;
 
-       ac = talloc_get_type(h->private_data, struct ph_async_context);
+       ac = talloc_get_type(h->private_data, struct ph_context);
 
        /* get object domain sid */
-       domain_sid = samdb_result_sid_prefix(ac, ac->search_res->message, "objectSid");
-       if (domain_sid == NULL) {
+       ac->domain_sid = samdb_result_sid_prefix(ac, ac->search_res->message, "objectSid");
+       if (ac->domain_sid == NULL) {
                ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* get user domain data */
-       ret = build_domain_data_request(ac, domain_sid);
+       ret = build_domain_data_request(ac);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -873,16 +930,18 @@ static int password_hash_mod_search_dom(struct ldb_async_handle *h) {
        return ldb_next_request(ac->module, ac->dom_req);
 }
 
-static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
+static int password_hash_mod_do_mod(struct ldb_handle *h) {
 
-       struct ph_async_context *ac;
+       struct ph_context *ac;
        struct domain_data *domain;
        struct smb_krb5_context *smb_krb5_context;
        struct ldb_message_element *sambaAttr;
        struct ldb_message *msg;
        int phlen;
+       int ret;
+       BOOL added_hashes = False;
 
-       ac = talloc_get_type(h->private_data, struct ph_async_context);
+       ac = talloc_get_type(h->private_data, struct ph_context);
 
        domain = get_domain_data(ac->module, ac, ac->dom_res);
        if (domain == NULL) {
@@ -915,7 +974,7 @@ static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* if we have sambaPassword in the original message add the operatio on it here */
+       /* if we have sambaPassword in the original message add the operation on it here */
        sambaAttr = ldb_msg_find_element(ac->orig_req->op.mod.message, "sambaPassword");
        if (sambaAttr) {
 
@@ -923,21 +982,26 @@ static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
                        return LDB_ERR_OPERATIONS_ERROR;
                }
 
-               /* we are not deleteing it add password hashes */
-               if ((sambaAttr->flags & LDB_FLAG_MOD_MASK) != LDB_FLAG_MOD_DELETE) {
-               
+               /* if we are actually settting a new unicode password,
+                * use it to generate the password hashes */
+               if (((sambaAttr->flags & LDB_FLAG_MOD_MASK) != LDB_FLAG_MOD_DELETE)
+                   && (sambaAttr->num_values == 1)) {
                        /* we can compute new password hashes from the unicode password */
-                       if (add_password_hashes(ac->module, msg, 1) != LDB_SUCCESS) {
-                               return LDB_ERR_OPERATIONS_ERROR;
+                       ret = add_password_hashes(ac->module, msg, 1);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
                        }
 
+                       added_hashes = True;
+
                        /* now add krb5 keys based on unicode password */
-                       if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
-                               ldb_msg_find_string(ac->search_res->message, "samAccountName", NULL),
-                               ldb_msg_find_string(ac->search_res->message, "userPrincipalName", NULL),
-                               ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "computer")
-                                                      ) != LDB_SUCCESS) {
-                               return LDB_ERR_OPERATIONS_ERROR;
+                       ret = add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
+                                                         ldb_msg_find_string(ac->search_res->message, "samAccountName", NULL),
+                                                         ldb_msg_find_string(ac->search_res->message, "userPrincipalName", NULL),
+                                                         ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "computer"));
+
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
                        }
 
                        /* if the domain properties or the user account controls do not permit
@@ -950,8 +1014,8 @@ static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
                }
        }
 
-       /* if we don't have sambaPassword or we are trying to delete it try with nt or lm hasehs */
-       if ((!sambaAttr) || ((sambaAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE)) {
+       /* if we didn't create the hashes above, try using values supplied directly */
+       if (!added_hashes) {
                struct ldb_message_element *el;
                
                el = ldb_msg_find_element(ac->orig_req->op.mod.message, "ntPwdHash");
@@ -965,7 +1029,7 @@ static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
                }
        }
 
-       /* add also kr5 keys based on NT the hash */
+       /* add also krb5 keys based on NT the hash */
        if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -976,10 +1040,14 @@ static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
        }
 
        /* don't touch it if a value is set. It could be an incoming samsync */
-       if (add_keyVersionNumber(ac->module, msg,
-                                ldb_msg_find_uint(msg, "msDS-KeyVersionNumber", 0)
-                               ) != LDB_SUCCESS) {
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (!ldb_msg_find_element(ac->orig_req->op.mod.message, 
+                                "msDS-KeyVersionNumber")) {
+               if (add_keyVersionNumber(ac->module, msg,
+                                        ldb_msg_find_uint(ac->search_res->message, 
+                                                          "msDS-KeyVersionNumber", 0)
+                           ) != LDB_SUCCESS) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
        }
 
        if ((phlen = samdb_result_uint(ac->dom_res->message, "pwdHistoryLength", 0)) > 0) {
@@ -993,12 +1061,14 @@ static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
 
        ac->step = PH_MOD_DO_MOD;
 
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
+
        /* perform the search */
        return ldb_next_request(ac->module, ac->mod_req);
 }
 
-static int ph_async_wait(struct ldb_async_handle *handle) {
-       struct ph_async_context *ac;
+static int ph_wait(struct ldb_handle *handle) {
+       struct ph_context *ac;
        int ret;
     
        if (!handle || !handle->private_data) {
@@ -1012,22 +1082,22 @@ static int ph_async_wait(struct ldb_async_handle *handle) {
        handle->state = LDB_ASYNC_PENDING;
        handle->status = LDB_SUCCESS;
 
-       ac = talloc_get_type(handle->private_data, struct ph_async_context);
+       ac = talloc_get_type(handle->private_data, struct ph_context);
 
        switch (ac->step) {
        case PH_ADD_SEARCH_DOM:
-               ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
+               ret = ldb_wait(ac->dom_req->handle, LDB_WAIT_NONE);
 
                if (ret != LDB_SUCCESS) {
                        handle->status = ret;
                        goto done;
                }
-               if (ac->dom_req->async.handle->status != LDB_SUCCESS) {
-                       handle->status = ac->dom_req->async.handle->status;
+               if (ac->dom_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->dom_req->handle->status;
                        goto done;
                }
 
-               if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
+               if (ac->dom_req->handle->state != LDB_ASYNC_DONE) {
                        return LDB_SUCCESS;
                }
 
@@ -1035,36 +1105,36 @@ static int ph_async_wait(struct ldb_async_handle *handle) {
                return password_hash_add_do_add(handle);
 
        case PH_ADD_DO_ADD:
-               ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
+               ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
 
                if (ret != LDB_SUCCESS) {
                        handle->status = ret;
                        goto done;
                }
-               if (ac->down_req->async.handle->status != LDB_SUCCESS) {
-                       handle->status = ac->down_req->async.handle->status;
+               if (ac->down_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->down_req->handle->status;
                        goto done;
                }
 
-               if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
+               if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
                        return LDB_SUCCESS;
                }
 
                break;
                
        case PH_MOD_DO_REQ:
-               ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
+               ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
 
                if (ret != LDB_SUCCESS) {
                        handle->status = ret;
                        goto done;
                }
-               if (ac->down_req->async.handle->status != LDB_SUCCESS) {
-                       handle->status = ac->down_req->async.handle->status;
+               if (ac->down_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->down_req->handle->status;
                        goto done;
                }
 
-               if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
+               if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
                        return LDB_SUCCESS;
                }
 
@@ -1072,18 +1142,18 @@ static int ph_async_wait(struct ldb_async_handle *handle) {
                return password_hash_mod_search_self(handle);
                
        case PH_MOD_SEARCH_SELF:
-               ret = ldb_async_wait(ac->search_req->async.handle, LDB_WAIT_NONE);
+               ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
 
                if (ret != LDB_SUCCESS) {
                        handle->status = ret;
                        goto done;
                }
-               if (ac->search_req->async.handle->status != LDB_SUCCESS) {
-                       handle->status = ac->search_req->async.handle->status;
+               if (ac->search_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->search_req->handle->status;
                        goto done;
                }
 
-               if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
+               if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
                        return LDB_SUCCESS;
                }
 
@@ -1091,18 +1161,18 @@ static int ph_async_wait(struct ldb_async_handle *handle) {
                return password_hash_mod_search_dom(handle);
                
        case PH_MOD_SEARCH_DOM:
-               ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
+               ret = ldb_wait(ac->dom_req->handle, LDB_WAIT_NONE);
 
                if (ret != LDB_SUCCESS) {
                        handle->status = ret;
                        goto done;
                }
-               if (ac->dom_req->async.handle->status != LDB_SUCCESS) {
-                       handle->status = ac->dom_req->async.handle->status;
+               if (ac->dom_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->dom_req->handle->status;
                        goto done;
                }
 
-               if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
+               if (ac->dom_req->handle->state != LDB_ASYNC_DONE) {
                        return LDB_SUCCESS;
                }
 
@@ -1110,18 +1180,18 @@ static int ph_async_wait(struct ldb_async_handle *handle) {
                return password_hash_mod_do_mod(handle);
 
        case PH_MOD_DO_MOD:
-               ret = ldb_async_wait(ac->mod_req->async.handle, LDB_WAIT_NONE);
+               ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
 
                if (ret != LDB_SUCCESS) {
                        handle->status = ret;
                        goto done;
                }
-               if (ac->mod_req->async.handle->status != LDB_SUCCESS) {
-                       handle->status = ac->mod_req->async.handle->status;
+               if (ac->mod_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->mod_req->handle->status;
                        goto done;
                }
 
-               if (ac->mod_req->async.handle->state != LDB_ASYNC_DONE) {
+               if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
                        return LDB_SUCCESS;
                }
 
@@ -1139,12 +1209,12 @@ done:
        return ret;
 }
 
-static int ph_async_wait_all(struct ldb_async_handle *handle) {
+static int ph_wait_all(struct ldb_handle *handle) {
 
        int ret;
 
        while (handle->state != LDB_ASYNC_DONE) {
-               ret = ph_async_wait(handle);
+               ret = ph_wait(handle);
                if (ret != LDB_SUCCESS) {
                        return ret;
                }
@@ -1153,12 +1223,12 @@ static int ph_async_wait_all(struct ldb_async_handle *handle) {
        return handle->status;
 }
 
-static int password_hash_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
+static int password_hash_wait(struct ldb_handle *handle, enum ldb_wait_type type)
 {
        if (type == LDB_WAIT_ALL) {
-               return ph_async_wait_all(handle);
+               return ph_wait_all(handle);
        } else {
-               return ph_async_wait(handle);
+               return ph_wait(handle);
        }
 }
 
@@ -1166,7 +1236,7 @@ static const struct ldb_module_ops password_hash_ops = {
        .name          = "password_hash",
        .add           = password_hash_add,
        .modify        = password_hash_modify,
-       .async_wait    = password_hash_async_wait
+       .wait          = password_hash_wait
 };