4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Stefan Metzmacher 2007
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 * Component: ldb password_hash module
28 * Description: correctly update hash values based on changes to userPassword and friends
30 * Author: Andrew Bartlett
31 * Author: Stefan Metzmacher
35 #include "libcli/ldap/ldap_ndr.h"
36 #include "ldb_module.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 "../libds/common/flags.h"
46 #include "dsdb/samdb/ldb_modules/password_modules.h"
47 #include "librpc/ndr/libndr.h"
48 #include "librpc/gen_ndr/ndr_drsblobs.h"
49 #include "../lib/crypto/crypto.h"
50 #include "param/param.h"
52 /* If we have decided there is reason to work on this request, then
53 * setup all the password hash types correctly.
55 * If the administrator doesn't want the userPassword stored (set in the
56 * domain and per-account policies) then we must strip that out before
57 * we do the first operation.
59 * Once this is done (which could update anything at all), we
60 * calculate the password hashes.
62 * This function must not only update the unicodePwd, dBCSPwd and
63 * supplementalCredentials fields, it must also atomicly increment the
64 * msDS-KeyVersionNumber. We should be in a transaction, so all this
65 * should be quite safe...
67 * Finally, if the administrator has requested that a password history
68 * be maintained, then this should also be written out.
74 struct ldb_module *module;
75 struct ldb_request *req;
77 struct ldb_request *dom_req;
78 struct ldb_reply *dom_res;
80 struct ldb_reply *search_res;
82 struct domain_data *domain;
87 unsigned int pwdProperties;
88 unsigned int pwdHistoryLength;
89 const char *netbios_domain;
90 const char *dns_domain;
94 struct setup_password_fields_io {
95 struct ph_context *ac;
96 struct domain_data *domain;
97 struct smb_krb5_context *smb_krb5_context;
99 /* infos about the user account */
101 uint32_t user_account_control;
102 const char *sAMAccountName;
103 const char *user_principal_name;
107 /* new credentials */
109 const struct ldb_val *cleartext_utf8;
110 const struct ldb_val *cleartext_utf16;
111 struct ldb_val quoted_utf16;
112 struct samr_Password *nt_hash;
113 struct samr_Password *lm_hash;
116 /* old credentials */
118 uint32_t nt_history_len;
119 struct samr_Password *nt_history;
120 uint32_t lm_history_len;
121 struct samr_Password *lm_history;
122 const struct ldb_val *supplemental;
123 struct supplementalCredentialsBlob scb;
127 /* generated credentials */
129 struct samr_Password *nt_hash;
130 struct samr_Password *lm_hash;
131 uint32_t nt_history_len;
132 struct samr_Password *nt_history;
133 uint32_t lm_history_len;
134 struct samr_Password *lm_history;
140 struct ldb_val supplemental;
146 /* Get the NT hash, and fill it in as an entry in the password history,
147 and specify it into io->g.nt_hash */
149 static int setup_nt_fields(struct setup_password_fields_io *io)
151 struct ldb_context *ldb;
154 io->g.nt_hash = io->n.nt_hash;
155 ldb = ldb_module_get_ctx(io->ac->module);
157 if (io->domain->pwdHistoryLength == 0) {
161 /* We might not have an old NT password */
162 io->g.nt_history = talloc_array(io->ac,
163 struct samr_Password,
164 io->domain->pwdHistoryLength);
165 if (!io->g.nt_history) {
167 return LDB_ERR_OPERATIONS_ERROR;
170 for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.nt_history_len); i++) {
171 io->g.nt_history[i+1] = io->o.nt_history[i];
173 io->g.nt_history_len = i + 1;
176 io->g.nt_history[0] = *io->g.nt_hash;
179 * TODO: is this correct?
180 * the simular behavior is correct for the lm history case
182 E_md4hash("", io->g.nt_history[0].hash);
188 /* Get the LANMAN hash, and fill it in as an entry in the password history,
189 and specify it into io->g.lm_hash */
191 static int setup_lm_fields(struct setup_password_fields_io *io)
193 struct ldb_context *ldb;
196 io->g.lm_hash = io->n.lm_hash;
197 ldb = ldb_module_get_ctx(io->ac->module);
199 if (io->domain->pwdHistoryLength == 0) {
203 /* We might not have an old NT password */
204 io->g.lm_history = talloc_array(io->ac,
205 struct samr_Password,
206 io->domain->pwdHistoryLength);
207 if (!io->g.lm_history) {
209 return LDB_ERR_OPERATIONS_ERROR;
212 for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.lm_history_len); i++) {
213 io->g.lm_history[i+1] = io->o.lm_history[i];
215 io->g.lm_history_len = i + 1;
218 io->g.lm_history[0] = *io->g.lm_hash;
220 E_deshash("", io->g.lm_history[0].hash);
226 static int setup_kerberos_keys(struct setup_password_fields_io *io)
228 struct ldb_context *ldb;
229 krb5_error_code krb5_ret;
230 Principal *salt_principal;
233 krb5_data cleartext_data;
235 ldb = ldb_module_get_ctx(io->ac->module);
236 cleartext_data.data = io->n.cleartext_utf8->data;
237 cleartext_data.length = io->n.cleartext_utf8->length;
239 /* Many, many thanks to lukeh@padl.com for this
240 * algorithm, described in his Nov 10 2004 mail to
241 * samba-technical@samba.org */
244 * Determine a salting principal
246 if (io->u.is_computer) {
250 name = strlower_talloc(io->ac, io->u.sAMAccountName);
253 return LDB_ERR_OPERATIONS_ERROR;
256 if (name[strlen(name)-1] == '$') {
257 name[strlen(name)-1] = '\0';
260 saltbody = talloc_asprintf(io->ac, "%s.%s", name, io->domain->dns_domain);
263 return LDB_ERR_OPERATIONS_ERROR;
266 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
268 io->domain->realm, "host",
270 } else if (io->u.user_principal_name) {
271 char *user_principal_name;
274 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
275 if (!user_principal_name) {
277 return LDB_ERR_OPERATIONS_ERROR;
280 p = strchr(user_principal_name, '@');
285 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
287 io->domain->realm, user_principal_name,
290 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
292 io->domain->realm, io->u.sAMAccountName,
296 ldb_asprintf_errstring(ldb,
297 "setup_kerberos_keys: "
298 "generation of a salting principal failed: %s",
299 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
300 return LDB_ERR_OPERATIONS_ERROR;
304 * create salt from salt_principal
306 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
307 salt_principal, &salt);
308 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
310 ldb_asprintf_errstring(ldb,
311 "setup_kerberos_keys: "
312 "generation of krb5_salt failed: %s",
313 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
314 return LDB_ERR_OPERATIONS_ERROR;
316 /* create a talloc copy */
317 io->g.salt = talloc_strndup(io->ac,
319 salt.saltvalue.length);
320 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
323 return LDB_ERR_OPERATIONS_ERROR;
325 salt.saltvalue.data = discard_const(io->g.salt);
326 salt.saltvalue.length = strlen(io->g.salt);
329 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
330 * the salt and the cleartext password
332 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
333 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
338 ldb_asprintf_errstring(ldb,
339 "setup_kerberos_keys: "
340 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
341 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
342 return LDB_ERR_OPERATIONS_ERROR;
344 io->g.aes_256 = data_blob_talloc(io->ac,
346 key.keyvalue.length);
347 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
348 if (!io->g.aes_256.data) {
350 return LDB_ERR_OPERATIONS_ERROR;
354 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
355 * the salt and the cleartext password
357 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
358 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
363 ldb_asprintf_errstring(ldb,
364 "setup_kerberos_keys: "
365 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
366 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
367 return LDB_ERR_OPERATIONS_ERROR;
369 io->g.aes_128 = data_blob_talloc(io->ac,
371 key.keyvalue.length);
372 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
373 if (!io->g.aes_128.data) {
375 return LDB_ERR_OPERATIONS_ERROR;
379 * create ENCTYPE_DES_CBC_MD5 key out of
380 * the salt and the cleartext password
382 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
388 ldb_asprintf_errstring(ldb,
389 "setup_kerberos_keys: "
390 "generation of a des-cbc-md5 key failed: %s",
391 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
392 return LDB_ERR_OPERATIONS_ERROR;
394 io->g.des_md5 = data_blob_talloc(io->ac,
396 key.keyvalue.length);
397 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
398 if (!io->g.des_md5.data) {
400 return LDB_ERR_OPERATIONS_ERROR;
404 * create ENCTYPE_DES_CBC_CRC key out of
405 * the salt and the cleartext password
407 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
413 ldb_asprintf_errstring(ldb,
414 "setup_kerberos_keys: "
415 "generation of a des-cbc-crc key failed: %s",
416 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
417 return LDB_ERR_OPERATIONS_ERROR;
419 io->g.des_crc = data_blob_talloc(io->ac,
421 key.keyvalue.length);
422 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
423 if (!io->g.des_crc.data) {
425 return LDB_ERR_OPERATIONS_ERROR;
431 static int setup_primary_kerberos(struct setup_password_fields_io *io,
432 const struct supplementalCredentialsBlob *old_scb,
433 struct package_PrimaryKerberosBlob *pkb)
435 struct ldb_context *ldb;
436 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
437 struct supplementalCredentialsPackage *old_scp = NULL;
438 struct package_PrimaryKerberosBlob _old_pkb;
439 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
441 enum ndr_err_code ndr_err;
443 ldb = ldb_module_get_ctx(io->ac->module);
446 * prepare generation of keys
448 * ENCTYPE_DES_CBC_MD5
449 * ENCTYPE_DES_CBC_CRC
452 pkb3->salt.string = io->g.salt;
454 pkb3->keys = talloc_array(io->ac,
455 struct package_PrimaryKerberosKey3,
459 return LDB_ERR_OPERATIONS_ERROR;
462 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
463 pkb3->keys[0].value = &io->g.des_md5;
464 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
465 pkb3->keys[1].value = &io->g.des_crc;
467 /* initialize the old keys to zero */
468 pkb3->num_old_keys = 0;
469 pkb3->old_keys = NULL;
471 /* if there're no old keys, then we're done */
476 for (i=0; i < old_scb->sub.num_packages; i++) {
477 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
481 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
485 old_scp = &old_scb->sub.packages[i];
488 /* Primary:Kerberos element of supplementalCredentials */
492 blob = strhex_to_data_blob(io->ac, old_scp->data);
495 return LDB_ERR_OPERATIONS_ERROR;
498 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
499 ndr_err = ndr_pull_struct_blob(&blob, io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &_old_pkb,
500 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
501 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
502 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
503 ldb_asprintf_errstring(ldb,
504 "setup_primary_kerberos: "
505 "failed to pull old package_PrimaryKerberosBlob: %s",
507 return LDB_ERR_OPERATIONS_ERROR;
510 if (_old_pkb.version != 3) {
511 ldb_asprintf_errstring(ldb,
512 "setup_primary_kerberos: "
513 "package_PrimaryKerberosBlob version[%u] expected[3]",
515 return LDB_ERR_OPERATIONS_ERROR;
518 old_pkb3 = &_old_pkb.ctr.ctr3;
521 /* if we didn't found the old keys we're done */
526 /* fill in the old keys */
527 pkb3->num_old_keys = old_pkb3->num_keys;
528 pkb3->old_keys = old_pkb3->keys;
533 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
534 const struct supplementalCredentialsBlob *old_scb,
535 struct package_PrimaryKerberosBlob *pkb)
537 struct ldb_context *ldb;
538 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
539 struct supplementalCredentialsPackage *old_scp = NULL;
540 struct package_PrimaryKerberosBlob _old_pkb;
541 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
543 enum ndr_err_code ndr_err;
545 ldb = ldb_module_get_ctx(io->ac->module);
548 * prepare generation of keys
550 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
551 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
552 * ENCTYPE_DES_CBC_MD5
553 * ENCTYPE_DES_CBC_CRC
556 pkb4->salt.string = io->g.salt;
557 pkb4->default_iteration_count = 4096;
560 pkb4->keys = talloc_array(io->ac,
561 struct package_PrimaryKerberosKey4,
565 return LDB_ERR_OPERATIONS_ERROR;
568 pkb4->keys[0].iteration_count = 4096;
569 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
570 pkb4->keys[0].value = &io->g.aes_256;
571 pkb4->keys[1].iteration_count = 4096;
572 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
573 pkb4->keys[1].value = &io->g.aes_128;
574 pkb4->keys[2].iteration_count = 4096;
575 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
576 pkb4->keys[2].value = &io->g.des_md5;
577 pkb4->keys[3].iteration_count = 4096;
578 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
579 pkb4->keys[3].value = &io->g.des_crc;
581 /* initialize the old keys to zero */
582 pkb4->num_old_keys = 0;
583 pkb4->old_keys = NULL;
584 pkb4->num_older_keys = 0;
585 pkb4->older_keys = NULL;
587 /* if there're no old keys, then we're done */
592 for (i=0; i < old_scb->sub.num_packages; i++) {
593 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
597 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
601 old_scp = &old_scb->sub.packages[i];
604 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
608 blob = strhex_to_data_blob(io->ac, old_scp->data);
611 return LDB_ERR_OPERATIONS_ERROR;
614 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
615 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
616 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
618 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
619 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
620 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
621 ldb_asprintf_errstring(ldb,
622 "setup_primary_kerberos_newer: "
623 "failed to pull old package_PrimaryKerberosBlob: %s",
625 return LDB_ERR_OPERATIONS_ERROR;
628 if (_old_pkb.version != 4) {
629 ldb_asprintf_errstring(ldb,
630 "setup_primary_kerberos_newer: "
631 "package_PrimaryKerberosBlob version[%u] expected[4]",
633 return LDB_ERR_OPERATIONS_ERROR;
636 old_pkb4 = &_old_pkb.ctr.ctr4;
639 /* if we didn't found the old keys we're done */
644 /* fill in the old keys */
645 pkb4->num_old_keys = old_pkb4->num_keys;
646 pkb4->old_keys = old_pkb4->keys;
647 pkb4->num_older_keys = old_pkb4->num_old_keys;
648 pkb4->older_keys = old_pkb4->old_keys;
653 static int setup_primary_wdigest(struct setup_password_fields_io *io,
654 const struct supplementalCredentialsBlob *old_scb,
655 struct package_PrimaryWDigestBlob *pdb)
657 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
658 DATA_BLOB sAMAccountName;
659 DATA_BLOB sAMAccountName_l;
660 DATA_BLOB sAMAccountName_u;
661 const char *user_principal_name = io->u.user_principal_name;
662 DATA_BLOB userPrincipalName;
663 DATA_BLOB userPrincipalName_l;
664 DATA_BLOB userPrincipalName_u;
665 DATA_BLOB netbios_domain;
666 DATA_BLOB netbios_domain_l;
667 DATA_BLOB netbios_domain_u;
668 DATA_BLOB dns_domain;
669 DATA_BLOB dns_domain_l;
670 DATA_BLOB dns_domain_u;
682 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
683 * for what precalculated hashes are supposed to be stored...
685 * I can't reproduce all values which should contain "Digest" as realm,
686 * am I doing something wrong or is w2k3 just broken...?
688 * W2K3 fills in following for a user:
690 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
691 * sAMAccountName: NewUser2Sam
692 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
694 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
695 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
696 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
697 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
698 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
699 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
700 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
701 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
702 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
703 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
704 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
705 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
706 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
707 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
708 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
709 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
710 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
711 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
712 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
713 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
714 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
715 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
716 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
717 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
718 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
719 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
720 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
721 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
722 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
724 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
725 * sAMAccountName: NewUser2Sam
727 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
728 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
729 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
730 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
731 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
732 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
733 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
734 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
735 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
736 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
737 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
738 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
739 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
740 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
741 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
742 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
743 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
744 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
745 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
746 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
747 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
748 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
749 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
750 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
751 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
752 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
753 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
754 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
755 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
759 * sAMAccountName, netbios_domain
762 .user = &sAMAccountName,
763 .realm = &netbios_domain,
766 .user = &sAMAccountName_l,
767 .realm = &netbios_domain_l,
770 .user = &sAMAccountName_u,
771 .realm = &netbios_domain_u,
774 .user = &sAMAccountName,
775 .realm = &netbios_domain_u,
778 .user = &sAMAccountName,
779 .realm = &netbios_domain_l,
782 .user = &sAMAccountName_u,
783 .realm = &netbios_domain_l,
786 .user = &sAMAccountName_l,
787 .realm = &netbios_domain_u,
790 * sAMAccountName, dns_domain
793 .user = &sAMAccountName,
794 .realm = &dns_domain,
797 .user = &sAMAccountName_l,
798 .realm = &dns_domain_l,
801 .user = &sAMAccountName_u,
802 .realm = &dns_domain_u,
805 .user = &sAMAccountName,
806 .realm = &dns_domain_u,
809 .user = &sAMAccountName,
810 .realm = &dns_domain_l,
813 .user = &sAMAccountName_u,
814 .realm = &dns_domain_l,
817 .user = &sAMAccountName_l,
818 .realm = &dns_domain_u,
821 * userPrincipalName, no realm
824 .user = &userPrincipalName,
828 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
829 * the fallback to the sAMAccountName based userPrincipalName is correct
831 .user = &userPrincipalName_l,
834 .user = &userPrincipalName_u,
837 * nt4dom\sAMAccountName, no realm
840 .user = &sAMAccountName,
841 .nt4dom = &netbios_domain
844 .user = &sAMAccountName_l,
845 .nt4dom = &netbios_domain_l
848 .user = &sAMAccountName_u,
849 .nt4dom = &netbios_domain_u
853 * the following ones are guessed depending on the technet2 article
854 * but not reproducable on a w2k3 server
856 /* sAMAccountName with "Digest" realm */
858 .user = &sAMAccountName,
862 .user = &sAMAccountName_l,
866 .user = &sAMAccountName_u,
869 /* userPrincipalName with "Digest" realm */
871 .user = &userPrincipalName,
875 .user = &userPrincipalName_l,
879 .user = &userPrincipalName_u,
882 /* nt4dom\\sAMAccountName with "Digest" realm */
884 .user = &sAMAccountName,
885 .nt4dom = &netbios_domain,
889 .user = &sAMAccountName_l,
890 .nt4dom = &netbios_domain_l,
894 .user = &sAMAccountName_u,
895 .nt4dom = &netbios_domain_u,
900 /* prepare DATA_BLOB's used in the combinations array */
901 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
902 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
903 if (!sAMAccountName_l.data) {
905 return LDB_ERR_OPERATIONS_ERROR;
907 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
908 if (!sAMAccountName_u.data) {
910 return LDB_ERR_OPERATIONS_ERROR;
913 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
914 if (!user_principal_name) {
915 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
916 io->u.sAMAccountName,
917 io->domain->dns_domain);
918 if (!user_principal_name) {
920 return LDB_ERR_OPERATIONS_ERROR;
923 userPrincipalName = data_blob_string_const(user_principal_name);
924 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
925 if (!userPrincipalName_l.data) {
927 return LDB_ERR_OPERATIONS_ERROR;
929 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
930 if (!userPrincipalName_u.data) {
932 return LDB_ERR_OPERATIONS_ERROR;
935 netbios_domain = data_blob_string_const(io->domain->netbios_domain);
936 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac, io->domain->netbios_domain));
937 if (!netbios_domain_l.data) {
939 return LDB_ERR_OPERATIONS_ERROR;
941 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac, io->domain->netbios_domain));
942 if (!netbios_domain_u.data) {
944 return LDB_ERR_OPERATIONS_ERROR;
947 dns_domain = data_blob_string_const(io->domain->dns_domain);
948 dns_domain_l = data_blob_string_const(io->domain->dns_domain);
949 dns_domain_u = data_blob_string_const(io->domain->realm);
951 digest = data_blob_string_const("Digest");
953 delim = data_blob_string_const(":");
954 backslash = data_blob_string_const("\\");
956 pdb->num_hashes = ARRAY_SIZE(wdigest);
957 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash, pdb->num_hashes);
960 return LDB_ERR_OPERATIONS_ERROR;
963 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
964 struct MD5Context md5;
966 if (wdigest[i].nt4dom) {
967 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
968 MD5Update(&md5, backslash.data, backslash.length);
970 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
971 MD5Update(&md5, delim.data, delim.length);
972 if (wdigest[i].realm) {
973 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
975 MD5Update(&md5, delim.data, delim.length);
976 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
977 MD5Final(pdb->hashes[i].hash, &md5);
983 static int setup_supplemental_field(struct setup_password_fields_io *io)
985 struct ldb_context *ldb;
986 struct supplementalCredentialsBlob scb;
987 struct supplementalCredentialsBlob _old_scb;
988 struct supplementalCredentialsBlob *old_scb = NULL;
989 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
990 uint32_t num_names = 0;
991 const char *names[1+4];
992 uint32_t num_packages = 0;
993 struct supplementalCredentialsPackage packages[1+4];
995 struct supplementalCredentialsPackage *pp = NULL;
996 struct package_PackagesBlob pb;
999 /* Primary:Kerberos-Newer-Keys */
1000 const char **nkn = NULL;
1001 struct supplementalCredentialsPackage *pkn = NULL;
1002 struct package_PrimaryKerberosBlob pknb;
1003 DATA_BLOB pknb_blob;
1005 /* Primary:Kerberos */
1006 const char **nk = NULL;
1007 struct supplementalCredentialsPackage *pk = NULL;
1008 struct package_PrimaryKerberosBlob pkb;
1011 /* Primary:WDigest */
1012 const char **nd = NULL;
1013 struct supplementalCredentialsPackage *pd = NULL;
1014 struct package_PrimaryWDigestBlob pdb;
1017 /* Primary:CLEARTEXT */
1018 const char **nc = NULL;
1019 struct supplementalCredentialsPackage *pc = NULL;
1020 struct package_PrimaryCLEARTEXTBlob pcb;
1024 enum ndr_err_code ndr_err;
1026 bool do_newer_keys = false;
1027 bool do_cleartext = false;
1029 ZERO_STRUCT(zero16);
1032 ldb = ldb_module_get_ctx(io->ac->module);
1034 if (!io->n.cleartext_utf8) {
1036 * when we don't have a cleartext password
1037 * we can't setup a supplementalCredential value
1042 /* if there's an old supplementaCredentials blob then parse it */
1043 if (io->o.supplemental) {
1044 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1045 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1047 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1048 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1049 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1050 ldb_asprintf_errstring(ldb,
1051 "setup_supplemental_field: "
1052 "failed to pull old supplementalCredentialsBlob: %s",
1054 return LDB_ERR_OPERATIONS_ERROR;
1057 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1058 old_scb = &_old_scb;
1060 ldb_debug(ldb, LDB_DEBUG_ERROR,
1061 "setup_supplemental_field: "
1062 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1063 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1066 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1067 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1069 if (io->domain->store_cleartext &&
1070 (io->u.user_account_control & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1071 do_cleartext = true;
1075 * The ordering is this
1077 * Primary:Kerberos-Newer-Keys (optional)
1080 * Primary:CLEARTEXT (optional)
1082 * And the 'Packages' package is insert before the last
1085 if (do_newer_keys) {
1086 /* Primary:Kerberos-Newer-Keys */
1087 nkn = &names[num_names++];
1088 pkn = &packages[num_packages++];
1091 /* Primary:Kerberos */
1092 nk = &names[num_names++];
1093 pk = &packages[num_packages++];
1095 if (!do_cleartext) {
1097 pp = &packages[num_packages++];
1100 /* Primary:WDigest */
1101 nd = &names[num_names++];
1102 pd = &packages[num_packages++];
1106 pp = &packages[num_packages++];
1108 /* Primary:CLEARTEXT */
1109 nc = &names[num_names++];
1110 pc = &packages[num_packages++];
1115 * setup 'Primary:Kerberos-Newer-Keys' element
1117 *nkn = "Kerberos-Newer-Keys";
1119 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1120 if (ret != LDB_SUCCESS) {
1124 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1125 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1127 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1128 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1129 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1130 ldb_asprintf_errstring(ldb,
1131 "setup_supplemental_field: "
1132 "failed to push package_PrimaryKerberosNeverBlob: %s",
1134 return LDB_ERR_OPERATIONS_ERROR;
1136 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1139 return LDB_ERR_OPERATIONS_ERROR;
1141 pkn->name = "Primary:Kerberos-Newer-Keys";
1143 pkn->data = pknb_hexstr;
1147 * setup 'Primary:Kerberos' element
1151 ret = setup_primary_kerberos(io, old_scb, &pkb);
1152 if (ret != LDB_SUCCESS) {
1156 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1157 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1159 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1160 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1161 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1162 ldb_asprintf_errstring(ldb,
1163 "setup_supplemental_field: "
1164 "failed to push package_PrimaryKerberosBlob: %s",
1166 return LDB_ERR_OPERATIONS_ERROR;
1168 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1171 return LDB_ERR_OPERATIONS_ERROR;
1173 pk->name = "Primary:Kerberos";
1175 pk->data = pkb_hexstr;
1178 * setup 'Primary:WDigest' element
1182 ret = setup_primary_wdigest(io, old_scb, &pdb);
1183 if (ret != LDB_SUCCESS) {
1187 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1188 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1190 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1191 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1192 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1193 ldb_asprintf_errstring(ldb,
1194 "setup_supplemental_field: "
1195 "failed to push package_PrimaryWDigestBlob: %s",
1197 return LDB_ERR_OPERATIONS_ERROR;
1199 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1202 return LDB_ERR_OPERATIONS_ERROR;
1204 pd->name = "Primary:WDigest";
1206 pd->data = pdb_hexstr;
1209 * setup 'Primary:CLEARTEXT' element
1214 pcb.cleartext = *io->n.cleartext_utf16;
1216 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1217 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1219 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1220 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1221 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1222 ldb_asprintf_errstring(ldb,
1223 "setup_supplemental_field: "
1224 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1226 return LDB_ERR_OPERATIONS_ERROR;
1228 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1231 return LDB_ERR_OPERATIONS_ERROR;
1233 pc->name = "Primary:CLEARTEXT";
1235 pc->data = pcb_hexstr;
1239 * setup 'Packages' element
1242 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1243 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1245 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1246 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1247 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1248 ldb_asprintf_errstring(ldb,
1249 "setup_supplemental_field: "
1250 "failed to push package_PackagesBlob: %s",
1252 return LDB_ERR_OPERATIONS_ERROR;
1254 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1257 return LDB_ERR_OPERATIONS_ERROR;
1259 pp->name = "Packages";
1261 pp->data = pb_hexstr;
1264 * setup 'supplementalCredentials' value
1267 scb.sub.num_packages = num_packages;
1268 scb.sub.packages = packages;
1270 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1271 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1273 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1274 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1275 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1276 ldb_asprintf_errstring(ldb,
1277 "setup_supplemental_field: "
1278 "failed to push supplementalCredentialsBlob: %s",
1280 return LDB_ERR_OPERATIONS_ERROR;
1286 static int setup_last_set_field(struct setup_password_fields_io *io)
1289 unix_to_nt_time(&io->g.last_set, time(NULL));
1294 static int setup_kvno_field(struct setup_password_fields_io *io)
1296 /* increment by one */
1297 io->g.kvno = io->o.kvno + 1;
1302 static int setup_password_fields(struct setup_password_fields_io *io)
1304 struct ldb_context *ldb;
1307 size_t converted_pw_len;
1309 ldb = ldb_module_get_ctx(io->ac->module);
1312 * refuse the change if someone want to change the cleartext
1313 * and supply his own hashes at the same time...
1315 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16) && (io->n.nt_hash || io->n.lm_hash)) {
1316 ldb_asprintf_errstring(ldb,
1317 "setup_password_fields: "
1318 "it's only allowed to set the cleartext password or the password hashes");
1319 return LDB_ERR_UNWILLING_TO_PERFORM;
1322 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
1323 ldb_asprintf_errstring(ldb,
1324 "setup_password_fields: "
1325 "it's only allowed to set the cleartext password as userPassword or clearTextPasssword, not both at once");
1326 return LDB_ERR_UNWILLING_TO_PERFORM;
1329 if (io->n.cleartext_utf8) {
1330 char **cleartext_utf16_str;
1331 struct ldb_val *cleartext_utf16_blob;
1332 io->n.cleartext_utf16 = cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1333 if (!io->n.cleartext_utf16) {
1335 return LDB_ERR_OPERATIONS_ERROR;
1337 if (!convert_string_talloc_convenience(io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1338 CH_UTF8, CH_UTF16, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length,
1339 (void **)&cleartext_utf16_str, &converted_pw_len, false)) {
1340 ldb_asprintf_errstring(ldb,
1341 "setup_password_fields: "
1342 "failed to generate UTF16 password from cleartext UTF8 password");
1343 return LDB_ERR_OPERATIONS_ERROR;
1345 *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str, converted_pw_len);
1346 } else if (io->n.cleartext_utf16) {
1347 char *cleartext_utf8_str;
1348 struct ldb_val *cleartext_utf8_blob;
1349 io->n.cleartext_utf8 = cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1350 if (!io->n.cleartext_utf8) {
1352 return LDB_ERR_OPERATIONS_ERROR;
1354 if (!convert_string_talloc_convenience(io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1355 CH_UTF16MUNGED, CH_UTF8, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length,
1356 (void **)&cleartext_utf8_str, &converted_pw_len, false)) {
1357 /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
1358 io->n.cleartext_utf8 = NULL;
1359 talloc_free(cleartext_utf8_blob);
1361 *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str, converted_pw_len);
1363 if (io->n.cleartext_utf16) {
1364 struct samr_Password *nt_hash;
1365 nt_hash = talloc(io->ac, struct samr_Password);
1368 return LDB_ERR_OPERATIONS_ERROR;
1370 io->n.nt_hash = nt_hash;
1372 /* compute the new nt hash */
1373 mdfour(nt_hash->hash, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length);
1376 if (io->n.cleartext_utf8) {
1377 struct samr_Password *lm_hash;
1378 char *cleartext_unix;
1379 if (lp_lanman_auth(ldb_get_opaque(ldb, "loadparm")) &&
1380 convert_string_talloc_convenience(io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1381 CH_UTF8, CH_UNIX, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length,
1382 (void **)&cleartext_unix, &converted_pw_len, false)) {
1383 lm_hash = talloc(io->ac, struct samr_Password);
1386 return LDB_ERR_OPERATIONS_ERROR;
1389 /* compute the new lm hash. */
1390 ok = E_deshash((char *)cleartext_unix, lm_hash->hash);
1392 io->n.lm_hash = lm_hash;
1394 talloc_free(lm_hash->hash);
1398 ret = setup_kerberos_keys(io);
1399 if (ret != LDB_SUCCESS) {
1404 ret = setup_nt_fields(io);
1405 if (ret != LDB_SUCCESS) {
1409 ret = setup_lm_fields(io);
1410 if (ret != LDB_SUCCESS) {
1414 ret = setup_supplemental_field(io);
1415 if (ret != LDB_SUCCESS) {
1419 ret = setup_last_set_field(io);
1420 if (ret != LDB_SUCCESS) {
1424 ret = setup_kvno_field(io);
1425 if (ret != LDB_SUCCESS) {
1432 static int setup_io(struct ph_context *ac,
1433 const struct ldb_message *new_msg,
1434 const struct ldb_message *searched_msg,
1435 struct setup_password_fields_io *io)
1437 const struct ldb_val *quoted_utf16;
1438 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1442 /* Some operations below require kerberos contexts */
1443 if (smb_krb5_init_context(ac,
1444 ldb_get_event_context(ldb),
1445 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
1446 &io->smb_krb5_context) != 0) {
1447 return LDB_ERR_OPERATIONS_ERROR;
1451 io->domain = ac->domain;
1453 io->u.user_account_control = samdb_result_uint(searched_msg, "userAccountControl", 0);
1454 io->u.sAMAccountName = samdb_result_string(searched_msg, "samAccountName", NULL);
1455 io->u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
1456 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
1458 io->n.cleartext_utf8 = ldb_msg_find_ldb_val(new_msg, "userPassword");
1459 io->n.cleartext_utf16 = ldb_msg_find_ldb_val(new_msg, "clearTextPassword");
1461 /* this rather strange looking piece of code is there to
1462 handle a ldap client setting a password remotely using the
1463 unicodePwd ldap field. The syntax is that the password is
1464 in UTF-16LE, with a " at either end. Unfortunately the
1465 unicodePwd field is also used to store the nt hashes
1466 internally in Samba, and is used in the nt hash format on
1467 the wire in DRS replication, so we have a single name for
1468 two distinct values. The code below leaves us with a small
1469 chance (less than 1 in 2^32) of a mixup, if someone manages
1470 to create a MD4 hash which starts and ends in 0x22 0x00, as
1471 that would then be treated as a UTF16 password rather than
1473 quoted_utf16 = ldb_msg_find_ldb_val(new_msg, "unicodePwd");
1475 quoted_utf16->length >= 4 &&
1476 quoted_utf16->data[0] == '"' &&
1477 quoted_utf16->data[1] == 0 &&
1478 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
1479 quoted_utf16->data[quoted_utf16->length-1] == 0) {
1480 io->n.quoted_utf16.data = talloc_memdup(io->ac, quoted_utf16->data+2, quoted_utf16->length-4);
1481 io->n.quoted_utf16.length = quoted_utf16->length-4;
1482 io->n.cleartext_utf16 = &io->n.quoted_utf16;
1483 io->n.nt_hash = NULL;
1485 io->n.nt_hash = samdb_result_hash(io->ac, new_msg, "unicodePwd");
1488 io->n.lm_hash = samdb_result_hash(io->ac, new_msg, "dBCSPwd");
1490 if(io->u.sAMAccountName == NULL)
1492 ldb_asprintf_errstring(ldb, "samAccountName is missing on %s for attempted password set/change",
1493 ldb_dn_get_linearized(new_msg->dn));
1494 return(LDB_ERR_CONSTRAINT_VIOLATION);
1500 static struct ph_context *ph_init_context(struct ldb_module *module,
1501 struct ldb_request *req)
1503 struct ldb_context *ldb;
1504 struct ph_context *ac;
1506 ldb = ldb_module_get_ctx(module);
1508 ac = talloc_zero(req, struct ph_context);
1510 ldb_set_errstring(ldb, "Out of Memory");
1514 ac->module = module;
1520 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
1522 struct ph_context *ac;
1524 ac = talloc_get_type(req->context, struct ph_context);
1527 return ldb_module_done(ac->req, NULL, NULL,
1528 LDB_ERR_OPERATIONS_ERROR);
1531 if (ares->type == LDB_REPLY_REFERRAL) {
1532 return ldb_module_send_referral(ac->req, ares->referral);
1535 if (ares->error != LDB_SUCCESS) {
1536 return ldb_module_done(ac->req, ares->controls,
1537 ares->response, ares->error);
1540 if (ares->type != LDB_REPLY_DONE) {
1542 return ldb_module_done(ac->req, NULL, NULL,
1543 LDB_ERR_OPERATIONS_ERROR);
1546 return ldb_module_done(ac->req, ares->controls,
1547 ares->response, ares->error);
1550 static int password_hash_add_do_add(struct ph_context *ac);
1551 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
1552 static int password_hash_mod_search_self(struct ph_context *ac);
1553 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
1554 static int password_hash_mod_do_mod(struct ph_context *ac);
1556 static int get_domain_data_callback(struct ldb_request *req,
1557 struct ldb_reply *ares)
1559 struct ldb_context *ldb;
1560 struct domain_data *data;
1561 struct ph_context *ac;
1562 struct loadparm_context *lp_ctx;
1565 ac = talloc_get_type(req->context, struct ph_context);
1566 ldb = ldb_module_get_ctx(ac->module);
1569 return ldb_module_done(ac->req, NULL, NULL,
1570 LDB_ERR_OPERATIONS_ERROR);
1572 if (ares->error != LDB_SUCCESS) {
1573 return ldb_module_done(ac->req, ares->controls,
1574 ares->response, ares->error);
1577 switch (ares->type) {
1578 case LDB_REPLY_ENTRY:
1579 if (ac->domain != NULL) {
1580 ldb_set_errstring(ldb, "Too many results");
1581 return ldb_module_done(ac->req, NULL, NULL,
1582 LDB_ERR_OPERATIONS_ERROR);
1585 data = talloc_zero(ac, struct domain_data);
1587 return ldb_module_done(ac->req, NULL, NULL,
1588 LDB_ERR_OPERATIONS_ERROR);
1591 data->pwdProperties = samdb_result_uint(ares->message, "pwdProperties", 0);
1592 data->store_cleartext = data->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
1593 data->pwdHistoryLength = samdb_result_uint(ares->message, "pwdHistoryLength", 0);
1595 /* For a domain DN, this puts things in dotted notation */
1596 /* For builtin domains, this will give details for the host,
1597 * but that doesn't really matter, as it's just used for salt
1598 * and kerberos principals, which don't exist here */
1600 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1601 struct loadparm_context);
1603 data->dns_domain = lp_dnsdomain(lp_ctx);
1604 data->realm = lp_realm(lp_ctx);
1605 data->netbios_domain = lp_sam_name(lp_ctx);
1610 case LDB_REPLY_DONE:
1612 /* call the next step */
1613 switch (ac->req->operation) {
1615 ret = password_hash_add_do_add(ac);
1619 ret = password_hash_mod_do_mod(ac);
1623 ret = LDB_ERR_OPERATIONS_ERROR;
1626 if (ret != LDB_SUCCESS) {
1627 return ldb_module_done(ac->req, NULL, NULL, ret);
1631 case LDB_REPLY_REFERRAL:
1640 static int build_domain_data_request(struct ph_context *ac)
1642 /* attrs[] is returned from this function in
1643 ac->dom_req->op.search.attrs, so it must be static, as
1644 otherwise the compiler can put it on the stack */
1645 struct ldb_context *ldb;
1646 static const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", NULL };
1648 ldb = ldb_module_get_ctx(ac->module);
1650 return ldb_build_search_req(&ac->dom_req, ldb, ac,
1651 ldb_get_default_basedn(ldb),
1655 ac, get_domain_data_callback,
1659 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
1661 struct ldb_context *ldb;
1662 struct ph_context *ac;
1663 struct ldb_message_element *sambaAttr;
1664 struct ldb_message_element *clearTextPasswordAttr;
1665 struct ldb_message_element *ntAttr;
1666 struct ldb_message_element *lmAttr;
1669 ldb = ldb_module_get_ctx(module);
1671 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
1673 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
1674 return ldb_next_request(module, req);
1677 /* If the caller is manipulating the local passwords directly, let them pass */
1678 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1679 req->op.add.message->dn) == 0) {
1680 return ldb_next_request(module, req);
1683 /* nobody must touch these fields */
1684 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
1685 return LDB_ERR_UNWILLING_TO_PERFORM;
1687 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
1688 return LDB_ERR_UNWILLING_TO_PERFORM;
1690 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
1691 return LDB_ERR_UNWILLING_TO_PERFORM;
1694 /* If no part of this ADD touches the userPassword, or the NT
1695 * or LM hashes, then we don't need to make any changes. */
1697 sambaAttr = ldb_msg_find_element(req->op.add.message, "userPassword");
1698 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
1699 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
1700 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
1702 if ((!sambaAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
1703 return ldb_next_request(module, req);
1706 /* if it is not an entry of type person its an error */
1707 /* TODO: remove this when userPassword will be in schema */
1708 if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
1709 ldb_set_errstring(ldb, "Cannot set a password on entry that does not have objectClass 'person'");
1710 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1713 /* check userPassword is single valued here */
1714 /* TODO: remove this when userPassword will be single valued in schema */
1715 if (sambaAttr && sambaAttr->num_values > 1) {
1716 ldb_set_errstring(ldb, "mupltiple values for userPassword not allowed!\n");
1717 return LDB_ERR_CONSTRAINT_VIOLATION;
1719 if (clearTextPasswordAttr && clearTextPasswordAttr->num_values > 1) {
1720 ldb_set_errstring(ldb, "mupltiple values for clearTextPassword not allowed!\n");
1721 return LDB_ERR_CONSTRAINT_VIOLATION;
1724 if (ntAttr && (ntAttr->num_values > 1)) {
1725 ldb_set_errstring(ldb, "mupltiple values for unicodePwd not allowed!\n");
1726 return LDB_ERR_CONSTRAINT_VIOLATION;
1728 if (lmAttr && (lmAttr->num_values > 1)) {
1729 ldb_set_errstring(ldb, "mupltiple values for dBCSPwd not allowed!\n");
1730 return LDB_ERR_CONSTRAINT_VIOLATION;
1733 if (sambaAttr && sambaAttr->num_values == 0) {
1734 ldb_set_errstring(ldb, "userPassword must have a value!\n");
1735 return LDB_ERR_CONSTRAINT_VIOLATION;
1738 if (clearTextPasswordAttr && clearTextPasswordAttr->num_values == 0) {
1739 ldb_set_errstring(ldb, "clearTextPassword must have a value!\n");
1740 return LDB_ERR_CONSTRAINT_VIOLATION;
1743 if (ntAttr && (ntAttr->num_values == 0)) {
1744 ldb_set_errstring(ldb, "unicodePwd must have a value!\n");
1745 return LDB_ERR_CONSTRAINT_VIOLATION;
1747 if (lmAttr && (lmAttr->num_values == 0)) {
1748 ldb_set_errstring(ldb, "dBCSPwd must have a value!\n");
1749 return LDB_ERR_CONSTRAINT_VIOLATION;
1752 ac = ph_init_context(module, req);
1754 return LDB_ERR_OPERATIONS_ERROR;
1757 /* get user domain data */
1758 ret = build_domain_data_request(ac);
1759 if (ret != LDB_SUCCESS) {
1763 return ldb_next_request(module, ac->dom_req);
1766 static int password_hash_add_do_add(struct ph_context *ac)
1768 struct ldb_context *ldb;
1769 struct ldb_request *down_req;
1770 struct ldb_message *msg;
1771 struct setup_password_fields_io io;
1774 /* Prepare the internal data structure containing the passwords */
1775 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
1776 if (ret != LDB_SUCCESS) {
1780 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
1782 return LDB_ERR_OPERATIONS_ERROR;
1785 /* remove attributes that we just read into 'io' */
1786 ldb_msg_remove_attr(msg, "userPassword");
1787 ldb_msg_remove_attr(msg, "clearTextPassword");
1788 ldb_msg_remove_attr(msg, "unicodePwd");
1789 ldb_msg_remove_attr(msg, "dBCSPwd");
1790 ldb_msg_remove_attr(msg, "pwdLastSet");
1791 io.o.kvno = samdb_result_uint(msg, "msDs-KeyVersionNumber", 1) - 1;
1792 ldb_msg_remove_attr(msg, "msDs-KeyVersionNumber");
1794 ldb = ldb_module_get_ctx(ac->module);
1796 ret = setup_password_fields(&io);
1797 if (ret != LDB_SUCCESS) {
1802 ret = samdb_msg_add_hash(ldb, ac, msg,
1803 "unicodePwd", io.g.nt_hash);
1804 if (ret != LDB_SUCCESS) {
1809 ret = samdb_msg_add_hash(ldb, ac, msg,
1810 "dBCSPwd", io.g.lm_hash);
1811 if (ret != LDB_SUCCESS) {
1815 if (io.g.nt_history_len > 0) {
1816 ret = samdb_msg_add_hashes(ac, msg,
1819 io.g.nt_history_len);
1820 if (ret != LDB_SUCCESS) {
1824 if (io.g.lm_history_len > 0) {
1825 ret = samdb_msg_add_hashes(ac, msg,
1828 io.g.lm_history_len);
1829 if (ret != LDB_SUCCESS) {
1833 if (io.g.supplemental.length > 0) {
1834 ret = ldb_msg_add_value(msg, "supplementalCredentials",
1835 &io.g.supplemental, NULL);
1836 if (ret != LDB_SUCCESS) {
1840 ret = samdb_msg_add_uint64(ldb, ac, msg,
1843 if (ret != LDB_SUCCESS) {
1846 ret = samdb_msg_add_uint(ldb, ac, msg,
1847 "msDs-KeyVersionNumber",
1849 if (ret != LDB_SUCCESS) {
1853 ret = ldb_build_add_req(&down_req, ldb, ac,
1858 if (ret != LDB_SUCCESS) {
1862 return ldb_next_request(ac->module, down_req);
1865 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
1867 struct ldb_context *ldb;
1868 struct ph_context *ac;
1869 struct ldb_message_element *sambaAttr;
1870 struct ldb_message_element *clearTextAttr;
1871 struct ldb_message_element *ntAttr;
1872 struct ldb_message_element *lmAttr;
1873 struct ldb_message *msg;
1874 struct ldb_request *down_req;
1877 ldb = ldb_module_get_ctx(module);
1879 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
1881 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
1882 return ldb_next_request(module, req);
1885 /* If the caller is manipulating the local passwords directly, let them pass */
1886 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1887 req->op.mod.message->dn) == 0) {
1888 return ldb_next_request(module, req);
1891 /* nobody must touch password Histories */
1892 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
1893 return LDB_ERR_UNWILLING_TO_PERFORM;
1895 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
1896 return LDB_ERR_UNWILLING_TO_PERFORM;
1898 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
1899 return LDB_ERR_UNWILLING_TO_PERFORM;
1902 sambaAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
1903 clearTextAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
1904 ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
1905 lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
1907 /* If no part of this touches the userPassword OR
1908 * clearTextPassword OR unicodePwd and/or dBCSPwd, then we
1909 * don't need to make any changes. For password changes/set
1910 * there should be a 'delete' or a 'modify' on this
1912 if ((!sambaAttr) && (!clearTextAttr) && (!ntAttr) && (!lmAttr)) {
1913 return ldb_next_request(module, req);
1916 /* check passwords are single valued here */
1917 /* TODO: remove this when passwords will be single valued in schema */
1918 if (sambaAttr && (sambaAttr->num_values > 1)) {
1919 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1920 return LDB_ERR_CONSTRAINT_VIOLATION;
1922 if (clearTextAttr && (clearTextAttr->num_values > 1)) {
1923 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1924 return LDB_ERR_CONSTRAINT_VIOLATION;
1926 if (ntAttr && (ntAttr->num_values > 1)) {
1927 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1928 return LDB_ERR_CONSTRAINT_VIOLATION;
1930 if (lmAttr && (lmAttr->num_values > 1)) {
1931 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1932 return LDB_ERR_CONSTRAINT_VIOLATION;
1935 ac = ph_init_context(module, req);
1937 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1938 return LDB_ERR_OPERATIONS_ERROR;
1941 /* use a new message structure so that we can modify it */
1942 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
1945 return LDB_ERR_OPERATIONS_ERROR;
1948 /* - remove any modification to the password from the first commit
1949 * we will make the real modification later */
1950 if (sambaAttr) ldb_msg_remove_attr(msg, "userPassword");
1951 if (clearTextAttr) ldb_msg_remove_attr(msg, "clearTextPassword");
1952 if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd");
1953 if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd");
1955 /* if there was nothing else to be modified skip to next step */
1956 if (msg->num_elements == 0) {
1957 return password_hash_mod_search_self(ac);
1960 ret = ldb_build_mod_req(&down_req, ldb, ac,
1963 ac, ph_modify_callback,
1965 if (ret != LDB_SUCCESS) {
1969 return ldb_next_request(module, down_req);
1972 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
1974 struct ph_context *ac;
1977 ac = talloc_get_type(req->context, struct ph_context);
1980 return ldb_module_done(ac->req, NULL, NULL,
1981 LDB_ERR_OPERATIONS_ERROR);
1984 if (ares->type == LDB_REPLY_REFERRAL) {
1985 return ldb_module_send_referral(ac->req, ares->referral);
1988 if (ares->error != LDB_SUCCESS) {
1989 return ldb_module_done(ac->req, ares->controls,
1990 ares->response, ares->error);
1993 if (ares->type != LDB_REPLY_DONE) {
1995 return ldb_module_done(ac->req, NULL, NULL,
1996 LDB_ERR_OPERATIONS_ERROR);
1999 ret = password_hash_mod_search_self(ac);
2000 if (ret != LDB_SUCCESS) {
2001 return ldb_module_done(ac->req, NULL, NULL, ret);
2008 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
2010 struct ldb_context *ldb;
2011 struct ph_context *ac;
2014 ac = talloc_get_type(req->context, struct ph_context);
2015 ldb = ldb_module_get_ctx(ac->module);
2018 return ldb_module_done(ac->req, NULL, NULL,
2019 LDB_ERR_OPERATIONS_ERROR);
2021 if (ares->error != LDB_SUCCESS) {
2022 return ldb_module_done(ac->req, ares->controls,
2023 ares->response, ares->error);
2026 /* we are interested only in the single reply (base search) */
2027 switch (ares->type) {
2028 case LDB_REPLY_ENTRY:
2030 if (ac->search_res != NULL) {
2031 ldb_set_errstring(ldb, "Too many results");
2033 return ldb_module_done(ac->req, NULL, NULL,
2034 LDB_ERR_OPERATIONS_ERROR);
2037 /* if it is not an entry of type person this is an error */
2038 /* TODO: remove this when sambaPassword will be in schema */
2039 if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
2040 ldb_set_errstring(ldb, "Object class violation");
2042 return ldb_module_done(ac->req, NULL, NULL,
2043 LDB_ERR_OBJECT_CLASS_VIOLATION);
2046 ac->search_res = talloc_steal(ac, ares);
2049 case LDB_REPLY_DONE:
2051 /* get user domain data */
2052 ret = build_domain_data_request(ac);
2053 if (ret != LDB_SUCCESS) {
2054 return ldb_module_done(ac->req, NULL, NULL,ret);
2057 return ldb_next_request(ac->module, ac->dom_req);
2059 case LDB_REPLY_REFERRAL:
2060 /*ignore anything else for now */
2068 static int password_hash_mod_search_self(struct ph_context *ac)
2070 struct ldb_context *ldb;
2071 static const char * const attrs[] = { "userAccountControl", "lmPwdHistory",
2073 "objectSid", "msDS-KeyVersionNumber",
2074 "objectClass", "userPrincipalName",
2076 "dBCSPwd", "unicodePwd",
2077 "supplementalCredentials",
2079 struct ldb_request *search_req;
2082 ldb = ldb_module_get_ctx(ac->module);
2084 ret = ldb_build_search_req(&search_req, ldb, ac,
2085 ac->req->op.mod.message->dn,
2090 ac, ph_mod_search_callback,
2093 if (ret != LDB_SUCCESS) {
2097 return ldb_next_request(ac->module, search_req);
2100 static int password_hash_mod_do_mod(struct ph_context *ac)
2102 struct ldb_context *ldb;
2103 struct ldb_request *mod_req;
2104 struct ldb_message *msg;
2105 const struct ldb_message *searched_msg;
2106 struct setup_password_fields_io io;
2109 ldb = ldb_module_get_ctx(ac->module);
2111 /* use a new message structure so that we can modify it */
2112 msg = ldb_msg_new(ac);
2114 return LDB_ERR_OPERATIONS_ERROR;
2118 msg->dn = ac->req->op.mod.message->dn;
2120 /* Prepare the internal data structure containing the passwords */
2122 ac->req->op.mod.message,
2123 ac->search_res->message,
2125 if (ret != LDB_SUCCESS) {
2129 searched_msg = ac->search_res->message;
2131 /* Fill in some final details (only relevent once the password has been set) */
2132 io.o.kvno = samdb_result_uint(searched_msg, "msDs-KeyVersionNumber", 0);
2133 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
2134 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
2135 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
2137 ret = setup_password_fields(&io);
2138 if (ret != LDB_SUCCESS) {
2142 /* make sure we replace all the old attributes */
2143 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
2144 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
2145 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2146 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2147 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
2148 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
2149 ret = ldb_msg_add_empty(msg, "msDs-KeyVersionNumber", LDB_FLAG_MOD_REPLACE, NULL);
2152 ret = samdb_msg_add_hash(ldb, ac, msg,
2153 "unicodePwd", io.g.nt_hash);
2154 if (ret != LDB_SUCCESS) {
2159 ret = samdb_msg_add_hash(ldb, ac, msg,
2160 "dBCSPwd", io.g.lm_hash);
2161 if (ret != LDB_SUCCESS) {
2165 if (io.g.nt_history_len > 0) {
2166 ret = samdb_msg_add_hashes(ac, msg,
2169 io.g.nt_history_len);
2170 if (ret != LDB_SUCCESS) {
2174 if (io.g.lm_history_len > 0) {
2175 ret = samdb_msg_add_hashes(ac, msg,
2178 io.g.lm_history_len);
2179 if (ret != LDB_SUCCESS) {
2183 if (io.g.supplemental.length > 0) {
2184 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2185 &io.g.supplemental, NULL);
2186 if (ret != LDB_SUCCESS) {
2190 ret = samdb_msg_add_uint64(ldb, ac, msg,
2193 if (ret != LDB_SUCCESS) {
2196 ret = samdb_msg_add_uint(ldb, ac, msg,
2197 "msDs-KeyVersionNumber",
2199 if (ret != LDB_SUCCESS) {
2203 ret = ldb_build_mod_req(&mod_req, ldb, ac,
2208 if (ret != LDB_SUCCESS) {
2212 return ldb_next_request(ac->module, mod_req);
2215 _PUBLIC_ const struct ldb_module_ops ldb_password_hash_module_ops = {
2216 .name = "password_hash",
2217 .add = password_hash_add,
2218 .modify = password_hash_modify,