r15913: Error passing in the async code is not in agood shape
[kai/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         /* attrs[] is returned from this function in
1149            ac->dom_req->op.search.attrs, so it must be static, as
1150            otherwise the compiler can put it on the stack */
1151         static const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", "dnsDomain", NULL };
1152         char *filter;
1153
1154         ac->dom_req = talloc_zero(ac, struct ldb_request);
1155         if (ac->dom_req == NULL) {
1156                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
1157                 return LDB_ERR_OPERATIONS_ERROR;
1158         }
1159         ac->dom_req->operation = LDB_ASYNC_SEARCH;
1160         ac->dom_req->op.search.base = NULL;
1161         ac->dom_req->op.search.scope = LDB_SCOPE_SUBTREE;
1162
1163         filter = talloc_asprintf(ac->dom_req, "(&(objectSid=%s)(objectClass=domain))", dom_sid_string(ac->dom_req, sid));
1164         if (filter == NULL) {
1165                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
1166                 talloc_free(ac->dom_req);
1167                 return LDB_ERR_OPERATIONS_ERROR;
1168         }
1169
1170         ac->dom_req->op.search.tree = ldb_parse_tree(ac->module->ldb, filter);
1171         if (ac->dom_req->op.search.tree == NULL) {
1172                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
1173                 talloc_free(ac->dom_req);
1174                 return LDB_ERR_OPERATIONS_ERROR;
1175         }
1176         ac->dom_req->op.search.attrs = attrs;
1177         ac->dom_req->controls = NULL;
1178         ac->dom_req->creds = ac->orig_req->creds;
1179         ac->dom_req->async.context = ac;
1180         ac->dom_req->async.callback = get_domain_data_callback;
1181         ac->dom_req->async.timeout = ac->orig_req->async.timeout;
1182
1183         return LDB_SUCCESS;
1184 }
1185
1186 static struct domain_data *get_domain_data(struct ldb_module *module, void *mem_ctx, struct ldb_async_result *res)
1187 {
1188         struct domain_data *data;
1189         const char *tmp;
1190         
1191         data = talloc_zero(mem_ctx, struct domain_data);
1192         if (data == NULL) {
1193                 return NULL;
1194         }
1195
1196         data->pwdProperties = samdb_result_uint(res->message, "pwdProperties", 0);
1197         data->pwdHistoryLength = samdb_result_uint(res->message, "pwdHistoryLength", 0);
1198         tmp = ldb_msg_find_string(res->message, "dnsDomain", NULL);
1199
1200         if (tmp != NULL) {
1201                 data->dnsDomain = talloc_strdup(data, tmp);
1202                 if (data->dnsDomain == NULL) {
1203                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
1204                         return NULL;
1205                 }
1206                 data->realm = strupper_talloc(mem_ctx, tmp);
1207                 if (data->realm == NULL) {
1208                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
1209                         return NULL;
1210                 }
1211         }
1212
1213         return data;
1214 }
1215
1216 static int password_hash_add_async(struct ldb_module *module, struct ldb_request *req)
1217 {
1218         struct ldb_async_handle *h;
1219         struct ph_async_context *ac;
1220         struct ldb_message_element *attribute;
1221         struct dom_sid *domain_sid;
1222         int ret;
1223
1224         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add_async\n");
1225
1226         if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
1227                 return ldb_next_request(module, req);
1228         }
1229
1230         /* nobody must touch password Histories */
1231         if (ldb_msg_find_element(req->op.add.message, "sambaNTPwdHistory") ||
1232             ldb_msg_find_element(req->op.add.message, "sambaLMPwdHistory")) {
1233                 return LDB_ERR_UNWILLING_TO_PERFORM;
1234         }
1235
1236         /* If no part of this touches the sambaPassword, then we don't
1237          * need to make any changes.  For password changes/set there should
1238          * be a 'delete' or a 'modify' on this attribute. */
1239         if ((attribute = ldb_msg_find_element(req->op.add.message, "sambaPassword")) == NULL ) {
1240                 return ldb_next_request(module, req);
1241         }
1242
1243         /* if it is not an entry of type person its an error */
1244         /* TODO: remove this when sambaPassword will be in schema */
1245         if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
1246                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1247         }
1248
1249         /* check sambaPassword is single valued here */
1250         /* TODO: remove this when sambaPassword will be single valued in schema */
1251         if (attribute->num_values > 1) {
1252                 ldb_set_errstring(module->ldb, talloc_asprintf(req,
1253                                         "mupltiple values for sambaPassword not allowed!\n"));
1254                 return LDB_ERR_CONSTRAINT_VIOLATION;
1255         }
1256
1257         /* get user domain data */
1258         domain_sid = samdb_result_sid_prefix(req, req->op.add.message, "objectSid");
1259         if (domain_sid == NULL) {
1260                 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
1261                 return LDB_ERR_OPERATIONS_ERROR;
1262         }
1263
1264         h = ph_init_handle(req, module, PH_ADD);
1265         if (!h) {
1266                 return LDB_ERR_OPERATIONS_ERROR;
1267         }
1268         ac = talloc_get_type(h->private_data, struct ph_async_context);
1269
1270         ret = build_domain_data_request(ac, domain_sid);
1271         if (ret != LDB_SUCCESS) {
1272                 return ret;
1273         }
1274
1275         ac->step = PH_ADD_SEARCH_DOM;
1276
1277         req->async.handle = h;
1278
1279         return ldb_next_request(module, ac->dom_req);
1280 }
1281
1282 static int password_hash_add_async_do_add(struct ldb_async_handle *h) {
1283
1284         struct ph_async_context *ac;
1285         struct domain_data *domain;
1286         struct smb_krb5_context *smb_krb5_context;
1287         struct ldb_message *msg;
1288
1289         ac = talloc_get_type(h->private_data, struct ph_async_context);
1290
1291         domain = get_domain_data(ac->module, ac, ac->dom_res);
1292         if (domain == NULL) {
1293                 return LDB_ERR_OPERATIONS_ERROR;
1294         }
1295
1296         ac->down_req = talloc(ac, struct ldb_request);
1297         if (ac->down_req == NULL) {
1298                 return LDB_ERR_OPERATIONS_ERROR;
1299         }
1300
1301         *(ac->down_req) = *(ac->orig_req);
1302         ac->down_req->op.add.message = msg = ldb_msg_copy_shallow(ac->down_req, ac->orig_req->op.add.message);
1303         if (ac->down_req->op.add.message == NULL) {
1304                 return LDB_ERR_OPERATIONS_ERROR;
1305         }
1306         
1307         /* Some operations below require kerberos contexts */
1308         if (smb_krb5_init_context(ac->down_req, &smb_krb5_context) != 0) {
1309                 return LDB_ERR_OPERATIONS_ERROR;
1310         }
1311
1312         /* we can compute new password hashes from the unicode password */
1313         if (add_password_hashes(ac->module, msg, 0) != LDB_SUCCESS) {
1314                 return LDB_ERR_OPERATIONS_ERROR;
1315         }
1316
1317         /* now add krb5 keys based on unicode password */
1318         if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
1319                                         ldb_msg_find_string(msg, "samAccountName", NULL),
1320                                         ldb_msg_find_string(msg, "userPrincipalName", NULL),
1321                                         ldb_msg_check_string_attribute(msg, "objectClass", "computer")
1322                                        ) != LDB_SUCCESS) {
1323                 return LDB_ERR_OPERATIONS_ERROR;
1324         }
1325
1326         /* add also kr5 keys based on NT the hash */
1327         if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
1328                 return LDB_ERR_OPERATIONS_ERROR;
1329         }
1330
1331         /* if both the domain properties and the user account controls do not permit
1332          * clear text passwords then wipe out the sambaPassword */
1333         if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
1334             (!(ldb_msg_find_uint(msg, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
1335                 ldb_msg_remove_attr(msg, "sambaPassword");
1336         }
1337
1338         /* don't touch it if a value is set. It could be an incoming samsync */
1339         if (ldb_msg_find_uint64(msg, "pwdLastSet", 0) == 0) {
1340                 if (set_pwdLastSet(ac->module, msg, 0) != LDB_SUCCESS) {
1341                         return LDB_ERR_OPERATIONS_ERROR;
1342                 }
1343         }
1344
1345         /* don't touch it if a value is set. It could be an incoming samsync */
1346         if (!ldb_msg_find_element(msg, "msDS-KeyVersionNumber")) {
1347                 if (add_keyVersionNumber(ac->module, msg, 0) != LDB_SUCCESS) {
1348                         return LDB_ERR_OPERATIONS_ERROR;
1349                 }
1350         }
1351
1352         h->state = LDB_ASYNC_INIT;
1353         h->status = LDB_SUCCESS;
1354
1355         ac->step = PH_ADD_DO_ADD;
1356
1357         /* perform the operation */
1358         return ldb_next_request(ac->module, ac->down_req);
1359 }
1360
1361 static int password_hash_mod_async_search_self(struct ldb_async_handle *h);
1362
1363 static int password_hash_modify_async(struct ldb_module *module, struct ldb_request *req)
1364 {
1365         struct ldb_async_handle *h;
1366         struct ph_async_context *ac;
1367         struct ldb_message_element *sambaAttr;
1368         struct ldb_message_element *ntAttr;
1369         struct ldb_message_element *lmAttr;
1370
1371         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add_async\n");
1372
1373         if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
1374                 return ldb_next_request(module, req);
1375         }
1376         
1377         /* nobody must touch password Histories */
1378         if (ldb_msg_find_element(req->op.mod.message, "sambaNTPwdHistory") ||
1379             ldb_msg_find_element(req->op.mod.message, "sambaLMPwdHistory")) {
1380                 return LDB_ERR_UNWILLING_TO_PERFORM;
1381         }
1382
1383         sambaAttr = ldb_msg_find_element(req->op.mod.message, "sambaPassword");
1384         ntAttr = ldb_msg_find_element(req->op.mod.message, "ntPwdHash");
1385         lmAttr = ldb_msg_find_element(req->op.mod.message, "lmPwdHash");
1386
1387         /* check passwords are single valued here */
1388         /* TODO: remove this when passwords will be single valued in schema */
1389         if (sambaAttr && (sambaAttr->num_values > 1)) {
1390                 return LDB_ERR_CONSTRAINT_VIOLATION;
1391         }
1392         if (ntAttr && (ntAttr->num_values > 1)) {
1393                 return LDB_ERR_CONSTRAINT_VIOLATION;
1394         }
1395         if (lmAttr && (lmAttr->num_values > 1)) {
1396                 return LDB_ERR_CONSTRAINT_VIOLATION;
1397         }
1398
1399         /* If no part of this touches the sambaPassword OR ntPwdHash and/or lmPwdHash, then we don't
1400          * need to make any changes.  For password changes/set there should
1401          * be a 'delete' or a 'modify' on this attribute. */
1402         /* If the only operation is the deletion of the passwords then go on */
1403         if (       ((!sambaAttr) || ((sambaAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))
1404                 && ((!ntAttr) || ((ntAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))
1405                 && ((!lmAttr) || ((lmAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))  ) {
1406
1407                 return ldb_next_request(module, req);
1408         }
1409
1410         h = ph_init_handle(req, module, PH_MOD);
1411         if (!h) {
1412                 return LDB_ERR_OPERATIONS_ERROR;
1413         }
1414         ac = talloc_get_type(h->private_data, struct ph_async_context);
1415
1416         /* return or own handle to deal with this call */
1417         req->async.handle = h;
1418
1419         /* prepare the first operation */
1420         ac->down_req = talloc_zero(ac, struct ldb_request);
1421         if (ac->down_req == NULL) {
1422                 ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
1423                 return LDB_ERR_OPERATIONS_ERROR;
1424         }
1425
1426         *(ac->down_req) = *req; /* copy the request */
1427
1428         /* use a new message structure so that we can modify it */
1429         ac->down_req->op.mod.message = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
1430
1431         /* - remove any imodification to the password from the first commit
1432          *   we will make the real modification later */
1433         if (sambaAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "sambaPassword");
1434         if (ntAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "ntPwdHash");
1435         if (lmAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "lmPwdHash");
1436
1437         /* if there was nothing else to be modify skip to next step */
1438         if (ac->down_req->op.mod.message->num_elements == 0) {
1439                 talloc_free(ac->down_req);
1440                 ac->down_req = NULL;
1441                 return password_hash_mod_async_search_self(h);
1442         }
1443         
1444         ac->down_req->async.context = NULL;
1445         ac->down_req->async.callback = NULL;
1446
1447         ac->step = PH_MOD_DO_REQ;
1448
1449         return ldb_next_request(module, ac->down_req);
1450 }
1451
1452 static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
1453 {
1454         struct ph_async_context *ac;
1455
1456         if (!context || !ares) {
1457                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
1458                 return LDB_ERR_OPERATIONS_ERROR;
1459         }
1460
1461         ac = talloc_get_type(context, struct ph_async_context);
1462
1463         /* we are interested only in the single reply (base search) we receive here */
1464         if (ares->type == LDB_REPLY_ENTRY) {
1465                 if (ac->search_res != NULL) {
1466                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
1467                         talloc_free(ares);
1468                         return LDB_ERR_OPERATIONS_ERROR;
1469                 }
1470
1471                 /* if it is not an entry of type person this is an error */
1472                 /* TODO: remove this when sambaPassword will be in schema */
1473                 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
1474                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Object class violation"));
1475                         talloc_free(ares);
1476                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
1477                 }
1478
1479                 ac->search_res = talloc_steal(ac, ares);
1480         } else {
1481                 talloc_free(ares);
1482         }
1483
1484         return LDB_SUCCESS;
1485 }
1486
1487 static int password_hash_mod_async_search_self(struct ldb_async_handle *h) {
1488
1489         struct ph_async_context *ac;
1490
1491         ac = talloc_get_type(h->private_data, struct ph_async_context);
1492
1493         /* prepare the search operation */
1494         ac->search_req = talloc_zero(ac, struct ldb_request);
1495         if (ac->search_req == NULL) {
1496                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
1497                 return LDB_ERR_OPERATIONS_ERROR;
1498         }
1499
1500         ac->search_req->operation = LDB_ASYNC_SEARCH;
1501         ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
1502         ac->search_req->op.search.scope = LDB_SCOPE_BASE;
1503         ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
1504         if (ac->search_req->op.search.tree == NULL) {
1505                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
1506                 return LDB_ERR_OPERATIONS_ERROR;
1507         }
1508         ac->search_req->op.search.attrs = NULL;
1509         ac->search_req->controls = NULL;
1510         ac->search_req->creds = ac->orig_req->creds;
1511         ac->search_req->async.context = ac;
1512         ac->search_req->async.callback = get_self_callback;
1513         ac->search_req->async.timeout = ac->orig_req->async.timeout;
1514
1515         ac->step = PH_MOD_SEARCH_SELF;
1516
1517         return ldb_next_request(ac->module, ac->search_req);
1518 }
1519
1520 static int password_hash_mod_async_search_dom(struct ldb_async_handle *h) {
1521
1522         struct ph_async_context *ac;
1523         struct dom_sid *domain_sid;
1524         int ret;
1525
1526         ac = talloc_get_type(h->private_data, struct ph_async_context);
1527
1528         /* get object domain sid */
1529         domain_sid = samdb_result_sid_prefix(ac, ac->search_res->message, "objectSid");
1530         if (domain_sid == NULL) {
1531                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
1532                 return LDB_ERR_OPERATIONS_ERROR;
1533         }
1534
1535         /* get user domain data */
1536         ret = build_domain_data_request(ac, domain_sid);
1537         if (ret != LDB_SUCCESS) {
1538                 return ret;
1539         }
1540
1541         ac->step = PH_MOD_SEARCH_DOM;
1542
1543         return ldb_next_request(ac->module, ac->dom_req);
1544 }
1545
1546 static int password_hash_mod_async_do_mod(struct ldb_async_handle *h) {
1547
1548         struct ph_async_context *ac;
1549         struct domain_data *domain;
1550         struct smb_krb5_context *smb_krb5_context;
1551         struct ldb_message_element *sambaAttr;
1552         struct ldb_message *msg;
1553         int phlen;
1554
1555         ac = talloc_get_type(h->private_data, struct ph_async_context);
1556
1557         domain = get_domain_data(ac->module, ac, ac->dom_res);
1558         if (domain == NULL) {
1559                 return LDB_ERR_OPERATIONS_ERROR;
1560         }
1561
1562         ac->mod_req = talloc(ac, struct ldb_request);
1563         if (ac->mod_req == NULL) {
1564                 return LDB_ERR_OPERATIONS_ERROR;
1565         }
1566
1567         *(ac->mod_req) = *(ac->orig_req);
1568         
1569         /* use a new message structure so that we can modify it */
1570         ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
1571         if (msg == NULL) {
1572                 return LDB_ERR_OPERATIONS_ERROR;
1573         }
1574
1575         /* modify dn */
1576         msg->dn = ac->orig_req->op.mod.message->dn;
1577
1578         /* Some operations below require kerberos contexts */
1579         if (smb_krb5_init_context(ac->mod_req, &smb_krb5_context) != 0) {
1580                 return LDB_ERR_OPERATIONS_ERROR;
1581         }
1582
1583         /* we are going to replace the existing krb5key or delete it */
1584         if (ldb_msg_add_empty(msg, "krb5key", LDB_FLAG_MOD_REPLACE) != 0) {
1585                 return LDB_ERR_OPERATIONS_ERROR;
1586         }
1587
1588         /* if we have sambaPassword in the original message add the operatio on it here */
1589         sambaAttr = ldb_msg_find_element(ac->orig_req->op.mod.message, "sambaPassword");
1590         if (sambaAttr) {
1591
1592                 if (ldb_msg_add(msg, sambaAttr, sambaAttr->flags) != 0) {
1593                         return LDB_ERR_OPERATIONS_ERROR;
1594                 }
1595
1596                 /* we are not deleteing it add password hashes */
1597                 if ((sambaAttr->flags & LDB_FLAG_MOD_MASK) != LDB_FLAG_MOD_DELETE) {
1598                 
1599                         /* we can compute new password hashes from the unicode password */
1600                         if (add_password_hashes(ac->module, msg, 1) != LDB_SUCCESS) {
1601                                 return LDB_ERR_OPERATIONS_ERROR;
1602                         }
1603
1604                         /* now add krb5 keys based on unicode password */
1605                         if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
1606                                 ldb_msg_find_string(ac->search_res->message, "samAccountName", NULL),
1607                                 ldb_msg_find_string(ac->search_res->message, "userPrincipalName", NULL),
1608                                 ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "computer")
1609                                                        ) != LDB_SUCCESS) {
1610                                 return LDB_ERR_OPERATIONS_ERROR;
1611                         }
1612
1613                         /* if the domain properties or the user account controls do not permit
1614                          * clear text passwords then wipe out the sambaPassword */
1615                         if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
1616                             (!(ldb_msg_find_uint(ac->search_res->message, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
1617                                 ldb_msg_remove_attr(msg, "sambaPassword");
1618                         }
1619
1620                 }
1621         }
1622
1623         /* if we don't have sambaPassword or we are trying to delete it try with nt or lm hasehs */
1624         if ((!sambaAttr) || ((sambaAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE)) {
1625                 struct ldb_message_element *el;
1626                 
1627                 el = ldb_msg_find_element(ac->orig_req->op.mod.message, "ntPwdHash");
1628                 if (ldb_msg_add(msg, el, el->flags) != 0) {
1629                         return LDB_ERR_OPERATIONS_ERROR;
1630                 }
1631                 
1632                 el = ldb_msg_find_element(ac->orig_req->op.mod.message, "lmPwdHash");
1633                 if (ldb_msg_add(msg, el, el->flags) != 0) {
1634                         return LDB_ERR_OPERATIONS_ERROR;
1635                 }
1636         }
1637
1638         /* add also kr5 keys based on NT the hash */
1639         if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
1640                 return LDB_ERR_OPERATIONS_ERROR;
1641         }
1642
1643         /* set change time */
1644         if (set_pwdLastSet(ac->module, msg, 1) != LDB_SUCCESS) {
1645                 return LDB_ERR_OPERATIONS_ERROR;
1646         }
1647
1648         /* don't touch it if a value is set. It could be an incoming samsync */
1649         if (add_keyVersionNumber(ac->module, msg,
1650                                  ldb_msg_find_uint(msg, "msDS-KeyVersionNumber", 0)
1651                                 ) != LDB_SUCCESS) {
1652                 return LDB_ERR_OPERATIONS_ERROR;
1653         }
1654
1655         if ((phlen = samdb_result_uint(ac->dom_res->message, "pwdHistoryLength", 0)) > 0) {
1656                 if (setPwdHistory(ac->module, msg, ac->search_res->message, phlen) != LDB_SUCCESS) {
1657                         return LDB_ERR_OPERATIONS_ERROR;
1658                 }
1659         }
1660
1661         h->state = LDB_ASYNC_INIT;
1662         h->status = LDB_SUCCESS;
1663
1664         ac->step = PH_MOD_DO_MOD;
1665
1666         /* perform the search */
1667         return ldb_next_request(ac->module, ac->mod_req);
1668 }
1669
1670 static int ph_async_wait(struct ldb_async_handle *handle) {
1671         struct ph_async_context *ac;
1672         int ret;
1673     
1674         if (!handle || !handle->private_data) {
1675                 return LDB_ERR_OPERATIONS_ERROR;
1676         }
1677
1678         if (handle->state == LDB_ASYNC_DONE) {
1679                 return handle->status;
1680         }
1681
1682         handle->state = LDB_ASYNC_PENDING;
1683         handle->status = LDB_SUCCESS;
1684
1685         ac = talloc_get_type(handle->private_data, struct ph_async_context);
1686
1687         switch (ac->step) {
1688         case PH_ADD_SEARCH_DOM:
1689                 ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
1690
1691                 if (ret != LDB_SUCCESS) {
1692                         handle->status = ret;
1693                         goto done;
1694                 }
1695                 if (ac->dom_req->async.handle->status != LDB_SUCCESS) {
1696                         handle->status = ac->dom_req->async.handle->status;
1697                         goto done;
1698                 }
1699
1700                 if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1701                         return LDB_SUCCESS;
1702                 }
1703
1704                 /* domain search done, go on */
1705                 return password_hash_add_async_do_add(handle);
1706
1707         case PH_ADD_DO_ADD:
1708                 ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
1709
1710                 if (ret != LDB_SUCCESS) {
1711                         handle->status = ret;
1712                         goto done;
1713                 }
1714                 if (ac->down_req->async.handle->status != LDB_SUCCESS) {
1715                         handle->status = ac->down_req->async.handle->status;
1716                         goto done;
1717                 }
1718
1719                 if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1720                         return LDB_SUCCESS;
1721                 }
1722
1723                 break;
1724                 
1725         case PH_MOD_DO_REQ:
1726                 ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
1727
1728                 if (ret != LDB_SUCCESS) {
1729                         handle->status = ret;
1730                         goto done;
1731                 }
1732                 if (ac->down_req->async.handle->status != LDB_SUCCESS) {
1733                         handle->status = ac->down_req->async.handle->status;
1734                         goto done;
1735                 }
1736
1737                 if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1738                         return LDB_SUCCESS;
1739                 }
1740
1741                 /* non-password mods done, go on */
1742                 return password_hash_mod_async_search_self(handle);
1743                 
1744         case PH_MOD_SEARCH_SELF:
1745                 ret = ldb_async_wait(ac->search_req->async.handle, LDB_WAIT_NONE);
1746
1747                 if (ret != LDB_SUCCESS) {
1748                         handle->status = ret;
1749                         goto done;
1750                 }
1751                 if (ac->search_req->async.handle->status != LDB_SUCCESS) {
1752                         handle->status = ac->search_req->async.handle->status;
1753                         goto done;
1754                 }
1755
1756                 if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
1757                         return LDB_SUCCESS;
1758                 }
1759
1760                 /* self search done, go on */
1761                 return password_hash_mod_async_search_dom(handle);
1762                 
1763         case PH_MOD_SEARCH_DOM:
1764                 ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
1765
1766                 if (ret != LDB_SUCCESS) {
1767                         handle->status = ret;
1768                         goto done;
1769                 }
1770                 if (ac->dom_req->async.handle->status != LDB_SUCCESS) {
1771                         handle->status = ac->dom_req->async.handle->status;
1772                         goto done;
1773                 }
1774
1775                 if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1776                         return LDB_SUCCESS;
1777                 }
1778
1779                 /* domain search done, go on */
1780                 return password_hash_mod_async_do_mod(handle);
1781
1782         case PH_MOD_DO_MOD:
1783                 ret = ldb_async_wait(ac->mod_req->async.handle, LDB_WAIT_NONE);
1784
1785                 if (ret != LDB_SUCCESS) {
1786                         handle->status = ret;
1787                         goto done;
1788                 }
1789                 if (ac->mod_req->async.handle->status != LDB_SUCCESS) {
1790                         handle->status = ac->mod_req->async.handle->status;
1791                         goto done;
1792                 }
1793
1794                 if (ac->mod_req->async.handle->state != LDB_ASYNC_DONE) {
1795                         return LDB_SUCCESS;
1796                 }
1797
1798                 break;
1799                 
1800         default:
1801                 ret = LDB_ERR_OPERATIONS_ERROR;
1802                 goto done;
1803         }
1804
1805         ret = LDB_SUCCESS;
1806
1807 done:
1808         handle->state = LDB_ASYNC_DONE;
1809         return ret;
1810 }
1811
1812 static int ph_async_wait_all(struct ldb_async_handle *handle) {
1813
1814         int ret;
1815
1816         while (handle->state != LDB_ASYNC_DONE) {
1817                 ret = ph_async_wait(handle);
1818                 if (ret != LDB_SUCCESS) {
1819                         return ret;
1820                 }
1821         }
1822
1823         return handle->status;
1824 }
1825
1826 static int password_hash_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
1827 {
1828         if (type == LDB_WAIT_ALL) {
1829                 return ph_async_wait_all(handle);
1830         } else {
1831                 return ph_async_wait(handle);
1832         }
1833 }
1834
1835 static int password_hash_request(struct ldb_module *module, struct ldb_request *req)
1836 {
1837         switch (req->operation) {
1838
1839         case LDB_REQ_ADD:
1840                 return password_hash_add(module, req);
1841
1842         case LDB_REQ_MODIFY:
1843                 return password_hash_modify(module, req);
1844
1845         case LDB_ASYNC_ADD:
1846                 return password_hash_add_async(module, req);
1847
1848         case LDB_ASYNC_MODIFY:
1849                 return password_hash_modify_async(module, req);
1850
1851         default:
1852                 return ldb_next_request(module, req);
1853
1854         }
1855 }
1856
1857 static const struct ldb_module_ops password_hash_ops = {
1858         .name          = "password_hash",
1859         .request       = password_hash_request,
1860         .async_wait    = password_hash_async_wait
1861 };
1862
1863
1864 int password_hash_module_init(void)
1865 {
1866         return ldb_register_module(&password_hash_ops);
1867 }