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