r15999: password_hash module changes:
[bbaumbach/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
1 /* 
2    ldb database module
3
4    Copyright (C) Simo Sorce  2004-2006
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
6    Copyright (C) Andrew Tridgell 2004
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 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 struct ph_async_context {
69
70         enum ph_type {PH_ADD, PH_MOD} type;
71         enum ph_step {PH_ADD_SEARCH_DOM, PH_ADD_DO_ADD, PH_MOD_DO_REQ, PH_MOD_SEARCH_SELF, PH_MOD_SEARCH_DOM, PH_MOD_DO_MOD} step;
72
73         struct ldb_module *module;
74         struct ldb_request *orig_req;
75
76         struct ldb_request *dom_req;
77         struct ldb_async_result *dom_res;
78
79         struct ldb_request *down_req;
80
81         struct ldb_request *search_req;
82         struct ldb_async_result *search_res;
83
84         struct ldb_request *mod_req;
85 };
86
87 struct domain_data {
88         uint_t pwdProperties;
89         uint_t pwdHistoryLength;
90         char *dnsDomain;
91         char *realm;
92 };
93
94 static int add_password_hashes(struct ldb_module *module, struct ldb_message *msg, int is_mod)
95 {
96         const char *sambaPassword;
97         struct samr_Password tmp_hash;
98         
99         sambaPassword = ldb_msg_find_string(msg, "sambaPassword", NULL);
100         if (sambaPassword == NULL) { /* impossible, what happened ?! */
101                 return LDB_ERR_OPERATIONS_ERROR;
102         }
103
104         if (is_mod) {
105                 if (ldb_msg_add_empty(msg, "ntPwdHash", LDB_FLAG_MOD_REPLACE) != 0) {
106                         return LDB_ERR_OPERATIONS_ERROR;
107                 }
108                 if (ldb_msg_add_empty(msg, "lmPwdHash", LDB_FLAG_MOD_REPLACE) != 0) {
109                         return LDB_ERR_OPERATIONS_ERROR;
110                 }
111         }       
112
113         /* compute the new nt and lm hashes */
114         E_md4hash(sambaPassword, tmp_hash.hash);
115         if (samdb_msg_add_hash(module->ldb, msg, msg, "ntPwdHash", &tmp_hash) != 0) {
116                 return LDB_ERR_OPERATIONS_ERROR;
117         }
118
119         if (E_deshash(sambaPassword, tmp_hash.hash)) {
120                 if (samdb_msg_add_hash(module->ldb, msg, msg, "lmPwdHash", &tmp_hash) != 0) {
121                         return LDB_ERR_OPERATIONS_ERROR;
122                 }
123         }
124
125         return LDB_SUCCESS;
126 }
127
128 static int add_krb5_keys_from_password(struct ldb_module *module, struct ldb_message *msg,
129                                         struct smb_krb5_context *smb_krb5_context,
130                                         struct domain_data *domain,
131                                         const char *samAccountName,
132                                         const char *user_principal_name,
133                                         int is_computer)
134 {
135         const char *sambaPassword;
136         Principal *salt_principal;
137         krb5_error_code krb5_ret;
138         size_t num_keys;
139         Key *keys;
140         int i;
141
142         /* Many, many thanks to lukeh@padl.com for this
143          * algorithm, described in his Nov 10 2004 mail to
144          * samba-technical@samba.org */
145
146         sambaPassword = ldb_msg_find_string(msg, "sambaPassword", NULL);
147         if (sambaPassword == NULL) { /* impossible, what happened ?! */
148                 return LDB_ERR_OPERATIONS_ERROR;
149         }
150
151         if (is_computer) {
152                 /* Determine a salting principal */
153                 char *name = talloc_strdup(msg, samAccountName);
154                 char *saltbody;
155                 if (name == NULL) {
156                         ldb_set_errstring(module->ldb,
157                                           talloc_asprintf(msg, "password_hash_handle: "
158                                                           "generation of new kerberos keys failed: %s is a computer without a samAccountName",
159                                                           ldb_dn_linearize(msg, msg->dn)));
160                         return LDB_ERR_OPERATIONS_ERROR;
161                 }
162                 if (name[strlen(name)-1] == '$') {
163                         name[strlen(name)-1] = '\0';
164                 }
165                 saltbody = talloc_asprintf(msg, "%s.%s", name, domain->dnsDomain);
166                 
167                 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context,
168                                                 &salt_principal,
169                                                 domain->realm, "host",
170                                                 saltbody, NULL);
171         } else if (user_principal_name) {
172                 char *p;
173                 user_principal_name = talloc_strdup(msg, user_principal_name);
174                 if (user_principal_name == NULL) {
175                         return LDB_ERR_OPERATIONS_ERROR;
176                 } else {
177                         p = strchr(user_principal_name, '@');
178                         if (p) {
179                                 p[0] = '\0';
180                         }
181                         krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context,
182                                                         &salt_principal,
183                                                         domain->realm, user_principal_name, NULL);
184                 } 
185         } else {
186                 if (!samAccountName) {
187                         ldb_set_errstring(module->ldb,
188                                           talloc_asprintf(msg, "password_hash_handle: "
189                                                           "generation of new kerberos keys failed: %s has no samAccountName",
190                                                           ldb_dn_linearize(msg, msg->dn)));
191                         return LDB_ERR_OPERATIONS_ERROR;
192                 }
193                 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context,
194                                                 &salt_principal,
195                                                 domain->realm, samAccountName,
196                                                 NULL);
197         }
198
199         if (krb5_ret) {
200                 ldb_set_errstring(module->ldb,
201                                   talloc_asprintf(msg, "password_hash_handle: "
202                                                   "generation of a saltking principal failed: %s",
203                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
204                                                                              krb5_ret, msg)));
205                 return LDB_ERR_OPERATIONS_ERROR;
206         }
207
208         /* TODO: We may wish to control the encryption types chosen in future */
209         krb5_ret = hdb_generate_key_set_password(smb_krb5_context->krb5_context,
210                                                  salt_principal, sambaPassword, &keys, &num_keys);
211         krb5_free_principal(smb_krb5_context->krb5_context, salt_principal);
212
213         if (krb5_ret) {
214                 ldb_set_errstring(module->ldb,
215                                   talloc_asprintf(msg, "password_hash_handle: "
216                                                   "generation of new kerberos keys failed: %s",
217                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
218                                                                              krb5_ret, msg)));
219                 return LDB_ERR_OPERATIONS_ERROR;
220         }
221
222         /* Walking all the key types generated, transform each
223          * key into an ASN.1 blob
224          */
225         for (i=0; i < num_keys; i++) {
226                 unsigned char *buf;
227                 size_t buf_size;
228                 size_t len;
229                 struct ldb_val val;
230                 int ret;
231                 
232                 if (keys[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
233                         /* We might end up doing this below:
234                          * This ensures we get the unicode
235                          * conversion right.  This should also
236                          * be fixed in the Heimdal libs */
237                         continue;
238                 }
239                 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &keys[i], &len, krb5_ret);
240                 if (krb5_ret) {
241                         return LDB_ERR_OPERATIONS_ERROR;
242                 }
243                 
244                 val.data = talloc_memdup(msg, buf, len);
245                 val.length = len;
246                 free(buf);
247                 if (!val.data || krb5_ret) {
248                         hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
249                         return LDB_ERR_OPERATIONS_ERROR;
250                 }
251                 ret = ldb_msg_add_value(msg, "krb5Key", &val);
252                 if (ret != LDB_SUCCESS) {
253                         hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
254                         return ret;
255                 }
256         }
257         
258         hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
259
260         return LDB_SUCCESS;
261 }
262
263 static int add_krb5_keys_from_NThash(struct ldb_module *module, struct ldb_message *msg,
264                                         struct smb_krb5_context *smb_krb5_context)
265 {
266         struct samr_Password *ntPwdHash;
267         krb5_error_code krb5_ret;
268         unsigned char *buf;
269         size_t buf_size;
270         size_t len;
271         struct ldb_val val;
272         Key key;
273         
274         key.mkvno = 0;
275         key.salt = NULL; /* No salt for this enc type */
276
277         ntPwdHash = samdb_result_hash(msg, msg, "ntPwdHash");
278         if (ntPwdHash == NULL) { /* what happened ?! */
279                 return LDB_ERR_OPERATIONS_ERROR;
280         }
281
282         krb5_ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
283                                       ETYPE_ARCFOUR_HMAC_MD5,
284                                       ntPwdHash->hash, sizeof(ntPwdHash->hash), 
285                                       &key.key);
286         if (krb5_ret) {
287                 return LDB_ERR_OPERATIONS_ERROR;
288         }
289         ASN1_MALLOC_ENCODE(Key, buf, buf_size, &key, &len, krb5_ret);
290         if (krb5_ret) {
291                 return LDB_ERR_OPERATIONS_ERROR;
292         }
293         krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
294                                     &key.key);
295         
296         val.data = talloc_memdup(msg, buf, len);
297         val.length = len;
298         free(buf);
299         if (!val.data) {
300                 return LDB_ERR_OPERATIONS_ERROR;
301         }
302         if (ldb_msg_add_value(msg, "krb5Key", &val) != 0) {
303                 return LDB_ERR_OPERATIONS_ERROR;
304         }
305
306         return LDB_SUCCESS;
307 }
308
309 static int set_pwdLastSet(struct ldb_module *module, struct ldb_message *msg, int is_mod)
310 {
311         NTTIME now_nt;
312
313         /* set it as now */
314         unix_to_nt_time(&now_nt, time(NULL));
315
316         if (!is_mod) {
317                 /* be sure there isn't a 0 value set (eg. coming from the template) */
318                 ldb_msg_remove_attr(msg, "pwdLastSet");
319                 /* add */
320                 if (ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_ADD) != 0) {
321                         return LDB_ERR_OPERATIONS_ERROR;
322                 }
323         } else {
324                 /* replace */
325                 if (ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE) != 0) {
326                         return LDB_ERR_OPERATIONS_ERROR;
327                 }
328         }
329
330         if (samdb_msg_add_uint64(module->ldb, msg, msg, "pwdLastSet", now_nt) != 0) {
331                 return LDB_ERR_OPERATIONS_ERROR;
332         }
333
334         return LDB_SUCCESS;
335 }
336
337 static int add_keyVersionNumber(struct ldb_module *module, struct ldb_message *msg, int previous)
338 {
339         /* replace or add */
340         if (ldb_msg_add_empty(msg, "msDS-KeyVersionNumber", LDB_FLAG_MOD_REPLACE) != 0) {
341                 return LDB_ERR_OPERATIONS_ERROR;
342         }
343
344         if (samdb_msg_add_uint(module->ldb, msg, msg, "msDS-KeyVersionNumber", previous+1) != 0) {
345                 return LDB_ERR_OPERATIONS_ERROR;
346         }
347
348         return LDB_SUCCESS;
349 }
350
351 static int setPwdHistory(struct ldb_module *module, struct ldb_message *msg, struct ldb_message *old_msg, int hlen)
352 {
353         struct samr_Password *nt_hash;
354         struct samr_Password *lm_hash;
355         struct samr_Password *nt_history;
356         struct samr_Password *lm_history;
357         struct samr_Password *new_nt_history;
358         struct samr_Password *new_lm_history;
359         int nt_hist_len;
360         int lm_hist_len;
361         int i;
362
363         nt_hash = samdb_result_hash(msg, old_msg, "ntPwdHash");
364         lm_hash = samdb_result_hash(msg, old_msg, "lmPwdHash");
365
366         /* if no previous passwords just return */
367         if (nt_hash == NULL && lm_hash == NULL) return LDB_SUCCESS;
368
369         nt_hist_len = samdb_result_hashes(msg, old_msg, "sambaNTPwdHistory", &nt_history);
370         lm_hist_len = samdb_result_hashes(msg, old_msg, "sambaLMPwdHistory", &lm_history);
371
372         /* We might not have an old NT password */
373         new_nt_history = talloc_array(msg, struct samr_Password, hlen);
374         if (new_nt_history == NULL) {
375                 return LDB_ERR_OPERATIONS_ERROR;
376         }
377         for (i = 0; i < MIN(hlen-1, nt_hist_len); i++) {
378                 new_nt_history[i+1] = nt_history[i];
379         }
380         nt_hist_len = i + 1;
381         if (nt_hash) {
382                 new_nt_history[0] = *nt_hash;
383         } else {
384                 ZERO_STRUCT(new_nt_history[0]);
385         }
386         if (ldb_msg_add_empty(msg, "sambaNTPwdHistory", LDB_FLAG_MOD_REPLACE) != LDB_SUCCESS) {
387                 return LDB_ERR_OPERATIONS_ERROR;
388         }
389         if (samdb_msg_add_hashes(msg, msg, "sambaNTPwdHistory", new_nt_history, nt_hist_len) != LDB_SUCCESS) {
390                 return LDB_ERR_OPERATIONS_ERROR;
391         }
392                 
393
394         /* Don't store 'long' passwords in the LM history, 
395            but make sure to 'expire' one password off the other end */
396         new_lm_history = talloc_array(msg, struct samr_Password, hlen);
397         if (new_lm_history == NULL) {
398                 return LDB_ERR_OPERATIONS_ERROR;
399         }
400         for (i = 0; i < MIN(hlen-1, lm_hist_len); i++) {
401                 new_lm_history[i+1] = lm_history[i];
402         }
403         lm_hist_len = i + 1;
404         if (lm_hash) {
405                 new_lm_history[0] = *lm_hash;
406         } else {
407                 ZERO_STRUCT(new_lm_history[0]);
408         }
409         if (ldb_msg_add_empty(msg, "sambaLMPwdHistory", LDB_FLAG_MOD_REPLACE) != LDB_SUCCESS) {
410                 return LDB_ERR_OPERATIONS_ERROR;
411         }
412         if (samdb_msg_add_hashes(msg, msg, "sambaLMPwdHistory", new_lm_history, lm_hist_len) != LDB_SUCCESS) {
413                 return LDB_ERR_OPERATIONS_ERROR;
414         }
415
416         return LDB_SUCCESS;
417 }
418
419 static struct ldb_async_handle *ph_init_handle(struct ldb_request *req, struct ldb_module *module, enum ph_type type)
420 {
421         struct ph_async_context *ac;
422         struct ldb_async_handle *h;
423
424         h = talloc_zero(req, struct ldb_async_handle);
425         if (h == NULL) {
426                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
427                 return NULL;
428         }
429
430         h->module = module;
431
432         ac = talloc_zero(h, struct ph_async_context);
433         if (ac == NULL) {
434                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
435                 talloc_free(h);
436                 return NULL;
437         }
438
439         h->private_data = (void *)ac;
440
441         h->state = LDB_ASYNC_INIT;
442         h->status = LDB_SUCCESS;
443
444         ac->type = type;
445         ac->module = module;
446         ac->orig_req = req;
447
448         return h;
449 }
450
451 static int get_domain_data_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
452 {
453         struct ph_async_context *ac;
454
455         if (!context || !ares) {
456                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
457                 return LDB_ERR_OPERATIONS_ERROR;
458         }
459
460         ac = talloc_get_type(context, struct ph_async_context);
461
462         /* we are interested only in the single reply (base search) we receive here */
463         if (ares->type == LDB_REPLY_ENTRY) {
464                 if (ac->dom_res != NULL) {
465                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
466                         talloc_free(ares);
467                         return LDB_ERR_OPERATIONS_ERROR;
468                 }
469                 ac->dom_res = talloc_steal(ac, ares);
470         } else {
471                 talloc_free(ares);
472         }
473
474         return LDB_SUCCESS;
475 }
476
477 static int build_domain_data_request(struct ph_async_context *ac,
478                                      struct dom_sid *sid)
479 {
480         /* attrs[] is returned from this function in
481            ac->dom_req->op.search.attrs, so it must be static, as
482            otherwise the compiler can put it on the stack */
483         static const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", "dnsDomain", NULL };
484         char *filter;
485
486         ac->dom_req = talloc_zero(ac, struct ldb_request);
487         if (ac->dom_req == NULL) {
488                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
489                 return LDB_ERR_OPERATIONS_ERROR;
490         }
491         ac->dom_req->operation = LDB_SEARCH;
492         ac->dom_req->op.search.base = NULL;
493         ac->dom_req->op.search.scope = LDB_SCOPE_SUBTREE;
494
495         filter = talloc_asprintf(ac->dom_req, "(&(objectSid=%s)(objectClass=domain))", dom_sid_string(ac->dom_req, sid));
496         if (filter == NULL) {
497                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
498                 talloc_free(ac->dom_req);
499                 return LDB_ERR_OPERATIONS_ERROR;
500         }
501
502         ac->dom_req->op.search.tree = ldb_parse_tree(ac->module->ldb, filter);
503         if (ac->dom_req->op.search.tree == NULL) {
504                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
505                 talloc_free(ac->dom_req);
506                 return LDB_ERR_OPERATIONS_ERROR;
507         }
508         ac->dom_req->op.search.attrs = attrs;
509         ac->dom_req->controls = NULL;
510         ac->dom_req->async.context = ac;
511         ac->dom_req->async.callback = get_domain_data_callback;
512         ac->dom_req->async.timeout = ac->orig_req->async.timeout;
513
514         return LDB_SUCCESS;
515 }
516
517 static struct domain_data *get_domain_data(struct ldb_module *module, void *mem_ctx, struct ldb_async_result *res)
518 {
519         struct domain_data *data;
520         const char *tmp;
521         
522         data = talloc_zero(mem_ctx, struct domain_data);
523         if (data == NULL) {
524                 return NULL;
525         }
526
527         data->pwdProperties = samdb_result_uint(res->message, "pwdProperties", 0);
528         data->pwdHistoryLength = samdb_result_uint(res->message, "pwdHistoryLength", 0);
529         tmp = ldb_msg_find_string(res->message, "dnsDomain", NULL);
530
531         if (tmp != NULL) {
532                 data->dnsDomain = talloc_strdup(data, tmp);
533                 if (data->dnsDomain == NULL) {
534                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
535                         return NULL;
536                 }
537                 data->realm = strupper_talloc(mem_ctx, tmp);
538                 if (data->realm == NULL) {
539                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
540                         return NULL;
541                 }
542         }
543
544         return data;
545 }
546
547 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
548 {
549         struct ldb_async_handle *h;
550         struct ph_async_context *ac;
551         struct ldb_message_element *attribute;
552         struct dom_sid *domain_sid;
553         int ret;
554
555         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
556
557         if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
558                 return ldb_next_request(module, req);
559         }
560
561         /* nobody must touch password Histories */
562         if (ldb_msg_find_element(req->op.add.message, "sambaNTPwdHistory") ||
563             ldb_msg_find_element(req->op.add.message, "sambaLMPwdHistory")) {
564                 return LDB_ERR_UNWILLING_TO_PERFORM;
565         }
566
567         /* If no part of this touches the sambaPassword, then we don't
568          * need to make any changes.  For password changes/set there should
569          * be a 'delete' or a 'modify' on this attribute. */
570         if ((attribute = ldb_msg_find_element(req->op.add.message, "sambaPassword")) == NULL ) {
571                 return ldb_next_request(module, req);
572         }
573
574         /* if it is not an entry of type person its an error */
575         /* TODO: remove this when sambaPassword will be in schema */
576         if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
577                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
578         }
579
580         /* check sambaPassword is single valued here */
581         /* TODO: remove this when sambaPassword will be single valued in schema */
582         if (attribute->num_values > 1) {
583                 ldb_set_errstring(module->ldb, talloc_asprintf(req,
584                                         "mupltiple values for sambaPassword not allowed!\n"));
585                 return LDB_ERR_CONSTRAINT_VIOLATION;
586         }
587
588         /* get user domain data */
589         domain_sid = samdb_result_sid_prefix(req, req->op.add.message, "objectSid");
590         if (domain_sid == NULL) {
591                 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
592                 return LDB_ERR_OPERATIONS_ERROR;
593         }
594
595         h = ph_init_handle(req, module, PH_ADD);
596         if (!h) {
597                 return LDB_ERR_OPERATIONS_ERROR;
598         }
599         ac = talloc_get_type(h->private_data, struct ph_async_context);
600
601         ret = build_domain_data_request(ac, domain_sid);
602         if (ret != LDB_SUCCESS) {
603                 return ret;
604         }
605
606         ac->step = PH_ADD_SEARCH_DOM;
607
608         req->async.handle = h;
609
610         return ldb_next_request(module, ac->dom_req);
611 }
612
613 static int password_hash_add_do_add(struct ldb_async_handle *h) {
614
615         struct ph_async_context *ac;
616         struct domain_data *domain;
617         struct smb_krb5_context *smb_krb5_context;
618         struct ldb_message *msg;
619
620         ac = talloc_get_type(h->private_data, struct ph_async_context);
621
622         domain = get_domain_data(ac->module, ac, ac->dom_res);
623         if (domain == NULL) {
624                 return LDB_ERR_OPERATIONS_ERROR;
625         }
626
627         ac->down_req = talloc(ac, struct ldb_request);
628         if (ac->down_req == NULL) {
629                 return LDB_ERR_OPERATIONS_ERROR;
630         }
631
632         *(ac->down_req) = *(ac->orig_req);
633         ac->down_req->op.add.message = msg = ldb_msg_copy_shallow(ac->down_req, ac->orig_req->op.add.message);
634         if (ac->down_req->op.add.message == NULL) {
635                 return LDB_ERR_OPERATIONS_ERROR;
636         }
637         
638         /* Some operations below require kerberos contexts */
639         if (smb_krb5_init_context(ac->down_req, &smb_krb5_context) != 0) {
640                 return LDB_ERR_OPERATIONS_ERROR;
641         }
642
643         /* we can compute new password hashes from the unicode password */
644         if (add_password_hashes(ac->module, msg, 0) != LDB_SUCCESS) {
645                 return LDB_ERR_OPERATIONS_ERROR;
646         }
647
648         /* now add krb5 keys based on unicode password */
649         if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
650                                         ldb_msg_find_string(msg, "samAccountName", NULL),
651                                         ldb_msg_find_string(msg, "userPrincipalName", NULL),
652                                         ldb_msg_check_string_attribute(msg, "objectClass", "computer")
653                                        ) != LDB_SUCCESS) {
654                 return LDB_ERR_OPERATIONS_ERROR;
655         }
656
657         /* add also kr5 keys based on NT the hash */
658         if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
659                 return LDB_ERR_OPERATIONS_ERROR;
660         }
661
662         /* if both the domain properties and the user account controls do not permit
663          * clear text passwords then wipe out the sambaPassword */
664         if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
665             (!(ldb_msg_find_uint(msg, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
666                 ldb_msg_remove_attr(msg, "sambaPassword");
667         }
668
669         /* don't touch it if a value is set. It could be an incoming samsync */
670         if (ldb_msg_find_uint64(msg, "pwdLastSet", 0) == 0) {
671                 if (set_pwdLastSet(ac->module, msg, 0) != LDB_SUCCESS) {
672                         return LDB_ERR_OPERATIONS_ERROR;
673                 }
674         }
675
676         /* don't touch it if a value is set. It could be an incoming samsync */
677         if (!ldb_msg_find_element(msg, "msDS-KeyVersionNumber")) {
678                 if (add_keyVersionNumber(ac->module, msg, 0) != LDB_SUCCESS) {
679                         return LDB_ERR_OPERATIONS_ERROR;
680                 }
681         }
682
683         h->state = LDB_ASYNC_INIT;
684         h->status = LDB_SUCCESS;
685
686         ac->step = PH_ADD_DO_ADD;
687
688         /* perform the operation */
689         return ldb_next_request(ac->module, ac->down_req);
690 }
691
692 static int password_hash_mod_search_self(struct ldb_async_handle *h);
693
694 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
695 {
696         struct ldb_async_handle *h;
697         struct ph_async_context *ac;
698         struct ldb_message_element *sambaAttr;
699         struct ldb_message_element *ntAttr;
700         struct ldb_message_element *lmAttr;
701
702         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
703
704         if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
705                 return ldb_next_request(module, req);
706         }
707         
708         /* nobody must touch password Histories */
709         if (ldb_msg_find_element(req->op.mod.message, "sambaNTPwdHistory") ||
710             ldb_msg_find_element(req->op.mod.message, "sambaLMPwdHistory")) {
711                 return LDB_ERR_UNWILLING_TO_PERFORM;
712         }
713
714         sambaAttr = ldb_msg_find_element(req->op.mod.message, "sambaPassword");
715         ntAttr = ldb_msg_find_element(req->op.mod.message, "ntPwdHash");
716         lmAttr = ldb_msg_find_element(req->op.mod.message, "lmPwdHash");
717
718         /* check passwords are single valued here */
719         /* TODO: remove this when passwords will be single valued in schema */
720         if (sambaAttr && (sambaAttr->num_values > 1)) {
721                 return LDB_ERR_CONSTRAINT_VIOLATION;
722         }
723         if (ntAttr && (ntAttr->num_values > 1)) {
724                 return LDB_ERR_CONSTRAINT_VIOLATION;
725         }
726         if (lmAttr && (lmAttr->num_values > 1)) {
727                 return LDB_ERR_CONSTRAINT_VIOLATION;
728         }
729
730         /* If no part of this touches the sambaPassword OR ntPwdHash and/or lmPwdHash, then we don't
731          * need to make any changes.  For password changes/set there should
732          * be a 'delete' or a 'modify' on this attribute. */
733         /* If the only operation is the deletion of the passwords then go on */
734         if (       ((!sambaAttr) || ((sambaAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))
735                 && ((!ntAttr) || ((ntAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))
736                 && ((!lmAttr) || ((lmAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE))  ) {
737
738                 return ldb_next_request(module, req);
739         }
740
741         h = ph_init_handle(req, module, PH_MOD);
742         if (!h) {
743                 return LDB_ERR_OPERATIONS_ERROR;
744         }
745         ac = talloc_get_type(h->private_data, struct ph_async_context);
746
747         /* return or own handle to deal with this call */
748         req->async.handle = h;
749
750         /* prepare the first operation */
751         ac->down_req = talloc_zero(ac, struct ldb_request);
752         if (ac->down_req == NULL) {
753                 ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
754                 return LDB_ERR_OPERATIONS_ERROR;
755         }
756
757         *(ac->down_req) = *req; /* copy the request */
758
759         /* use a new message structure so that we can modify it */
760         ac->down_req->op.mod.message = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
761
762         /* - remove any imodification to the password from the first commit
763          *   we will make the real modification later */
764         if (sambaAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "sambaPassword");
765         if (ntAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "ntPwdHash");
766         if (lmAttr) ldb_msg_remove_attr(ac->down_req->op.mod.message, "lmPwdHash");
767
768         /* if there was nothing else to be modify skip to next step */
769         if (ac->down_req->op.mod.message->num_elements == 0) {
770                 talloc_free(ac->down_req);
771                 ac->down_req = NULL;
772                 return password_hash_mod_search_self(h);
773         }
774         
775         ac->down_req->async.context = NULL;
776         ac->down_req->async.callback = NULL;
777
778         ac->step = PH_MOD_DO_REQ;
779
780         return ldb_next_request(module, ac->down_req);
781 }
782
783 static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
784 {
785         struct ph_async_context *ac;
786
787         if (!context || !ares) {
788                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
789                 return LDB_ERR_OPERATIONS_ERROR;
790         }
791
792         ac = talloc_get_type(context, struct ph_async_context);
793
794         /* we are interested only in the single reply (base search) we receive here */
795         if (ares->type == LDB_REPLY_ENTRY) {
796                 if (ac->search_res != NULL) {
797                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
798                         talloc_free(ares);
799                         return LDB_ERR_OPERATIONS_ERROR;
800                 }
801
802                 /* if it is not an entry of type person this is an error */
803                 /* TODO: remove this when sambaPassword will be in schema */
804                 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
805                         ldb_set_errstring(ldb, talloc_asprintf(ldb, "Object class violation"));
806                         talloc_free(ares);
807                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
808                 }
809
810                 ac->search_res = talloc_steal(ac, ares);
811         } else {
812                 talloc_free(ares);
813         }
814
815         return LDB_SUCCESS;
816 }
817
818 static int password_hash_mod_search_self(struct ldb_async_handle *h) {
819
820         struct ph_async_context *ac;
821         static const char * const attrs[] = { "userAccountControl", "sambaLMPwdHistory", 
822                                               "sambaNTPwdHistory", 
823                                               "ntPwdHash", 
824                                               "objectSid", "msDS-KeyVersionNumber", 
825                                               "objectClass", "userPrincipalName",
826                                               "samAccountName", 
827                                               "lmPwdHash", "ntPwdHash",
828                                               NULL };
829
830         ac = talloc_get_type(h->private_data, struct ph_async_context);
831
832         /* prepare the search operation */
833         ac->search_req = talloc_zero(ac, struct ldb_request);
834         if (ac->search_req == NULL) {
835                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
836                 return LDB_ERR_OPERATIONS_ERROR;
837         }
838
839         ac->search_req->operation = LDB_SEARCH;
840         ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
841         ac->search_req->op.search.scope = LDB_SCOPE_BASE;
842         ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
843         if (ac->search_req->op.search.tree == NULL) {
844                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "Invalid search filter"));
845                 return LDB_ERR_OPERATIONS_ERROR;
846         }
847         ac->search_req->op.search.attrs = attrs;
848         ac->search_req->controls = NULL;
849         ac->search_req->async.context = ac;
850         ac->search_req->async.callback = get_self_callback;
851         ac->search_req->async.timeout = ac->orig_req->async.timeout;
852
853         ac->step = PH_MOD_SEARCH_SELF;
854
855         return ldb_next_request(ac->module, ac->search_req);
856 }
857
858 static int password_hash_mod_search_dom(struct ldb_async_handle *h) {
859
860         struct ph_async_context *ac;
861         struct dom_sid *domain_sid;
862         int ret;
863
864         ac = talloc_get_type(h->private_data, struct ph_async_context);
865
866         /* get object domain sid */
867         domain_sid = samdb_result_sid_prefix(ac, ac->search_res->message, "objectSid");
868         if (domain_sid == NULL) {
869                 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
870                 return LDB_ERR_OPERATIONS_ERROR;
871         }
872
873         /* get user domain data */
874         ret = build_domain_data_request(ac, domain_sid);
875         if (ret != LDB_SUCCESS) {
876                 return ret;
877         }
878
879         ac->step = PH_MOD_SEARCH_DOM;
880
881         return ldb_next_request(ac->module, ac->dom_req);
882 }
883
884 static int password_hash_mod_do_mod(struct ldb_async_handle *h) {
885
886         struct ph_async_context *ac;
887         struct domain_data *domain;
888         struct smb_krb5_context *smb_krb5_context;
889         struct ldb_message_element *sambaAttr;
890         struct ldb_message *msg;
891         int phlen;
892
893         ac = talloc_get_type(h->private_data, struct ph_async_context);
894
895         domain = get_domain_data(ac->module, ac, ac->dom_res);
896         if (domain == NULL) {
897                 return LDB_ERR_OPERATIONS_ERROR;
898         }
899
900         ac->mod_req = talloc(ac, struct ldb_request);
901         if (ac->mod_req == NULL) {
902                 return LDB_ERR_OPERATIONS_ERROR;
903         }
904
905         *(ac->mod_req) = *(ac->orig_req);
906         
907         /* use a new message structure so that we can modify it */
908         ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
909         if (msg == NULL) {
910                 return LDB_ERR_OPERATIONS_ERROR;
911         }
912
913         /* modify dn */
914         msg->dn = ac->orig_req->op.mod.message->dn;
915
916         /* Some operations below require kerberos contexts */
917         if (smb_krb5_init_context(ac->mod_req, &smb_krb5_context) != 0) {
918                 return LDB_ERR_OPERATIONS_ERROR;
919         }
920
921         /* we are going to replace the existing krb5key or delete it */
922         if (ldb_msg_add_empty(msg, "krb5key", LDB_FLAG_MOD_REPLACE) != 0) {
923                 return LDB_ERR_OPERATIONS_ERROR;
924         }
925
926         /* if we have sambaPassword in the original message add the operatio on it here */
927         sambaAttr = ldb_msg_find_element(ac->orig_req->op.mod.message, "sambaPassword");
928         if (sambaAttr) {
929
930                 if (ldb_msg_add(msg, sambaAttr, sambaAttr->flags) != 0) {
931                         return LDB_ERR_OPERATIONS_ERROR;
932                 }
933
934                 /* we are not deleteing it add password hashes */
935                 if ((sambaAttr->flags & LDB_FLAG_MOD_MASK) != LDB_FLAG_MOD_DELETE) {
936                 
937                         /* we can compute new password hashes from the unicode password */
938                         if (add_password_hashes(ac->module, msg, 1) != LDB_SUCCESS) {
939                                 return LDB_ERR_OPERATIONS_ERROR;
940                         }
941
942                         /* now add krb5 keys based on unicode password */
943                         if (add_krb5_keys_from_password(ac->module, msg, smb_krb5_context, domain,
944                                 ldb_msg_find_string(ac->search_res->message, "samAccountName", NULL),
945                                 ldb_msg_find_string(ac->search_res->message, "userPrincipalName", NULL),
946                                 ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "computer")
947                                                        ) != LDB_SUCCESS) {
948                                 return LDB_ERR_OPERATIONS_ERROR;
949                         }
950
951                         /* if the domain properties or the user account controls do not permit
952                          * clear text passwords then wipe out the sambaPassword */
953                         if ((!(domain->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT)) ||
954                             (!(ldb_msg_find_uint(ac->search_res->message, "userAccountControl", 0) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED))) {
955                                 ldb_msg_remove_attr(msg, "sambaPassword");
956                         }
957
958                 }
959         }
960
961         /* if we don't have sambaPassword or we are trying to delete it try with nt or lm hasehs */
962         if ((!sambaAttr) || ((sambaAttr->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE)) {
963                 struct ldb_message_element *el;
964                 
965                 el = ldb_msg_find_element(ac->orig_req->op.mod.message, "ntPwdHash");
966                 if (ldb_msg_add(msg, el, el->flags) != 0) {
967                         return LDB_ERR_OPERATIONS_ERROR;
968                 }
969                 
970                 el = ldb_msg_find_element(ac->orig_req->op.mod.message, "lmPwdHash");
971                 if (ldb_msg_add(msg, el, el->flags) != 0) {
972                         return LDB_ERR_OPERATIONS_ERROR;
973                 }
974         }
975
976         /* add also krb5 keys based on NT the hash */
977         if (add_krb5_keys_from_NThash(ac->module, msg, smb_krb5_context) != LDB_SUCCESS) {
978                 return LDB_ERR_OPERATIONS_ERROR;
979         }
980
981         /* set change time */
982         if (set_pwdLastSet(ac->module, msg, 1) != LDB_SUCCESS) {
983                 return LDB_ERR_OPERATIONS_ERROR;
984         }
985
986         /* don't touch it if a value is set. It could be an incoming samsync */
987         if (add_keyVersionNumber(ac->module, msg,
988                                  ldb_msg_find_uint(msg, "msDS-KeyVersionNumber", 0)
989                                 ) != LDB_SUCCESS) {
990                 return LDB_ERR_OPERATIONS_ERROR;
991         }
992
993         if ((phlen = samdb_result_uint(ac->dom_res->message, "pwdHistoryLength", 0)) > 0) {
994                 if (setPwdHistory(ac->module, msg, ac->search_res->message, phlen) != LDB_SUCCESS) {
995                         return LDB_ERR_OPERATIONS_ERROR;
996                 }
997         }
998
999         h->state = LDB_ASYNC_INIT;
1000         h->status = LDB_SUCCESS;
1001
1002         ac->step = PH_MOD_DO_MOD;
1003
1004         /* perform the search */
1005         return ldb_next_request(ac->module, ac->mod_req);
1006 }
1007
1008 static int ph_async_wait(struct ldb_async_handle *handle) {
1009         struct ph_async_context *ac;
1010         int ret;
1011     
1012         if (!handle || !handle->private_data) {
1013                 return LDB_ERR_OPERATIONS_ERROR;
1014         }
1015
1016         if (handle->state == LDB_ASYNC_DONE) {
1017                 return handle->status;
1018         }
1019
1020         handle->state = LDB_ASYNC_PENDING;
1021         handle->status = LDB_SUCCESS;
1022
1023         ac = talloc_get_type(handle->private_data, struct ph_async_context);
1024
1025         switch (ac->step) {
1026         case PH_ADD_SEARCH_DOM:
1027                 ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
1028
1029                 if (ret != LDB_SUCCESS) {
1030                         handle->status = ret;
1031                         goto done;
1032                 }
1033                 if (ac->dom_req->async.handle->status != LDB_SUCCESS) {
1034                         handle->status = ac->dom_req->async.handle->status;
1035                         goto done;
1036                 }
1037
1038                 if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1039                         return LDB_SUCCESS;
1040                 }
1041
1042                 /* domain search done, go on */
1043                 return password_hash_add_do_add(handle);
1044
1045         case PH_ADD_DO_ADD:
1046                 ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
1047
1048                 if (ret != LDB_SUCCESS) {
1049                         handle->status = ret;
1050                         goto done;
1051                 }
1052                 if (ac->down_req->async.handle->status != LDB_SUCCESS) {
1053                         handle->status = ac->down_req->async.handle->status;
1054                         goto done;
1055                 }
1056
1057                 if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1058                         return LDB_SUCCESS;
1059                 }
1060
1061                 break;
1062                 
1063         case PH_MOD_DO_REQ:
1064                 ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
1065
1066                 if (ret != LDB_SUCCESS) {
1067                         handle->status = ret;
1068                         goto done;
1069                 }
1070                 if (ac->down_req->async.handle->status != LDB_SUCCESS) {
1071                         handle->status = ac->down_req->async.handle->status;
1072                         goto done;
1073                 }
1074
1075                 if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
1076                         return LDB_SUCCESS;
1077                 }
1078
1079                 /* non-password mods done, go on */
1080                 return password_hash_mod_search_self(handle);
1081                 
1082         case PH_MOD_SEARCH_SELF:
1083                 ret = ldb_async_wait(ac->search_req->async.handle, LDB_WAIT_NONE);
1084
1085                 if (ret != LDB_SUCCESS) {
1086                         handle->status = ret;
1087                         goto done;
1088                 }
1089                 if (ac->search_req->async.handle->status != LDB_SUCCESS) {
1090                         handle->status = ac->search_req->async.handle->status;
1091                         goto done;
1092                 }
1093
1094                 if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
1095                         return LDB_SUCCESS;
1096                 }
1097
1098                 /* self search done, go on */
1099                 return password_hash_mod_search_dom(handle);
1100                 
1101         case PH_MOD_SEARCH_DOM:
1102                 ret = ldb_async_wait(ac->dom_req->async.handle, LDB_WAIT_NONE);
1103
1104                 if (ret != LDB_SUCCESS) {
1105                         handle->status = ret;
1106                         goto done;
1107                 }
1108                 if (ac->dom_req->async.handle->status != LDB_SUCCESS) {
1109                         handle->status = ac->dom_req->async.handle->status;
1110                         goto done;
1111                 }
1112
1113                 if (ac->dom_req->async.handle->state != LDB_ASYNC_DONE) {
1114                         return LDB_SUCCESS;
1115                 }
1116
1117                 /* domain search done, go on */
1118                 return password_hash_mod_do_mod(handle);
1119
1120         case PH_MOD_DO_MOD:
1121                 ret = ldb_async_wait(ac->mod_req->async.handle, LDB_WAIT_NONE);
1122
1123                 if (ret != LDB_SUCCESS) {
1124                         handle->status = ret;
1125                         goto done;
1126                 }
1127                 if (ac->mod_req->async.handle->status != LDB_SUCCESS) {
1128                         handle->status = ac->mod_req->async.handle->status;
1129                         goto done;
1130                 }
1131
1132                 if (ac->mod_req->async.handle->state != LDB_ASYNC_DONE) {
1133                         return LDB_SUCCESS;
1134                 }
1135
1136                 break;
1137                 
1138         default:
1139                 ret = LDB_ERR_OPERATIONS_ERROR;
1140                 goto done;
1141         }
1142
1143         ret = LDB_SUCCESS;
1144
1145 done:
1146         handle->state = LDB_ASYNC_DONE;
1147         return ret;
1148 }
1149
1150 static int ph_async_wait_all(struct ldb_async_handle *handle) {
1151
1152         int ret;
1153
1154         while (handle->state != LDB_ASYNC_DONE) {
1155                 ret = ph_async_wait(handle);
1156                 if (ret != LDB_SUCCESS) {
1157                         return ret;
1158                 }
1159         }
1160
1161         return handle->status;
1162 }
1163
1164 static int password_hash_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
1165 {
1166         if (type == LDB_WAIT_ALL) {
1167                 return ph_async_wait_all(handle);
1168         } else {
1169                 return ph_async_wait(handle);
1170         }
1171 }
1172
1173 static const struct ldb_module_ops password_hash_ops = {
1174         .name          = "password_hash",
1175         .add           = password_hash_add,
1176         .modify        = password_hash_modify,
1177         .async_wait    = password_hash_async_wait
1178 };
1179
1180
1181 int password_hash_module_init(void)
1182 {
1183         return ldb_register_module(&password_hash_ops);
1184 }