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
8 Copyright (C) Matthias Dieter Wallnöfer 2009-2010
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb password_hash module
29 * Description: correctly handle AD password changes fields
31 * Author: Andrew Bartlett
32 * Author: Stefan Metzmacher
36 #include "libcli/ldap/ldap_ndr.h"
37 #include "ldb_module.h"
38 #include "librpc/gen_ndr/misc.h"
39 #include "librpc/gen_ndr/samr.h"
40 #include "libcli/auth/libcli_auth.h"
41 #include "libcli/security/security.h"
42 #include "system/kerberos.h"
43 #include "auth/kerberos/kerberos.h"
44 #include "system/time.h"
45 #include "dsdb/samdb/samdb.h"
46 #include "../libds/common/flags.h"
47 #include "dsdb/samdb/ldb_modules/password_modules.h"
48 #include "librpc/ndr/libndr.h"
49 #include "librpc/gen_ndr/ndr_drsblobs.h"
50 #include "../lib/crypto/crypto.h"
51 #include "param/param.h"
53 /* If we have decided there is a reason to work on this request, then
54 * setup all the password hash types correctly.
56 * If we haven't the hashes yet but the password given as plain-text (attributes
57 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
58 * the constraints. Once this is done, we calculate the password hashes.
60 * Notice: unlike the real AD which only supports the UTF16 special based
61 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
62 * understand also a UTF16 based 'clearTextPassword' one.
63 * The latter is also accessible through LDAP so it can also be set by external
64 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
66 * Also when the module receives only the password hashes (possible through
67 * specifying an internal LDB control - for security reasons) some checks are
68 * performed depending on the operation mode (see below) (e.g. if the password
69 * has been in use before if the password memory policy was activated).
71 * Attention: There is a difference between "modify" and "reset" operations
72 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
73 * operation for a password attribute we thread this as a "modify"; if it sends
74 * only a "replace" one we have an (administrative) reset.
76 * Finally, if the administrator has requested that a password history
77 * be maintained, then this should also be written out.
81 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
82 * - Check for right connection encryption
86 struct ldb_module *module;
87 struct ldb_request *req;
89 struct ldb_request *dom_req;
90 struct ldb_reply *dom_res;
92 struct ldb_reply *search_res;
94 struct domain_data *domain;
99 uint32_t pwdProperties;
100 uint32_t pwdHistoryLength;
103 uint32_t minPwdLength;
104 const char *netbios_domain;
105 const char *dns_domain;
109 struct setup_password_fields_io {
110 struct ph_context *ac;
111 struct domain_data *domain;
112 struct smb_krb5_context *smb_krb5_context;
114 /* infos about the user account */
116 uint32_t userAccountControl;
118 const char *sAMAccountName;
119 const char *user_principal_name;
123 /* new credentials */
125 const struct ldb_val *cleartext_utf8;
126 const struct ldb_val *cleartext_utf16;
127 struct ldb_val quoted_utf16;
128 struct samr_Password *nt_hash;
129 struct samr_Password *lm_hash;
132 /* old credentials */
134 uint32_t nt_history_len;
135 struct samr_Password *nt_history;
136 uint32_t lm_history_len;
137 struct samr_Password *lm_history;
138 const struct ldb_val *supplemental;
139 struct supplementalCredentialsBlob scb;
142 /* generated credentials */
144 struct samr_Password *nt_hash;
145 struct samr_Password *lm_hash;
146 uint32_t nt_history_len;
147 struct samr_Password *nt_history;
148 uint32_t lm_history_len;
149 struct samr_Password *lm_history;
155 struct ldb_val supplemental;
160 /* Get the NT hash, and fill it in as an entry in the password history,
161 and specify it into io->g.nt_hash */
163 static int setup_nt_fields(struct setup_password_fields_io *io)
165 struct ldb_context *ldb;
168 io->g.nt_hash = io->n.nt_hash;
169 ldb = ldb_module_get_ctx(io->ac->module);
171 if (io->domain->pwdHistoryLength == 0) {
175 /* We might not have an old NT password */
176 io->g.nt_history = talloc_array(io->ac,
177 struct samr_Password,
178 io->domain->pwdHistoryLength);
179 if (!io->g.nt_history) {
181 return LDB_ERR_OPERATIONS_ERROR;
184 for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.nt_history_len); i++) {
185 io->g.nt_history[i+1] = io->o.nt_history[i];
187 io->g.nt_history_len = i + 1;
190 io->g.nt_history[0] = *io->g.nt_hash;
193 * TODO: is this correct?
194 * the simular behavior is correct for the lm history case
196 E_md4hash("", io->g.nt_history[0].hash);
202 /* Get the LANMAN hash, and fill it in as an entry in the password history,
203 and specify it into io->g.lm_hash */
205 static int setup_lm_fields(struct setup_password_fields_io *io)
207 struct ldb_context *ldb;
210 io->g.lm_hash = io->n.lm_hash;
211 ldb = ldb_module_get_ctx(io->ac->module);
213 if (io->domain->pwdHistoryLength == 0) {
217 /* We might not have an old NT password */
218 io->g.lm_history = talloc_array(io->ac,
219 struct samr_Password,
220 io->domain->pwdHistoryLength);
221 if (!io->g.lm_history) {
223 return LDB_ERR_OPERATIONS_ERROR;
226 for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.lm_history_len); i++) {
227 io->g.lm_history[i+1] = io->o.lm_history[i];
229 io->g.lm_history_len = i + 1;
232 io->g.lm_history[0] = *io->g.lm_hash;
234 E_deshash("", io->g.lm_history[0].hash);
240 static int setup_kerberos_keys(struct setup_password_fields_io *io)
242 struct ldb_context *ldb;
243 krb5_error_code krb5_ret;
244 Principal *salt_principal;
247 krb5_data cleartext_data;
249 ldb = ldb_module_get_ctx(io->ac->module);
250 cleartext_data.data = io->n.cleartext_utf8->data;
251 cleartext_data.length = io->n.cleartext_utf8->length;
253 /* Many, many thanks to lukeh@padl.com for this
254 * algorithm, described in his Nov 10 2004 mail to
255 * samba-technical@samba.org */
258 * Determine a salting principal
260 if (io->u.is_computer) {
264 name = strlower_talloc(io->ac, io->u.sAMAccountName);
267 return LDB_ERR_OPERATIONS_ERROR;
270 if (name[strlen(name)-1] == '$') {
271 name[strlen(name)-1] = '\0';
274 saltbody = talloc_asprintf(io->ac, "%s.%s", name, io->domain->dns_domain);
277 return LDB_ERR_OPERATIONS_ERROR;
280 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
282 io->domain->realm, "host",
284 } else if (io->u.user_principal_name) {
285 char *user_principal_name;
288 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
289 if (!user_principal_name) {
291 return LDB_ERR_OPERATIONS_ERROR;
294 p = strchr(user_principal_name, '@');
299 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
301 io->domain->realm, user_principal_name,
304 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
306 io->domain->realm, io->u.sAMAccountName,
310 ldb_asprintf_errstring(ldb,
311 "setup_kerberos_keys: "
312 "generation of a salting principal failed: %s",
313 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
315 return LDB_ERR_OPERATIONS_ERROR;
319 * create salt from salt_principal
321 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
322 salt_principal, &salt);
323 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
325 ldb_asprintf_errstring(ldb,
326 "setup_kerberos_keys: "
327 "generation of krb5_salt failed: %s",
328 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
330 return LDB_ERR_OPERATIONS_ERROR;
332 /* create a talloc copy */
333 io->g.salt = talloc_strndup(io->ac,
335 salt.saltvalue.length);
336 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
339 return LDB_ERR_OPERATIONS_ERROR;
341 salt.saltvalue.data = discard_const(io->g.salt);
342 salt.saltvalue.length = strlen(io->g.salt);
345 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
346 * the salt and the cleartext password
348 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
349 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
354 ldb_asprintf_errstring(ldb,
355 "setup_kerberos_keys: "
356 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
357 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
359 return LDB_ERR_OPERATIONS_ERROR;
361 io->g.aes_256 = data_blob_talloc(io->ac,
363 key.keyvalue.length);
364 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
365 if (!io->g.aes_256.data) {
367 return LDB_ERR_OPERATIONS_ERROR;
371 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
372 * the salt and the cleartext password
374 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
375 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
380 ldb_asprintf_errstring(ldb,
381 "setup_kerberos_keys: "
382 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
383 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
385 return LDB_ERR_OPERATIONS_ERROR;
387 io->g.aes_128 = data_blob_talloc(io->ac,
389 key.keyvalue.length);
390 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
391 if (!io->g.aes_128.data) {
393 return LDB_ERR_OPERATIONS_ERROR;
397 * create ENCTYPE_DES_CBC_MD5 key out of
398 * the salt and the cleartext password
400 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
406 ldb_asprintf_errstring(ldb,
407 "setup_kerberos_keys: "
408 "generation of a des-cbc-md5 key failed: %s",
409 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
411 return LDB_ERR_OPERATIONS_ERROR;
413 io->g.des_md5 = data_blob_talloc(io->ac,
415 key.keyvalue.length);
416 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
417 if (!io->g.des_md5.data) {
419 return LDB_ERR_OPERATIONS_ERROR;
423 * create ENCTYPE_DES_CBC_CRC key out of
424 * the salt and the cleartext password
426 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
432 ldb_asprintf_errstring(ldb,
433 "setup_kerberos_keys: "
434 "generation of a des-cbc-crc key failed: %s",
435 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
437 return LDB_ERR_OPERATIONS_ERROR;
439 io->g.des_crc = data_blob_talloc(io->ac,
441 key.keyvalue.length);
442 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
443 if (!io->g.des_crc.data) {
445 return LDB_ERR_OPERATIONS_ERROR;
451 static int setup_primary_kerberos(struct setup_password_fields_io *io,
452 const struct supplementalCredentialsBlob *old_scb,
453 struct package_PrimaryKerberosBlob *pkb)
455 struct ldb_context *ldb;
456 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
457 struct supplementalCredentialsPackage *old_scp = NULL;
458 struct package_PrimaryKerberosBlob _old_pkb;
459 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
461 enum ndr_err_code ndr_err;
463 ldb = ldb_module_get_ctx(io->ac->module);
466 * prepare generation of keys
468 * ENCTYPE_DES_CBC_MD5
469 * ENCTYPE_DES_CBC_CRC
472 pkb3->salt.string = io->g.salt;
474 pkb3->keys = talloc_array(io->ac,
475 struct package_PrimaryKerberosKey3,
479 return LDB_ERR_OPERATIONS_ERROR;
482 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
483 pkb3->keys[0].value = &io->g.des_md5;
484 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
485 pkb3->keys[1].value = &io->g.des_crc;
487 /* initialize the old keys to zero */
488 pkb3->num_old_keys = 0;
489 pkb3->old_keys = NULL;
491 /* if there're no old keys, then we're done */
496 for (i=0; i < old_scb->sub.num_packages; i++) {
497 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
501 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
505 old_scp = &old_scb->sub.packages[i];
508 /* Primary:Kerberos element of supplementalCredentials */
512 blob = strhex_to_data_blob(io->ac, old_scp->data);
515 return LDB_ERR_OPERATIONS_ERROR;
518 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
519 ndr_err = ndr_pull_struct_blob(&blob, io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &_old_pkb,
520 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
521 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
522 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
523 ldb_asprintf_errstring(ldb,
524 "setup_primary_kerberos: "
525 "failed to pull old package_PrimaryKerberosBlob: %s",
527 return LDB_ERR_OPERATIONS_ERROR;
530 if (_old_pkb.version != 3) {
531 ldb_asprintf_errstring(ldb,
532 "setup_primary_kerberos: "
533 "package_PrimaryKerberosBlob version[%u] expected[3]",
535 return LDB_ERR_OPERATIONS_ERROR;
538 old_pkb3 = &_old_pkb.ctr.ctr3;
541 /* if we didn't found the old keys we're done */
546 /* fill in the old keys */
547 pkb3->num_old_keys = old_pkb3->num_keys;
548 pkb3->old_keys = old_pkb3->keys;
553 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
554 const struct supplementalCredentialsBlob *old_scb,
555 struct package_PrimaryKerberosBlob *pkb)
557 struct ldb_context *ldb;
558 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
559 struct supplementalCredentialsPackage *old_scp = NULL;
560 struct package_PrimaryKerberosBlob _old_pkb;
561 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
563 enum ndr_err_code ndr_err;
565 ldb = ldb_module_get_ctx(io->ac->module);
568 * prepare generation of keys
570 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
571 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
572 * ENCTYPE_DES_CBC_MD5
573 * ENCTYPE_DES_CBC_CRC
576 pkb4->salt.string = io->g.salt;
577 pkb4->default_iteration_count = 4096;
580 pkb4->keys = talloc_array(io->ac,
581 struct package_PrimaryKerberosKey4,
585 return LDB_ERR_OPERATIONS_ERROR;
588 pkb4->keys[0].iteration_count = 4096;
589 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
590 pkb4->keys[0].value = &io->g.aes_256;
591 pkb4->keys[1].iteration_count = 4096;
592 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
593 pkb4->keys[1].value = &io->g.aes_128;
594 pkb4->keys[2].iteration_count = 4096;
595 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
596 pkb4->keys[2].value = &io->g.des_md5;
597 pkb4->keys[3].iteration_count = 4096;
598 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
599 pkb4->keys[3].value = &io->g.des_crc;
601 /* initialize the old keys to zero */
602 pkb4->num_old_keys = 0;
603 pkb4->old_keys = NULL;
604 pkb4->num_older_keys = 0;
605 pkb4->older_keys = NULL;
607 /* if there're no old keys, then we're done */
612 for (i=0; i < old_scb->sub.num_packages; i++) {
613 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
617 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
621 old_scp = &old_scb->sub.packages[i];
624 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
628 blob = strhex_to_data_blob(io->ac, old_scp->data);
631 return LDB_ERR_OPERATIONS_ERROR;
634 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
635 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
636 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
638 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
639 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
640 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
641 ldb_asprintf_errstring(ldb,
642 "setup_primary_kerberos_newer: "
643 "failed to pull old package_PrimaryKerberosBlob: %s",
645 return LDB_ERR_OPERATIONS_ERROR;
648 if (_old_pkb.version != 4) {
649 ldb_asprintf_errstring(ldb,
650 "setup_primary_kerberos_newer: "
651 "package_PrimaryKerberosBlob version[%u] expected[4]",
653 return LDB_ERR_OPERATIONS_ERROR;
656 old_pkb4 = &_old_pkb.ctr.ctr4;
659 /* if we didn't found the old keys we're done */
664 /* fill in the old keys */
665 pkb4->num_old_keys = old_pkb4->num_keys;
666 pkb4->old_keys = old_pkb4->keys;
667 pkb4->num_older_keys = old_pkb4->num_old_keys;
668 pkb4->older_keys = old_pkb4->old_keys;
673 static int setup_primary_wdigest(struct setup_password_fields_io *io,
674 const struct supplementalCredentialsBlob *old_scb,
675 struct package_PrimaryWDigestBlob *pdb)
677 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
678 DATA_BLOB sAMAccountName;
679 DATA_BLOB sAMAccountName_l;
680 DATA_BLOB sAMAccountName_u;
681 const char *user_principal_name = io->u.user_principal_name;
682 DATA_BLOB userPrincipalName;
683 DATA_BLOB userPrincipalName_l;
684 DATA_BLOB userPrincipalName_u;
685 DATA_BLOB netbios_domain;
686 DATA_BLOB netbios_domain_l;
687 DATA_BLOB netbios_domain_u;
688 DATA_BLOB dns_domain;
689 DATA_BLOB dns_domain_l;
690 DATA_BLOB dns_domain_u;
702 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
703 * for what precalculated hashes are supposed to be stored...
705 * I can't reproduce all values which should contain "Digest" as realm,
706 * am I doing something wrong or is w2k3 just broken...?
708 * W2K3 fills in following for a user:
710 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
711 * sAMAccountName: NewUser2Sam
712 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
714 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
715 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
716 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
717 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
718 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
719 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
720 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
721 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
722 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
723 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
724 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
725 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
726 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
727 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
728 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
729 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
730 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
731 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
732 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
733 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
734 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
735 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
736 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
737 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
738 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
739 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
740 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
741 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
742 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
744 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
745 * sAMAccountName: NewUser2Sam
747 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
748 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
749 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
750 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
751 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
752 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
753 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
754 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
755 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
756 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
757 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
758 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
759 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
760 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
761 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
762 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
763 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
764 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
765 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
766 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
767 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
768 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
769 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
770 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
771 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
772 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
773 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
774 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
775 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
779 * sAMAccountName, netbios_domain
782 .user = &sAMAccountName,
783 .realm = &netbios_domain,
786 .user = &sAMAccountName_l,
787 .realm = &netbios_domain_l,
790 .user = &sAMAccountName_u,
791 .realm = &netbios_domain_u,
794 .user = &sAMAccountName,
795 .realm = &netbios_domain_u,
798 .user = &sAMAccountName,
799 .realm = &netbios_domain_l,
802 .user = &sAMAccountName_u,
803 .realm = &netbios_domain_l,
806 .user = &sAMAccountName_l,
807 .realm = &netbios_domain_u,
810 * sAMAccountName, dns_domain
813 .user = &sAMAccountName,
814 .realm = &dns_domain,
817 .user = &sAMAccountName_l,
818 .realm = &dns_domain_l,
821 .user = &sAMAccountName_u,
822 .realm = &dns_domain_u,
825 .user = &sAMAccountName,
826 .realm = &dns_domain_u,
829 .user = &sAMAccountName,
830 .realm = &dns_domain_l,
833 .user = &sAMAccountName_u,
834 .realm = &dns_domain_l,
837 .user = &sAMAccountName_l,
838 .realm = &dns_domain_u,
841 * userPrincipalName, no realm
844 .user = &userPrincipalName,
848 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
849 * the fallback to the sAMAccountName based userPrincipalName is correct
851 .user = &userPrincipalName_l,
854 .user = &userPrincipalName_u,
857 * nt4dom\sAMAccountName, no realm
860 .user = &sAMAccountName,
861 .nt4dom = &netbios_domain
864 .user = &sAMAccountName_l,
865 .nt4dom = &netbios_domain_l
868 .user = &sAMAccountName_u,
869 .nt4dom = &netbios_domain_u
873 * the following ones are guessed depending on the technet2 article
874 * but not reproducable on a w2k3 server
876 /* sAMAccountName with "Digest" realm */
878 .user = &sAMAccountName,
882 .user = &sAMAccountName_l,
886 .user = &sAMAccountName_u,
889 /* userPrincipalName with "Digest" realm */
891 .user = &userPrincipalName,
895 .user = &userPrincipalName_l,
899 .user = &userPrincipalName_u,
902 /* nt4dom\\sAMAccountName with "Digest" realm */
904 .user = &sAMAccountName,
905 .nt4dom = &netbios_domain,
909 .user = &sAMAccountName_l,
910 .nt4dom = &netbios_domain_l,
914 .user = &sAMAccountName_u,
915 .nt4dom = &netbios_domain_u,
920 /* prepare DATA_BLOB's used in the combinations array */
921 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
922 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
923 if (!sAMAccountName_l.data) {
925 return LDB_ERR_OPERATIONS_ERROR;
927 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
928 if (!sAMAccountName_u.data) {
930 return LDB_ERR_OPERATIONS_ERROR;
933 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
934 if (!user_principal_name) {
935 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
936 io->u.sAMAccountName,
937 io->domain->dns_domain);
938 if (!user_principal_name) {
940 return LDB_ERR_OPERATIONS_ERROR;
943 userPrincipalName = data_blob_string_const(user_principal_name);
944 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
945 if (!userPrincipalName_l.data) {
947 return LDB_ERR_OPERATIONS_ERROR;
949 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
950 if (!userPrincipalName_u.data) {
952 return LDB_ERR_OPERATIONS_ERROR;
955 netbios_domain = data_blob_string_const(io->domain->netbios_domain);
956 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac, io->domain->netbios_domain));
957 if (!netbios_domain_l.data) {
959 return LDB_ERR_OPERATIONS_ERROR;
961 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac, io->domain->netbios_domain));
962 if (!netbios_domain_u.data) {
964 return LDB_ERR_OPERATIONS_ERROR;
967 dns_domain = data_blob_string_const(io->domain->dns_domain);
968 dns_domain_l = data_blob_string_const(io->domain->dns_domain);
969 dns_domain_u = data_blob_string_const(io->domain->realm);
971 digest = data_blob_string_const("Digest");
973 delim = data_blob_string_const(":");
974 backslash = data_blob_string_const("\\");
976 pdb->num_hashes = ARRAY_SIZE(wdigest);
977 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
981 return LDB_ERR_OPERATIONS_ERROR;
984 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
985 struct MD5Context md5;
987 if (wdigest[i].nt4dom) {
988 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
989 MD5Update(&md5, backslash.data, backslash.length);
991 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
992 MD5Update(&md5, delim.data, delim.length);
993 if (wdigest[i].realm) {
994 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
996 MD5Update(&md5, delim.data, delim.length);
997 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
998 MD5Final(pdb->hashes[i].hash, &md5);
1004 static int setup_supplemental_field(struct setup_password_fields_io *io)
1006 struct ldb_context *ldb;
1007 struct supplementalCredentialsBlob scb;
1008 struct supplementalCredentialsBlob _old_scb;
1009 struct supplementalCredentialsBlob *old_scb = NULL;
1010 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1011 uint32_t num_names = 0;
1012 const char *names[1+4];
1013 uint32_t num_packages = 0;
1014 struct supplementalCredentialsPackage packages[1+4];
1016 struct supplementalCredentialsPackage *pp = NULL;
1017 struct package_PackagesBlob pb;
1020 /* Primary:Kerberos-Newer-Keys */
1021 const char **nkn = NULL;
1022 struct supplementalCredentialsPackage *pkn = NULL;
1023 struct package_PrimaryKerberosBlob pknb;
1024 DATA_BLOB pknb_blob;
1026 /* Primary:Kerberos */
1027 const char **nk = NULL;
1028 struct supplementalCredentialsPackage *pk = NULL;
1029 struct package_PrimaryKerberosBlob pkb;
1032 /* Primary:WDigest */
1033 const char **nd = NULL;
1034 struct supplementalCredentialsPackage *pd = NULL;
1035 struct package_PrimaryWDigestBlob pdb;
1038 /* Primary:CLEARTEXT */
1039 const char **nc = NULL;
1040 struct supplementalCredentialsPackage *pc = NULL;
1041 struct package_PrimaryCLEARTEXTBlob pcb;
1045 enum ndr_err_code ndr_err;
1047 bool do_newer_keys = false;
1048 bool do_cleartext = false;
1050 ZERO_STRUCT(zero16);
1053 ldb = ldb_module_get_ctx(io->ac->module);
1055 if (!io->n.cleartext_utf8) {
1057 * when we don't have a cleartext password
1058 * we can't setup a supplementalCredential value
1063 /* if there's an old supplementaCredentials blob then parse it */
1064 if (io->o.supplemental) {
1065 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1066 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1068 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1069 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1070 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1071 ldb_asprintf_errstring(ldb,
1072 "setup_supplemental_field: "
1073 "failed to pull old supplementalCredentialsBlob: %s",
1075 return LDB_ERR_OPERATIONS_ERROR;
1078 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1079 old_scb = &_old_scb;
1081 ldb_debug(ldb, LDB_DEBUG_ERROR,
1082 "setup_supplemental_field: "
1083 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1084 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1087 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1088 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1090 if (io->domain->store_cleartext &&
1091 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1092 do_cleartext = true;
1096 * The ordering is this
1098 * Primary:Kerberos-Newer-Keys (optional)
1101 * Primary:CLEARTEXT (optional)
1103 * And the 'Packages' package is insert before the last
1106 if (do_newer_keys) {
1107 /* Primary:Kerberos-Newer-Keys */
1108 nkn = &names[num_names++];
1109 pkn = &packages[num_packages++];
1112 /* Primary:Kerberos */
1113 nk = &names[num_names++];
1114 pk = &packages[num_packages++];
1116 if (!do_cleartext) {
1118 pp = &packages[num_packages++];
1121 /* Primary:WDigest */
1122 nd = &names[num_names++];
1123 pd = &packages[num_packages++];
1127 pp = &packages[num_packages++];
1129 /* Primary:CLEARTEXT */
1130 nc = &names[num_names++];
1131 pc = &packages[num_packages++];
1136 * setup 'Primary:Kerberos-Newer-Keys' element
1138 *nkn = "Kerberos-Newer-Keys";
1140 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1141 if (ret != LDB_SUCCESS) {
1145 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1146 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1148 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1149 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1150 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1151 ldb_asprintf_errstring(ldb,
1152 "setup_supplemental_field: "
1153 "failed to push package_PrimaryKerberosNeverBlob: %s",
1155 return LDB_ERR_OPERATIONS_ERROR;
1157 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1160 return LDB_ERR_OPERATIONS_ERROR;
1162 pkn->name = "Primary:Kerberos-Newer-Keys";
1164 pkn->data = pknb_hexstr;
1168 * setup 'Primary:Kerberos' element
1172 ret = setup_primary_kerberos(io, old_scb, &pkb);
1173 if (ret != LDB_SUCCESS) {
1177 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1178 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1180 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1181 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1182 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1183 ldb_asprintf_errstring(ldb,
1184 "setup_supplemental_field: "
1185 "failed to push package_PrimaryKerberosBlob: %s",
1187 return LDB_ERR_OPERATIONS_ERROR;
1189 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1192 return LDB_ERR_OPERATIONS_ERROR;
1194 pk->name = "Primary:Kerberos";
1196 pk->data = pkb_hexstr;
1199 * setup 'Primary:WDigest' element
1203 ret = setup_primary_wdigest(io, old_scb, &pdb);
1204 if (ret != LDB_SUCCESS) {
1208 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1209 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1211 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1212 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1213 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1214 ldb_asprintf_errstring(ldb,
1215 "setup_supplemental_field: "
1216 "failed to push package_PrimaryWDigestBlob: %s",
1218 return LDB_ERR_OPERATIONS_ERROR;
1220 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1223 return LDB_ERR_OPERATIONS_ERROR;
1225 pd->name = "Primary:WDigest";
1227 pd->data = pdb_hexstr;
1230 * setup 'Primary:CLEARTEXT' element
1235 pcb.cleartext = *io->n.cleartext_utf16;
1237 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1238 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1240 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1241 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1242 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1243 ldb_asprintf_errstring(ldb,
1244 "setup_supplemental_field: "
1245 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1247 return LDB_ERR_OPERATIONS_ERROR;
1249 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1252 return LDB_ERR_OPERATIONS_ERROR;
1254 pc->name = "Primary:CLEARTEXT";
1256 pc->data = pcb_hexstr;
1260 * setup 'Packages' element
1263 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1264 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1266 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1267 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1268 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1269 ldb_asprintf_errstring(ldb,
1270 "setup_supplemental_field: "
1271 "failed to push package_PackagesBlob: %s",
1273 return LDB_ERR_OPERATIONS_ERROR;
1275 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1278 return LDB_ERR_OPERATIONS_ERROR;
1280 pp->name = "Packages";
1282 pp->data = pb_hexstr;
1285 * setup 'supplementalCredentials' value
1288 scb.sub.num_packages = num_packages;
1289 scb.sub.packages = packages;
1291 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1292 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1294 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1295 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1296 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1297 ldb_asprintf_errstring(ldb,
1298 "setup_supplemental_field: "
1299 "failed to push supplementalCredentialsBlob: %s",
1301 return LDB_ERR_OPERATIONS_ERROR;
1307 static int setup_last_set_field(struct setup_password_fields_io *io)
1310 unix_to_nt_time(&io->g.last_set, time(NULL));
1315 static int setup_password_fields(struct setup_password_fields_io *io)
1317 struct ldb_context *ldb;
1320 size_t converted_pw_len;
1322 ldb = ldb_module_get_ctx(io->ac->module);
1325 * refuse the change if someone want to change the cleartext
1326 * and supply his own hashes at the same time...
1328 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16) && (io->n.nt_hash || io->n.lm_hash)) {
1329 ldb_asprintf_errstring(ldb,
1330 "setup_password_fields: "
1331 "it's only allowed to set the cleartext password or the password hashes");
1332 return LDB_ERR_UNWILLING_TO_PERFORM;
1335 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
1336 ldb_asprintf_errstring(ldb,
1337 "setup_password_fields: "
1338 "it's only allowed to set the cleartext password as userPassword or clearTextPasssword, not both at once");
1339 return LDB_ERR_UNWILLING_TO_PERFORM;
1342 if (io->n.cleartext_utf8) {
1343 char **cleartext_utf16_str;
1344 struct ldb_val *cleartext_utf16_blob;
1345 io->n.cleartext_utf16 = cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1346 if (!io->n.cleartext_utf16) {
1348 return LDB_ERR_OPERATIONS_ERROR;
1350 if (!convert_string_talloc_convenience(io->ac,
1351 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1353 io->n.cleartext_utf8->data,
1354 io->n.cleartext_utf8->length,
1355 (void **)&cleartext_utf16_str,
1356 &converted_pw_len, false)) {
1357 ldb_asprintf_errstring(ldb,
1358 "setup_password_fields: "
1359 "failed to generate UTF16 password from cleartext UTF8 password");
1360 return LDB_ERR_OPERATIONS_ERROR;
1362 *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str,
1364 } else if (io->n.cleartext_utf16) {
1365 char *cleartext_utf8_str;
1366 struct ldb_val *cleartext_utf8_blob;
1367 io->n.cleartext_utf8 = cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1368 if (!io->n.cleartext_utf8) {
1370 return LDB_ERR_OPERATIONS_ERROR;
1372 if (!convert_string_talloc_convenience(io->ac,
1373 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1374 CH_UTF16MUNGED, CH_UTF8,
1375 io->n.cleartext_utf16->data,
1376 io->n.cleartext_utf16->length,
1377 (void **)&cleartext_utf8_str,
1378 &converted_pw_len, false)) {
1379 /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
1380 io->n.cleartext_utf8 = NULL;
1381 talloc_free(cleartext_utf8_blob);
1383 *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str,
1386 if (io->n.cleartext_utf16) {
1387 struct samr_Password *nt_hash;
1388 nt_hash = talloc(io->ac, struct samr_Password);
1391 return LDB_ERR_OPERATIONS_ERROR;
1393 io->n.nt_hash = nt_hash;
1395 /* compute the new nt hash */
1396 mdfour(nt_hash->hash, io->n.cleartext_utf16->data,
1397 io->n.cleartext_utf16->length);
1400 if (io->n.cleartext_utf8) {
1401 struct samr_Password *lm_hash;
1402 char *cleartext_unix;
1403 if (lp_lanman_auth(ldb_get_opaque(ldb, "loadparm")) &&
1404 convert_string_talloc_convenience(io->ac,
1405 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1407 io->n.cleartext_utf8->data,
1408 io->n.cleartext_utf8->length,
1409 (void **)&cleartext_unix,
1410 &converted_pw_len, false)) {
1411 lm_hash = talloc(io->ac, struct samr_Password);
1414 return LDB_ERR_OPERATIONS_ERROR;
1417 /* compute the new lm hash */
1418 ok = E_deshash((char *)cleartext_unix, lm_hash->hash);
1420 io->n.lm_hash = lm_hash;
1422 talloc_free(lm_hash->hash);
1426 ret = setup_kerberos_keys(io);
1427 if (ret != LDB_SUCCESS) {
1432 ret = setup_nt_fields(io);
1433 if (ret != LDB_SUCCESS) {
1437 ret = setup_lm_fields(io);
1438 if (ret != LDB_SUCCESS) {
1442 ret = setup_supplemental_field(io);
1443 if (ret != LDB_SUCCESS) {
1447 ret = setup_last_set_field(io);
1448 if (ret != LDB_SUCCESS) {
1455 static int setup_io(struct ph_context *ac,
1456 const struct ldb_message *orig_msg,
1457 const struct ldb_message *searched_msg,
1458 struct setup_password_fields_io *io)
1460 const struct ldb_val *quoted_utf16;
1461 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1465 /* Some operations below require kerberos contexts */
1466 if (smb_krb5_init_context(ac,
1467 ldb_get_event_context(ldb),
1468 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
1469 &io->smb_krb5_context) != 0) {
1470 return LDB_ERR_OPERATIONS_ERROR;
1474 io->domain = ac->domain;
1476 io->u.userAccountControl = samdb_result_uint(searched_msg, "userAccountControl", 0);
1477 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
1478 io->u.sAMAccountName = samdb_result_string(searched_msg, "sAMAccountName", NULL);
1479 io->u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
1480 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
1482 if (io->u.sAMAccountName == NULL) {
1483 ldb_asprintf_errstring(ldb,
1484 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
1485 ldb_dn_get_linearized(searched_msg->dn));
1487 return LDB_ERR_CONSTRAINT_VIOLATION;
1490 io->n.cleartext_utf8 = ldb_msg_find_ldb_val(orig_msg, "userPassword");
1491 io->n.cleartext_utf16 = ldb_msg_find_ldb_val(orig_msg, "clearTextPassword");
1493 /* this rather strange looking piece of code is there to
1494 handle a ldap client setting a password remotely using the
1495 unicodePwd ldap field. The syntax is that the password is
1496 in UTF-16LE, with a " at either end. Unfortunately the
1497 unicodePwd field is also used to store the nt hashes
1498 internally in Samba, and is used in the nt hash format on
1499 the wire in DRS replication, so we have a single name for
1500 two distinct values. The code below leaves us with a small
1501 chance (less than 1 in 2^32) of a mixup, if someone manages
1502 to create a MD4 hash which starts and ends in 0x22 0x00, as
1503 that would then be treated as a UTF16 password rather than
1505 quoted_utf16 = ldb_msg_find_ldb_val(orig_msg, "unicodePwd");
1507 quoted_utf16->length >= 4 &&
1508 quoted_utf16->data[0] == '"' &&
1509 quoted_utf16->data[1] == 0 &&
1510 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
1511 quoted_utf16->data[quoted_utf16->length-1] == 0) {
1512 io->n.quoted_utf16.data = talloc_memdup(io->ac, quoted_utf16->data+2, quoted_utf16->length-4);
1513 io->n.quoted_utf16.length = quoted_utf16->length-4;
1514 io->n.cleartext_utf16 = &io->n.quoted_utf16;
1515 io->n.nt_hash = NULL;
1517 io->n.nt_hash = samdb_result_hash(io->ac, orig_msg, "unicodePwd");
1520 io->n.lm_hash = samdb_result_hash(io->ac, orig_msg, "dBCSPwd");
1525 static struct ph_context *ph_init_context(struct ldb_module *module,
1526 struct ldb_request *req)
1528 struct ldb_context *ldb;
1529 struct ph_context *ac;
1531 ldb = ldb_module_get_ctx(module);
1533 ac = talloc_zero(req, struct ph_context);
1535 ldb_set_errstring(ldb, "Out of Memory");
1539 ac->module = module;
1545 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
1547 struct ph_context *ac;
1549 ac = talloc_get_type(req->context, struct ph_context);
1552 return ldb_module_done(ac->req, NULL, NULL,
1553 LDB_ERR_OPERATIONS_ERROR);
1556 if (ares->type == LDB_REPLY_REFERRAL) {
1557 return ldb_module_send_referral(ac->req, ares->referral);
1560 if (ares->error != LDB_SUCCESS) {
1561 return ldb_module_done(ac->req, ares->controls,
1562 ares->response, ares->error);
1565 if (ares->type != LDB_REPLY_DONE) {
1567 return ldb_module_done(ac->req, NULL, NULL,
1568 LDB_ERR_OPERATIONS_ERROR);
1571 return ldb_module_done(ac->req, ares->controls,
1572 ares->response, ares->error);
1575 static int password_hash_add_do_add(struct ph_context *ac);
1576 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
1577 static int password_hash_mod_search_self(struct ph_context *ac);
1578 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
1579 static int password_hash_mod_do_mod(struct ph_context *ac);
1581 static int get_domain_data_callback(struct ldb_request *req,
1582 struct ldb_reply *ares)
1584 struct ldb_context *ldb;
1585 struct domain_data *data;
1586 struct ph_context *ac;
1587 struct loadparm_context *lp_ctx;
1590 ac = talloc_get_type(req->context, struct ph_context);
1591 ldb = ldb_module_get_ctx(ac->module);
1594 ret = LDB_ERR_OPERATIONS_ERROR;
1597 if (ares->error != LDB_SUCCESS) {
1598 return ldb_module_done(ac->req, ares->controls,
1599 ares->response, ares->error);
1602 switch (ares->type) {
1603 case LDB_REPLY_ENTRY:
1604 if (ac->domain != NULL) {
1607 ldb_set_errstring(ldb, "Too many results");
1608 ret = LDB_ERR_OPERATIONS_ERROR;
1612 data = talloc_zero(ac, struct domain_data);
1617 ret = LDB_ERR_OPERATIONS_ERROR;
1621 data->pwdProperties = samdb_result_uint(ares->message, "pwdProperties", -1);
1622 data->pwdHistoryLength = samdb_result_uint(ares->message, "pwdHistoryLength", -1);
1623 data->maxPwdAge = samdb_result_int64(ares->message, "maxPwdAge", -1);
1624 data->minPwdAge = samdb_result_int64(ares->message, "minPwdAge", -1);
1625 data->minPwdLength = samdb_result_uint(ares->message, "minPwdLength", -1);
1626 data->store_cleartext =
1627 data->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
1631 /* For a domain DN, this puts things in dotted notation */
1632 /* For builtin domains, this will give details for the host,
1633 * but that doesn't really matter, as it's just used for salt
1634 * and kerberos principals, which don't exist here */
1636 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1637 struct loadparm_context);
1639 data->dns_domain = lp_dnsdomain(lp_ctx);
1640 data->realm = lp_realm(lp_ctx);
1641 data->netbios_domain = lp_sam_name(lp_ctx);
1648 case LDB_REPLY_REFERRAL:
1654 case LDB_REPLY_DONE:
1656 /* call the next step */
1657 switch (ac->req->operation) {
1659 ret = password_hash_add_do_add(ac);
1663 ret = password_hash_mod_do_mod(ac);
1667 ret = LDB_ERR_OPERATIONS_ERROR;
1675 if (ret != LDB_SUCCESS) {
1676 return ldb_module_done(ac->req, NULL, NULL, ret);
1682 static int build_domain_data_request(struct ph_context *ac)
1684 /* attrs[] is returned from this function in
1685 ac->dom_req->op.search.attrs, so it must be static, as
1686 otherwise the compiler can put it on the stack */
1687 struct ldb_context *ldb;
1688 static const char * const attrs[] = { "pwdProperties",
1695 ldb = ldb_module_get_ctx(ac->module);
1697 return ldb_build_search_req(&ac->dom_req, ldb, ac,
1698 ldb_get_default_basedn(ldb),
1702 ac, get_domain_data_callback,
1706 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
1708 struct ldb_context *ldb;
1709 struct ph_context *ac;
1710 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
1714 ldb = ldb_module_get_ctx(module);
1716 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
1718 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
1719 return ldb_next_request(module, req);
1722 /* If the caller is manipulating the local passwords directly, let them pass */
1723 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1724 req->op.add.message->dn) == 0) {
1725 return ldb_next_request(module, req);
1728 /* nobody must touch password histories and 'supplementalCredentials' */
1729 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
1730 return LDB_ERR_UNWILLING_TO_PERFORM;
1732 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
1733 return LDB_ERR_UNWILLING_TO_PERFORM;
1735 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
1736 return LDB_ERR_UNWILLING_TO_PERFORM;
1739 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
1740 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
1742 userPasswordAttr = ldb_msg_find_element(req->op.add.message, "userPassword");
1743 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
1744 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
1745 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
1747 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
1748 return ldb_next_request(module, req);
1751 if (userPasswordAttr && (userPasswordAttr->num_values != 1)) {
1752 ldb_set_errstring(ldb, "'userPassword' must have exactly one value!");
1753 return LDB_ERR_CONSTRAINT_VIOLATION;
1755 if (clearTextPasswordAttr && (clearTextPasswordAttr->num_values != 1)) {
1756 ldb_set_errstring(ldb, "'clearTextPassword' must have exactly one value!");
1757 return LDB_ERR_CONSTRAINT_VIOLATION;
1759 if (ntAttr && (ntAttr->num_values != 1)) {
1760 ldb_set_errstring(ldb, "'unicodePwd' must have exactly one value!");
1761 return LDB_ERR_CONSTRAINT_VIOLATION;
1763 if (lmAttr && (lmAttr->num_values != 1)) {
1764 ldb_set_errstring(ldb, "'dBCSPwd' must have exactly one value!");
1765 return LDB_ERR_CONSTRAINT_VIOLATION;
1768 /* Make sure we are performing the password set action on a (for us)
1769 * valid object. Those are instances of either "user" and/or
1770 * "inetOrgPerson". Otherwise continue with the submodules. */
1771 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
1772 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
1774 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
1775 ldb_set_errstring(ldb,
1776 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
1777 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1780 return ldb_next_request(module, req);
1783 ac = ph_init_context(module, req);
1785 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1786 return LDB_ERR_OPERATIONS_ERROR;
1789 /* get user domain data */
1790 ret = build_domain_data_request(ac);
1791 if (ret != LDB_SUCCESS) {
1795 return ldb_next_request(module, ac->dom_req);
1798 static int password_hash_add_do_add(struct ph_context *ac)
1800 struct ldb_context *ldb;
1801 struct ldb_request *down_req;
1802 struct ldb_message *msg;
1803 struct setup_password_fields_io io;
1806 /* Prepare the internal data structure containing the passwords */
1807 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
1808 if (ret != LDB_SUCCESS) {
1812 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
1814 return LDB_ERR_OPERATIONS_ERROR;
1817 /* remove attributes that we just read into 'io' */
1818 ldb_msg_remove_attr(msg, "userPassword");
1819 ldb_msg_remove_attr(msg, "clearTextPassword");
1820 ldb_msg_remove_attr(msg, "unicodePwd");
1821 ldb_msg_remove_attr(msg, "dBCSPwd");
1822 ldb_msg_remove_attr(msg, "pwdLastSet");
1824 ldb = ldb_module_get_ctx(ac->module);
1826 ret = setup_password_fields(&io);
1827 if (ret != LDB_SUCCESS) {
1832 ret = samdb_msg_add_hash(ldb, ac, msg,
1833 "unicodePwd", io.g.nt_hash);
1834 if (ret != LDB_SUCCESS) {
1839 ret = samdb_msg_add_hash(ldb, ac, msg,
1840 "dBCSPwd", io.g.lm_hash);
1841 if (ret != LDB_SUCCESS) {
1845 if (io.g.nt_history_len > 0) {
1846 ret = samdb_msg_add_hashes(ac, msg,
1849 io.g.nt_history_len);
1850 if (ret != LDB_SUCCESS) {
1854 if (io.g.lm_history_len > 0) {
1855 ret = samdb_msg_add_hashes(ac, msg,
1858 io.g.lm_history_len);
1859 if (ret != LDB_SUCCESS) {
1863 if (io.g.supplemental.length > 0) {
1864 ret = ldb_msg_add_value(msg, "supplementalCredentials",
1865 &io.g.supplemental, NULL);
1866 if (ret != LDB_SUCCESS) {
1870 ret = samdb_msg_add_uint64(ldb, ac, msg,
1873 if (ret != LDB_SUCCESS) {
1877 ret = ldb_build_add_req(&down_req, ldb, ac,
1882 if (ret != LDB_SUCCESS) {
1886 return ldb_next_request(ac->module, down_req);
1889 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
1891 struct ldb_context *ldb;
1892 struct ph_context *ac;
1893 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
1895 struct ldb_message *msg;
1896 struct ldb_request *down_req;
1899 ldb = ldb_module_get_ctx(module);
1901 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
1903 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
1904 return ldb_next_request(module, req);
1907 /* If the caller is manipulating the local passwords directly, let them pass */
1908 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1909 req->op.mod.message->dn) == 0) {
1910 return ldb_next_request(module, req);
1913 /* nobody must touch password histories and 'supplementalCredentials' */
1914 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
1915 return LDB_ERR_UNWILLING_TO_PERFORM;
1917 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
1918 return LDB_ERR_UNWILLING_TO_PERFORM;
1920 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
1921 return LDB_ERR_UNWILLING_TO_PERFORM;
1924 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
1925 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
1926 * For password changes/set there should be a 'delete' or a 'modify'
1927 * on these attributes. */
1929 userPasswordAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
1930 clearTextPasswordAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
1931 ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
1932 lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
1934 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
1935 return ldb_next_request(module, req);
1938 if (userPasswordAttr && (userPasswordAttr->num_values != 1)) {
1939 ldb_set_errstring(ldb, "'userPassword' must have exactly one value!");
1940 return LDB_ERR_CONSTRAINT_VIOLATION;
1942 if (clearTextPasswordAttr && (clearTextPasswordAttr->num_values != 1)) {
1943 ldb_set_errstring(ldb, "'clearTextPassword' must have exactly one value!");
1944 return LDB_ERR_CONSTRAINT_VIOLATION;
1946 if (ntAttr && (ntAttr->num_values != 1)) {
1947 ldb_set_errstring(ldb, "'unicodePwd' must have exactly one value!");
1948 return LDB_ERR_CONSTRAINT_VIOLATION;
1950 if (lmAttr && (lmAttr->num_values != 1)) {
1951 ldb_set_errstring(ldb, "'dBCSPwd' must have exactly one value!");
1952 return LDB_ERR_CONSTRAINT_VIOLATION;
1955 ac = ph_init_context(module, req);
1957 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1958 return LDB_ERR_OPERATIONS_ERROR;
1961 /* use a new message structure so that we can modify it */
1962 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
1965 return LDB_ERR_OPERATIONS_ERROR;
1968 /* - remove any modification to the password from the first commit
1969 * we will make the real modification later */
1970 if (userPasswordAttr) ldb_msg_remove_attr(msg, "userPassword");
1971 if (clearTextPasswordAttr) ldb_msg_remove_attr(msg, "clearTextPassword");
1972 if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd");
1973 if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd");
1975 /* if there was nothing else to be modified skip to next step */
1976 if (msg->num_elements == 0) {
1977 return password_hash_mod_search_self(ac);
1980 ret = ldb_build_mod_req(&down_req, ldb, ac,
1983 ac, ph_modify_callback,
1985 if (ret != LDB_SUCCESS) {
1989 return ldb_next_request(module, down_req);
1992 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
1994 struct ph_context *ac;
1996 ac = talloc_get_type(req->context, struct ph_context);
1999 return ldb_module_done(ac->req, NULL, NULL,
2000 LDB_ERR_OPERATIONS_ERROR);
2003 if (ares->type == LDB_REPLY_REFERRAL) {
2004 return ldb_module_send_referral(ac->req, ares->referral);
2007 if (ares->error != LDB_SUCCESS) {
2008 return ldb_module_done(ac->req, ares->controls,
2009 ares->response, ares->error);
2012 if (ares->type != LDB_REPLY_DONE) {
2014 return ldb_module_done(ac->req, NULL, NULL,
2015 LDB_ERR_OPERATIONS_ERROR);
2020 return password_hash_mod_search_self(ac);
2023 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
2025 struct ldb_context *ldb;
2026 struct ph_context *ac;
2029 ac = talloc_get_type(req->context, struct ph_context);
2030 ldb = ldb_module_get_ctx(ac->module);
2033 ret = LDB_ERR_OPERATIONS_ERROR;
2036 if (ares->error != LDB_SUCCESS) {
2037 return ldb_module_done(ac->req, ares->controls,
2038 ares->response, ares->error);
2041 /* we are interested only in the single reply (base search) */
2042 switch (ares->type) {
2043 case LDB_REPLY_ENTRY:
2044 /* Make sure we are performing the password change action on a
2045 * (for us) valid object. Those are instances of either "user"
2046 * and/or "inetOrgPerson". Otherwise continue with the
2048 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
2049 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
2052 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
2053 ldb_set_errstring(ldb,
2054 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2055 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
2059 ret = ldb_next_request(ac->module, ac->req);
2063 if (ac->search_res != NULL) {
2066 ldb_set_errstring(ldb, "Too many results");
2067 ret = LDB_ERR_OPERATIONS_ERROR;
2071 ac->search_res = talloc_steal(ac, ares);
2075 case LDB_REPLY_REFERRAL:
2076 /* ignore anything else for now */
2081 case LDB_REPLY_DONE:
2084 /* get user domain data */
2085 ret = build_domain_data_request(ac);
2086 if (ret != LDB_SUCCESS) {
2087 return ldb_module_done(ac->req, NULL, NULL, ret);
2090 ret = ldb_next_request(ac->module, ac->dom_req);
2095 if (ret != LDB_SUCCESS) {
2096 return ldb_module_done(ac->req, NULL, NULL, ret);
2102 static int password_hash_mod_search_self(struct ph_context *ac)
2104 struct ldb_context *ldb;
2105 static const char * const attrs[] = { "objectClass",
2106 "userAccountControl",
2110 "userPrincipalName",
2111 "supplementalCredentials",
2117 struct ldb_request *search_req;
2120 ldb = ldb_module_get_ctx(ac->module);
2122 ret = ldb_build_search_req(&search_req, ldb, ac,
2123 ac->req->op.mod.message->dn,
2128 ac, ph_mod_search_callback,
2131 if (ret != LDB_SUCCESS) {
2135 return ldb_next_request(ac->module, search_req);
2138 static int password_hash_mod_do_mod(struct ph_context *ac)
2140 struct ldb_context *ldb;
2141 struct ldb_request *mod_req;
2142 struct ldb_message *msg;
2143 const struct ldb_message *orig_msg, *searched_msg;
2144 struct setup_password_fields_io io;
2147 ldb = ldb_module_get_ctx(ac->module);
2149 /* use a new message structure so that we can modify it */
2150 msg = ldb_msg_new(ac);
2152 return LDB_ERR_OPERATIONS_ERROR;
2156 msg->dn = ac->req->op.mod.message->dn;
2158 orig_msg = ac->req->op.mod.message;
2159 searched_msg = ac->search_res->message;
2161 /* Prepare the internal data structure containing the passwords */
2162 ret = setup_io(ac, orig_msg, searched_msg, &io);
2163 if (ret != LDB_SUCCESS) {
2167 /* Fill in some final details (only relevent once the password has been set) */
2168 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
2169 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
2170 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
2172 ret = setup_password_fields(&io);
2173 if (ret != LDB_SUCCESS) {
2177 /* make sure we replace all the old attributes */
2178 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
2179 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
2180 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2181 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2182 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
2183 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
2186 ret = samdb_msg_add_hash(ldb, ac, msg,
2187 "unicodePwd", io.g.nt_hash);
2188 if (ret != LDB_SUCCESS) {
2193 ret = samdb_msg_add_hash(ldb, ac, msg,
2194 "dBCSPwd", io.g.lm_hash);
2195 if (ret != LDB_SUCCESS) {
2199 if (io.g.nt_history_len > 0) {
2200 ret = samdb_msg_add_hashes(ac, msg,
2203 io.g.nt_history_len);
2204 if (ret != LDB_SUCCESS) {
2208 if (io.g.lm_history_len > 0) {
2209 ret = samdb_msg_add_hashes(ac, msg,
2212 io.g.lm_history_len);
2213 if (ret != LDB_SUCCESS) {
2217 if (io.g.supplemental.length > 0) {
2218 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2219 &io.g.supplemental, NULL);
2220 if (ret != LDB_SUCCESS) {
2224 ret = samdb_msg_add_uint64(ldb, ac, msg,
2227 if (ret != LDB_SUCCESS) {
2231 ret = ldb_build_mod_req(&mod_req, ldb, ac,
2236 if (ret != LDB_SUCCESS) {
2240 return ldb_next_request(ac->module, mod_req);
2243 _PUBLIC_ const struct ldb_module_ops ldb_password_hash_module_ops = {
2244 .name = "password_hash",
2245 .add = password_hash_add,
2246 .modify = password_hash_modify