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-2010
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 "ldb_module.h"
37 #include "libcli/auth/libcli_auth.h"
38 #include "system/kerberos.h"
39 #include "auth/kerberos/kerberos.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/samdb/ldb_modules/util.h"
42 #include "dsdb/samdb/ldb_modules/password_modules.h"
43 #include "librpc/gen_ndr/ndr_drsblobs.h"
44 #include "../lib/crypto/crypto.h"
45 #include "param/param.h"
47 /* If we have decided there is a reason to work on this request, then
48 * setup all the password hash types correctly.
50 * If we haven't the hashes yet but the password given as plain-text (attributes
51 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
52 * the constraints. Once this is done, we calculate the password hashes.
54 * Notice: unlike the real AD which only supports the UTF16 special based
55 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
56 * understand also a UTF16 based 'clearTextPassword' one.
57 * The latter is also accessible through LDAP so it can also be set by external
58 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
60 * Also when the module receives only the password hashes (possible through
61 * specifying an internal LDB control - for security reasons) some checks are
62 * performed depending on the operation mode (see below) (e.g. if the password
63 * has been in use before if the password memory policy was activated).
65 * Attention: There is a difference between "modify" and "reset" operations
66 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
67 * operation for a password attribute we thread this as a "modify"; if it sends
68 * only a "replace" one we have an (administrative) reset.
70 * Finally, if the administrator has requested that a password history
71 * be maintained, then this should also be written out.
75 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
76 * - Check for right connection encryption
79 /* Notice: Definition of "dsdb_control_password_change_status" moved into
83 struct ldb_module *module;
84 struct ldb_request *req;
86 struct ldb_request *dom_req;
87 struct ldb_reply *dom_res;
89 struct ldb_reply *search_res;
91 struct dsdb_control_password_change_status *status;
92 struct dsdb_control_password_change *change;
101 struct setup_password_fields_io {
102 struct ph_context *ac;
104 struct smb_krb5_context *smb_krb5_context;
106 /* infos about the user account */
108 uint32_t userAccountControl;
110 const char *sAMAccountName;
111 const char *user_principal_name;
113 uint32_t restrictions;
116 /* new credentials and old given credentials */
117 struct setup_password_fields_given {
118 const struct ldb_val *cleartext_utf8;
119 const struct ldb_val *cleartext_utf16;
120 struct samr_Password *nt_hash;
121 struct samr_Password *lm_hash;
124 /* old credentials */
126 struct samr_Password *nt_hash;
127 struct samr_Password *lm_hash;
128 uint32_t nt_history_len;
129 struct samr_Password *nt_history;
130 uint32_t lm_history_len;
131 struct samr_Password *lm_history;
132 const struct ldb_val *supplemental;
133 struct supplementalCredentialsBlob scb;
136 /* generated credentials */
138 struct samr_Password *nt_hash;
139 struct samr_Password *lm_hash;
140 uint32_t nt_history_len;
141 struct samr_Password *nt_history;
142 uint32_t lm_history_len;
143 struct samr_Password *lm_history;
149 struct ldb_val supplemental;
154 /* Get the NT hash, and fill it in as an entry in the password history,
155 and specify it into io->g.nt_hash */
157 static int setup_nt_fields(struct setup_password_fields_io *io)
159 struct ldb_context *ldb;
162 io->g.nt_hash = io->n.nt_hash;
163 ldb = ldb_module_get_ctx(io->ac->module);
165 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
169 /* We might not have an old NT password */
170 io->g.nt_history = talloc_array(io->ac,
171 struct samr_Password,
172 io->ac->status->domain_data.pwdHistoryLength);
173 if (!io->g.nt_history) {
177 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
178 io->o.nt_history_len); i++) {
179 io->g.nt_history[i+1] = io->o.nt_history[i];
181 io->g.nt_history_len = i + 1;
184 io->g.nt_history[0] = *io->g.nt_hash;
187 * TODO: is this correct?
188 * the simular behavior is correct for the lm history case
190 E_md4hash("", io->g.nt_history[0].hash);
196 /* Get the LANMAN hash, and fill it in as an entry in the password history,
197 and specify it into io->g.lm_hash */
199 static int setup_lm_fields(struct setup_password_fields_io *io)
201 struct ldb_context *ldb;
204 io->g.lm_hash = io->n.lm_hash;
205 ldb = ldb_module_get_ctx(io->ac->module);
207 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
211 /* We might not have an old LM password */
212 io->g.lm_history = talloc_array(io->ac,
213 struct samr_Password,
214 io->ac->status->domain_data.pwdHistoryLength);
215 if (!io->g.lm_history) {
219 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
220 io->o.lm_history_len); i++) {
221 io->g.lm_history[i+1] = io->o.lm_history[i];
223 io->g.lm_history_len = i + 1;
226 io->g.lm_history[0] = *io->g.lm_hash;
228 E_deshash("", io->g.lm_history[0].hash);
234 static int setup_kerberos_keys(struct setup_password_fields_io *io)
236 struct ldb_context *ldb;
237 krb5_error_code krb5_ret;
238 Principal *salt_principal;
241 krb5_data cleartext_data;
243 ldb = ldb_module_get_ctx(io->ac->module);
244 cleartext_data.data = io->n.cleartext_utf8->data;
245 cleartext_data.length = io->n.cleartext_utf8->length;
247 /* Many, many thanks to lukeh@padl.com for this
248 * algorithm, described in his Nov 10 2004 mail to
249 * samba-technical@samba.org */
252 * Determine a salting principal
254 if (io->u.is_computer) {
258 name = strlower_talloc(io->ac, io->u.sAMAccountName);
263 if (name[strlen(name)-1] == '$') {
264 name[strlen(name)-1] = '\0';
267 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
268 io->ac->status->domain_data.dns_domain);
273 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
275 io->ac->status->domain_data.realm,
276 "host", saltbody, NULL);
277 } else if (io->u.user_principal_name) {
278 char *user_principal_name;
281 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
282 if (!user_principal_name) {
286 p = strchr(user_principal_name, '@');
291 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
293 io->ac->status->domain_data.realm,
294 user_principal_name, NULL);
296 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
298 io->ac->status->domain_data.realm,
299 io->u.sAMAccountName, NULL);
302 ldb_asprintf_errstring(ldb,
303 "setup_kerberos_keys: "
304 "generation of a salting principal failed: %s",
305 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
307 return LDB_ERR_OPERATIONS_ERROR;
311 * create salt from salt_principal
313 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
314 salt_principal, &salt);
315 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
317 ldb_asprintf_errstring(ldb,
318 "setup_kerberos_keys: "
319 "generation of krb5_salt failed: %s",
320 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
322 return LDB_ERR_OPERATIONS_ERROR;
324 /* create a talloc copy */
325 io->g.salt = talloc_strndup(io->ac,
326 (char *)salt.saltvalue.data,
327 salt.saltvalue.length);
328 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
332 salt.saltvalue.data = discard_const(io->g.salt);
333 salt.saltvalue.length = strlen(io->g.salt);
336 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
337 * the salt and the cleartext password
339 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
340 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
345 ldb_asprintf_errstring(ldb,
346 "setup_kerberos_keys: "
347 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
348 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
350 return LDB_ERR_OPERATIONS_ERROR;
352 io->g.aes_256 = data_blob_talloc(io->ac,
354 key.keyvalue.length);
355 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
356 if (!io->g.aes_256.data) {
361 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
362 * the salt and the cleartext password
364 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
365 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
370 ldb_asprintf_errstring(ldb,
371 "setup_kerberos_keys: "
372 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
373 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
375 return LDB_ERR_OPERATIONS_ERROR;
377 io->g.aes_128 = data_blob_talloc(io->ac,
379 key.keyvalue.length);
380 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
381 if (!io->g.aes_128.data) {
386 * create ENCTYPE_DES_CBC_MD5 key out of
387 * the salt and the cleartext password
389 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
395 ldb_asprintf_errstring(ldb,
396 "setup_kerberos_keys: "
397 "generation of a des-cbc-md5 key failed: %s",
398 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
400 return LDB_ERR_OPERATIONS_ERROR;
402 io->g.des_md5 = data_blob_talloc(io->ac,
404 key.keyvalue.length);
405 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
406 if (!io->g.des_md5.data) {
411 * create ENCTYPE_DES_CBC_CRC key out of
412 * the salt and the cleartext password
414 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
420 ldb_asprintf_errstring(ldb,
421 "setup_kerberos_keys: "
422 "generation of a des-cbc-crc key failed: %s",
423 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
425 return LDB_ERR_OPERATIONS_ERROR;
427 io->g.des_crc = data_blob_talloc(io->ac,
429 key.keyvalue.length);
430 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
431 if (!io->g.des_crc.data) {
438 static int setup_primary_kerberos(struct setup_password_fields_io *io,
439 const struct supplementalCredentialsBlob *old_scb,
440 struct package_PrimaryKerberosBlob *pkb)
442 struct ldb_context *ldb;
443 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
444 struct supplementalCredentialsPackage *old_scp = NULL;
445 struct package_PrimaryKerberosBlob _old_pkb;
446 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
448 enum ndr_err_code ndr_err;
450 ldb = ldb_module_get_ctx(io->ac->module);
453 * prepare generation of keys
455 * ENCTYPE_DES_CBC_MD5
456 * ENCTYPE_DES_CBC_CRC
459 pkb3->salt.string = io->g.salt;
461 pkb3->keys = talloc_array(io->ac,
462 struct package_PrimaryKerberosKey3,
468 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
469 pkb3->keys[0].value = &io->g.des_md5;
470 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
471 pkb3->keys[1].value = &io->g.des_crc;
473 /* initialize the old keys to zero */
474 pkb3->num_old_keys = 0;
475 pkb3->old_keys = NULL;
477 /* if there're no old keys, then we're done */
482 for (i=0; i < old_scb->sub.num_packages; i++) {
483 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
487 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
491 old_scp = &old_scb->sub.packages[i];
494 /* Primary:Kerberos element of supplementalCredentials */
498 blob = strhex_to_data_blob(io->ac, old_scp->data);
503 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
504 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
505 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
506 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
507 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
508 ldb_asprintf_errstring(ldb,
509 "setup_primary_kerberos: "
510 "failed to pull old package_PrimaryKerberosBlob: %s",
512 return LDB_ERR_OPERATIONS_ERROR;
515 if (_old_pkb.version != 3) {
516 ldb_asprintf_errstring(ldb,
517 "setup_primary_kerberos: "
518 "package_PrimaryKerberosBlob version[%u] expected[3]",
520 return LDB_ERR_OPERATIONS_ERROR;
523 old_pkb3 = &_old_pkb.ctr.ctr3;
526 /* if we didn't found the old keys we're done */
531 /* fill in the old keys */
532 pkb3->num_old_keys = old_pkb3->num_keys;
533 pkb3->old_keys = old_pkb3->keys;
538 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
539 const struct supplementalCredentialsBlob *old_scb,
540 struct package_PrimaryKerberosBlob *pkb)
542 struct ldb_context *ldb;
543 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
544 struct supplementalCredentialsPackage *old_scp = NULL;
545 struct package_PrimaryKerberosBlob _old_pkb;
546 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
548 enum ndr_err_code ndr_err;
550 ldb = ldb_module_get_ctx(io->ac->module);
553 * prepare generation of keys
555 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
556 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
557 * ENCTYPE_DES_CBC_MD5
558 * ENCTYPE_DES_CBC_CRC
561 pkb4->salt.string = io->g.salt;
562 pkb4->default_iteration_count = 4096;
565 pkb4->keys = talloc_array(io->ac,
566 struct package_PrimaryKerberosKey4,
572 pkb4->keys[0].iteration_count = 4096;
573 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
574 pkb4->keys[0].value = &io->g.aes_256;
575 pkb4->keys[1].iteration_count = 4096;
576 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
577 pkb4->keys[1].value = &io->g.aes_128;
578 pkb4->keys[2].iteration_count = 4096;
579 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
580 pkb4->keys[2].value = &io->g.des_md5;
581 pkb4->keys[3].iteration_count = 4096;
582 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
583 pkb4->keys[3].value = &io->g.des_crc;
585 /* initialize the old keys to zero */
586 pkb4->num_old_keys = 0;
587 pkb4->old_keys = NULL;
588 pkb4->num_older_keys = 0;
589 pkb4->older_keys = NULL;
591 /* if there're no old keys, then we're done */
596 for (i=0; i < old_scb->sub.num_packages; i++) {
597 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
601 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
605 old_scp = &old_scb->sub.packages[i];
608 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
612 blob = strhex_to_data_blob(io->ac, old_scp->data);
617 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
618 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
620 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
621 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
622 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
623 ldb_asprintf_errstring(ldb,
624 "setup_primary_kerberos_newer: "
625 "failed to pull old package_PrimaryKerberosBlob: %s",
627 return LDB_ERR_OPERATIONS_ERROR;
630 if (_old_pkb.version != 4) {
631 ldb_asprintf_errstring(ldb,
632 "setup_primary_kerberos_newer: "
633 "package_PrimaryKerberosBlob version[%u] expected[4]",
635 return LDB_ERR_OPERATIONS_ERROR;
638 old_pkb4 = &_old_pkb.ctr.ctr4;
641 /* if we didn't found the old keys we're done */
646 /* fill in the old keys */
647 pkb4->num_old_keys = old_pkb4->num_keys;
648 pkb4->old_keys = old_pkb4->keys;
649 pkb4->num_older_keys = old_pkb4->num_old_keys;
650 pkb4->older_keys = old_pkb4->old_keys;
655 static int setup_primary_wdigest(struct setup_password_fields_io *io,
656 const struct supplementalCredentialsBlob *old_scb,
657 struct package_PrimaryWDigestBlob *pdb)
659 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
660 DATA_BLOB sAMAccountName;
661 DATA_BLOB sAMAccountName_l;
662 DATA_BLOB sAMAccountName_u;
663 const char *user_principal_name = io->u.user_principal_name;
664 DATA_BLOB userPrincipalName;
665 DATA_BLOB userPrincipalName_l;
666 DATA_BLOB userPrincipalName_u;
667 DATA_BLOB netbios_domain;
668 DATA_BLOB netbios_domain_l;
669 DATA_BLOB netbios_domain_u;
670 DATA_BLOB dns_domain;
671 DATA_BLOB dns_domain_l;
672 DATA_BLOB dns_domain_u;
684 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
685 * for what precalculated hashes are supposed to be stored...
687 * I can't reproduce all values which should contain "Digest" as realm,
688 * am I doing something wrong or is w2k3 just broken...?
690 * W2K3 fills in following for a user:
692 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
693 * sAMAccountName: NewUser2Sam
694 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
696 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
697 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
698 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
699 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
700 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
701 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
702 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
703 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
704 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
705 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
706 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
707 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
708 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
709 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
710 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
711 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
712 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
713 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
714 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
715 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
716 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
717 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
718 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
719 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
720 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
721 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
722 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
723 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
724 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
726 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
727 * sAMAccountName: NewUser2Sam
729 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
730 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
731 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
732 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
733 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
734 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
735 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
736 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
737 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
738 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
739 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
740 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
741 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
742 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
743 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
744 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
745 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
746 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
747 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
748 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
749 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
750 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
751 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
752 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
753 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
754 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
755 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
756 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
757 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
761 * sAMAccountName, netbios_domain
764 .user = &sAMAccountName,
765 .realm = &netbios_domain,
768 .user = &sAMAccountName_l,
769 .realm = &netbios_domain_l,
772 .user = &sAMAccountName_u,
773 .realm = &netbios_domain_u,
776 .user = &sAMAccountName,
777 .realm = &netbios_domain_u,
780 .user = &sAMAccountName,
781 .realm = &netbios_domain_l,
784 .user = &sAMAccountName_u,
785 .realm = &netbios_domain_l,
788 .user = &sAMAccountName_l,
789 .realm = &netbios_domain_u,
792 * sAMAccountName, dns_domain
795 .user = &sAMAccountName,
796 .realm = &dns_domain,
799 .user = &sAMAccountName_l,
800 .realm = &dns_domain_l,
803 .user = &sAMAccountName_u,
804 .realm = &dns_domain_u,
807 .user = &sAMAccountName,
808 .realm = &dns_domain_u,
811 .user = &sAMAccountName,
812 .realm = &dns_domain_l,
815 .user = &sAMAccountName_u,
816 .realm = &dns_domain_l,
819 .user = &sAMAccountName_l,
820 .realm = &dns_domain_u,
823 * userPrincipalName, no realm
826 .user = &userPrincipalName,
830 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
831 * the fallback to the sAMAccountName based userPrincipalName is correct
833 .user = &userPrincipalName_l,
836 .user = &userPrincipalName_u,
839 * nt4dom\sAMAccountName, no realm
842 .user = &sAMAccountName,
843 .nt4dom = &netbios_domain
846 .user = &sAMAccountName_l,
847 .nt4dom = &netbios_domain_l
850 .user = &sAMAccountName_u,
851 .nt4dom = &netbios_domain_u
855 * the following ones are guessed depending on the technet2 article
856 * but not reproducable on a w2k3 server
858 /* sAMAccountName with "Digest" realm */
860 .user = &sAMAccountName,
864 .user = &sAMAccountName_l,
868 .user = &sAMAccountName_u,
871 /* userPrincipalName with "Digest" realm */
873 .user = &userPrincipalName,
877 .user = &userPrincipalName_l,
881 .user = &userPrincipalName_u,
884 /* nt4dom\\sAMAccountName with "Digest" realm */
886 .user = &sAMAccountName,
887 .nt4dom = &netbios_domain,
891 .user = &sAMAccountName_l,
892 .nt4dom = &netbios_domain_l,
896 .user = &sAMAccountName_u,
897 .nt4dom = &netbios_domain_u,
902 /* prepare DATA_BLOB's used in the combinations array */
903 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
904 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
905 if (!sAMAccountName_l.data) {
908 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
909 if (!sAMAccountName_u.data) {
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->ac->status->domain_data.dns_domain);
918 if (!user_principal_name) {
922 userPrincipalName = data_blob_string_const(user_principal_name);
923 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
924 if (!userPrincipalName_l.data) {
927 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
928 if (!userPrincipalName_u.data) {
932 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
933 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
934 io->ac->status->domain_data.netbios_domain));
935 if (!netbios_domain_l.data) {
938 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
939 io->ac->status->domain_data.netbios_domain));
940 if (!netbios_domain_u.data) {
944 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
945 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
946 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
948 digest = data_blob_string_const("Digest");
950 delim = data_blob_string_const(":");
951 backslash = data_blob_string_const("\\");
953 pdb->num_hashes = ARRAY_SIZE(wdigest);
954 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
960 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
961 struct MD5Context md5;
963 if (wdigest[i].nt4dom) {
964 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
965 MD5Update(&md5, backslash.data, backslash.length);
967 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
968 MD5Update(&md5, delim.data, delim.length);
969 if (wdigest[i].realm) {
970 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
972 MD5Update(&md5, delim.data, delim.length);
973 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
974 MD5Final(pdb->hashes[i].hash, &md5);
980 static int setup_supplemental_field(struct setup_password_fields_io *io)
982 struct ldb_context *ldb;
983 struct supplementalCredentialsBlob scb;
984 struct supplementalCredentialsBlob _old_scb;
985 struct supplementalCredentialsBlob *old_scb = NULL;
986 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
987 uint32_t num_names = 0;
988 const char *names[1+4];
989 uint32_t num_packages = 0;
990 struct supplementalCredentialsPackage packages[1+4];
992 struct supplementalCredentialsPackage *pp = NULL;
993 struct package_PackagesBlob pb;
996 /* Primary:Kerberos-Newer-Keys */
997 const char **nkn = NULL;
998 struct supplementalCredentialsPackage *pkn = NULL;
999 struct package_PrimaryKerberosBlob pknb;
1000 DATA_BLOB pknb_blob;
1002 /* Primary:Kerberos */
1003 const char **nk = NULL;
1004 struct supplementalCredentialsPackage *pk = NULL;
1005 struct package_PrimaryKerberosBlob pkb;
1008 /* Primary:WDigest */
1009 const char **nd = NULL;
1010 struct supplementalCredentialsPackage *pd = NULL;
1011 struct package_PrimaryWDigestBlob pdb;
1014 /* Primary:CLEARTEXT */
1015 const char **nc = NULL;
1016 struct supplementalCredentialsPackage *pc = NULL;
1017 struct package_PrimaryCLEARTEXTBlob pcb;
1021 enum ndr_err_code ndr_err;
1023 bool do_newer_keys = false;
1024 bool do_cleartext = false;
1026 ZERO_STRUCT(zero16);
1029 ldb = ldb_module_get_ctx(io->ac->module);
1031 if (!io->n.cleartext_utf8) {
1033 * when we don't have a cleartext password
1034 * we can't setup a supplementalCredential value
1039 /* if there's an old supplementaCredentials blob then parse it */
1040 if (io->o.supplemental) {
1041 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1043 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1044 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1045 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1046 ldb_asprintf_errstring(ldb,
1047 "setup_supplemental_field: "
1048 "failed to pull old supplementalCredentialsBlob: %s",
1050 return LDB_ERR_OPERATIONS_ERROR;
1053 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1054 old_scb = &_old_scb;
1056 ldb_debug(ldb, LDB_DEBUG_ERROR,
1057 "setup_supplemental_field: "
1058 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1059 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1062 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1063 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1065 if (io->ac->status->domain_data.store_cleartext &&
1066 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1067 do_cleartext = true;
1071 * The ordering is this
1073 * Primary:Kerberos-Newer-Keys (optional)
1076 * Primary:CLEARTEXT (optional)
1078 * And the 'Packages' package is insert before the last
1081 if (do_newer_keys) {
1082 /* Primary:Kerberos-Newer-Keys */
1083 nkn = &names[num_names++];
1084 pkn = &packages[num_packages++];
1087 /* Primary:Kerberos */
1088 nk = &names[num_names++];
1089 pk = &packages[num_packages++];
1091 if (!do_cleartext) {
1093 pp = &packages[num_packages++];
1096 /* Primary:WDigest */
1097 nd = &names[num_names++];
1098 pd = &packages[num_packages++];
1102 pp = &packages[num_packages++];
1104 /* Primary:CLEARTEXT */
1105 nc = &names[num_names++];
1106 pc = &packages[num_packages++];
1111 * setup 'Primary:Kerberos-Newer-Keys' element
1113 *nkn = "Kerberos-Newer-Keys";
1115 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1116 if (ret != LDB_SUCCESS) {
1120 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1122 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1124 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1125 ldb_asprintf_errstring(ldb,
1126 "setup_supplemental_field: "
1127 "failed to push package_PrimaryKerberosNeverBlob: %s",
1129 return LDB_ERR_OPERATIONS_ERROR;
1131 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1133 return ldb_oom(ldb);
1135 pkn->name = "Primary:Kerberos-Newer-Keys";
1137 pkn->data = pknb_hexstr;
1141 * setup 'Primary:Kerberos' element
1145 ret = setup_primary_kerberos(io, old_scb, &pkb);
1146 if (ret != LDB_SUCCESS) {
1150 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1152 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1153 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1154 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1155 ldb_asprintf_errstring(ldb,
1156 "setup_supplemental_field: "
1157 "failed to push package_PrimaryKerberosBlob: %s",
1159 return LDB_ERR_OPERATIONS_ERROR;
1161 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1163 return ldb_oom(ldb);
1165 pk->name = "Primary:Kerberos";
1167 pk->data = pkb_hexstr;
1170 * setup 'Primary:WDigest' element
1174 ret = setup_primary_wdigest(io, old_scb, &pdb);
1175 if (ret != LDB_SUCCESS) {
1179 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1181 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1182 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1183 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1184 ldb_asprintf_errstring(ldb,
1185 "setup_supplemental_field: "
1186 "failed to push package_PrimaryWDigestBlob: %s",
1188 return LDB_ERR_OPERATIONS_ERROR;
1190 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1192 return ldb_oom(ldb);
1194 pd->name = "Primary:WDigest";
1196 pd->data = pdb_hexstr;
1199 * setup 'Primary:CLEARTEXT' element
1204 pcb.cleartext = *io->n.cleartext_utf16;
1206 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1208 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1209 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1210 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1211 ldb_asprintf_errstring(ldb,
1212 "setup_supplemental_field: "
1213 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1215 return LDB_ERR_OPERATIONS_ERROR;
1217 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1219 return ldb_oom(ldb);
1221 pc->name = "Primary:CLEARTEXT";
1223 pc->data = pcb_hexstr;
1227 * setup 'Packages' element
1230 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1232 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1233 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1234 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1235 ldb_asprintf_errstring(ldb,
1236 "setup_supplemental_field: "
1237 "failed to push package_PackagesBlob: %s",
1239 return LDB_ERR_OPERATIONS_ERROR;
1241 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1243 return ldb_oom(ldb);
1245 pp->name = "Packages";
1247 pp->data = pb_hexstr;
1250 * setup 'supplementalCredentials' value
1253 scb.sub.num_packages = num_packages;
1254 scb.sub.packages = packages;
1256 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1258 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1259 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1260 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1261 ldb_asprintf_errstring(ldb,
1262 "setup_supplemental_field: "
1263 "failed to push supplementalCredentialsBlob: %s",
1265 return LDB_ERR_OPERATIONS_ERROR;
1271 static int setup_last_set_field(struct setup_password_fields_io *io)
1274 unix_to_nt_time(&io->g.last_set, time(NULL));
1279 static int setup_given_passwords(struct setup_password_fields_io *io,
1280 struct setup_password_fields_given *g)
1282 struct ldb_context *ldb;
1285 ldb = ldb_module_get_ctx(io->ac->module);
1287 if (g->cleartext_utf8) {
1288 struct ldb_val *cleartext_utf16_blob;
1290 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1291 if (!cleartext_utf16_blob) {
1292 return ldb_oom(ldb);
1294 if (!convert_string_talloc(io->ac,
1296 g->cleartext_utf8->data,
1297 g->cleartext_utf8->length,
1298 (void *)&cleartext_utf16_blob->data,
1299 &cleartext_utf16_blob->length,
1301 talloc_free(cleartext_utf16_blob);
1302 ldb_asprintf_errstring(ldb,
1303 "setup_password_fields: "
1304 "failed to generate UTF16 password from cleartext UTF8 password for user %s", io->u.sAMAccountName);
1305 return LDB_ERR_CONSTRAINT_VIOLATION;
1307 g->cleartext_utf16 = cleartext_utf16_blob;
1309 } else if (g->cleartext_utf16) {
1310 char *cleartext_utf8_str;
1311 struct ldb_val *cleartext_utf8_blob;
1312 size_t converted_pw_len;
1314 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1315 if (!cleartext_utf8_blob) {
1316 return ldb_oom(ldb);
1318 if (!convert_string_talloc(io->ac,
1319 CH_UTF16MUNGED, CH_UTF8,
1320 g->cleartext_utf16->data,
1321 g->cleartext_utf16->length,
1322 (void *)&cleartext_utf8_str,
1323 &converted_pw_len, false)) {
1324 /* We must bail out here, the input wasn't even a
1325 * multiple of 2 bytes */
1326 talloc_free(cleartext_utf8_blob);
1327 ldb_asprintf_errstring(ldb,
1328 "setup_password_fields: "
1329 "UTF16 password for user %s had odd length (length must be a multiple of 2)", io->u.sAMAccountName);
1330 return LDB_ERR_CONSTRAINT_VIOLATION;
1332 *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str,
1334 g->cleartext_utf8 = cleartext_utf8_blob;
1338 if (g->cleartext_utf16) {
1339 struct samr_Password *nt_hash;
1341 nt_hash = talloc(io->ac, struct samr_Password);
1343 return ldb_oom(ldb);
1345 g->nt_hash = nt_hash;
1347 /* compute the new nt hash */
1348 mdfour(nt_hash->hash,
1349 g->cleartext_utf16->data,
1350 g->cleartext_utf16->length);
1353 if (g->cleartext_utf8) {
1354 struct samr_Password *lm_hash;
1356 lm_hash = talloc(io->ac, struct samr_Password);
1358 return ldb_oom(ldb);
1361 /* compute the new lm hash */
1362 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1364 g->lm_hash = lm_hash;
1366 talloc_free(lm_hash);
1373 static int setup_password_fields(struct setup_password_fields_io *io)
1375 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1376 struct loadparm_context *lp_ctx =
1377 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1378 struct loadparm_context);
1381 /* transform the old password (for password changes) */
1382 ret = setup_given_passwords(io, &io->og);
1383 if (ret != LDB_SUCCESS) {
1387 /* transform the new password */
1388 ret = setup_given_passwords(io, &io->n);
1389 if (ret != LDB_SUCCESS) {
1393 if (io->n.cleartext_utf8) {
1394 ret = setup_kerberos_keys(io);
1395 if (ret != LDB_SUCCESS) {
1400 ret = setup_nt_fields(io);
1401 if (ret != LDB_SUCCESS) {
1405 if (lpcfg_lanman_auth(lp_ctx)) {
1406 ret = setup_lm_fields(io);
1407 if (ret != LDB_SUCCESS) {
1411 io->g.lm_hash = NULL;
1412 io->g.lm_history_len = 0;
1415 ret = setup_supplemental_field(io);
1416 if (ret != LDB_SUCCESS) {
1420 ret = setup_last_set_field(io);
1421 if (ret != LDB_SUCCESS) {
1428 static int check_password_restrictions(struct setup_password_fields_io *io)
1430 struct ldb_context *ldb;
1432 enum samr_ValidationStatus stat;
1434 ldb = ldb_module_get_ctx(io->ac->module);
1436 /* First check the old password is correct, for password changes */
1437 if (!io->ac->pwd_reset) {
1438 bool nt_hash_checked = false;
1440 /* we need the old nt or lm hash given by the client */
1441 if (!io->og.nt_hash && !io->og.lm_hash) {
1442 ldb_asprintf_errstring(ldb,
1443 "check_password_restrictions: "
1444 "You need to provide the old password in order "
1446 return LDB_ERR_UNWILLING_TO_PERFORM;
1449 /* The password modify through the NT hash is encouraged and
1450 has no problems at all */
1451 if (io->og.nt_hash) {
1452 if (!io->o.nt_hash) {
1453 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1454 ldb_asprintf_errstring(ldb,
1455 "%08X: %s - check_password_restrictions: "
1456 "There's no old nt_hash, which is needed "
1457 "in order to change your password!",
1458 W_ERROR_V(WERR_INVALID_PASSWORD),
1463 if (memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
1464 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1465 ldb_asprintf_errstring(ldb,
1466 "%08X: %s - check_password_restrictions: "
1467 "The old password specified doesn't match!",
1468 W_ERROR_V(WERR_INVALID_PASSWORD),
1473 nt_hash_checked = true;
1476 /* But it is also possible to change a password by the LM hash
1477 * alone for compatibility reasons. This check is optional if
1478 * the NT hash was already checked - otherwise it's mandatory.
1479 * (as the SAMR operations request it). */
1480 if (io->og.lm_hash) {
1481 if (!io->o.lm_hash && !nt_hash_checked) {
1482 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1483 ldb_asprintf_errstring(ldb,
1484 "%08X: %s - check_password_restrictions: "
1485 "There's no old lm_hash, which is needed "
1486 "in order to change your password!",
1487 W_ERROR_V(WERR_INVALID_PASSWORD),
1492 if (io->o.lm_hash &&
1493 memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0) {
1494 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1495 ldb_asprintf_errstring(ldb,
1496 "%08X: %s - check_password_restrictions: "
1497 "The old password specified doesn't match!",
1498 W_ERROR_V(WERR_INVALID_PASSWORD),
1505 if (io->u.restrictions == 0) {
1506 /* FIXME: Is this right? */
1511 * Fundamental password checks done by the call
1512 * "samdb_check_password".
1513 * It is also in use by "dcesrv_samr_ValidatePassword".
1515 if (io->n.cleartext_utf8 != NULL) {
1516 stat = samdb_check_password(io->n.cleartext_utf8,
1517 io->ac->status->domain_data.pwdProperties,
1518 io->ac->status->domain_data.minPwdLength);
1520 case SAMR_VALIDATION_STATUS_SUCCESS:
1521 /* perfect -> proceed! */
1524 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
1525 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1526 ldb_asprintf_errstring(ldb,
1527 "%08X: %s - check_password_restrictions: "
1528 "the password is too short. It should be equal or longer than %u characters!",
1529 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1531 io->ac->status->domain_data.minPwdLength);
1532 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1535 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
1536 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1537 ldb_asprintf_errstring(ldb,
1538 "%08X: %s - check_password_restrictions: "
1539 "the password does not meet the complexity criterias!",
1540 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1542 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1546 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1547 ldb_asprintf_errstring(ldb,
1548 "%08X: %s - check_password_restrictions: "
1549 "the password doesn't fit by a certain reason!",
1550 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1556 if (io->ac->pwd_reset) {
1560 if (io->n.nt_hash) {
1563 /* checks the NT hash password history */
1564 for (i = 0; i < io->o.nt_history_len; i++) {
1565 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
1567 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1568 ldb_asprintf_errstring(ldb,
1569 "%08X: %s - check_password_restrictions: "
1570 "the password was already used (in history)!",
1571 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1573 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1579 if (io->n.lm_hash) {
1582 /* checks the LM hash password history */
1583 for (i = 0; i < io->o.lm_history_len; i++) {
1584 ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
1586 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1587 ldb_asprintf_errstring(ldb,
1588 "%08X: %s - check_password_restrictions: "
1589 "the password was already used (in history)!",
1590 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1592 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1598 /* are all password changes disallowed? */
1599 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1600 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1601 ldb_asprintf_errstring(ldb,
1602 "%08X: %s - check_password_restrictions: "
1603 "password changes disabled!",
1604 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1609 /* can this user change the password? */
1610 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
1611 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1612 ldb_asprintf_errstring(ldb,
1613 "%08X: %s - check_password_restrictions: "
1614 "password can't be changed on this account!",
1615 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1620 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
1621 if (io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) {
1622 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1623 ldb_asprintf_errstring(ldb,
1624 "%08X: %s - check_password_restrictions: "
1625 "password is too young to change!",
1626 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1635 * This is intended for use by the "password_hash" module since there
1636 * password changes can be specified through one message element with the
1637 * new password (to set) and another one with the old password (to unset).
1639 * The first which sets a password (new value) can have flags
1640 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
1641 * for entries). The latter (old value) has always specified
1642 * LDB_FLAG_MOD_DELETE.
1644 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
1645 * matching message elements are malformed in respect to the set/change rules.
1646 * Otherwise it returns LDB_SUCCESS.
1648 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
1650 enum ldb_request_type operation,
1651 const struct ldb_val **new_val,
1652 const struct ldb_val **old_val)
1663 for (i = 0; i < msg->num_elements; i++) {
1664 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
1668 if ((operation == LDB_MODIFY) &&
1669 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
1670 /* 0 values are allowed */
1671 if (msg->elements[i].num_values == 1) {
1672 *old_val = &msg->elements[i].values[0];
1673 } else if (msg->elements[i].num_values > 1) {
1674 return LDB_ERR_CONSTRAINT_VIOLATION;
1676 } else if ((operation == LDB_MODIFY) &&
1677 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
1678 if (msg->elements[i].num_values > 0) {
1679 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
1681 return LDB_ERR_UNWILLING_TO_PERFORM;
1684 /* Add operations and LDB_FLAG_MOD_ADD */
1685 if (msg->elements[i].num_values > 0) {
1686 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
1688 return LDB_ERR_CONSTRAINT_VIOLATION;
1696 static int setup_io(struct ph_context *ac,
1697 const struct ldb_message *orig_msg,
1698 const struct ldb_message *searched_msg,
1699 struct setup_password_fields_io *io)
1701 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
1702 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1703 struct loadparm_context *lp_ctx =
1704 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1705 struct loadparm_context);
1710 /* Some operations below require kerberos contexts */
1712 if (smb_krb5_init_context(ac,
1713 ldb_get_event_context(ldb),
1714 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
1715 &io->smb_krb5_context) != 0) {
1716 return ldb_operr(ldb);
1721 io->u.userAccountControl = ldb_msg_find_attr_as_uint(searched_msg,
1722 "userAccountControl", 0);
1723 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
1724 io->u.sAMAccountName = ldb_msg_find_attr_as_string(searched_msg,
1725 "sAMAccountName", NULL);
1726 io->u.user_principal_name = ldb_msg_find_attr_as_string(searched_msg,
1727 "userPrincipalName", NULL);
1728 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
1730 if (io->u.sAMAccountName == NULL) {
1731 ldb_asprintf_errstring(ldb,
1732 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
1733 ldb_dn_get_linearized(searched_msg->dn));
1735 return LDB_ERR_CONSTRAINT_VIOLATION;
1738 /* Only non-trust accounts have restrictions (possibly this test is the
1739 * wrong way around, but we like to be restrictive if possible */
1740 io->u.restrictions = !(io->u.userAccountControl
1741 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
1742 | UF_SERVER_TRUST_ACCOUNT));
1744 if ((io->u.userAccountControl & UF_PASSWD_NOTREQD) != 0) {
1745 /* see [MS-ADTS] 2.2.15 */
1746 io->u.restrictions = 0;
1749 if (ac->userPassword) {
1750 ret = msg_find_old_and_new_pwd_val(orig_msg, "userPassword",
1752 &io->n.cleartext_utf8,
1753 &io->og.cleartext_utf8);
1754 if (ret != LDB_SUCCESS) {
1755 ldb_asprintf_errstring(ldb,
1757 "it's only allowed to set the old password once!");
1762 ret = msg_find_old_and_new_pwd_val(orig_msg, "clearTextPassword",
1764 &io->n.cleartext_utf16,
1765 &io->og.cleartext_utf16);
1766 if (ret != LDB_SUCCESS) {
1767 ldb_asprintf_errstring(ldb,
1769 "it's only allowed to set the old password once!");
1773 /* this rather strange looking piece of code is there to
1774 handle a ldap client setting a password remotely using the
1775 unicodePwd ldap field. The syntax is that the password is
1776 in UTF-16LE, with a " at either end. Unfortunately the
1777 unicodePwd field is also used to store the nt hashes
1778 internally in Samba, and is used in the nt hash format on
1779 the wire in DRS replication, so we have a single name for
1780 two distinct values. The code below leaves us with a small
1781 chance (less than 1 in 2^32) of a mixup, if someone manages
1782 to create a MD4 hash which starts and ends in 0x22 0x00, as
1783 that would then be treated as a UTF16 password rather than
1786 ret = msg_find_old_and_new_pwd_val(orig_msg, "unicodePwd",
1790 if (ret != LDB_SUCCESS) {
1791 ldb_asprintf_errstring(ldb,
1793 "it's only allowed to set the old password once!");
1797 /* Checks and converts the actual "unicodePwd" attribute */
1799 quoted_utf16->length >= 4 &&
1800 quoted_utf16->data[0] == '"' &&
1801 quoted_utf16->data[1] == 0 &&
1802 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
1803 quoted_utf16->data[quoted_utf16->length-1] == 0) {
1804 struct ldb_val *quoted_utf16_2;
1806 if (io->n.cleartext_utf16) {
1807 /* refuse the change if someone wants to change with
1808 with both UTF16 possibilities at the same time... */
1809 ldb_asprintf_errstring(ldb,
1811 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1812 return LDB_ERR_UNWILLING_TO_PERFORM;
1816 * adapt the quoted UTF16 string to be a real
1819 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1820 if (quoted_utf16_2 == NULL) {
1821 return ldb_oom(ldb);
1824 quoted_utf16_2->data = quoted_utf16->data + 2;
1825 quoted_utf16_2->length = quoted_utf16->length-4;
1826 io->n.cleartext_utf16 = quoted_utf16_2;
1827 io->n.nt_hash = NULL;
1829 } else if (quoted_utf16) {
1830 /* We have only the hash available -> so no plaintext here */
1831 if (!ac->hash_values) {
1832 /* refuse the change if someone wants to change
1833 the hash without control specified... */
1834 ldb_asprintf_errstring(ldb,
1836 "it's not allowed to set the NT hash password directly'");
1837 /* this looks odd but this is what Windows does:
1838 returns "UNWILLING_TO_PERFORM" on wrong
1839 password sets and "CONSTRAINT_VIOLATION" on
1840 wrong password changes. */
1841 if (old_quoted_utf16 == NULL) {
1842 return LDB_ERR_UNWILLING_TO_PERFORM;
1845 return LDB_ERR_CONSTRAINT_VIOLATION;
1848 io->n.nt_hash = talloc(io->ac, struct samr_Password);
1849 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
1850 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
1853 /* Checks and converts the previous "unicodePwd" attribute */
1854 if (old_quoted_utf16 &&
1855 old_quoted_utf16->length >= 4 &&
1856 old_quoted_utf16->data[0] == '"' &&
1857 old_quoted_utf16->data[1] == 0 &&
1858 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
1859 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
1860 struct ldb_val *old_quoted_utf16_2;
1862 if (io->og.cleartext_utf16) {
1863 /* refuse the change if someone wants to change with
1864 both UTF16 possibilities at the same time... */
1865 ldb_asprintf_errstring(ldb,
1867 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1868 return LDB_ERR_UNWILLING_TO_PERFORM;
1872 * adapt the quoted UTF16 string to be a real
1875 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1876 if (old_quoted_utf16_2 == NULL) {
1877 return ldb_oom(ldb);
1880 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
1881 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
1883 io->og.cleartext_utf16 = old_quoted_utf16_2;
1884 io->og.nt_hash = NULL;
1885 } else if (old_quoted_utf16) {
1886 /* We have only the hash available -> so no plaintext here */
1887 if (!ac->hash_values) {
1888 /* refuse the change if someone wants to change
1889 the hash without control specified... */
1890 ldb_asprintf_errstring(ldb,
1892 "it's not allowed to set the NT hash password directly'");
1893 return LDB_ERR_UNWILLING_TO_PERFORM;
1896 io->og.nt_hash = talloc(io->ac, struct samr_Password);
1897 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
1898 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
1901 /* Handles the "dBCSPwd" attribute (LM hash) */
1902 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
1903 ret = msg_find_old_and_new_pwd_val(orig_msg, "dBCSPwd",
1905 &lm_hash, &old_lm_hash);
1906 if (ret != LDB_SUCCESS) {
1907 ldb_asprintf_errstring(ldb,
1909 "it's only allowed to set the old password once!");
1913 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
1914 /* refuse the change if someone wants to change the hash
1915 without control specified... */
1916 ldb_asprintf_errstring(ldb,
1918 "it's not allowed to set the LM hash password directly'");
1919 return LDB_ERR_UNWILLING_TO_PERFORM;
1922 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
1923 io->n.lm_hash = talloc(io->ac, struct samr_Password);
1924 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
1925 sizeof(io->n.lm_hash->hash)));
1927 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
1928 io->og.lm_hash = talloc(io->ac, struct samr_Password);
1929 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
1930 sizeof(io->og.lm_hash->hash)));
1934 * Handles the password change control if it's specified. It has the
1935 * precedance and overrides already specified old password values of
1936 * change requests (but that shouldn't happen since the control is
1937 * fully internal and only used in conjunction with replace requests!).
1939 if (ac->change != NULL) {
1940 io->og.nt_hash = NULL;
1941 if (ac->change->old_nt_pwd_hash != NULL) {
1942 io->og.nt_hash = talloc_memdup(io->ac,
1943 ac->change->old_nt_pwd_hash,
1944 sizeof(struct samr_Password));
1946 io->og.lm_hash = NULL;
1947 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
1948 io->og.lm_hash = talloc_memdup(io->ac,
1949 ac->change->old_lm_pwd_hash,
1950 sizeof(struct samr_Password));
1954 /* refuse the change if someone wants to change the clear-
1955 text and supply his own hashes at the same time... */
1956 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
1957 && (io->n.nt_hash || io->n.lm_hash)) {
1958 ldb_asprintf_errstring(ldb,
1960 "it's only allowed to set the password in form of cleartext attributes or as hashes");
1961 return LDB_ERR_UNWILLING_TO_PERFORM;
1964 /* refuse the change if someone wants to change the password
1965 using both plaintext methods (UTF8 and UTF16) at the same time... */
1966 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
1967 ldb_asprintf_errstring(ldb,
1969 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
1970 return LDB_ERR_UNWILLING_TO_PERFORM;
1973 /* refuse the change if someone tries to set/change the password by
1974 * the lanman hash alone and we've deactivated that mechanism. This
1975 * would end in an account without any password! */
1976 if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
1977 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
1978 ldb_asprintf_errstring(ldb,
1980 "It' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
1981 /* on "userPassword" and "clearTextPassword" we've to return
1982 * something different, since these are virtual attributes */
1983 if ((ldb_msg_find_element(orig_msg, "userPassword") != NULL) ||
1984 (ldb_msg_find_element(orig_msg, "clearTextPassword") != NULL)) {
1985 return LDB_ERR_CONSTRAINT_VIOLATION;
1987 return LDB_ERR_UNWILLING_TO_PERFORM;
1990 /* refuse the change if someone wants to compare against a plaintext
1991 or hash at the same time for a "password modify" operation... */
1992 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
1993 && (io->og.nt_hash || io->og.lm_hash)) {
1994 ldb_asprintf_errstring(ldb,
1996 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
1997 return LDB_ERR_UNWILLING_TO_PERFORM;
2000 /* refuse the change if someone wants to compare against both
2001 * plaintexts at the same time for a "password modify" operation... */
2002 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2003 ldb_asprintf_errstring(ldb,
2005 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2006 return LDB_ERR_UNWILLING_TO_PERFORM;
2009 /* Decides if we have a password modify or password reset operation */
2010 if (ac->req->operation == LDB_ADD) {
2011 /* On "add" we have only "password reset" */
2012 ac->pwd_reset = true;
2013 } else if (ac->req->operation == LDB_MODIFY) {
2014 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2015 || io->og.nt_hash || io->og.lm_hash) {
2016 /* If we have an old password specified then for sure it
2017 * is a user "password change" */
2018 ac->pwd_reset = false;
2020 /* Otherwise we have also here a "password reset" */
2021 ac->pwd_reset = true;
2024 /* this shouldn't happen */
2025 return ldb_operr(ldb);
2031 static struct ph_context *ph_init_context(struct ldb_module *module,
2032 struct ldb_request *req,
2035 struct ldb_context *ldb;
2036 struct ph_context *ac;
2038 ldb = ldb_module_get_ctx(module);
2040 ac = talloc_zero(req, struct ph_context);
2042 ldb_set_errstring(ldb, "Out of Memory");
2046 ac->module = module;
2048 ac->userPassword = userPassword;
2053 static void ph_apply_controls(struct ph_context *ac)
2055 struct ldb_control *ctrl;
2057 ac->change_status = false;
2058 ctrl = ldb_request_get_control(ac->req,
2059 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2061 ac->change_status = true;
2063 /* Mark the "change status" control as uncritical (done) */
2064 ctrl->critical = false;
2067 ac->hash_values = false;
2068 ctrl = ldb_request_get_control(ac->req,
2069 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2071 ac->hash_values = true;
2073 /* Mark the "hash values" control as uncritical (done) */
2074 ctrl->critical = false;
2077 ctrl = ldb_request_get_control(ac->req,
2078 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2080 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2082 /* Mark the "change" control as uncritical (done) */
2083 ctrl->critical = false;
2087 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2089 struct ph_context *ac;
2091 ac = talloc_get_type(req->context, struct ph_context);
2094 return ldb_module_done(ac->req, NULL, NULL,
2095 LDB_ERR_OPERATIONS_ERROR);
2098 if (ares->type == LDB_REPLY_REFERRAL) {
2099 return ldb_module_send_referral(ac->req, ares->referral);
2102 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2103 /* On success and trivial errors a status control is being
2104 * added (used for example by the "samdb_set_password" call) */
2105 ldb_reply_add_control(ares,
2106 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2111 if (ares->error != LDB_SUCCESS) {
2112 return ldb_module_done(ac->req, ares->controls,
2113 ares->response, ares->error);
2116 if (ares->type != LDB_REPLY_DONE) {
2118 return ldb_module_done(ac->req, NULL, NULL,
2119 LDB_ERR_OPERATIONS_ERROR);
2122 return ldb_module_done(ac->req, ares->controls,
2123 ares->response, ares->error);
2126 static int password_hash_add_do_add(struct ph_context *ac);
2127 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2128 static int password_hash_mod_search_self(struct ph_context *ac);
2129 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2130 static int password_hash_mod_do_mod(struct ph_context *ac);
2132 static int get_domain_data_callback(struct ldb_request *req,
2133 struct ldb_reply *ares)
2135 struct ldb_context *ldb;
2136 struct ph_context *ac;
2137 struct loadparm_context *lp_ctx;
2140 ac = talloc_get_type(req->context, struct ph_context);
2141 ldb = ldb_module_get_ctx(ac->module);
2144 ret = LDB_ERR_OPERATIONS_ERROR;
2147 if (ares->error != LDB_SUCCESS) {
2148 return ldb_module_done(ac->req, ares->controls,
2149 ares->response, ares->error);
2152 switch (ares->type) {
2153 case LDB_REPLY_ENTRY:
2154 if (ac->status != NULL) {
2157 ldb_set_errstring(ldb, "Too many results");
2158 ret = LDB_ERR_OPERATIONS_ERROR;
2162 /* Setup the "status" structure (used as control later) */
2163 ac->status = talloc_zero(ac->req,
2164 struct dsdb_control_password_change_status);
2165 if (ac->status == NULL) {
2169 ret = LDB_ERR_OPERATIONS_ERROR;
2173 /* Setup the "domain data" structure */
2174 ac->status->domain_data.pwdProperties =
2175 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
2176 ac->status->domain_data.pwdHistoryLength =
2177 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
2178 ac->status->domain_data.maxPwdAge =
2179 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
2180 ac->status->domain_data.minPwdAge =
2181 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
2182 ac->status->domain_data.minPwdLength =
2183 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
2184 ac->status->domain_data.store_cleartext =
2185 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2189 /* For a domain DN, this puts things in dotted notation */
2190 /* For builtin domains, this will give details for the host,
2191 * but that doesn't really matter, as it's just used for salt
2192 * and kerberos principals, which don't exist here */
2194 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2195 struct loadparm_context);
2197 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
2198 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
2199 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
2201 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2206 case LDB_REPLY_REFERRAL:
2212 case LDB_REPLY_DONE:
2214 /* call the next step */
2215 switch (ac->req->operation) {
2217 ret = password_hash_add_do_add(ac);
2221 ret = password_hash_mod_do_mod(ac);
2225 ret = LDB_ERR_OPERATIONS_ERROR;
2232 if (ret != LDB_SUCCESS) {
2233 struct ldb_reply *new_ares;
2235 new_ares = talloc_zero(ac->req, struct ldb_reply);
2236 if (new_ares == NULL) {
2238 return ldb_module_done(ac->req, NULL, NULL,
2239 LDB_ERR_OPERATIONS_ERROR);
2242 new_ares->error = ret;
2243 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2244 /* On success and trivial errors a status control is being
2245 * added (used for example by the "samdb_set_password" call) */
2246 ldb_reply_add_control(new_ares,
2247 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2252 return ldb_module_done(ac->req, new_ares->controls,
2253 new_ares->response, new_ares->error);
2259 static int build_domain_data_request(struct ph_context *ac)
2261 /* attrs[] is returned from this function in
2262 ac->dom_req->op.search.attrs, so it must be static, as
2263 otherwise the compiler can put it on the stack */
2264 struct ldb_context *ldb;
2265 static const char * const attrs[] = { "pwdProperties",
2273 ldb = ldb_module_get_ctx(ac->module);
2275 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
2276 ldb_get_default_basedn(ldb),
2280 ac, get_domain_data_callback,
2282 LDB_REQ_SET_LOCATION(ac->dom_req);
2286 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2288 struct ldb_context *ldb;
2289 struct ph_context *ac;
2290 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2293 struct ldb_control *bypass = NULL;
2294 bool userPassword = dsdb_user_password_support(module, req);
2296 ldb = ldb_module_get_ctx(module);
2298 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2300 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2301 return ldb_next_request(module, req);
2304 /* If the caller is manipulating the local passwords directly, let them pass */
2305 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2306 req->op.add.message->dn) == 0) {
2307 return ldb_next_request(module, req);
2310 bypass = ldb_request_get_control(req,
2311 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2312 if (bypass != NULL) {
2313 /* Mark the "bypass" control as uncritical (done) */
2314 bypass->critical = false;
2315 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
2316 return ldb_next_request(module, req);
2319 /* nobody must touch password histories and 'supplementalCredentials' */
2320 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2321 return LDB_ERR_UNWILLING_TO_PERFORM;
2323 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2324 return LDB_ERR_UNWILLING_TO_PERFORM;
2326 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2327 return LDB_ERR_UNWILLING_TO_PERFORM;
2330 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2331 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2333 userPasswordAttr = NULL;
2335 userPasswordAttr = ldb_msg_find_element(req->op.add.message,
2337 /* MS-ADTS 3.1.1.3.1.5.2 */
2338 if ((userPasswordAttr != NULL) &&
2339 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2340 return LDB_ERR_CONSTRAINT_VIOLATION;
2343 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2344 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2345 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2347 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2348 return ldb_next_request(module, req);
2351 /* Make sure we are performing the password set action on a (for us)
2352 * valid object. Those are instances of either "user" and/or
2353 * "inetOrgPerson". Otherwise continue with the submodules. */
2354 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2355 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2357 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2358 ldb_set_errstring(ldb,
2359 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2360 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2363 return ldb_next_request(module, req);
2366 ac = ph_init_context(module, req, userPassword);
2368 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2369 return ldb_operr(ldb);
2371 ph_apply_controls(ac);
2373 /* get user domain data */
2374 ret = build_domain_data_request(ac);
2375 if (ret != LDB_SUCCESS) {
2379 return ldb_next_request(module, ac->dom_req);
2382 static int password_hash_add_do_add(struct ph_context *ac)
2384 struct ldb_context *ldb;
2385 struct ldb_request *down_req;
2386 struct ldb_message *msg;
2387 struct setup_password_fields_io io;
2390 /* Prepare the internal data structure containing the passwords */
2391 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2392 if (ret != LDB_SUCCESS) {
2396 ldb = ldb_module_get_ctx(ac->module);
2398 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2400 return ldb_operr(ldb);
2403 /* remove attributes that we just read into 'io' */
2404 if (ac->userPassword) {
2405 ldb_msg_remove_attr(msg, "userPassword");
2407 ldb_msg_remove_attr(msg, "clearTextPassword");
2408 ldb_msg_remove_attr(msg, "unicodePwd");
2409 ldb_msg_remove_attr(msg, "dBCSPwd");
2410 ldb_msg_remove_attr(msg, "pwdLastSet");
2412 ret = setup_password_fields(&io);
2413 if (ret != LDB_SUCCESS) {
2417 ret = check_password_restrictions(&io);
2418 if (ret != LDB_SUCCESS) {
2423 ret = samdb_msg_add_hash(ldb, ac, msg,
2424 "unicodePwd", io.g.nt_hash);
2425 if (ret != LDB_SUCCESS) {
2430 ret = samdb_msg_add_hash(ldb, ac, msg,
2431 "dBCSPwd", io.g.lm_hash);
2432 if (ret != LDB_SUCCESS) {
2436 if (io.g.nt_history_len > 0) {
2437 ret = samdb_msg_add_hashes(ldb, ac, msg,
2440 io.g.nt_history_len);
2441 if (ret != LDB_SUCCESS) {
2445 if (io.g.lm_history_len > 0) {
2446 ret = samdb_msg_add_hashes(ldb, ac, msg,
2449 io.g.lm_history_len);
2450 if (ret != LDB_SUCCESS) {
2454 if (io.g.supplemental.length > 0) {
2455 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2456 &io.g.supplemental, NULL);
2457 if (ret != LDB_SUCCESS) {
2461 ret = samdb_msg_add_uint64(ldb, ac, msg,
2464 if (ret != LDB_SUCCESS) {
2468 ret = ldb_build_add_req(&down_req, ldb, ac,
2473 LDB_REQ_SET_LOCATION(down_req);
2474 if (ret != LDB_SUCCESS) {
2478 return ldb_next_request(ac->module, down_req);
2481 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
2483 struct ldb_context *ldb;
2484 struct ph_context *ac;
2485 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
2486 "unicodePwd", "dBCSPwd", NULL }, **l;
2487 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
2488 struct ldb_message_element *passwordAttr;
2489 struct ldb_message *msg;
2490 struct ldb_request *down_req;
2492 struct ldb_control *bypass = NULL;
2493 bool userPassword = dsdb_user_password_support(module, req);
2495 ldb = ldb_module_get_ctx(module);
2497 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
2499 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
2500 return ldb_next_request(module, req);
2503 /* If the caller is manipulating the local passwords directly, let them pass */
2504 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2505 req->op.mod.message->dn) == 0) {
2506 return ldb_next_request(module, req);
2509 bypass = ldb_request_get_control(req,
2510 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2511 if (bypass != NULL) {
2512 /* Mark the "bypass" control as uncritical (done) */
2513 bypass->critical = false;
2514 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
2515 return ldb_next_request(module, req);
2518 /* nobody must touch password histories and 'supplementalCredentials' */
2519 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
2520 return LDB_ERR_UNWILLING_TO_PERFORM;
2522 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
2523 return LDB_ERR_UNWILLING_TO_PERFORM;
2525 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
2526 return LDB_ERR_UNWILLING_TO_PERFORM;
2529 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2530 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
2531 * For password changes/set there should be a 'delete' or a 'modify'
2532 * on these attributes. */
2534 for (l = passwordAttrs; *l != NULL; l++) {
2535 if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) {
2539 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
2540 /* MS-ADTS 3.1.1.3.1.5.2 */
2541 if ((ldb_attr_cmp(*l, "userPassword") == 0) &&
2542 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2543 return LDB_ERR_CONSTRAINT_VIOLATION;
2549 if (attr_cnt == 0) {
2550 return ldb_next_request(module, req);
2553 ac = ph_init_context(module, req, userPassword);
2555 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2556 return ldb_operr(ldb);
2558 ph_apply_controls(ac);
2560 /* use a new message structure so that we can modify it */
2561 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2563 return ldb_oom(ldb);
2566 /* - check for single-valued password attributes
2567 * (if not return "CONSTRAINT_VIOLATION")
2568 * - check that for a password change operation one add and one delete
2570 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
2571 * - check that a password change and a password set operation cannot
2573 * (if not return "UNWILLING_TO_PERFORM")
2574 * - remove all password attributes modifications from the first change
2575 * operation (anything without the passwords) - we will make the real
2576 * modification later */
2580 for (l = passwordAttrs; *l != NULL; l++) {
2581 if ((!ac->userPassword) &&
2582 (ldb_attr_cmp(*l, "userPassword") == 0)) {
2586 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
2587 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
2590 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
2593 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
2596 if ((passwordAttr->num_values != 1) &&
2597 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
2599 ldb_asprintf_errstring(ldb,
2600 "'%s' attribute must have exactly one value on add operations!",
2602 return LDB_ERR_CONSTRAINT_VIOLATION;
2604 if ((passwordAttr->num_values > 1) &&
2605 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
2607 ldb_asprintf_errstring(ldb,
2608 "'%s' attribute must have zero or one value(s) on delete operations!",
2610 return LDB_ERR_CONSTRAINT_VIOLATION;
2612 ldb_msg_remove_element(msg, passwordAttr);
2615 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
2617 ldb_set_errstring(ldb,
2618 "Only the add action for a password change specified!");
2619 return LDB_ERR_UNWILLING_TO_PERFORM;
2621 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
2623 ldb_set_errstring(ldb,
2624 "Only one delete and one add action for a password change allowed!");
2625 return LDB_ERR_UNWILLING_TO_PERFORM;
2627 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
2629 ldb_set_errstring(ldb,
2630 "Either a password change or a password set operation is allowed!");
2631 return LDB_ERR_UNWILLING_TO_PERFORM;
2634 /* if there was nothing else to be modified skip to next step */
2635 if (msg->num_elements == 0) {
2636 return password_hash_mod_search_self(ac);
2639 ret = ldb_build_mod_req(&down_req, ldb, ac,
2642 ac, ph_modify_callback,
2644 LDB_REQ_SET_LOCATION(down_req);
2645 if (ret != LDB_SUCCESS) {
2649 return ldb_next_request(module, down_req);
2652 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
2654 struct ph_context *ac;
2656 ac = talloc_get_type(req->context, struct ph_context);
2659 return ldb_module_done(ac->req, NULL, NULL,
2660 LDB_ERR_OPERATIONS_ERROR);
2663 if (ares->type == LDB_REPLY_REFERRAL) {
2664 return ldb_module_send_referral(ac->req, ares->referral);
2667 if (ares->error != LDB_SUCCESS) {
2668 return ldb_module_done(ac->req, ares->controls,
2669 ares->response, ares->error);
2672 if (ares->type != LDB_REPLY_DONE) {
2674 return ldb_module_done(ac->req, NULL, NULL,
2675 LDB_ERR_OPERATIONS_ERROR);
2680 return password_hash_mod_search_self(ac);
2683 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
2685 struct ldb_context *ldb;
2686 struct ph_context *ac;
2689 ac = talloc_get_type(req->context, struct ph_context);
2690 ldb = ldb_module_get_ctx(ac->module);
2693 ret = LDB_ERR_OPERATIONS_ERROR;
2696 if (ares->error != LDB_SUCCESS) {
2697 return ldb_module_done(ac->req, ares->controls,
2698 ares->response, ares->error);
2701 /* we are interested only in the single reply (base search) */
2702 switch (ares->type) {
2703 case LDB_REPLY_ENTRY:
2704 /* Make sure we are performing the password change action on a
2705 * (for us) valid object. Those are instances of either "user"
2706 * and/or "inetOrgPerson". Otherwise continue with the
2708 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
2709 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
2712 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
2713 ldb_set_errstring(ldb,
2714 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2715 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
2719 ret = ldb_next_request(ac->module, ac->req);
2723 if (ac->search_res != NULL) {
2726 ldb_set_errstring(ldb, "Too many results");
2727 ret = LDB_ERR_OPERATIONS_ERROR;
2731 ac->search_res = talloc_steal(ac, ares);
2735 case LDB_REPLY_REFERRAL:
2736 /* ignore anything else for now */
2741 case LDB_REPLY_DONE:
2744 /* get user domain data */
2745 ret = build_domain_data_request(ac);
2746 if (ret != LDB_SUCCESS) {
2747 return ldb_module_done(ac->req, NULL, NULL, ret);
2750 ret = ldb_next_request(ac->module, ac->dom_req);
2755 if (ret != LDB_SUCCESS) {
2756 return ldb_module_done(ac->req, NULL, NULL, ret);
2762 static int password_hash_mod_search_self(struct ph_context *ac)
2764 struct ldb_context *ldb;
2765 static const char * const attrs[] = { "objectClass",
2766 "userAccountControl",
2770 "userPrincipalName",
2771 "supplementalCredentials",
2777 struct ldb_request *search_req;
2780 ldb = ldb_module_get_ctx(ac->module);
2782 ret = ldb_build_search_req(&search_req, ldb, ac,
2783 ac->req->op.mod.message->dn,
2788 ac, ph_mod_search_callback,
2790 LDB_REQ_SET_LOCATION(search_req);
2791 if (ret != LDB_SUCCESS) {
2795 return ldb_next_request(ac->module, search_req);
2798 static int password_hash_mod_do_mod(struct ph_context *ac)
2800 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2801 struct loadparm_context *lp_ctx =
2802 talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2803 struct loadparm_context);
2804 struct ldb_request *mod_req;
2805 struct ldb_message *msg;
2806 const struct ldb_message *orig_msg, *searched_msg;
2807 struct setup_password_fields_io io;
2811 /* use a new message structure so that we can modify it */
2812 msg = ldb_msg_new(ac);
2814 return ldb_operr(ldb);
2818 msg->dn = ac->req->op.mod.message->dn;
2820 orig_msg = ac->req->op.mod.message;
2821 searched_msg = ac->search_res->message;
2823 /* Prepare the internal data structure containing the passwords */
2824 ret = setup_io(ac, orig_msg, searched_msg, &io);
2825 if (ret != LDB_SUCCESS) {
2829 /* Get the old password from the database */
2830 status = samdb_result_passwords(io.ac,
2832 discard_const_p(struct ldb_message, searched_msg),
2833 &io.o.lm_hash, &io.o.nt_hash);
2834 if (!NT_STATUS_IS_OK(status)) {
2835 return ldb_operr(ldb);
2838 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
2839 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
2840 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
2842 ret = setup_password_fields(&io);
2843 if (ret != LDB_SUCCESS) {
2847 ret = check_password_restrictions(&io);
2848 if (ret != LDB_SUCCESS) {
2852 /* make sure we replace all the old attributes */
2853 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
2854 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
2855 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2856 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2857 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
2858 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
2861 ret = samdb_msg_add_hash(ldb, ac, msg,
2862 "unicodePwd", io.g.nt_hash);
2863 if (ret != LDB_SUCCESS) {
2868 ret = samdb_msg_add_hash(ldb, ac, msg,
2869 "dBCSPwd", io.g.lm_hash);
2870 if (ret != LDB_SUCCESS) {
2874 if (io.g.nt_history_len > 0) {
2875 ret = samdb_msg_add_hashes(ldb, ac, msg,
2878 io.g.nt_history_len);
2879 if (ret != LDB_SUCCESS) {
2883 if (io.g.lm_history_len > 0) {
2884 ret = samdb_msg_add_hashes(ldb, ac, msg,
2887 io.g.lm_history_len);
2888 if (ret != LDB_SUCCESS) {
2892 if (io.g.supplemental.length > 0) {
2893 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2894 &io.g.supplemental, NULL);
2895 if (ret != LDB_SUCCESS) {
2899 ret = samdb_msg_add_uint64(ldb, ac, msg,
2902 if (ret != LDB_SUCCESS) {
2906 ret = ldb_build_mod_req(&mod_req, ldb, ac,
2911 LDB_REQ_SET_LOCATION(mod_req);
2912 if (ret != LDB_SUCCESS) {
2916 return ldb_next_request(ac->module, mod_req);
2919 static const struct ldb_module_ops ldb_password_hash_module_ops = {
2920 .name = "password_hash",
2921 .add = password_hash_add,
2922 .modify = password_hash_modify
2925 int ldb_password_hash_module_init(const char *version)
2927 LDB_MODULE_CHECK_VERSION(version);
2928 return ldb_register_module(&ldb_password_hash_module_ops);