r15761: Fix-as-you-go ...
[kai/samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
1 /* 
2    ldb database module
3
4    Copyright (C) Simo Sorce  2004
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24  *  Name: ldb
25  *
26  *  Component: ldb password_hash module
27  *
28  *  Description: correctly update hash values based on changes to sambaPassword and friends
29  *
30  *  Author: Andrew Bartlett
31  */
32
33 #include "includes.h"
34 #include "libcli/ldap/ldap.h"
35 #include "ldb/include/ldb_errors.h"
36 #include "ldb/include/ldb_private.h"
37 #include "librpc/gen_ndr/misc.h"
38 #include "librpc/gen_ndr/samr.h"
39 #include "libcli/auth/libcli_auth.h"
40 #include "libcli/security/security.h"
41 #include "system/kerberos.h"
42 #include "auth/kerberos/kerberos.h"
43 #include "system/time.h"
44 #include "dsdb/samdb/samdb.h"
45 #include "ads.h"
46 #include "hdb.h"
47
48 /* If we have decided there is reason to work on this request, then
49  * setup all the password hash types correctly.
50  *
51  * If the administrator doesn't want the sambaPassword stored (set in the
52  * domain and per-account policies) then we must strip that out before
53  * we do the first operation.
54  *
55  * Once this is done (which could update anything at all), we
56  * calculate the password hashes.
57  *
58  * This function must not only update the ntPwdHash, lmPwdHash and
59  * krb5Key fields, it must also atomicly increment the
60  * msDS-KeyVersionNumber.  We should be in a transaction, so all this
61  * should be quite safe...
62  *
63  * Finally, if the administrator has requested that a password history
64  * be maintained, then this should also be written out.
65  *
66  */
67
68
69 static int password_hash_handle(struct ldb_module *module, struct ldb_request *req, 
70                              const struct ldb_message *msg)
71 {
72         int ret, old_ret = -1;
73         uint_t pwdProperties, pwdHistoryLength;
74         uint_t userAccountControl;
75         const char *dnsDomain, *realm;
76         const char *sambaPassword = NULL;
77         struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory;
78         struct samr_Password *lmPwdHash, *ntPwdHash;
79         struct samr_Password *lmOldHash = NULL, *ntOldHash = NULL;
80         struct samr_Password *new_sambaLMPwdHistory, *new_sambaNTPwdHistory;
81         struct samr_Password local_lmNewHash, local_ntNewHash;
82         int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
83         uint_t kvno;
84         struct dom_sid *domain_sid;
85         time_t now = time(NULL);
86         NTTIME now_nt;
87         int i;
88         krb5_error_code krb5_ret;
89
90         struct smb_krb5_context *smb_krb5_context;
91
92         struct ldb_message_element *attribute;
93         struct ldb_dn *dn = msg->dn;
94         struct ldb_message *msg2;
95
96         struct ldb_request *search_request = NULL;
97         struct ldb_request *modify_request;
98         struct ldb_request *modified_orig_request;
99         struct ldb_result *res, *dom_res, *old_res;
100
101         struct ldb_message_element *objectclasses;
102         struct ldb_val computer_val;
103         struct ldb_val person_val;
104         BOOL is_computer;
105
106         struct ldb_message *modify_msg;
107
108         const char *domain_expression;
109         const char *old_user_attrs[] = { "lmPwdHash", "ntPwdHash", NULL };
110         const char *user_attrs[] = { "userAccountControl", "sambaLMPwdHistory", 
111                                      "sambaNTPwdHistory", 
112                                      "ntPwdHash", 
113                                      "objectSid", "msDS-KeyVersionNumber", 
114                                      "objectClass", "userPrincipalName",
115                                      "samAccountName", 
116                                      NULL };
117         const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
118                                               "dnsDomain", NULL };
119
120         TALLOC_CTX *mem_ctx;
121
122         /* Do the original action */
123         
124         mem_ctx = talloc_new(module);
125         if (!mem_ctx) {
126                 return LDB_ERR_OPERATIONS_ERROR;
127         }
128
129         if (req->operation == LDB_REQ_MODIFY) {
130                 search_request = talloc(mem_ctx, struct ldb_request);
131                 if (!search_request) {
132                         talloc_free(mem_ctx);
133                         return LDB_ERR_OPERATIONS_ERROR;
134                 }
135
136                 /* Look up the old ntPwdHash and lmPwdHash values, so
137                  * we can later place these into the password
138                  * history */
139
140                 search_request->operation = LDB_REQ_SEARCH;
141                 search_request->op.search.base = dn;
142                 search_request->op.search.scope = LDB_SCOPE_BASE;
143                 search_request->op.search.tree = ldb_parse_tree(module->ldb, NULL);
144                 search_request->op.search.attrs = old_user_attrs;
145                 search_request->controls = NULL;
146                 
147                 old_ret = ldb_next_request(module, search_request);
148         }
149
150         /* we can't change things untill we copy it */
151         msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
152
153         /* look again, this time at the copied attribute */
154         if (!msg2 || (attribute = ldb_msg_find_element(msg2, "sambaPassword")) == NULL ) {
155                 talloc_free(mem_ctx);
156                 /* Gah?  where did it go?  Oh well... */
157                 return LDB_ERR_OPERATIONS_ERROR;
158         }
159
160         /* Wipe out the sambaPassword attribute set, we will handle it in
161          * the second modify.  We might not want it written to disk */
162         
163         if (req->operation == LDB_REQ_ADD) {
164                 if (attribute->num_values > 1) {
165                         ldb_set_errstring(module->ldb,
166                                           talloc_asprintf(mem_ctx, "sambaPassword_handle: "
167                                                           "attempted set of multiple sambaPassword attributes on %s rejected",
168                                                           ldb_dn_linearize(mem_ctx, dn)));
169                         talloc_free(mem_ctx);
170                         return LDB_ERR_CONSTRAINT_VIOLATION;
171                 }
172
173                 if (attribute->num_values == 1) {
174                         sambaPassword = (const char *)attribute->values[0].data;
175                         ldb_msg_remove_attr(msg2, "sambaPassword");
176                 }
177         } else if (((attribute->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_ADD)
178                    || ((attribute->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE)) {
179                 if (attribute->num_values > 1) {
180                         ldb_set_errstring(module->ldb,
181                                           talloc_asprintf(mem_ctx, "sambaPassword_handle: "
182                                                           "attempted set of multiple sambaPassword attributes on %s rejected",
183                                                           ldb_dn_linearize(mem_ctx, dn)));
184                         talloc_free(mem_ctx);
185                         return LDB_ERR_CONSTRAINT_VIOLATION;
186                 }
187                 
188                 if (attribute->num_values == 1) {
189                         sambaPassword = (const char *)attribute->values[0].data;
190                         ldb_msg_remove_attr(msg2, "sambaPassword");
191                 }
192         }
193
194         modified_orig_request = talloc(mem_ctx, struct ldb_request);
195         if (!modified_orig_request) {
196                 talloc_free(mem_ctx);
197                 return LDB_ERR_OPERATIONS_ERROR;
198         }
199
200         *modified_orig_request = *req;
201         switch (modified_orig_request->operation) {
202         case LDB_REQ_ADD:
203                 modified_orig_request->op.add.message = msg2;
204                 break;
205         case LDB_REQ_MODIFY:
206                 modified_orig_request->op.mod.message = msg2;
207                 break;
208         }
209
210         /* Send the (modified) request of the original caller down to the database */
211         ret = ldb_next_request(module, modified_orig_request);
212         if (ret) {
213                 talloc_free(mem_ctx);
214                 return ret;
215         }
216
217         /* While we do the search first (for the old password hashes),
218          * we don't want to override any error that the modify may
219          * have returned.  Now check the error */
220         if (req->operation == LDB_REQ_MODIFY) {
221                 if (old_ret) {
222                         talloc_free(mem_ctx);
223                         return old_ret;
224                 }
225
226                 /* Find out the old passwords details of the user */
227                 old_res = search_request->op.search.res;
228                 talloc_steal(mem_ctx, old_res);
229                 talloc_free(search_request);
230                 
231                 if (old_res->count != 1) {
232                         ldb_set_errstring(module->ldb, 
233                                           talloc_asprintf(mem_ctx, "password_hash_handle: "
234                                                           "(pre) search for %s found %d != 1 objects, for entry we just modified",
235                                                           ldb_dn_linearize(mem_ctx, dn),
236                                                           old_res->count));
237                         /* What happend?  The above add/modify worked... */
238                         talloc_free(mem_ctx);
239                         return LDB_ERR_NO_SUCH_OBJECT;
240                 }
241
242                 lmOldHash = samdb_result_hash(mem_ctx, old_res->msgs[0],   "lmPwdHash");
243                 ntOldHash = samdb_result_hash(mem_ctx, old_res->msgs[0],   "ntPwdHash");
244         }
245
246         /* Start finding out details we need for the second modify.
247          * We do this after the first add/modify because other modules
248          * will have filled in the templates, and we may have had
249          * things like the username (affecting the salt) changed along
250          * with the password. */
251
252         /* Now find out what is on the entry after the above add/modify */
253         search_request = talloc(mem_ctx, struct ldb_request);
254         if (!search_request) {
255                 talloc_free(mem_ctx);
256                 return LDB_ERR_OPERATIONS_ERROR;
257         }
258
259         search_request->operation       = LDB_REQ_SEARCH;
260         search_request->op.search.base  = dn;
261         search_request->op.search.scope = LDB_SCOPE_BASE;
262         search_request->op.search.tree  = ldb_parse_tree(module->ldb, NULL);
263         search_request->op.search.attrs = user_attrs;
264         search_request->controls = NULL;
265         
266         ret = ldb_next_request(module, search_request);
267         if (ret) {
268                 talloc_free(mem_ctx);
269                 return ret;
270         }
271
272         /* Find out the full details of the user */
273         res = search_request->op.search.res;
274         talloc_steal(mem_ctx, res);
275         talloc_free(search_request);
276
277         if (res->count != 1) {
278                 ldb_set_errstring(module->ldb,
279                                   talloc_asprintf(mem_ctx, "password_hash_handle: "
280                                                   "search for %s found %d != 1 objects, for entry we just added/modified",
281                                                   ldb_dn_linearize(mem_ctx, dn),
282                                                   res->count));
283                 /* What happend?  The above add/modify worked... */
284                 talloc_free(mem_ctx);
285                 return LDB_ERR_NO_SUCH_OBJECT;
286         }
287
288         userAccountControl = samdb_result_uint(res->msgs[0],   "userAccountControl", 0);
289         sambaLMPwdHistory_len   = samdb_result_hashes(mem_ctx, res->msgs[0], 
290                                                  "sambaLMPwdHistory", &sambaLMPwdHistory);
291         sambaNTPwdHistory_len   = samdb_result_hashes(mem_ctx, res->msgs[0], 
292                                                  "sambaNTPwdHistory", &sambaNTPwdHistory);
293         ntPwdHash          = samdb_result_hash(mem_ctx, res->msgs[0],   "ntPwdHash");
294         kvno               = samdb_result_uint(res->msgs[0],   "msDS-KeyVersionNumber", 0);
295
296         domain_sid         = samdb_result_sid_prefix(mem_ctx, res->msgs[0], "objectSid");
297
298         
299         objectclasses = ldb_msg_find_element(res->msgs[0], "objectClass");
300         person_val = data_blob_string_const("person");
301         
302         if (!objectclasses || !ldb_msg_find_val(objectclasses, &person_val)) {
303                 /* Not a 'person', so the rest of this doesn't make
304                  * sense.  How we got a sambaPassword this far I don't
305                  * know... */
306                 ldb_set_errstring(module->ldb,
307                                   talloc_asprintf(mem_ctx, "password_hash_handle: "
308                                                   "attempted set of sambaPassword on non-'person' object %s rejected",
309                                                   ldb_dn_linearize(mem_ctx, dn)));
310                 talloc_free(mem_ctx);
311                 return LDB_ERR_CONSTRAINT_VIOLATION;
312         }
313
314         computer_val = data_blob_string_const("computer");
315         
316         if (ldb_msg_find_val(objectclasses, &computer_val)) {
317                 is_computer = True;
318         } else {
319                 is_computer = False;
320         }
321         
322         domain_expression  = talloc_asprintf(mem_ctx, "(&(objectSid=%s)(objectClass=domain))", 
323                                              ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
324
325         /* Find the user's domain, then find out the domain password
326          * properties */
327         ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, domain_expression, 
328                          domain_attrs, &dom_res);
329         if (ret) {
330                 talloc_free(mem_ctx);
331                 return ret;
332         }
333
334         if (dom_res->count != 1) {
335                 /* What happend?  The user we are modifying must be odd... */
336                 ldb_set_errstring(module->ldb, 
337                                   talloc_asprintf(mem_ctx, "password_hash_handle: "
338                                                   "search for domain %s found %d != 1 objects",
339                                                   dom_sid_string(mem_ctx, domain_sid),
340                                                   dom_res->count));
341                 talloc_free(mem_ctx);
342                 return LDB_ERR_NO_SUCH_OBJECT;
343         }
344
345         pwdProperties    = samdb_result_uint(dom_res->msgs[0],   "pwdProperties", 0);
346         pwdHistoryLength = samdb_result_uint(dom_res->msgs[0],   "pwdHistoryLength", 0);
347         dnsDomain        = ldb_msg_find_string(dom_res->msgs[0], "dnsDomain", NULL);
348         realm            = strupper_talloc(mem_ctx, dnsDomain);
349
350         /* Some operations below require kerberos contexts */
351         if (smb_krb5_init_context(mem_ctx, &smb_krb5_context) != 0) {
352                 talloc_free(mem_ctx);
353                 return LDB_ERR_OPERATIONS_ERROR;
354         }
355
356         /* Prepare the modifications to set all the hash/key types */
357         modify_msg = ldb_msg_new(req);
358         modify_msg->dn = talloc_reference(modify_msg, dn);
359
360 #define CHECK_RET(x) \
361         do {                                    \
362                 int check_ret = x;              \
363                 if (check_ret != LDB_SUCCESS) { \
364                         talloc_free(mem_ctx);   \
365                         return check_ret;       \
366                 }                               \
367         } while(0)
368
369         /* Setup krb5Key (we want to either delete an existing value,
370          * or replace with a new one).  Both the unicode and NT hash
371          * only branches append keys to this multivalued entry. */
372         CHECK_RET(ldb_msg_add_empty(modify_msg, "krb5Key", LDB_FLAG_MOD_REPLACE));
373
374         /* Yay, we can compute new password hashes from the unicode
375          * password */
376         if (sambaPassword) {
377                 Principal *salt_principal;
378                 const char *user_principal_name = ldb_msg_find_string(res->msgs[0], "userPrincipalName", NULL);
379                 
380                 Key *keys;
381                 size_t num_keys;
382
383                 /* compute the new nt and lm hashes */
384                 if (E_deshash(sambaPassword, local_lmNewHash.hash)) {
385                         lmPwdHash = &local_lmNewHash;
386                 } else {
387                         lmPwdHash = NULL;
388                 }
389                 E_md4hash(sambaPassword, local_ntNewHash.hash);
390                 ntPwdHash = &local_ntNewHash;
391                 CHECK_RET(ldb_msg_add_empty(modify_msg, "ntPwdHash", 
392                                             LDB_FLAG_MOD_REPLACE));
393                 CHECK_RET(samdb_msg_add_hash(module->ldb, req, 
394                                              modify_msg, "ntPwdHash", 
395                                              ntPwdHash));
396                 CHECK_RET(ldb_msg_add_empty(modify_msg, "lmPwdHash", 
397                                             LDB_FLAG_MOD_REPLACE));
398                 if (lmPwdHash) {
399                         CHECK_RET(samdb_msg_add_hash(module->ldb, req, 
400                                                      modify_msg, "lmPwdHash", 
401                                                      lmPwdHash));
402                 }
403
404                 /* Many, many thanks to lukeh@padl.com for this
405                  * algorithm, described in his Nov 10 2004 mail to
406                  * samba-technical@samba.org */
407
408                 if (is_computer) {
409                         /* Determine a salting principal */
410                         char *samAccountName = talloc_strdup(mem_ctx, ldb_msg_find_string(res->msgs[0], "samAccountName", NULL));
411                         char *saltbody;
412                         if (!samAccountName) {
413                                 ldb_set_errstring(module->ldb,
414                                                   talloc_asprintf(mem_ctx, "password_hash_handle: "
415                                                                   "generation of new kerberos keys failed: %s is a computer without a samAccountName",
416                                                                   ldb_dn_linearize(mem_ctx, dn)));
417                                 talloc_free(mem_ctx);
418                                 return LDB_ERR_OPERATIONS_ERROR;
419                         }
420                         if (samAccountName[strlen(samAccountName)-1] == '$') {
421                                 samAccountName[strlen(samAccountName)-1] = '\0';
422                         }
423                         saltbody = talloc_asprintf(mem_ctx, "%s.%s", samAccountName, dnsDomain);
424                         
425                         krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, "host", saltbody, NULL);
426                 } else if (user_principal_name) {
427                         char *p;
428                         user_principal_name = talloc_strdup(mem_ctx, user_principal_name);
429                         if (!user_principal_name) {
430                                 talloc_free(mem_ctx);
431                                 return LDB_ERR_OPERATIONS_ERROR;
432                         } else {
433                                 p = strchr(user_principal_name, '@');
434                                 if (p) {
435                                         p[0] = '\0';
436                                 }
437                                 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, user_principal_name, NULL);
438                         } 
439                 } else {
440                         const char *samAccountName = ldb_msg_find_string(res->msgs[0], "samAccountName", NULL);
441                         if (!samAccountName) {
442                                 ldb_set_errstring(module->ldb,
443                                                   talloc_asprintf(mem_ctx, "password_hash_handle: "
444                                                                   "generation of new kerberos keys failed: %s has no samAccountName",
445                                                                   ldb_dn_linearize(mem_ctx, dn)));
446                                 talloc_free(mem_ctx);
447                                 return LDB_ERR_OPERATIONS_ERROR;
448                         }
449                         krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, samAccountName, NULL);
450                 }
451
452
453                 if (krb5_ret) {
454                         ldb_set_errstring(module->ldb,
455                                           talloc_asprintf(mem_ctx, "password_hash_handle: "
456                                                           "generation of a saltking principal failed: %s",
457                                                           smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
458                                                                                      krb5_ret, mem_ctx)));
459                         talloc_free(mem_ctx);
460                         return LDB_ERR_OPERATIONS_ERROR;
461                 }
462
463                 /* TODO: We may wish to control the encryption types chosen in future */
464                 krb5_ret = hdb_generate_key_set_password(smb_krb5_context->krb5_context,
465                                                          salt_principal, sambaPassword, &keys, &num_keys);
466                 krb5_free_principal(smb_krb5_context->krb5_context, salt_principal);
467
468                 if (krb5_ret) {
469                         ldb_set_errstring(module->ldb,
470                                           talloc_asprintf(mem_ctx, "password_hash_handle: "
471                                                           "generation of new kerberos keys failed: %s",
472                                                           smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
473                                                                                      krb5_ret, mem_ctx)));
474                         talloc_free(mem_ctx);
475                         return LDB_ERR_OPERATIONS_ERROR;
476                 }
477
478                 /* Walking all the key types generated, transform each
479                  * key into an ASN.1 blob
480                  */
481                 for (i=0; i < num_keys; i++) {
482                         unsigned char *buf;
483                         size_t buf_size;
484                         size_t len;
485                         struct ldb_val val;
486                         
487                         if (keys[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
488                                 /* We might end up doing this below:
489                                  * This ensures we get the unicode
490                                  * conversion right.  This should also
491                                  * be fixed in the Heimdal libs */
492                                 continue;
493                         }
494                         ASN1_MALLOC_ENCODE(Key, buf, buf_size, &keys[i], &len, krb5_ret);
495                         if (krb5_ret) {
496                                 return LDB_ERR_OPERATIONS_ERROR;
497                         }
498                         
499                         val.data = talloc_memdup(req, buf, len);
500                         val.length = len;
501                         free(buf);
502                         if (!val.data || krb5_ret) {
503                                 hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
504                                 talloc_free(mem_ctx);
505                                 return LDB_ERR_OPERATIONS_ERROR;
506                         }
507                         ret = ldb_msg_add_value(modify_msg, "krb5Key", &val);
508                         if (ret != LDB_SUCCESS) {
509                                 hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
510                                 talloc_free(mem_ctx);
511                                 return ret;
512                         }
513                 }
514                 
515                 hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
516         }
517
518         /* Possibly kill off the cleartext or store it */
519         CHECK_RET(ldb_msg_add_empty(modify_msg, "sambaPassword", LDB_FLAG_MOD_REPLACE));
520
521         if (sambaPassword && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
522             (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
523                 CHECK_RET(ldb_msg_add_string(modify_msg, "sambaPassword", sambaPassword));
524         }
525         
526         /* Even if we didn't get a sambaPassword, we can still setup
527          * krb5Key from the NT hash. 
528          *
529          * This is an append, so it works with the 'continue' in the
530          * unicode loop above, to use Samba's NT hash function, which
531          * is more correct than Heimdal's
532          */
533         if (ntPwdHash) {
534                 unsigned char *buf;
535                 size_t buf_size;
536                 size_t len;
537                 struct ldb_val val;
538                 Key key;
539                 
540                 key.mkvno = 0;
541                 key.salt = NULL; /* No salt for this enc type */
542
543                 krb5_ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
544                                               ETYPE_ARCFOUR_HMAC_MD5,
545                                               ntPwdHash->hash, sizeof(ntPwdHash->hash), 
546                                               &key.key);
547                 if (krb5_ret) {
548                         return LDB_ERR_OPERATIONS_ERROR;
549                 }
550                 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &key, &len, krb5_ret);
551                 if (krb5_ret) {
552                         return LDB_ERR_OPERATIONS_ERROR;
553                 }
554                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
555                                             &key.key);
556                 
557                 val.data = talloc_memdup(req, buf, len);
558                 val.length = len;
559                 free(buf);
560                 if (!val.data || ret) {
561                         return LDB_ERR_OPERATIONS_ERROR;
562                 }
563                 CHECK_RET(ldb_msg_add_value(modify_msg, "krb5Key", &val));
564         }
565
566         /* If the original caller did anything with pwdLastSet then skip this.  It could be an incoming samsync */
567         attribute = ldb_msg_find_element(msg, "pwdLastSet");
568         if (attribute == NULL) {
569                 /* Update the password last set time */
570                 unix_to_nt_time(&now_nt, now);
571                 CHECK_RET(ldb_msg_add_empty(modify_msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE));
572                 CHECK_RET(samdb_msg_add_uint64(module->ldb, mem_ctx, modify_msg, "pwdLastSet", now_nt));
573         }
574
575         /* If the original caller did anything with "msDS-KeyVersionNumber" then skip this.  It could be an incoming samsync */
576         attribute = ldb_msg_find_element(msg, "msDS-KeyVersionNumber");
577         if (attribute == NULL) {
578                 if (kvno == 0) {
579                         CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
580                                                     LDB_FLAG_MOD_REPLACE));
581                         CHECK_RET(samdb_msg_add_uint(module->ldb, mem_ctx, modify_msg, "msDS-KeyVersionNumber", kvno + 1));
582                 } else {
583                         /* While we should be in a transaction, go one extra
584                          * step in the dance for an 'atomic' increment.  This
585                          * may be of value against remote LDAP servers.  (Note
586                          * however that Mulitmaster replication stil offers no
587                          * such guarantee) */
588                         
589                         struct ldb_val old_kvno, new_kvno;
590                         old_kvno.data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", kvno);
591                         if (!old_kvno.data) {
592                                 return -1;
593                         }
594                         old_kvno.length = strlen((char *)old_kvno.data);
595                         
596                         new_kvno.data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", kvno + 1);
597                         if (!new_kvno.data) {
598                                 return -1;
599                         }
600                         new_kvno.length = strlen((char *)new_kvno.data);
601                         
602                         CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
603                                                     LDB_FLAG_MOD_DELETE));
604                         CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
605                                                     LDB_FLAG_MOD_ADD));
606                         modify_msg->elements[modify_msg->num_elements - 2].num_values = 1;
607                         modify_msg->elements[modify_msg->num_elements - 2].values = &old_kvno;
608                         modify_msg->elements[modify_msg->num_elements - 1].num_values = 1;
609                         modify_msg->elements[modify_msg->num_elements - 1].values = &new_kvno;
610                 }
611         }
612
613         CHECK_RET(ldb_msg_add_empty(modify_msg, "sambaLMPwdHistory",
614                                     LDB_FLAG_MOD_REPLACE));
615         CHECK_RET(ldb_msg_add_empty(modify_msg, "sambaNTPwdHistory",
616                                     LDB_FLAG_MOD_REPLACE));
617
618         /* If we have something to put into the history, or an old
619          * history element to expire, update the history */
620         if (pwdHistoryLength > 0 && 
621             ((sambaNTPwdHistory_len > 0) || (sambaLMPwdHistory_len > 0) 
622              || lmOldHash || ntOldHash)) {
623                 /* store the password history */
624                 new_sambaLMPwdHistory = talloc_array(mem_ctx, struct samr_Password, 
625                                                 pwdHistoryLength);
626                 if (!new_sambaLMPwdHistory) {
627                         return LDB_ERR_OPERATIONS_ERROR;
628                 }
629                 new_sambaNTPwdHistory = talloc_array(mem_ctx, struct samr_Password, 
630                                                 pwdHistoryLength);
631                 if (!new_sambaNTPwdHistory) {
632                         return LDB_ERR_OPERATIONS_ERROR;
633                 }
634                 for (i=0;i<MIN(pwdHistoryLength-1, sambaLMPwdHistory_len);i++) {
635                         new_sambaLMPwdHistory[i+1] = sambaLMPwdHistory[i];
636                 }
637                 for (i=0;i<MIN(pwdHistoryLength-1, sambaNTPwdHistory_len);i++) {
638                         new_sambaNTPwdHistory[i+1] = sambaNTPwdHistory[i];
639                 }
640                 
641                 /* Don't store 'long' passwords in the LM history, 
642                    but make sure to 'expire' one password off the other end */
643                 if (lmOldHash) {
644                         new_sambaLMPwdHistory[0] = *lmOldHash;
645                 } else {
646                         ZERO_STRUCT(new_sambaLMPwdHistory[0]);
647                 }
648                 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len + 1, pwdHistoryLength);
649                 
650                 /* Likewise, we might not have an old NT password (lm
651                  * only password change function on previous change) */
652                 if (ntOldHash) {
653                         new_sambaNTPwdHistory[0] = *ntOldHash;
654                 } else {
655                         ZERO_STRUCT(new_sambaNTPwdHistory[0]);
656                 }
657                 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len + 1, pwdHistoryLength);
658                 
659                 CHECK_RET(samdb_msg_add_hashes(mem_ctx, modify_msg, 
660                                                "sambaLMPwdHistory", 
661                                                new_sambaLMPwdHistory, 
662                                                sambaLMPwdHistory_len));
663                 
664                 CHECK_RET(samdb_msg_add_hashes(mem_ctx, modify_msg, 
665                                                "sambaNTPwdHistory", 
666                                                new_sambaNTPwdHistory, 
667                                                sambaNTPwdHistory_len));
668         }
669
670         /* Too much code above, we should check we got it close to reasonable */
671         CHECK_RET(ldb_msg_sanity_check(modify_msg));
672
673         modify_request = talloc(mem_ctx, struct ldb_request);
674         if (!modify_request) {
675                 talloc_free(mem_ctx);
676                 return LDB_ERR_OPERATIONS_ERROR;
677         }
678
679         modify_request->operation = LDB_REQ_MODIFY;
680         modify_request->op.mod.message = modify_msg;
681         modify_request->controls = NULL;
682
683         ret = ldb_next_request(module, modify_request);
684         
685         talloc_free(mem_ctx);
686         return ret;
687 }
688
689 /* add_record: do things with the sambaPassword attribute */
690 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
691 {
692         const struct ldb_message *msg = req->op.add.message;
693
694         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add_record\n");
695
696         if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
697                 return ldb_next_request(module, req);
698         }
699         
700         /* If no part of this touches the sambaPassword, then we don't
701          * need to make any changes.  For password changes/set there should
702          * be a 'delete' or a 'modify' on this attribute. */
703         if (ldb_msg_find_element(msg, "sambaPassword") == NULL ) {
704                 return ldb_next_request(module, req);
705         }
706
707         return password_hash_handle(module, req, msg);
708 }
709
710 /* modify_record: do things with the sambaPassword attribute */
711 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
712 {
713         const struct ldb_message *msg = req->op.mod.message;
714
715         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_modify_record\n");
716
717         if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
718                 return ldb_next_request(module, req);
719         }
720         
721         /* If no part of this touches the sambaPassword, then we don't
722          * need to make any changes.  For password changes/set there should
723          * be a 'delete' or a 'modify' on this attribute. */
724         if (ldb_msg_find_element(msg, "sambaPassword") == NULL ) {
725                 return ldb_next_request(module, req);
726         }
727
728         return password_hash_handle(module, req, msg);
729 }
730
731 enum ph_type {PH_ADD, PH_MOD};
732 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};
733
734 struct ph_async_context {
735
736         enum ph_type type;
737         enum ph_step step;
738
739         struct ldb_module *module;
740         struct ldb_request *orig_req;
741
742         struct ldb_request *dom_req;
743         struct ldb_async_result *dom_res;
744
745         struct ldb_request *down_req;
746
747         struct ldb_request *search_req;
748         struct ldb_async_result *search_res;
749
750         struct ldb_request *mod_req;
751 };
752
753 struct domain_data {
754         uint_t pwdProperties;
755         uint_t pwdHistoryLength;
756         char *dnsDomain;
757         char *realm;
758 };
759
760 static int add_password_hashes(struct ldb_module *module, struct ldb_message *msg, int is_mod)
761 {
762         const char *sambaPassword;
763         struct samr_Password tmp_hash;
764         
765         sambaPassword = ldb_msg_find_string(msg, "sambaPassword", NULL);
766         if (sambaPassword == NULL) { /* impossible, what happened ?! */
767                 return LDB_ERR_OPERATIONS_ERROR;
768         }
769
770         /* compute the new nt and lm hashes */
771         if (is_mod) {
772                 if (ldb_msg_add_empty(msg, "ntPwdHash", LDB_FLAG_MOD_REPLACE) != 0) {
773                         return LDB_ERR_OPERATIONS_ERROR;
774                 }
775         }       
776         E_md4hash(sambaPassword, tmp_hash.hash);
777         if (samdb_msg_add_hash(module->ldb, msg, msg, "ntPwdHash", &tmp_hash) != 0) {
778                 return LDB_ERR_OPERATIONS_ERROR;
779         }
780
781         if (E_deshash(sambaPassword, tmp_hash.hash)) {
782                 if (is_mod) {
783                         if (ldb_msg_add_empty(msg, "lmPwdHash", LDB_FLAG_MOD_REPLACE) != 0) {
784                                 return LDB_ERR_OPERATIONS_ERROR;
785                         }
786                 }
787                 if (samdb_msg_add_hash(module->ldb, msg, msg, "lmPwdHash", &tmp_hash) != 0) {
788                         return LDB_ERR_OPERATIONS_ERROR;
789                 }
790         }
791
792         return LDB_SUCCESS;
793 }
794
795 static int add_krb5_keys_from_password(struct ldb_module *module, struct ldb_message *msg,
796                                         struct smb_krb5_context *smb_krb5_context,
797                                         struct domain_data *domain,
798                                         const char *samAccountName,
799                                         const char *user_principal_name,
800                                         int is_computer)
801 {
802         const char *sambaPassword;
803         Principal *salt_principal;
804         krb5_error_code krb5_ret;
805         size_t num_keys;
806         Key *keys;
807         int i;
808
809         /* Many, many thanks to lukeh@padl.com for this
810          * algorithm, described in his Nov 10 2004 mail to
811          * samba-technical@samba.org */
812
813         sambaPassword = ldb_msg_find_string(msg, "sambaPassword", NULL);
814         if (sambaPassword == NULL) { /* impossible, what happened ?! */
815                 return LDB_ERR_OPERATIONS_ERROR;
816         }
817
818         if (is_computer) {
819                 /* Determine a salting principal */
820                 char *name = talloc_strdup(msg, samAccountName);
821                 char *saltbody;
822                 if (name == NULL) {
823                         ldb_set_errstring(module->ldb,
824                                           talloc_asprintf(msg, "password_hash_handle: "
825                                                           "generation of new kerberos keys failed: %s is a computer without a samAccountName",
826                                                           ldb_dn_linearize(msg, msg->dn)));
827                         return LDB_ERR_OPERATIONS_ERROR;
828                 }
829                 if (name[strlen(name)-1] == '$') {
830                         name[strlen(name)-1] = '\0';
831                 }
832                 saltbody = talloc_asprintf(msg, "%s.%s", name, domain->dnsDomain);
833                 
834                 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context,
835                                                 &salt_principal,
836                                                 domain->realm, "host",
837                                                 saltbody, NULL);
838         } else if (user_principal_name) {
839                 char *p;
840                 user_principal_name = talloc_strdup(msg, user_principal_name);
841                 if (user_principal_name == NULL) {
842                         return LDB_ERR_OPERATIONS_ERROR;
843                 } else {
844                         p = strchr(user_principal_name, '@');
845                         if (p) {
846                                 p[0] = '\0';
847                         }
848                         krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context,
849                                                         &salt_principal,
850                                                         domain->realm, user_principal_name, NULL);
851                 } 
852         } else {
853                 if (!samAccountName) {
854                         ldb_set_errstring(module->ldb,
855                                           talloc_asprintf(msg, "password_hash_handle: "
856                                                           "generation of new kerberos keys failed: %s has no samAccountName",
857                                                           ldb_dn_linearize(msg, msg->dn)));
858                         return LDB_ERR_OPERATIONS_ERROR;
859                 }
860                 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context,
861                                                 &salt_principal,
862                                                 domain->realm, samAccountName,
863                                                 NULL);
864         }
865
866         if (krb5_ret) {
867                 ldb_set_errstring(module->ldb,
868                                   talloc_asprintf(msg, "password_hash_handle: "
869                                                   "generation of a saltking principal failed: %s",
870                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
871                                                                              krb5_ret, msg)));
872                 return LDB_ERR_OPERATIONS_ERROR;
873         }
874
875         /* TODO: We may wish to control the encryption types chosen in future */
876         krb5_ret = hdb_generate_key_set_password(smb_krb5_context->krb5_context,
877                                                  salt_principal, sambaPassword, &keys, &num_keys);
878         krb5_free_principal(smb_krb5_context->krb5_context, salt_principal);
879
880         if (krb5_ret) {
881                 ldb_set_errstring(module->ldb,
882                                   talloc_asprintf(msg, "password_hash_handle: "
883                                                   "generation of new kerberos keys failed: %s",
884                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
885                                                                              krb5_ret, msg)));
886                 return LDB_ERR_OPERATIONS_ERROR;
887         }
888
889         /* Walking all the key types generated, transform each
890          * key into an ASN.1 blob
891          */
892         for (i=0; i < num_keys; i++) {
893                 unsigned char *buf;
894                 size_t buf_size;
895                 size_t len;
896                 struct ldb_val val;
897                 int ret;
898                 
899                 if (keys[i].key.keytype == ENCTYPE_ARCFOUR_HMAC) {
900                         /* We might end up doing this below:
901                          * This ensures we get the unicode
902                          * conversion right.  This should also
903                          * be fixed in the Heimdal libs */
904                         continue;
905                 }
906                 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &keys[i], &len, krb5_ret);
907                 if (krb5_ret) {
908                         return LDB_ERR_OPERATIONS_ERROR;
909                 }
910                 
911                 val.data = talloc_memdup(msg, buf, len);
912                 val.length = len;
913                 free(buf);
914                 if (!val.data || krb5_ret) {
915                         hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
916                         return LDB_ERR_OPERATIONS_ERROR;
917                 }
918                 ret = ldb_msg_add_value(msg, "krb5Key", &val);
919                 if (ret != LDB_SUCCESS) {
920                         hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
921                         return ret;
922                 }
923         }
924         
925         hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
926
927         return LDB_SUCCESS;
928 }
929
930 static int add_krb5_keys_from_NThash(struct ldb_module *module, struct ldb_message *msg,
931                                         struct smb_krb5_context *smb_krb5_context)
932 {
933         struct samr_Password *ntPwdHash;
934         krb5_error_code krb5_ret;
935         unsigned char *buf;
936         size_t buf_size;
937         size_t len;
938         struct ldb_val val;
939         Key key;
940         
941         key.mkvno = 0;
942         key.salt = NULL; /* No salt for this enc type */
943
944         ntPwdHash = samdb_result_hash(msg, msg, "ntPwdHash");
945         if (ntPwdHash == NULL) { /* what happened ?! */
946                 return LDB_ERR_OPERATIONS_ERROR;
947         }
948
949         krb5_ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
950                                  ENCTYPE_ARCFOUR_HMAC,
951                                  ntPwdHash->hash, sizeof(ntPwdHash->hash), 
952                                  &key.key);
953         if (krb5_ret) {
954                 return LDB_ERR_OPERATIONS_ERROR;
955         }
956         ASN1_MALLOC_ENCODE(Key, buf, buf_size, &key, &len, krb5_ret);
957         if (krb5_ret) {
958                 return LDB_ERR_OPERATIONS_ERROR;
959         }
960         krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
961                                     &key.key);
962         
963         val.data = talloc_memdup(msg, buf, len);
964         val.length = len;
965         free(buf);
966         if (!val.data) {
967                 return LDB_ERR_OPERATIONS_ERROR;
968         }
969         if (ldb_msg_add_value(msg, "krb5Key", &val) != 0) {
970                 return LDB_ERR_OPERATIONS_ERROR;
971         }
972
973         return LDB_SUCCESS;
974 }
975
976 static int set_pwdLastSet(struct ldb_module *module, struct ldb_message *msg)
977 {
978         NTTIME now_nt;
979
980         /* set it as now */
981         unix_to_nt_time(&now_nt, time(NULL));
982
983         /* replace or add */
984         if (ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE) != 0) {
985                 return LDB_ERR_OPERATIONS_ERROR;
986         }
987
988         if (samdb_msg_add_uint64(module->ldb, msg, msg, "pwdLastSet", now_nt) != 0) {
989                 return LDB_ERR_OPERATIONS_ERROR;
990         }
991
992         return LDB_SUCCESS;
993 }
994
995 static int add_keyVersionNumber(struct ldb_module *module, struct ldb_message *msg, int previous)
996 {
997         /* replace or add */
998         if (ldb_msg_add_empty(msg, "msDS-KeyVersionNumber", LDB_FLAG_MOD_REPLACE) != 0) {
999                 return LDB_ERR_OPERATIONS_ERROR;
1000         }
1001
1002         if (samdb_msg_add_uint(module->ldb, msg, msg, "msDS-KeyVersionNumber", previous+1) != 0) {
1003                 return LDB_ERR_OPERATIONS_ERROR;
1004         }
1005
1006         return LDB_SUCCESS;
1007 }
1008
1009 static int setPwdHistory(struct ldb_module *module, struct ldb_message *msg, struct ldb_message *old_msg, int hlen)
1010 {
1011         struct samr_Password *nt_hash;
1012         struct samr_Password *lm_hash;
1013         struct samr_Password *nt_history;
1014         struct samr_Password *lm_history;
1015         struct samr_Password *new_nt_history;
1016         struct samr_Password *new_lm_history;
1017         int nt_hist_len;
1018         int lm_hist_len;
1019         int i;
1020
1021         nt_hash = samdb_result_hash(msg, old_msg, "ntPwdHash");
1022         lm_hash = samdb_result_hash(msg, old_msg, "lmPwdHash");
1023
1024         /* if no previous passwords just return */
1025         if (nt_hash == NULL && lm_hash == NULL) return LDB_SUCCESS;
1026
1027         nt_hist_len = samdb_result_hashes(msg, old_msg, "sambaNTPwdHistory", &nt_history);
1028         lm_hist_len = samdb_result_hashes(msg, old_msg, "sambaLMPwdHistory", &lm_history);
1029
1030         /* We might not have an old NT password */
1031         new_nt_history = talloc_array(msg, struct samr_Password, hlen);
1032         if (new_nt_history == NULL) {
1033                 return LDB_ERR_OPERATIONS_ERROR;
1034         }
1035         for (i = 0; i < MIN(hlen-1, nt_hist_len); i++) {
1036                 new_nt_history[i+1] = nt_history[i];
1037         }
1038         nt_hist_len = i + 1;
1039         if (nt_hash) {
1040                 new_nt_history[0] = *nt_hash;
1041         } else {
1042                 ZERO_STRUCT(new_nt_history[0]);
1043         }
1044         if (ldb_msg_add_empty(msg, "sambaNTPwdHistory", LDB_FLAG_MOD_REPLACE) != LDB_SUCCESS) {
1045                 return LDB_ERR_OPERATIONS_ERROR;
1046         }
1047         if (samdb_msg_add_hashes(msg, msg, "sambaNTPwdHistory", new_nt_history, nt_hist_len) != LDB_SUCCESS) {
1048                 return LDB_ERR_OPERATIONS_ERROR;
1049         }
1050                 
1051
1052         /* Don't store 'long' passwords in the LM history, 
1053            but make sure to 'expire' one password off the other end */
1054         new_lm_history = talloc_array(msg, struct samr_Password, hlen);
1055         if (new_lm_history == NULL) {
1056                 return LDB_ERR_OPERATIONS_ERROR;
1057         }
1058         for (i = 0; i < MIN(hlen-1, lm_hist_len); i++) {
1059                 new_lm_history[i+1] = lm_history[i];
1060         }
1061         lm_hist_len = i + 1;
1062         if (lm_hash) {
1063                 new_lm_history[0] = *lm_hash;
1064         } else {
1065                 ZERO_STRUCT(new_lm_history[0]);
1066         }
1067         if (ldb_msg_add_empty(msg, "sambaLMPwdHistory", LDB_FLAG_MOD_REPLACE) != LDB_SUCCESS) {
1068                 return LDB_ERR_OPERATIONS_ERROR;
1069         }
1070         if (samdb_msg_add_hashes(msg, msg, "sambaLMPwdHistory", new_lm_history, lm_hist_len) != LDB_SUCCESS) {
1071                 return LDB_ERR_OPERATIONS_ERROR;
1072         }
1073
1074         return LDB_SUCCESS;
1075 }
1076
1077 static struct ldb_async_handle *ph_init_handle(struct ldb_request *req, struct ldb_module *module, enum ph_type type)
1078 {
1079         struct ph_async_context *ac;
1080         struct ldb_async_handle *h;
1081
1082         h = talloc_zero(req, struct ldb_async_handle);
1083         if (h == NULL) {
1084                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
1085                 return NULL;
1086         }
1087
1088         h->module = module;
1089
1090         ac = talloc_zero(h, struct ph_async_context);
1091         if (ac == NULL) {
1092                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
1093                 talloc_free(h);
1094                 return NULL;
1095         }
1096
1097         h->private_data = (void *)ac;
1098
1099         h->state = LDB_ASYNC_INIT;
1100         h->status = LDB_SUCCESS;
1101
1102         ac->type = type;
1103         ac->module = module;
1104         ac->orig_req = req;
1105
1106         return h;
1107 }
1108
1109 static int get_domain_data_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
1110 {
1111         struct ph_async_context *ac;
1112
1113         if (!context || !ares) {
1114                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
1115                 return LDB_ERR_OPERATIONS_ERROR;
1116         }
1117
1118         ac = talloc_get_type(context, struct ph_async_context);
1119
1120         /* we are interested only in the single reply (base search) we receive here */
1121         if (ares->type == LDB_REPLY_ENTRY) {
1122                 if (ac->dom_res != NULL) {
1123                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
1124                         talloc_free(ares);
1125                         return LDB_ERR_OPERATIONS_ERROR;
1126                 }
1127                 ac->dom_res = talloc_steal(ac, ares);
1128         } else {
1129                 talloc_free(ares);
1130         }
1131
1132         return LDB_SUCCESS;
1133 }
1134
1135 static int build_domain_data_request(struct ph_async_context *ac,
1136                                      struct dom_sid *sid)
1137 {
1138         const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", "dnsDomain", NULL };
1139         char *filter;
1140
1141         ac->dom_req = talloc_zero(ac, struct ldb_request);
1142         if (ac->dom_req == NULL) {
1143                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
1144                 return LDB_ERR_OPERATIONS_ERROR;
1145         }
1146         ac->dom_req->operation = LDB_ASYNC_SEARCH;
1147         ac->dom_req->op.search.base = NULL;
1148         ac->dom_req->op.search.scope = LDB_SCOPE_SUBTREE;
1149
1150         filter = talloc_asprintf(ac->dom_req, "(&(objectSid=%s)(objectClass=domain))", dom_sid_string(ac->dom_req, sid));
1151         if (filter == NULL) {
1152                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
1153                 talloc_free(ac->dom_req);
1154                 return LDB_ERR_OPERATIONS_ERROR;
1155         }
1156
1157         ac->dom_req->op.search.tree = ldb_parse_tree(ac->module->ldb, filter);
1158         if (ac->dom_req->op.search.tree == NULL) {
1159                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
1160                 talloc_free(ac->dom_req);
1161                 return LDB_ERR_OPERATIONS_ERROR;
1162         }
1163         ac->dom_req->op.search.attrs = attrs;
1164         ac->dom_req->controls = NULL;
1165         ac->dom_req->creds = ac->orig_req->creds;
1166         ac->dom_req->async.context = ac;
1167         ac->dom_req->async.callback = get_domain_data_callback;
1168         ac->dom_req->async.timeout = ac->orig_req->async.timeout;
1169
1170         return LDB_SUCCESS;
1171 }
1172
1173 static struct domain_data *get_domain_data(struct ldb_module *module, void *mem_ctx, struct ldb_async_result *res)
1174 {
1175         struct domain_data *data;
1176         const char *tmp;
1177         
1178         data = talloc_zero(mem_ctx, struct domain_data);
1179         if (data == NULL) {
1180                 return NULL;
1181         }
1182
1183         data->pwdProperties = samdb_result_uint(res->message, "pwdProperties", 0);
1184         data->pwdHistoryLength = samdb_result_uint(res->message, "pwdHistoryLength", 0);
1185         tmp = ldb_msg_find_string(res->message, "dnsDomain", NULL);
1186
1187         if (tmp != NULL) {
1188                 data->dnsDomain = talloc_strdup(data, tmp);
1189                 if (data->dnsDomain == NULL) {
1190                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
1191                         return NULL;
1192                 }
1193                 data->realm = strupper_talloc(mem_ctx, tmp);
1194                 if (data->realm == NULL) {
1195                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
1196                         return NULL;
1197                 }
1198         }
1199
1200         return data;
1201 }
1202
1203 static int password_hash_add_async(struct ldb_module *module, struct ldb_request *req)
1204 {
1205         struct ldb_async_handle *h;
1206         struct ph_async_context *ac;
1207         struct ldb_message_element *attribute;
1208         struct dom_sid *domain_sid;
1209         int ret;
1210
1211         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add_async\n");
1212
1213         if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
1214                 return ldb_next_request(module, req);
1215         }
1216
1217         /* nobody must touch password Histories */
1218         if (ldb_msg_find_element(req->op.add.message, "sambaNTPwdHistory") ||
1219             ldb_msg_find_element(req->op.add.message, "sambaLMPwdHistory")) {
1220                 return LDB_ERR_UNWILLING_TO_PERFORM;
1221         }
1222
1223         /* If no part of this touches the sambaPassword, then we don't
1224          * need to make any changes.  For password changes/set there should
1225          * be a 'delete' or a 'modify' on this attribute. */
1226         if ((attribute = ldb_msg_find_element(req->op.add.message, "sambaPassword")) == NULL ) {
1227                 return ldb_next_request(module, req);
1228         }
1229
1230         /* if it is not an entry of type person its an error */
1231         /* TODO: remove this when sambaPassword will be in schema */
1232         if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
1233                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1234         }
1235
1236         /* check sambaPassword is single valued here */
1237         /* TODO: remove this when sambaPassword will be single valued in schema */
1238         if (attribute->num_values > 1) {
1239                 ldb_set_errstring(module->ldb, talloc_asprintf(req,
1240                                         "mupltiple values for sambaPassword not allowed!\n"));
1241                 return LDB_ERR_CONSTRAINT_VIOLATION;
1242         }
1243
1244         /* get user domain data */
1245         domain_sid = samdb_result_sid_prefix(req, req->op.add.message, "objectSid");
1246         if (domain_sid == NULL) {
1247                 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
1248                 return LDB_ERR_OPERATIONS_ERROR;
1249         }
1250
1251         h = ph_init_handle(req, module, PH_ADD);
1252         if (!h) {
1253                 return LDB_ERR_OPERATIONS_ERROR;
1254         }
1255         ac = talloc_get_type(h->private_data, struct ph_async_context);
1256
1257         ret = build_domain_data_request(ac, domain_sid);
1258         if (ret != LDB_SUCCESS) {
1259                 return ret;
1260         }
1261
1262         ac->step = PH_ADD_SEARCH_DOM;
1263
1264         req->async.handle = h;
1265
1266         return ldb_next_request(module, ac->dom_req);
1267 }
1268
1269 static int password_hash_add_async_do_add(struct ldb_async_handle *h) {
1270
1271         struct ph_async_context *ac;
1272         struct domain_data *domain;
1273         struct smb_krb5_context *smb_krb5_context;
1274         struct ldb_message *msg;
1275
1276         ac = talloc_get_type(h->private_data, struct ph_async_context);
1277
1278         domain = get_domain_data(ac->module, ac, ac->search_res);
1279         if (domain == NULL) {
1280                 return LDB_ERR_OPERATIONS_ERROR;
1281         }
1282
1283         ac->down_req = talloc(ac, struct ldb_request);
1284         if (ac->down_req == NULL) {
1285                 return LDB_ERR_OPERATIONS_ERROR;
1286         }
1287
1288         *(ac->down_req) = *(ac->orig_req);
1289         ac->down_req->op.add.message = msg = ldb_msg_copy_shallow(ac->down_req, ac->orig_req->op.add.message);
1290         if (ac->down_req->op.add.message == NULL) {
1291                 return LDB_ERR_OPERATIONS_ERROR;
1292         }
1293         
1294         /* Some operations below require kerberos contexts */
1295         if (smb_krb5_init_context(ac->down_req, &smb_krb5_context) != 0) {
1296                 return LDB_ERR_OPERATIONS_ERROR;
1297         }
1298
1299         /* we can compute new password hashes from the unicode password */
1300         if (add_password_hashes(ac->module, msg, 0) != LDB_SUCCESS) {
1301                 return LDB_ERR_OPERATIONS_ERROR;
1302         }
1303
1304         /* now add krb5 keys based on unicode password */
1305         if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
1306                                         ldb_msg_find_string(msg, "samAccountName", NULL),
1307                                         ldb_msg_find_string(msg, "userPrincipalName", NULL),
1308                                         ldb_msg_check_string_attribute(msg, "objectClass", "computer")
1309                                        ) != LDB_SUCCESS) {
1310                 return LDB_ERR_OPERATIONS_ERROR;
1311         }
1312
1313         /* add also kr5 keys based on NT the hash */
1314         if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
1315                 return LDB_ERR_OPERATIONS_ERROR;
1316         }
1317
1318         /* if both the domain properties and the user account controls do not permit
1319          * clear text passwords then wipe out the sambaPassword */
1320         if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
1321             (!(ldb_msg_find_uint(msg, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
1322                 ldb_msg_remove_attr(msg, "sambaPassword");
1323         }
1324
1325         /* don't touch it if a value is set. It could be an incoming samsync */
1326         if (ldb_msg_find_uint64(msg, "pwdLastSet", 0) == 0) {
1327                 if (set_pwdLastSet(ac->module, msg) != LDB_SUCCESS) {
1328                         return LDB_ERR_OPERATIONS_ERROR;
1329                 }
1330         }
1331
1332         /* don't touch it if a value is set. It could be an incoming samsync */
1333         if (!ldb_msg_find_element(msg, "msDS-KeyVersionNumber")) {
1334                 if (add_keyVersionNumber(ac->module, msg, 0) != LDB_SUCCESS) {
1335                         return LDB_ERR_OPERATIONS_ERROR;
1336                 }
1337         }
1338
1339         h->state = LDB_ASYNC_INIT;
1340         h->status = LDB_SUCCESS;
1341
1342         ac->step = PH_ADD_DO_ADD;
1343
1344         /* perform the operation */
1345         return ldb_next_request(ac->module, ac->down_req);
1346 }
1347
1348 static int password_hash_mod_async_search_self(struct ldb_async_handle *h);
1349
1350 static int password_hash_modify_async(struct ldb_module *module, struct ldb_request *req)
1351 {
1352         struct ldb_async_handle *h;
1353         struct ph_async_context *ac;
1354         struct ldb_message_element *sambaAttr;
1355         struct ldb_message_element *ntAttr;
1356         struct ldb_message_element *lmAttr;
1357
1358         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add_async\n");
1359
1360         if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
1361                 return ldb_next_request(module, req);
1362         }
1363         
1364         /* nobody must touch password Histories */
1365         if (ldb_msg_find_element(req->op.mod.message, "sambaNTPwdHistory") ||
1366             ldb_msg_find_element(req->op.mod.message, "sambaLMPwdHistory")) {
1367                 return LDB_ERR_UNWILLING_TO_PERFORM;
1368         }
1369
1370         sambaAttr = ldb_msg_find_element(req->op.mod.message, "sambaPassword");
1371         ntAttr = ldb_msg_find_element(req->op.mod.message, "ntPwdHash");
1372         lmAttr = ldb_msg_find_element(req->op.mod.message, "lmPwdHash");
1373
1374         /* check passwords are single valued here */
1375         /* TODO: remove this when passwords will be single valued in schema */
1376         if (sambaAttr && (sambaAttr->num_values > 1)) {
1377                 return LDB_ERR_CONSTRAINT_VIOLATION;
1378         }
1379         if (ntAttr && (ntAttr->num_values > 1)) {
1380                 return LDB_ERR_CONSTRAINT_VIOLATION;
1381         }
1382         if (lmAttr && (lmAttr->num_values > 1)) {
1383                 return LDB_ERR_CONSTRAINT_VIOLATION;
1384         }
1385
1386         /* If no part of this touches the sambaPassword OR ntPwdHash and/or lmPwdHash, then we don't
1387          * need to make any changes.  For password changes/set there should
1388          * be a 'delete' or a 'modify' on this attribute. */
1389         /* If the only operation is the deletion of the passwords then go on */
1390         if (       ((!sambaAttr) || ((sambaAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))
1391                 && ((!ntAttr) || ((ntAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))
1392                 && ((!lmAttr) || ((lmAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))  ) {
1393
1394                 return ldb_next_request(module, req);
1395         }
1396
1397         h = ph_init_handle(req, module, PH_MOD);
1398         if (!h) {
1399                 return LDB_ERR_OPERATIONS_ERROR;
1400         }
1401         ac = talloc_get_type(h->private_data, struct ph_async_context);
1402
1403         /* return or own handle to deal with this call */
1404         req->async.handle = h;
1405
1406         /* prepare the first operation */
1407         ac->down_req = talloc_zero(ac, struct ldb_request);
1408         if (ac->down_req == NULL) {
1409                 ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
1410                 return LDB_ERR_OPERATIONS_ERROR;
1411         }
1412
1413         *(ac->down_req) = *req; /* copy the request */
1414
1415         /* use a new message structure so that we can modify it */
1416         ac->down_req->op.mod.message = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
1417
1418         /* - remove any imodification to the password from the first commit
1419          *   we will make the real modification later */
1420         if (sambaAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "sambaPassword");
1421         if (ntAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "ntPwdHash");
1422         if (lmAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "lmPwdHash");
1423
1424         /* if there was nothing else to be modify skip to next step */
1425         if (ac->down_req->op.mod.message->num_elements == 0) {
1426                 talloc_free(ac->down_req);
1427                 ac->down_req = NULL;
1428                 return password_hash_mod_async_search_self(h);
1429         }
1430         
1431         ac->down_req->async.context = NULL;
1432         ac->down_req->async.callback = NULL;
1433
1434         ac->step = PH_MOD_DO_REQ;
1435
1436         return ldb_next_request(module, ac->down_req);
1437 }
1438
1439 static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
1440 {
1441         struct ph_async_context *ac;
1442
1443         if (!context || !ares) {
1444                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
1445                 return LDB_ERR_OPERATIONS_ERROR;
1446         }
1447
1448         ac = talloc_get_type(context, struct ph_async_context);
1449
1450         /* we are interested only in the single reply (base search) we receive here */
1451         if (ares->type == LDB_REPLY_ENTRY) {
1452                 if (ac->search_res != NULL) {
1453                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
1454                         talloc_free(ares);
1455                         return LDB_ERR_OPERATIONS_ERROR;
1456                 }
1457
1458                 /* if it is not an entry of type person this is an error */
1459                 /* TODO: remove this when sambaPassword will be in schema */
1460                 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
1461                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Object class violation"));
1462                         talloc_free(ares);
1463                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
1464                 }
1465
1466                 ac->search_res = talloc_steal(ac, ares);
1467         } else {
1468                 talloc_free(ares);
1469         }
1470
1471         return LDB_SUCCESS;
1472 }
1473
1474 static int password_hash_mod_async_search_self(struct ldb_async_handle *h) {
1475
1476         struct ph_async_context *ac;
1477
1478         ac = talloc_get_type(h->private_data, struct ph_async_context);
1479
1480         /* prepare the search operation */
1481         ac->search_req = talloc_zero(ac, struct ldb_request);
1482         if (ac->search_req == NULL) {
1483                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
1484                 return LDB_ERR_OPERATIONS_ERROR;
1485         }
1486
1487         ac->search_req->operation = LDB_ASYNC_SEARCH;
1488         ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
1489         ac->search_req->op.search.scope = LDB_SCOPE_BASE;
1490         ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
1491         if (ac->search_req->op.search.tree == NULL) {
1492                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
1493                 return LDB_ERR_OPERATIONS_ERROR;
1494         }
1495         ac->search_req->op.search.attrs = NULL;
1496         ac->search_req->controls = NULL;
1497         ac->search_req->creds = ac->orig_req->creds;
1498         ac->search_req->async.context = ac;
1499         ac->search_req->async.callback = get_self_callback;
1500         ac->search_req->async.timeout = ac->orig_req->async.timeout;
1501
1502         ac->step = PH_MOD_SEARCH_SELF;
1503
1504         return ldb_next_request(ac->module, ac->search_req);
1505 }
1506
1507 static int password_hash_mod_async_search_dom(struct ldb_async_handle *h) {
1508
1509         struct ph_async_context *ac;
1510         struct dom_sid *domain_sid;
1511         int ret;
1512
1513         ac = talloc_get_type(h->private_data, struct ph_async_context);
1514
1515         /* get object domain sid */
1516         domain_sid = samdb_result_sid_prefix(ac, ac->search_res->message, "objectSid");
1517         if (domain_sid == NULL) {
1518                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
1519                 return LDB_ERR_OPERATIONS_ERROR;
1520         }
1521
1522         /* get user domain data */
1523         ret = build_domain_data_request(ac, domain_sid);
1524         if (ret != LDB_SUCCESS) {
1525                 return ret;
1526         }
1527
1528         ac->step = PH_MOD_SEARCH_DOM;
1529
1530         return ldb_next_request(ac->module, ac->dom_req);
1531 }
1532
1533 static int password_hash_mod_async_do_mod(struct ldb_async_handle *h) {
1534
1535         struct ph_async_context *ac;
1536         struct domain_data *domain;
1537         struct smb_krb5_context *smb_krb5_context;
1538         struct ldb_message_element *sambaAttr;
1539         struct ldb_message *msg;
1540         int phlen;
1541
1542         ac = talloc_get_type(h->private_data, struct ph_async_context);
1543
1544         domain = get_domain_data(ac->module, ac, ac->dom_res);
1545         if (domain == NULL) {
1546                 return LDB_ERR_OPERATIONS_ERROR;
1547         }
1548
1549         ac->mod_req = talloc(ac, struct ldb_request);
1550         if (ac->mod_req == NULL) {
1551                 return LDB_ERR_OPERATIONS_ERROR;
1552         }
1553
1554         *(ac->mod_req) = *(ac->orig_req);
1555         
1556         /* use a new message structure so that we can modify it */
1557         ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
1558         if (msg == NULL) {
1559                 return LDB_ERR_OPERATIONS_ERROR;
1560         }
1561
1562         /* modify dn */
1563         msg->dn = ac->orig_req->op.mod.message->dn;
1564
1565         /* Some operations below require kerberos contexts */
1566         if (smb_krb5_init_context(ac->mod_req, &smb_krb5_context) != 0) {
1567                 return LDB_ERR_OPERATIONS_ERROR;
1568         }
1569
1570         /* we are going to replace the existing krb5key or delete it */
1571         if (ldb_msg_add_empty(msg, "krb5key", LDB_FLAG_MOD_REPLACE) != 0) {
1572                 return LDB_ERR_OPERATIONS_ERROR;
1573         }
1574
1575         /* if we have sambaPassword in the original message add the operatio on it here */
1576         sambaAttr = ldb_msg_find_element(ac->orig_req->op.mod.message, "sambaPassword");
1577         if (sambaAttr) {
1578
1579                 if (ldb_msg_add(msg, sambaAttr, sambaAttr->flags) != 0) {
1580                         return LDB_ERR_OPERATIONS_ERROR;
1581                 }
1582
1583                 /* we are not deleteing it add password hashes */
1584                 if ((sambaAttr->flags & LDB_FLAG_MOD_MASK) != LDB_FLAG_MOD_DELETE) {
1585                 
1586                         /* we can compute new password hashes from the unicode password */
1587                         if (add_password_hashes(ac->module, msg, 1) != LDB_SUCCESS) {
1588                                 return LDB_ERR_OPERATIONS_ERROR;
1589                         }
1590
1591                         /* now add krb5 keys based on unicode password */
1592                         if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
1593                                 ldb_msg_find_string(ac->search_res->message, "samAccountName", NULL),
1594                                 ldb_msg_find_string(ac->search_res->message, "userPrincipalName", NULL),
1595                                 ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "computer")
1596                                                        ) != LDB_SUCCESS) {
1597                                 return LDB_ERR_OPERATIONS_ERROR;
1598                         }
1599
1600                         /* if the domain properties or the user account controls do not permit
1601                          * clear text passwords then wipe out the sambaPassword */
1602                         if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
1603                             (!(ldb_msg_find_uint(ac->search_res->message, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
1604                                 ldb_msg_remove_attr(msg, "sambaPassword");
1605                         }
1606
1607                 }
1608         }
1609
1610         /* if we don't have sambaPassword or we are trying to delete it try with nt or lm hasehs */
1611         if ((!sambaAttr) || ((sambaAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE)) {
1612                 struct ldb_message_element *el;
1613                 
1614                 el = ldb_msg_find_element(ac->orig_req->op.mod.message, "ntPwdHash");
1615                 if (ldb_msg_add(msg, el, el->flags) != 0) {
1616                         return LDB_ERR_OPERATIONS_ERROR;
1617                 }
1618                 
1619                 el = ldb_msg_find_element(ac->orig_req->op.mod.message, "lmPwdHash");
1620                 if (ldb_msg_add(msg, el, el->flags) != 0) {
1621                         return LDB_ERR_OPERATIONS_ERROR;
1622                 }
1623         }
1624
1625         /* add also kr5 keys based on NT the hash */
1626         if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
1627                 return LDB_ERR_OPERATIONS_ERROR;
1628         }
1629
1630         /* set change time */
1631         if (set_pwdLastSet(ac->module, msg) != LDB_SUCCESS) {
1632                 return LDB_ERR_OPERATIONS_ERROR;
1633         }
1634
1635         /* don't touch it if a value is set. It could be an incoming samsync */
1636         if (add_keyVersionNumber(ac->module, msg,
1637                                  ldb_msg_find_uint(msg, "msDS-KeyVersionNumber", 0)
1638                                 ) != LDB_SUCCESS) {
1639                 return LDB_ERR_OPERATIONS_ERROR;
1640         }
1641
1642         if ((phlen = samdb_result_uint(ac->dom_res->message, "pwdHistoryLength", 0)) > 0) {
1643                 if (setPwdHistory(ac->module, msg, ac->search_res->message, phlen) != LDB_SUCCESS) {
1644                         return LDB_ERR_OPERATIONS_ERROR;
1645                 }
1646         }
1647
1648         h->state = LDB_ASYNC_INIT;
1649         h->status = LDB_SUCCESS;
1650
1651         ac->step = PH_MOD_DO_MOD;
1652
1653         /* perform the search */
1654         return ldb_next_request(ac->module, ac->mod_req);
1655 }
1656
1657 static int ph_async_wait(struct ldb_async_handle *handle) {
1658         struct ph_async_context *ac;
1659         int ret;
1660     
1661         if (!handle || !handle->private_data) {
1662                 return LDB_ERR_OPERATIONS_ERROR;
1663         }
1664
1665         if (handle->state == LDB_ASYNC_DONE) {
1666                 return handle->status;
1667         }
1668
1669         handle->state = LDB_ASYNC_PENDING;
1670
1671         ac = talloc_get_type(handle->private_data, struct ph_async_context);
1672
1673         switch (ac->step) {
1674         case PH_ADD_SEARCH_DOM:
1675                 if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1676                         ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
1677                         if (ret != LDB_SUCCESS) goto done;
1678
1679                         if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1680                                 return LDB_SUCCESS;
1681                         }
1682                 }
1683
1684                 /* domain search done, go on */
1685                 return password_hash_add_async_do_add(handle);
1686
1687         case PH_ADD_DO_ADD:
1688                 if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1689                         ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
1690                         if (ret != LDB_SUCCESS) goto done;
1691
1692                         if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1693                                 return LDB_SUCCESS;
1694                         }
1695                 }
1696                 return LDB_SUCCESS;
1697                 
1698         case PH_MOD_DO_REQ:
1699                 if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1700                         ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
1701                         if (ret != LDB_SUCCESS) goto done;
1702
1703                         if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1704                                 return LDB_SUCCESS;
1705                         }
1706                 }
1707
1708                 /* non-password mods done, go on */
1709                 return password_hash_mod_async_search_self(handle);
1710                 
1711         case PH_MOD_SEARCH_SELF:
1712                 if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
1713                         ret = ldb_async_wait(ac->search_req->async.handle, LDB_WAIT_NONE);
1714                         if (ret != LDB_SUCCESS) goto done;
1715
1716                         if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
1717                                 return LDB_SUCCESS;
1718                         }
1719                 }
1720
1721                 /* self search done, go on */
1722                 return password_hash_mod_async_search_dom(handle);
1723                 
1724         case PH_MOD_SEARCH_DOM:
1725                 if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1726                         ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
1727                         if (ret != LDB_SUCCESS) goto done;
1728
1729                         if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1730                                 return LDB_SUCCESS;
1731                         }
1732                 }
1733
1734                 /* domain search done, go on */
1735                 return password_hash_mod_async_do_mod(handle);
1736
1737         case PH_MOD_DO_MOD:
1738                 if (ac->mod_req->async.handle->state != LDB_ASYNC_DONE) {
1739                         ret = ldb_async_wait(ac->mod_req->async.handle, LDB_WAIT_NONE);
1740                         if (ret != LDB_SUCCESS) goto done;
1741
1742                         if (ac->mod_req->async.handle->state != LDB_ASYNC_DONE) {
1743                                 return LDB_SUCCESS;
1744                         }
1745                 }
1746
1747                 break;
1748                 
1749         default:
1750                 ret = LDB_ERR_OPERATIONS_ERROR;
1751                 goto done;
1752         }
1753
1754         ret = LDB_SUCCESS;
1755
1756 done:
1757         handle->state = LDB_ASYNC_DONE;
1758         handle->status = ret;
1759         return ret;
1760 }
1761
1762 static int ph_async_wait_all(struct ldb_async_handle *handle) {
1763
1764         int ret;
1765
1766         while (handle->state != LDB_ASYNC_DONE) {
1767                 ret = ph_async_wait(handle);
1768                 if (ret != LDB_SUCCESS) {
1769                         return ret;
1770                 }
1771         }
1772
1773         return handle->status;
1774 }
1775
1776 static int password_hash_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
1777 {
1778         if (type == LDB_WAIT_ALL) {
1779                 return ph_async_wait_all(handle);
1780         } else {
1781                 return ph_async_wait(handle);
1782         }
1783 }
1784
1785 static int password_hash_request(struct ldb_module *module, struct ldb_request *req)
1786 {
1787         switch (req->operation) {
1788
1789         case LDB_REQ_ADD:
1790                 return password_hash_add(module, req);
1791
1792         case LDB_REQ_MODIFY:
1793                 return password_hash_modify(module, req);
1794
1795         case LDB_ASYNC_ADD:
1796                 return password_hash_add_async(module, req);
1797
1798         case LDB_ASYNC_MODIFY:
1799                 return password_hash_modify_async(module, req);
1800
1801         default:
1802                 return ldb_next_request(module, req);
1803
1804         }
1805 }
1806
1807 static const struct ldb_module_ops password_hash_ops = {
1808         .name          = "password_hash",
1809         .request       = password_hash_request,
1810         .async_wait    = password_hash_async_wait
1811 };
1812
1813
1814 int password_hash_module_init(void)
1815 {
1816         return ldb_register_module(&password_hash_ops);
1817 }