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"
46 #include "lib/krb5_wrap/krb5_samba.h"
48 /* If we have decided there is a reason to work on this request, then
49 * setup all the password hash types correctly.
51 * If we haven't the hashes yet but the password given as plain-text (attributes
52 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
53 * the constraints. Once this is done, we calculate the password hashes.
55 * Notice: unlike the real AD which only supports the UTF16 special based
56 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
57 * understand also a UTF16 based 'clearTextPassword' one.
58 * The latter is also accessible through LDAP so it can also be set by external
59 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
61 * Also when the module receives only the password hashes (possible through
62 * specifying an internal LDB control - for security reasons) some checks are
63 * performed depending on the operation mode (see below) (e.g. if the password
64 * has been in use before if the password memory policy was activated).
66 * Attention: There is a difference between "modify" and "reset" operations
67 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
68 * operation for a password attribute we thread this as a "modify"; if it sends
69 * only a "replace" one we have an (administrative) reset.
71 * Finally, if the administrator has requested that a password history
72 * be maintained, then this should also be written out.
76 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
77 * - Check for right connection encryption
80 /* Notice: Definition of "dsdb_control_password_change_status" moved into
84 struct ldb_module *module;
85 struct ldb_request *req;
87 struct ldb_request *dom_req;
88 struct ldb_reply *dom_res;
90 struct ldb_reply *search_res;
92 struct ldb_message *update_msg;
94 struct dsdb_control_password_change_status *status;
95 struct dsdb_control_password_change *change;
101 bool update_password;
103 bool pwd_last_set_bypass;
104 bool pwd_last_set_default;
108 struct setup_password_fields_io {
109 struct ph_context *ac;
111 struct smb_krb5_context *smb_krb5_context;
113 /* infos about the user account */
115 uint32_t userAccountControl;
117 const char *sAMAccountName;
118 const char *user_principal_name;
120 uint32_t restrictions;
123 /* new credentials and old given credentials */
124 struct setup_password_fields_given {
125 const struct ldb_val *cleartext_utf8;
126 const struct ldb_val *cleartext_utf16;
127 struct samr_Password *nt_hash;
128 struct samr_Password *lm_hash;
131 /* old credentials */
133 struct samr_Password *nt_hash;
134 struct samr_Password *lm_hash;
135 uint32_t nt_history_len;
136 struct samr_Password *nt_history;
137 uint32_t lm_history_len;
138 struct samr_Password *lm_history;
139 const struct ldb_val *supplemental;
140 struct supplementalCredentialsBlob scb;
143 /* generated credentials */
145 struct samr_Password *nt_hash;
146 struct samr_Password *lm_hash;
147 uint32_t nt_history_len;
148 struct samr_Password *nt_history;
149 uint32_t lm_history_len;
150 struct samr_Password *lm_history;
156 struct ldb_val supplemental;
161 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
163 enum ldb_request_type operation,
164 const struct ldb_val **new_val,
165 const struct ldb_val **old_val);
167 static int password_hash_bypass(struct ldb_module *module, struct ldb_request *request)
169 struct ldb_context *ldb = ldb_module_get_ctx(module);
170 const struct ldb_message *msg;
171 struct ldb_message_element *nte;
172 struct ldb_message_element *lme;
173 struct ldb_message_element *nthe;
174 struct ldb_message_element *lmhe;
175 struct ldb_message_element *sce;
177 switch (request->operation) {
179 msg = request->op.add.message;
182 msg = request->op.mod.message;
185 return ldb_next_request(module, request);
188 /* nobody must touch password histories and 'supplementalCredentials' */
189 nte = dsdb_get_single_valued_attr(msg, "unicodePwd",
191 lme = dsdb_get_single_valued_attr(msg, "dBCSPwd",
193 nthe = dsdb_get_single_valued_attr(msg, "ntPwdHistory",
195 lmhe = dsdb_get_single_valued_attr(msg, "lmPwdHistory",
197 sce = dsdb_get_single_valued_attr(msg, "supplementalCredentials",
200 #define CHECK_HASH_ELEMENT(e, min, max) do {\
201 if (e && e->num_values) { \
202 unsigned int _count; \
203 if (e->num_values != 1) { \
204 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
205 "num_values != 1"); \
207 if ((e->values[0].length % 16) != 0) { \
208 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
209 "length % 16 != 0"); \
211 _count = e->values[0].length / 16; \
212 if (_count < min) { \
213 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
216 if (_count > max) { \
217 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
223 CHECK_HASH_ELEMENT(nte, 1, 1);
224 CHECK_HASH_ELEMENT(lme, 1, 1);
225 CHECK_HASH_ELEMENT(nthe, 1, INT32_MAX);
226 CHECK_HASH_ELEMENT(lmhe, 1, INT32_MAX);
228 if (sce && sce->num_values) {
229 enum ndr_err_code ndr_err;
230 struct supplementalCredentialsBlob *scb;
231 struct supplementalCredentialsPackage *scpp = NULL;
232 struct supplementalCredentialsPackage *scpk = NULL;
233 struct supplementalCredentialsPackage *scpkn = NULL;
234 struct supplementalCredentialsPackage *scpct = NULL;
235 DATA_BLOB scpbp = data_blob_null;
236 DATA_BLOB scpbk = data_blob_null;
237 DATA_BLOB scpbkn = data_blob_null;
238 DATA_BLOB scpbct = data_blob_null;
242 if (sce->num_values != 1) {
243 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
247 scb = talloc_zero(request, struct supplementalCredentialsBlob);
249 return ldb_module_oom(module);
252 ndr_err = ndr_pull_struct_blob_all(&sce->values[0], scb, scb,
253 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
255 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
256 "ndr_pull_struct_blob_all");
259 if (scb->sub.num_packages < 2) {
260 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
264 for (i=0; i < scb->sub.num_packages; i++) {
267 subblob = strhex_to_data_blob(scb, scb->sub.packages[i].data);
268 if (subblob.data == NULL) {
269 return ldb_module_oom(module);
272 if (strcmp(scb->sub.packages[i].name, "Packages") == 0) {
274 return ldb_error(ldb,
275 LDB_ERR_CONSTRAINT_VIOLATION,
278 scpp = &scb->sub.packages[i];
282 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos") == 0) {
284 return ldb_error(ldb,
285 LDB_ERR_CONSTRAINT_VIOLATION,
286 "Primary:Kerberos twice");
288 scpk = &scb->sub.packages[i];
292 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos-Newer-Keys") == 0) {
294 return ldb_error(ldb,
295 LDB_ERR_CONSTRAINT_VIOLATION,
296 "Primary:Kerberos-Newer-Keys twice");
298 scpkn = &scb->sub.packages[i];
302 if (strcmp(scb->sub.packages[i].name, "Primary:CLEARTEXT") == 0) {
304 return ldb_error(ldb,
305 LDB_ERR_CONSTRAINT_VIOLATION,
306 "Primary:CLEARTEXT twice");
308 scpct = &scb->sub.packages[i];
313 data_blob_free(&subblob);
317 return ldb_error(ldb,
318 LDB_ERR_CONSTRAINT_VIOLATION,
319 "Primary:Packages missing");
324 * If Primary:Kerberos is missing w2k8r2 reboots
325 * when a password is changed.
327 return ldb_error(ldb,
328 LDB_ERR_CONSTRAINT_VIOLATION,
329 "Primary:Kerberos missing");
333 struct package_PackagesBlob *p;
336 p = talloc_zero(scb, struct package_PackagesBlob);
338 return ldb_module_oom(module);
341 ndr_err = ndr_pull_struct_blob(&scpbp, p, p,
342 (ndr_pull_flags_fn_t)ndr_pull_package_PackagesBlob);
343 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
344 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
345 "ndr_pull_struct_blob Packages");
348 if (p->names == NULL) {
349 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
350 "Packages names == NULL");
353 for (n = 0; p->names[n]; n++) {
357 if (scb->sub.num_packages != (n + 1)) {
358 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
359 "Packages num_packages != num_names + 1");
366 struct package_PrimaryKerberosBlob *k;
368 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
370 return ldb_module_oom(module);
373 ndr_err = ndr_pull_struct_blob(&scpbk, k, k,
374 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
375 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
376 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
377 "ndr_pull_struct_blob PrimaryKerberos");
380 if (k->version != 3) {
381 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
382 "PrimaryKerberos version != 3");
385 if (k->ctr.ctr3.salt.string == NULL) {
386 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
387 "PrimaryKerberos salt == NULL");
390 if (strlen(k->ctr.ctr3.salt.string) == 0) {
391 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
392 "PrimaryKerberos strlen(salt) == 0");
395 if (k->ctr.ctr3.num_keys != 2) {
396 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
397 "PrimaryKerberos num_keys != 2");
400 if (k->ctr.ctr3.num_old_keys > k->ctr.ctr3.num_keys) {
401 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
402 "PrimaryKerberos num_old_keys > num_keys");
405 if (k->ctr.ctr3.keys[0].keytype != ENCTYPE_DES_CBC_MD5) {
406 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
407 "PrimaryKerberos key[0] != DES_CBC_MD5");
409 if (k->ctr.ctr3.keys[1].keytype != ENCTYPE_DES_CBC_CRC) {
410 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
411 "PrimaryKerberos key[1] != DES_CBC_CRC");
414 if (k->ctr.ctr3.keys[0].value_len != 8) {
415 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
416 "PrimaryKerberos key[0] value_len != 8");
418 if (k->ctr.ctr3.keys[1].value_len != 8) {
419 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
420 "PrimaryKerberos key[1] value_len != 8");
423 for (i = 0; i < k->ctr.ctr3.num_old_keys; i++) {
424 if (k->ctr.ctr3.old_keys[i].keytype ==
425 k->ctr.ctr3.keys[i].keytype &&
426 k->ctr.ctr3.old_keys[i].value_len ==
427 k->ctr.ctr3.keys[i].value_len) {
431 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
432 "PrimaryKerberos old_keys type/value_len doesn't match");
439 struct package_PrimaryKerberosBlob *k;
441 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
443 return ldb_module_oom(module);
446 ndr_err = ndr_pull_struct_blob(&scpbkn, k, k,
447 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
448 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
449 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
450 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
453 if (k->version != 4) {
454 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
455 "KerberosNerverKeys version != 4");
458 if (k->ctr.ctr4.salt.string == NULL) {
459 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
460 "KerberosNewerKeys salt == NULL");
463 if (strlen(k->ctr.ctr4.salt.string) == 0) {
464 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
465 "KerberosNewerKeys strlen(salt) == 0");
468 if (k->ctr.ctr4.num_keys != 4) {
469 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
470 "KerberosNewerKeys num_keys != 2");
473 if (k->ctr.ctr4.num_old_keys > k->ctr.ctr4.num_keys) {
474 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
475 "KerberosNewerKeys num_old_keys > num_keys");
478 if (k->ctr.ctr4.num_older_keys > k->ctr.ctr4.num_old_keys) {
479 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
480 "KerberosNewerKeys num_older_keys > num_old_keys");
483 if (k->ctr.ctr4.keys[0].keytype != ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
484 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
485 "KerberosNewerKeys key[0] != AES256");
487 if (k->ctr.ctr4.keys[1].keytype != ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
488 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
489 "KerberosNewerKeys key[1] != AES128");
491 if (k->ctr.ctr4.keys[2].keytype != ENCTYPE_DES_CBC_MD5) {
492 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
493 "KerberosNewerKeys key[2] != DES_CBC_MD5");
495 if (k->ctr.ctr4.keys[3].keytype != ENCTYPE_DES_CBC_CRC) {
496 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
497 "KerberosNewerKeys key[3] != DES_CBC_CRC");
500 if (k->ctr.ctr4.keys[0].value_len != 32) {
501 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
502 "KerberosNewerKeys key[0] value_len != 32");
504 if (k->ctr.ctr4.keys[1].value_len != 16) {
505 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
506 "KerberosNewerKeys key[1] value_len != 16");
508 if (k->ctr.ctr4.keys[2].value_len != 8) {
509 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
510 "KerberosNewerKeys key[2] value_len != 8");
512 if (k->ctr.ctr4.keys[3].value_len != 8) {
513 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
514 "KerberosNewerKeys key[3] value_len != 8");
519 * Maybe we can check old and older keys here.
520 * But we need to do some tests, if the old keys
521 * can be taken from the PrimaryKerberos blob
522 * (with only des keys), when the domain was upgraded
530 struct package_PrimaryCLEARTEXTBlob *ct;
532 ct = talloc_zero(scb, struct package_PrimaryCLEARTEXTBlob);
534 return ldb_module_oom(module);
537 ndr_err = ndr_pull_struct_blob(&scpbct, ct, ct,
538 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryCLEARTEXTBlob);
539 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
540 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
541 "ndr_pull_struct_blob PrimaryCLEARTEXT");
544 if ((ct->cleartext.length % 2) != 0) {
545 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
546 "PrimaryCLEARTEXT length % 2 != 0");
552 ndr_err = ndr_push_struct_blob(&blob, scb, scb,
553 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
554 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
555 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
556 "ndr_pull_struct_blob_all");
559 if (sce->values[0].length != blob.length) {
560 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
561 "supplementalCredentialsBlob length differ");
564 if (memcmp(sce->values[0].data, blob.data, blob.length) != 0) {
565 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
566 "supplementalCredentialsBlob memcmp differ");
572 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_bypass - validated\n");
573 return ldb_next_request(module, request);
576 /* Get the NT hash, and fill it in as an entry in the password history,
577 and specify it into io->g.nt_hash */
579 static int setup_nt_fields(struct setup_password_fields_io *io)
581 struct ldb_context *ldb;
584 io->g.nt_hash = io->n.nt_hash;
585 ldb = ldb_module_get_ctx(io->ac->module);
587 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
591 /* We might not have an old NT password */
592 io->g.nt_history = talloc_array(io->ac,
593 struct samr_Password,
594 io->ac->status->domain_data.pwdHistoryLength);
595 if (!io->g.nt_history) {
599 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
600 io->o.nt_history_len); i++) {
601 io->g.nt_history[i+1] = io->o.nt_history[i];
603 io->g.nt_history_len = i + 1;
606 io->g.nt_history[0] = *io->g.nt_hash;
609 * TODO: is this correct?
610 * the simular behavior is correct for the lm history case
612 E_md4hash("", io->g.nt_history[0].hash);
618 /* Get the LANMAN hash, and fill it in as an entry in the password history,
619 and specify it into io->g.lm_hash */
621 static int setup_lm_fields(struct setup_password_fields_io *io)
623 struct ldb_context *ldb;
626 io->g.lm_hash = io->n.lm_hash;
627 ldb = ldb_module_get_ctx(io->ac->module);
629 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
633 /* We might not have an old LM password */
634 io->g.lm_history = talloc_array(io->ac,
635 struct samr_Password,
636 io->ac->status->domain_data.pwdHistoryLength);
637 if (!io->g.lm_history) {
641 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
642 io->o.lm_history_len); i++) {
643 io->g.lm_history[i+1] = io->o.lm_history[i];
645 io->g.lm_history_len = i + 1;
648 io->g.lm_history[0] = *io->g.lm_hash;
650 E_deshash("", io->g.lm_history[0].hash);
656 static int setup_kerberos_keys(struct setup_password_fields_io *io)
658 struct ldb_context *ldb;
659 krb5_error_code krb5_ret;
660 krb5_principal salt_principal;
663 krb5_data cleartext_data;
665 ldb = ldb_module_get_ctx(io->ac->module);
666 cleartext_data.data = (char *)io->n.cleartext_utf8->data;
667 cleartext_data.length = io->n.cleartext_utf8->length;
669 /* Many, many thanks to lukeh@padl.com for this
670 * algorithm, described in his Nov 10 2004 mail to
671 * samba-technical@lists.samba.org */
674 * Determine a salting principal
676 if (io->u.is_computer) {
680 name = strlower_talloc(io->ac, io->u.sAMAccountName);
685 if (name[strlen(name)-1] == '$') {
686 name[strlen(name)-1] = '\0';
689 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
690 io->ac->status->domain_data.dns_domain);
695 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
697 io->ac->status->domain_data.realm,
698 "host", saltbody, NULL);
699 } else if (io->u.user_principal_name) {
700 char *user_principal_name;
703 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
704 if (!user_principal_name) {
708 p = strchr(user_principal_name, '@');
713 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
715 io->ac->status->domain_data.realm,
716 user_principal_name, NULL);
718 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
720 io->ac->status->domain_data.realm,
721 io->u.sAMAccountName, NULL);
724 ldb_asprintf_errstring(ldb,
725 "setup_kerberos_keys: "
726 "generation of a salting principal failed: %s",
727 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
729 return LDB_ERR_OPERATIONS_ERROR;
733 * create salt from salt_principal
735 krb5_ret = smb_krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
736 salt_principal, &salt);
737 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
739 ldb_asprintf_errstring(ldb,
740 "setup_kerberos_keys: "
741 "generation of krb5_salt failed: %s",
742 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
744 return LDB_ERR_OPERATIONS_ERROR;
746 /* create a talloc copy */
747 io->g.salt = talloc_strndup(io->ac,
750 kerberos_free_data_contents(io->smb_krb5_context->krb5_context, &salt);
754 /* now use the talloced copy of the salt */
755 salt.data = discard_const(io->g.salt);
756 salt.length = strlen(io->g.salt);
759 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
760 * the salt and the cleartext password
762 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
766 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
769 ldb_asprintf_errstring(ldb,
770 "setup_kerberos_keys: "
771 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
772 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
774 return LDB_ERR_OPERATIONS_ERROR;
776 io->g.aes_256 = data_blob_talloc(io->ac,
778 KRB5_KEY_LENGTH(&key));
779 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
780 if (!io->g.aes_256.data) {
785 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
786 * the salt and the cleartext password
788 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
792 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
795 ldb_asprintf_errstring(ldb,
796 "setup_kerberos_keys: "
797 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
798 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
800 return LDB_ERR_OPERATIONS_ERROR;
802 io->g.aes_128 = data_blob_talloc(io->ac,
804 KRB5_KEY_LENGTH(&key));
805 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
806 if (!io->g.aes_128.data) {
811 * create ENCTYPE_DES_CBC_MD5 key out of
812 * the salt and the cleartext password
814 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
821 ldb_asprintf_errstring(ldb,
822 "setup_kerberos_keys: "
823 "generation of a des-cbc-md5 key failed: %s",
824 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
826 return LDB_ERR_OPERATIONS_ERROR;
828 io->g.des_md5 = data_blob_talloc(io->ac,
830 KRB5_KEY_LENGTH(&key));
831 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
832 if (!io->g.des_md5.data) {
837 * create ENCTYPE_DES_CBC_CRC key out of
838 * the salt and the cleartext password
840 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
847 ldb_asprintf_errstring(ldb,
848 "setup_kerberos_keys: "
849 "generation of a des-cbc-crc key failed: %s",
850 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
852 return LDB_ERR_OPERATIONS_ERROR;
854 io->g.des_crc = data_blob_talloc(io->ac,
856 KRB5_KEY_LENGTH(&key));
857 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
858 if (!io->g.des_crc.data) {
865 static int setup_primary_kerberos(struct setup_password_fields_io *io,
866 const struct supplementalCredentialsBlob *old_scb,
867 struct package_PrimaryKerberosBlob *pkb)
869 struct ldb_context *ldb;
870 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
871 struct supplementalCredentialsPackage *old_scp = NULL;
872 struct package_PrimaryKerberosBlob _old_pkb;
873 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
875 enum ndr_err_code ndr_err;
877 ldb = ldb_module_get_ctx(io->ac->module);
880 * prepare generation of keys
882 * ENCTYPE_DES_CBC_MD5
883 * ENCTYPE_DES_CBC_CRC
886 pkb3->salt.string = io->g.salt;
888 pkb3->keys = talloc_array(io->ac,
889 struct package_PrimaryKerberosKey3,
895 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
896 pkb3->keys[0].value = &io->g.des_md5;
897 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
898 pkb3->keys[1].value = &io->g.des_crc;
900 /* initialize the old keys to zero */
901 pkb3->num_old_keys = 0;
902 pkb3->old_keys = NULL;
904 /* if there're no old keys, then we're done */
909 for (i=0; i < old_scb->sub.num_packages; i++) {
910 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
914 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
918 old_scp = &old_scb->sub.packages[i];
921 /* Primary:Kerberos element of supplementalCredentials */
925 blob = strhex_to_data_blob(io->ac, old_scp->data);
930 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
931 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
932 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
933 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
934 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
935 ldb_asprintf_errstring(ldb,
936 "setup_primary_kerberos: "
937 "failed to pull old package_PrimaryKerberosBlob: %s",
939 return LDB_ERR_OPERATIONS_ERROR;
942 if (_old_pkb.version != 3) {
943 ldb_asprintf_errstring(ldb,
944 "setup_primary_kerberos: "
945 "package_PrimaryKerberosBlob version[%u] expected[3]",
947 return LDB_ERR_OPERATIONS_ERROR;
950 old_pkb3 = &_old_pkb.ctr.ctr3;
953 /* if we didn't found the old keys we're done */
958 /* fill in the old keys */
959 pkb3->num_old_keys = old_pkb3->num_keys;
960 pkb3->old_keys = old_pkb3->keys;
965 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
966 const struct supplementalCredentialsBlob *old_scb,
967 struct package_PrimaryKerberosBlob *pkb)
969 struct ldb_context *ldb;
970 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
971 struct supplementalCredentialsPackage *old_scp = NULL;
972 struct package_PrimaryKerberosBlob _old_pkb;
973 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
975 enum ndr_err_code ndr_err;
977 ldb = ldb_module_get_ctx(io->ac->module);
980 * prepare generation of keys
982 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
983 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
984 * ENCTYPE_DES_CBC_MD5
985 * ENCTYPE_DES_CBC_CRC
988 pkb4->salt.string = io->g.salt;
989 pkb4->default_iteration_count = 4096;
992 pkb4->keys = talloc_array(io->ac,
993 struct package_PrimaryKerberosKey4,
999 pkb4->keys[0].iteration_count = 4096;
1000 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
1001 pkb4->keys[0].value = &io->g.aes_256;
1002 pkb4->keys[1].iteration_count = 4096;
1003 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
1004 pkb4->keys[1].value = &io->g.aes_128;
1005 pkb4->keys[2].iteration_count = 4096;
1006 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
1007 pkb4->keys[2].value = &io->g.des_md5;
1008 pkb4->keys[3].iteration_count = 4096;
1009 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
1010 pkb4->keys[3].value = &io->g.des_crc;
1012 /* initialize the old keys to zero */
1013 pkb4->num_old_keys = 0;
1014 pkb4->old_keys = NULL;
1015 pkb4->num_older_keys = 0;
1016 pkb4->older_keys = NULL;
1018 /* if there're no old keys, then we're done */
1023 for (i=0; i < old_scb->sub.num_packages; i++) {
1024 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
1028 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
1032 old_scp = &old_scb->sub.packages[i];
1035 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1039 blob = strhex_to_data_blob(io->ac, old_scp->data);
1041 return ldb_oom(ldb);
1044 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1045 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
1047 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
1048 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1049 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1050 ldb_asprintf_errstring(ldb,
1051 "setup_primary_kerberos_newer: "
1052 "failed to pull old package_PrimaryKerberosBlob: %s",
1054 return LDB_ERR_OPERATIONS_ERROR;
1057 if (_old_pkb.version != 4) {
1058 ldb_asprintf_errstring(ldb,
1059 "setup_primary_kerberos_newer: "
1060 "package_PrimaryKerberosBlob version[%u] expected[4]",
1062 return LDB_ERR_OPERATIONS_ERROR;
1065 old_pkb4 = &_old_pkb.ctr.ctr4;
1068 /* if we didn't found the old keys we're done */
1073 /* fill in the old keys */
1074 pkb4->num_old_keys = old_pkb4->num_keys;
1075 pkb4->old_keys = old_pkb4->keys;
1076 pkb4->num_older_keys = old_pkb4->num_old_keys;
1077 pkb4->older_keys = old_pkb4->old_keys;
1082 static int setup_primary_wdigest(struct setup_password_fields_io *io,
1083 const struct supplementalCredentialsBlob *old_scb,
1084 struct package_PrimaryWDigestBlob *pdb)
1086 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1087 DATA_BLOB sAMAccountName;
1088 DATA_BLOB sAMAccountName_l;
1089 DATA_BLOB sAMAccountName_u;
1090 const char *user_principal_name = io->u.user_principal_name;
1091 DATA_BLOB userPrincipalName;
1092 DATA_BLOB userPrincipalName_l;
1093 DATA_BLOB userPrincipalName_u;
1094 DATA_BLOB netbios_domain;
1095 DATA_BLOB netbios_domain_l;
1096 DATA_BLOB netbios_domain_u;
1097 DATA_BLOB dns_domain;
1098 DATA_BLOB dns_domain_l;
1099 DATA_BLOB dns_domain_u;
1102 DATA_BLOB backslash;
1111 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1112 * for what precalculated hashes are supposed to be stored...
1114 * I can't reproduce all values which should contain "Digest" as realm,
1115 * am I doing something wrong or is w2k3 just broken...?
1117 * W2K3 fills in following for a user:
1119 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1120 * sAMAccountName: NewUser2Sam
1121 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1123 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1124 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1125 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1126 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1127 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1128 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1129 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1130 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1131 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1132 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1133 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1134 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1135 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1136 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1137 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1138 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1139 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1140 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1141 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1142 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1143 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1144 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1145 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1146 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1147 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1148 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1149 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1150 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1151 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1153 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1154 * sAMAccountName: NewUser2Sam
1156 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1157 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1158 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1159 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1160 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1161 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1162 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1163 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1164 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1165 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1166 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1167 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1168 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1169 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1170 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1171 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1172 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1173 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1174 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1175 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1176 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1177 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1178 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1179 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1180 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1181 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1182 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1183 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1184 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1188 * sAMAccountName, netbios_domain
1191 .user = &sAMAccountName,
1192 .realm = &netbios_domain,
1195 .user = &sAMAccountName_l,
1196 .realm = &netbios_domain_l,
1199 .user = &sAMAccountName_u,
1200 .realm = &netbios_domain_u,
1203 .user = &sAMAccountName,
1204 .realm = &netbios_domain_u,
1207 .user = &sAMAccountName,
1208 .realm = &netbios_domain_l,
1211 .user = &sAMAccountName_u,
1212 .realm = &netbios_domain_l,
1215 .user = &sAMAccountName_l,
1216 .realm = &netbios_domain_u,
1219 * sAMAccountName, dns_domain
1222 .user = &sAMAccountName,
1223 .realm = &dns_domain,
1226 .user = &sAMAccountName_l,
1227 .realm = &dns_domain_l,
1230 .user = &sAMAccountName_u,
1231 .realm = &dns_domain_u,
1234 .user = &sAMAccountName,
1235 .realm = &dns_domain_u,
1238 .user = &sAMAccountName,
1239 .realm = &dns_domain_l,
1242 .user = &sAMAccountName_u,
1243 .realm = &dns_domain_l,
1246 .user = &sAMAccountName_l,
1247 .realm = &dns_domain_u,
1250 * userPrincipalName, no realm
1253 .user = &userPrincipalName,
1257 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1258 * the fallback to the sAMAccountName based userPrincipalName is correct
1260 .user = &userPrincipalName_l,
1263 .user = &userPrincipalName_u,
1266 * nt4dom\sAMAccountName, no realm
1269 .user = &sAMAccountName,
1270 .nt4dom = &netbios_domain
1273 .user = &sAMAccountName_l,
1274 .nt4dom = &netbios_domain_l
1277 .user = &sAMAccountName_u,
1278 .nt4dom = &netbios_domain_u
1282 * the following ones are guessed depending on the technet2 article
1283 * but not reproducable on a w2k3 server
1285 /* sAMAccountName with "Digest" realm */
1287 .user = &sAMAccountName,
1291 .user = &sAMAccountName_l,
1295 .user = &sAMAccountName_u,
1298 /* userPrincipalName with "Digest" realm */
1300 .user = &userPrincipalName,
1304 .user = &userPrincipalName_l,
1308 .user = &userPrincipalName_u,
1311 /* nt4dom\\sAMAccountName with "Digest" realm */
1313 .user = &sAMAccountName,
1314 .nt4dom = &netbios_domain,
1318 .user = &sAMAccountName_l,
1319 .nt4dom = &netbios_domain_l,
1323 .user = &sAMAccountName_u,
1324 .nt4dom = &netbios_domain_u,
1329 /* prepare DATA_BLOB's used in the combinations array */
1330 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
1331 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
1332 if (!sAMAccountName_l.data) {
1333 return ldb_oom(ldb);
1335 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
1336 if (!sAMAccountName_u.data) {
1337 return ldb_oom(ldb);
1340 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1341 if (!user_principal_name) {
1342 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
1343 io->u.sAMAccountName,
1344 io->ac->status->domain_data.dns_domain);
1345 if (!user_principal_name) {
1346 return ldb_oom(ldb);
1349 userPrincipalName = data_blob_string_const(user_principal_name);
1350 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
1351 if (!userPrincipalName_l.data) {
1352 return ldb_oom(ldb);
1354 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
1355 if (!userPrincipalName_u.data) {
1356 return ldb_oom(ldb);
1359 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
1360 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
1361 io->ac->status->domain_data.netbios_domain));
1362 if (!netbios_domain_l.data) {
1363 return ldb_oom(ldb);
1365 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
1366 io->ac->status->domain_data.netbios_domain));
1367 if (!netbios_domain_u.data) {
1368 return ldb_oom(ldb);
1371 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1372 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1373 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
1375 digest = data_blob_string_const("Digest");
1377 delim = data_blob_string_const(":");
1378 backslash = data_blob_string_const("\\");
1380 pdb->num_hashes = ARRAY_SIZE(wdigest);
1381 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
1384 return ldb_oom(ldb);
1387 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
1390 if (wdigest[i].nt4dom) {
1391 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
1392 MD5Update(&md5, backslash.data, backslash.length);
1394 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
1395 MD5Update(&md5, delim.data, delim.length);
1396 if (wdigest[i].realm) {
1397 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1399 MD5Update(&md5, delim.data, delim.length);
1400 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1401 MD5Final(pdb->hashes[i].hash, &md5);
1407 static int setup_supplemental_field(struct setup_password_fields_io *io)
1409 struct ldb_context *ldb;
1410 struct supplementalCredentialsBlob scb;
1411 struct supplementalCredentialsBlob *old_scb = NULL;
1412 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1413 uint32_t num_names = 0;
1414 const char *names[1+4];
1415 uint32_t num_packages = 0;
1416 struct supplementalCredentialsPackage packages[1+4];
1418 struct supplementalCredentialsPackage *pp = NULL;
1419 struct package_PackagesBlob pb;
1422 /* Primary:Kerberos-Newer-Keys */
1423 const char **nkn = NULL;
1424 struct supplementalCredentialsPackage *pkn = NULL;
1425 struct package_PrimaryKerberosBlob pknb;
1426 DATA_BLOB pknb_blob;
1428 /* Primary:Kerberos */
1429 const char **nk = NULL;
1430 struct supplementalCredentialsPackage *pk = NULL;
1431 struct package_PrimaryKerberosBlob pkb;
1434 /* Primary:WDigest */
1435 const char **nd = NULL;
1436 struct supplementalCredentialsPackage *pd = NULL;
1437 struct package_PrimaryWDigestBlob pdb;
1440 /* Primary:CLEARTEXT */
1441 const char **nc = NULL;
1442 struct supplementalCredentialsPackage *pc = NULL;
1443 struct package_PrimaryCLEARTEXTBlob pcb;
1447 enum ndr_err_code ndr_err;
1449 bool do_newer_keys = false;
1450 bool do_cleartext = false;
1452 ZERO_STRUCT(zero16);
1455 ldb = ldb_module_get_ctx(io->ac->module);
1457 if (!io->n.cleartext_utf8) {
1459 * when we don't have a cleartext password
1460 * we can't setup a supplementalCredential value
1465 /* if there's an old supplementaCredentials blob then use it */
1466 if (io->o.supplemental) {
1467 if (io->o.scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1468 old_scb = &io->o.scb;
1470 ldb_debug(ldb, LDB_DEBUG_ERROR,
1471 "setup_supplemental_field: "
1472 "supplementalCredentialsBlob "
1473 "signature[0x%04X] expected[0x%04X]",
1474 io->o.scb.sub.signature,
1475 SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1478 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1479 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1481 if (io->ac->status->domain_data.store_cleartext &&
1482 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1483 do_cleartext = true;
1487 * The ordering is this
1489 * Primary:Kerberos-Newer-Keys (optional)
1492 * Primary:CLEARTEXT (optional)
1494 * And the 'Packages' package is insert before the last
1497 if (do_newer_keys) {
1498 /* Primary:Kerberos-Newer-Keys */
1499 nkn = &names[num_names++];
1500 pkn = &packages[num_packages++];
1503 /* Primary:Kerberos */
1504 nk = &names[num_names++];
1505 pk = &packages[num_packages++];
1507 if (!do_cleartext) {
1509 pp = &packages[num_packages++];
1512 /* Primary:WDigest */
1513 nd = &names[num_names++];
1514 pd = &packages[num_packages++];
1518 pp = &packages[num_packages++];
1520 /* Primary:CLEARTEXT */
1521 nc = &names[num_names++];
1522 pc = &packages[num_packages++];
1527 * setup 'Primary:Kerberos-Newer-Keys' element
1529 *nkn = "Kerberos-Newer-Keys";
1531 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1532 if (ret != LDB_SUCCESS) {
1536 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1538 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1539 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1540 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1541 ldb_asprintf_errstring(ldb,
1542 "setup_supplemental_field: "
1543 "failed to push package_PrimaryKerberosNeverBlob: %s",
1545 return LDB_ERR_OPERATIONS_ERROR;
1547 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1549 return ldb_oom(ldb);
1551 pkn->name = "Primary:Kerberos-Newer-Keys";
1553 pkn->data = pknb_hexstr;
1557 * setup 'Primary:Kerberos' element
1561 ret = setup_primary_kerberos(io, old_scb, &pkb);
1562 if (ret != LDB_SUCCESS) {
1566 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1568 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1569 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1570 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1571 ldb_asprintf_errstring(ldb,
1572 "setup_supplemental_field: "
1573 "failed to push package_PrimaryKerberosBlob: %s",
1575 return LDB_ERR_OPERATIONS_ERROR;
1577 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1579 return ldb_oom(ldb);
1581 pk->name = "Primary:Kerberos";
1583 pk->data = pkb_hexstr;
1586 * setup 'Primary:WDigest' element
1590 ret = setup_primary_wdigest(io, old_scb, &pdb);
1591 if (ret != LDB_SUCCESS) {
1595 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1597 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1598 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1599 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1600 ldb_asprintf_errstring(ldb,
1601 "setup_supplemental_field: "
1602 "failed to push package_PrimaryWDigestBlob: %s",
1604 return LDB_ERR_OPERATIONS_ERROR;
1606 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1608 return ldb_oom(ldb);
1610 pd->name = "Primary:WDigest";
1612 pd->data = pdb_hexstr;
1615 * setup 'Primary:CLEARTEXT' element
1620 pcb.cleartext = *io->n.cleartext_utf16;
1622 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1624 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1625 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1626 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1627 ldb_asprintf_errstring(ldb,
1628 "setup_supplemental_field: "
1629 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1631 return LDB_ERR_OPERATIONS_ERROR;
1633 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1635 return ldb_oom(ldb);
1637 pc->name = "Primary:CLEARTEXT";
1639 pc->data = pcb_hexstr;
1643 * setup 'Packages' element
1646 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1648 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1649 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1650 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1651 ldb_asprintf_errstring(ldb,
1652 "setup_supplemental_field: "
1653 "failed to push package_PackagesBlob: %s",
1655 return LDB_ERR_OPERATIONS_ERROR;
1657 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1659 return ldb_oom(ldb);
1661 pp->name = "Packages";
1663 pp->data = pb_hexstr;
1666 * setup 'supplementalCredentials' value
1669 scb.sub.num_packages = num_packages;
1670 scb.sub.packages = packages;
1672 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1674 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1675 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1676 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1677 ldb_asprintf_errstring(ldb,
1678 "setup_supplemental_field: "
1679 "failed to push supplementalCredentialsBlob: %s",
1681 return LDB_ERR_OPERATIONS_ERROR;
1687 static int setup_last_set_field(struct setup_password_fields_io *io)
1689 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1690 const struct ldb_message *msg = NULL;
1691 struct timeval tv = { .tv_sec = 0 };
1692 const struct ldb_val *old_val = NULL;
1693 const struct ldb_val *new_val = NULL;
1696 switch (io->ac->req->operation) {
1698 msg = io->ac->req->op.add.message;
1701 msg = io->ac->req->op.mod.message;
1704 return LDB_ERR_OPERATIONS_ERROR;
1708 if (io->ac->pwd_last_set_bypass) {
1709 struct ldb_message_element *el1 = NULL;
1710 struct ldb_message_element *el2 = NULL;
1713 return LDB_ERR_CONSTRAINT_VIOLATION;
1716 el1 = dsdb_get_single_valued_attr(msg, "pwdLastSet",
1717 io->ac->req->operation);
1719 return LDB_ERR_CONSTRAINT_VIOLATION;
1721 el2 = ldb_msg_find_element(msg, "pwdLastSet");
1723 return LDB_ERR_CONSTRAINT_VIOLATION;
1726 return LDB_ERR_CONSTRAINT_VIOLATION;
1729 io->g.last_set = samdb_result_nttime(msg, "pwdLastSet", 0);
1733 ret = msg_find_old_and_new_pwd_val(msg, "pwdLastSet",
1734 io->ac->req->operation,
1735 &new_val, &old_val);
1736 if (ret != LDB_SUCCESS) {
1740 if (old_val != NULL && new_val == NULL) {
1741 ldb_set_errstring(ldb,
1742 "'pwdLastSet' deletion is not allowed!");
1743 return LDB_ERR_UNWILLING_TO_PERFORM;
1746 io->g.last_set = UINT64_MAX;
1747 if (new_val != NULL) {
1748 struct ldb_message *tmp_msg = NULL;
1750 tmp_msg = ldb_msg_new(io->ac);
1751 if (tmp_msg == NULL) {
1752 return ldb_module_oom(io->ac->module);
1755 if (old_val != NULL) {
1756 NTTIME old_last_set = 0;
1758 ret = ldb_msg_add_value(tmp_msg, "oldval",
1760 if (ret != LDB_SUCCESS) {
1764 old_last_set = samdb_result_nttime(tmp_msg,
1767 if (io->u.pwdLastSet != old_last_set) {
1768 return dsdb_module_werror(io->ac->module,
1769 LDB_ERR_NO_SUCH_ATTRIBUTE,
1770 WERR_DS_CANT_REM_MISSING_ATT_VAL,
1771 "setup_last_set_field: old pwdLastSet "
1772 "value not found!");
1776 ret = ldb_msg_add_value(tmp_msg, "newval",
1778 if (ret != LDB_SUCCESS) {
1782 io->g.last_set = samdb_result_nttime(tmp_msg,
1785 } else if (ldb_msg_find_element(msg, "pwdLastSet")) {
1786 ldb_set_errstring(ldb,
1787 "'pwdLastSet' deletion is not allowed!");
1788 return LDB_ERR_UNWILLING_TO_PERFORM;
1791 /* only 0 or -1 (0xFFFFFFFFFFFFFFFF) are allowed */
1792 switch (io->g.last_set) {
1794 if (!io->ac->pwd_last_set_default) {
1797 if (!io->ac->update_password) {
1802 if (!io->ac->update_password && io->u.pwdLastSet != 0) {
1804 * Just setting pwdLastSet to -1, while not changing
1805 * any password field has no effect if pwdLastSet
1806 * is already non-zero.
1808 io->ac->update_lastset = false;
1811 /* -1 means set it as now */
1815 io->g.last_set = timeval_to_nttime(&tv);
1819 if (io->ac->req->operation == LDB_ADD) {
1821 * We always need to store the value on add
1827 if (io->g.last_set == io->u.pwdLastSet) {
1829 * Just setting pwdLastSet to 0, is no-op if it's already 0.
1831 io->ac->update_lastset = false;
1837 static int setup_given_passwords(struct setup_password_fields_io *io,
1838 struct setup_password_fields_given *g)
1840 struct ldb_context *ldb;
1843 ldb = ldb_module_get_ctx(io->ac->module);
1845 if (g->cleartext_utf8) {
1846 struct ldb_val *cleartext_utf16_blob;
1848 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1849 if (!cleartext_utf16_blob) {
1850 return ldb_oom(ldb);
1852 if (!convert_string_talloc(io->ac,
1854 g->cleartext_utf8->data,
1855 g->cleartext_utf8->length,
1856 (void *)&cleartext_utf16_blob->data,
1857 &cleartext_utf16_blob->length)) {
1858 if (g->cleartext_utf8->length != 0) {
1859 talloc_free(cleartext_utf16_blob);
1860 ldb_asprintf_errstring(ldb,
1861 "setup_password_fields: "
1862 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1863 io->u.sAMAccountName);
1864 return LDB_ERR_CONSTRAINT_VIOLATION;
1866 /* passwords with length "0" are valid! */
1867 cleartext_utf16_blob->data = NULL;
1868 cleartext_utf16_blob->length = 0;
1871 g->cleartext_utf16 = cleartext_utf16_blob;
1872 } else if (g->cleartext_utf16) {
1873 struct ldb_val *cleartext_utf8_blob;
1875 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1876 if (!cleartext_utf8_blob) {
1877 return ldb_oom(ldb);
1879 if (!convert_string_talloc(io->ac,
1880 CH_UTF16MUNGED, CH_UTF8,
1881 g->cleartext_utf16->data,
1882 g->cleartext_utf16->length,
1883 (void *)&cleartext_utf8_blob->data,
1884 &cleartext_utf8_blob->length)) {
1885 if (g->cleartext_utf16->length != 0) {
1886 /* We must bail out here, the input wasn't even
1887 * a multiple of 2 bytes */
1888 talloc_free(cleartext_utf8_blob);
1889 ldb_asprintf_errstring(ldb,
1890 "setup_password_fields: "
1891 "failed to generate UTF8 password from cleartext UTF 16 one for user '%s' - the latter had odd length (length must be a multiple of 2)!",
1892 io->u.sAMAccountName);
1893 return LDB_ERR_CONSTRAINT_VIOLATION;
1895 /* passwords with length "0" are valid! */
1896 cleartext_utf8_blob->data = NULL;
1897 cleartext_utf8_blob->length = 0;
1900 g->cleartext_utf8 = cleartext_utf8_blob;
1903 if (g->cleartext_utf16) {
1904 struct samr_Password *nt_hash;
1906 nt_hash = talloc(io->ac, struct samr_Password);
1908 return ldb_oom(ldb);
1910 g->nt_hash = nt_hash;
1912 /* compute the new nt hash */
1913 mdfour(nt_hash->hash,
1914 g->cleartext_utf16->data,
1915 g->cleartext_utf16->length);
1918 if (g->cleartext_utf8) {
1919 struct samr_Password *lm_hash;
1921 lm_hash = talloc(io->ac, struct samr_Password);
1923 return ldb_oom(ldb);
1926 /* compute the new lm hash */
1927 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1929 g->lm_hash = lm_hash;
1931 talloc_free(lm_hash);
1938 static int setup_password_fields(struct setup_password_fields_io *io)
1940 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1941 struct loadparm_context *lp_ctx =
1942 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1943 struct loadparm_context);
1946 ret = setup_last_set_field(io);
1947 if (ret != LDB_SUCCESS) {
1951 if (!io->ac->update_password) {
1955 /* transform the old password (for password changes) */
1956 ret = setup_given_passwords(io, &io->og);
1957 if (ret != LDB_SUCCESS) {
1961 /* transform the new password */
1962 ret = setup_given_passwords(io, &io->n);
1963 if (ret != LDB_SUCCESS) {
1967 if (io->n.cleartext_utf8) {
1968 ret = setup_kerberos_keys(io);
1969 if (ret != LDB_SUCCESS) {
1974 ret = setup_nt_fields(io);
1975 if (ret != LDB_SUCCESS) {
1979 if (lpcfg_lanman_auth(lp_ctx)) {
1980 ret = setup_lm_fields(io);
1981 if (ret != LDB_SUCCESS) {
1985 io->g.lm_hash = NULL;
1986 io->g.lm_history_len = 0;
1989 ret = setup_supplemental_field(io);
1990 if (ret != LDB_SUCCESS) {
1997 static int make_error_and_update_badPwdCount(struct setup_password_fields_io *io)
1999 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
2000 struct ldb_message *mod_msg = NULL;
2004 status = dsdb_update_bad_pwd_count(io->ac, ldb,
2005 io->ac->search_res->message,
2006 io->ac->dom_res->message,
2008 if (!NT_STATUS_IS_OK(status)) {
2012 if (mod_msg == NULL) {
2017 * OK, horrible semantics ahead.
2019 * - We need to abort any existing transaction
2020 * - create a transaction arround the badPwdCount update
2021 * - re-open the transaction so the upper layer
2022 * doesn't know what happened.
2024 * This is needed because returning an error to the upper
2025 * layer will cancel the transaction and undo the badPwdCount
2030 * Checking errors here is a bit pointless.
2031 * What can we do if we can't end the transaction?
2033 ret = ldb_next_del_trans(io->ac->module);
2034 if (ret != LDB_SUCCESS) {
2035 ldb_debug(ldb, LDB_DEBUG_FATAL,
2036 "Failed to abort transaction prior to update of badPwdCount of %s: %s",
2037 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2038 ldb_errstring(ldb));
2040 * just return the original error
2045 /* Likewise, what should we do if we can't open a new transaction? */
2046 ret = ldb_next_start_trans(io->ac->module);
2047 if (ret != LDB_SUCCESS) {
2048 ldb_debug(ldb, LDB_DEBUG_ERROR,
2049 "Failed to open transaction to update badPwdCount of %s: %s",
2050 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2051 ldb_errstring(ldb));
2053 * just return the original error
2058 ret = dsdb_module_modify(io->ac->module, mod_msg,
2059 DSDB_FLAG_NEXT_MODULE,
2061 if (ret != LDB_SUCCESS) {
2062 ldb_debug(ldb, LDB_DEBUG_ERROR,
2063 "Failed to update badPwdCount of %s: %s",
2064 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2065 ldb_errstring(ldb));
2067 * We can only ignore this...
2071 ret = ldb_next_end_trans(io->ac->module);
2072 if (ret != LDB_SUCCESS) {
2073 ldb_debug(ldb, LDB_DEBUG_ERROR,
2074 "Failed to close transaction to update badPwdCount of %s: %s",
2075 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2076 ldb_errstring(ldb));
2078 * We can only ignore this...
2082 ret = ldb_next_start_trans(io->ac->module);
2083 if (ret != LDB_SUCCESS) {
2084 ldb_debug(ldb, LDB_DEBUG_ERROR,
2085 "Failed to open transaction after update of badPwdCount of %s: %s",
2086 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2087 ldb_errstring(ldb));
2089 * We can only ignore this...
2094 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2095 ldb_asprintf_errstring(ldb,
2096 "%08X: %s - check_password_restrictions: "
2097 "The old password specified doesn't match!",
2098 W_ERROR_V(WERR_INVALID_PASSWORD),
2103 static int check_password_restrictions(struct setup_password_fields_io *io)
2105 struct ldb_context *ldb;
2108 ldb = ldb_module_get_ctx(io->ac->module);
2110 if (!io->ac->update_password) {
2114 /* First check the old password is correct, for password changes */
2115 if (!io->ac->pwd_reset) {
2116 bool nt_hash_checked = false;
2118 /* we need the old nt or lm hash given by the client */
2119 if (!io->og.nt_hash && !io->og.lm_hash) {
2120 ldb_asprintf_errstring(ldb,
2121 "check_password_restrictions: "
2122 "You need to provide the old password in order "
2124 return LDB_ERR_UNWILLING_TO_PERFORM;
2127 /* The password modify through the NT hash is encouraged and
2128 has no problems at all */
2129 if (io->og.nt_hash) {
2130 if (!io->o.nt_hash || memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
2131 return make_error_and_update_badPwdCount(io);
2134 nt_hash_checked = true;
2137 /* But it is also possible to change a password by the LM hash
2138 * alone for compatibility reasons. This check is optional if
2139 * the NT hash was already checked - otherwise it's mandatory.
2140 * (as the SAMR operations request it). */
2141 if (io->og.lm_hash) {
2142 if ((!io->o.lm_hash && !nt_hash_checked)
2143 || (io->o.lm_hash && memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0)) {
2144 return make_error_and_update_badPwdCount(io);
2149 if (io->u.restrictions == 0) {
2150 /* FIXME: Is this right? */
2154 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2155 if ((io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) &&
2158 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2159 ldb_asprintf_errstring(ldb,
2160 "%08X: %s - check_password_restrictions: "
2161 "password is too young to change!",
2162 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2168 * Fundamental password checks done by the call
2169 * "samdb_check_password".
2170 * It is also in use by "dcesrv_samr_ValidatePassword".
2172 if (io->n.cleartext_utf8 != NULL) {
2173 enum samr_ValidationStatus vstat;
2174 vstat = samdb_check_password(io->n.cleartext_utf8,
2175 io->ac->status->domain_data.pwdProperties,
2176 io->ac->status->domain_data.minPwdLength);
2178 case SAMR_VALIDATION_STATUS_SUCCESS:
2179 /* perfect -> proceed! */
2182 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
2183 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2184 ldb_asprintf_errstring(ldb,
2185 "%08X: %s - check_password_restrictions: "
2186 "the password is too short. It should be equal or longer than %u characters!",
2187 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2189 io->ac->status->domain_data.minPwdLength);
2190 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
2193 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
2194 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2195 ldb_asprintf_errstring(ldb,
2196 "%08X: %s - check_password_restrictions: "
2197 "the password does not meet the complexity criteria!",
2198 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2200 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
2204 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2205 ldb_asprintf_errstring(ldb,
2206 "%08X: %s - check_password_restrictions: "
2207 "the password doesn't fit by a certain reason!",
2208 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2214 if (io->ac->pwd_reset) {
2218 if (io->n.nt_hash) {
2221 /* checks the NT hash password history */
2222 for (i = 0; i < io->o.nt_history_len; i++) {
2223 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
2225 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2226 ldb_asprintf_errstring(ldb,
2227 "%08X: %s - check_password_restrictions: "
2228 "the password was already used (in history)!",
2229 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2231 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2237 if (io->n.lm_hash) {
2240 /* checks the LM hash password history */
2241 for (i = 0; i < io->o.lm_history_len; i++) {
2242 ret = memcmp(io->n.lm_hash, io->o.lm_history[i].hash, 16);
2244 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2245 ldb_asprintf_errstring(ldb,
2246 "%08X: %s - check_password_restrictions: "
2247 "the password was already used (in history)!",
2248 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2250 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2256 /* are all password changes disallowed? */
2257 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
2258 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2259 ldb_asprintf_errstring(ldb,
2260 "%08X: %s - check_password_restrictions: "
2261 "password changes disabled!",
2262 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2267 /* can this user change the password? */
2268 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
2269 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2270 ldb_asprintf_errstring(ldb,
2271 "%08X: %s - check_password_restrictions: "
2272 "password can't be changed on this account!",
2273 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2281 static int update_final_msg(struct setup_password_fields_io *io)
2283 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
2287 if (io->ac->req->operation == LDB_MODIFY) {
2288 el_flags |= LDB_FLAG_MOD_REPLACE;
2291 /* make sure we replace all the old attributes */
2292 if (io->ac->update_password && el_flags != 0) {
2293 ret = ldb_msg_add_empty(io->ac->update_msg,
2296 if (ret != LDB_SUCCESS) {
2299 ret = ldb_msg_add_empty(io->ac->update_msg,
2302 if (ret != LDB_SUCCESS) {
2305 ret = ldb_msg_add_empty(io->ac->update_msg,
2308 if (ret != LDB_SUCCESS) {
2311 ret = ldb_msg_add_empty(io->ac->update_msg,
2314 if (ret != LDB_SUCCESS) {
2317 ret = ldb_msg_add_empty(io->ac->update_msg,
2318 "supplementalCredentials",
2320 if (ret != LDB_SUCCESS) {
2324 if (io->ac->update_lastset && el_flags != 0) {
2325 ret = ldb_msg_add_empty(io->ac->update_msg,
2328 if (ret != LDB_SUCCESS) {
2333 if (io->g.nt_hash != NULL) {
2334 ret = samdb_msg_add_hash(ldb, io->ac,
2338 if (ret != LDB_SUCCESS) {
2342 if (io->g.lm_hash != NULL) {
2343 ret = samdb_msg_add_hash(ldb, io->ac,
2347 if (ret != LDB_SUCCESS) {
2351 if (io->g.nt_history_len > 0) {
2352 ret = samdb_msg_add_hashes(ldb, io->ac,
2356 io->g.nt_history_len);
2357 if (ret != LDB_SUCCESS) {
2361 if (io->g.lm_history_len > 0) {
2362 ret = samdb_msg_add_hashes(ldb, io->ac,
2366 io->g.lm_history_len);
2367 if (ret != LDB_SUCCESS) {
2371 if (io->g.supplemental.length > 0) {
2372 ret = ldb_msg_add_value(io->ac->update_msg,
2373 "supplementalCredentials",
2374 &io->g.supplemental, NULL);
2375 if (ret != LDB_SUCCESS) {
2379 if (io->ac->update_lastset) {
2380 ret = samdb_msg_add_uint64(ldb, io->ac,
2384 if (ret != LDB_SUCCESS) {
2393 * This is intended for use by the "password_hash" module since there
2394 * password changes can be specified through one message element with the
2395 * new password (to set) and another one with the old password (to unset).
2397 * The first which sets a password (new value) can have flags
2398 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2399 * for entries). The latter (old value) has always specified
2400 * LDB_FLAG_MOD_DELETE.
2402 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2403 * matching message elements are malformed in respect to the set/change rules.
2404 * Otherwise it returns LDB_SUCCESS.
2406 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
2408 enum ldb_request_type operation,
2409 const struct ldb_val **new_val,
2410 const struct ldb_val **old_val)
2421 for (i = 0; i < msg->num_elements; i++) {
2422 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
2426 if ((operation == LDB_MODIFY) &&
2427 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
2428 /* 0 values are allowed */
2429 if (msg->elements[i].num_values == 1) {
2430 *old_val = &msg->elements[i].values[0];
2431 } else if (msg->elements[i].num_values > 1) {
2432 return LDB_ERR_CONSTRAINT_VIOLATION;
2434 } else if ((operation == LDB_MODIFY) &&
2435 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
2436 if (msg->elements[i].num_values > 0) {
2437 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2439 return LDB_ERR_UNWILLING_TO_PERFORM;
2442 /* Add operations and LDB_FLAG_MOD_ADD */
2443 if (msg->elements[i].num_values > 0) {
2444 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2446 return LDB_ERR_CONSTRAINT_VIOLATION;
2454 static int setup_io(struct ph_context *ac,
2455 const struct ldb_message *client_msg,
2456 const struct ldb_message *existing_msg,
2457 struct setup_password_fields_io *io)
2459 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
2460 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2461 struct loadparm_context *lp_ctx = talloc_get_type(
2462 ldb_get_opaque(ldb, "loadparm"), struct loadparm_context);
2464 const struct ldb_message *info_msg = NULL;
2468 /* Some operations below require kerberos contexts */
2470 if (existing_msg != NULL) {
2472 * This is a modify operation
2474 info_msg = existing_msg;
2477 * This is an add operation
2479 info_msg = client_msg;
2482 if (smb_krb5_init_context(ac,
2483 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
2484 &io->smb_krb5_context) != 0) {
2485 return ldb_operr(ldb);
2490 io->u.userAccountControl = ldb_msg_find_attr_as_uint(info_msg,
2491 "userAccountControl", 0);
2492 if (info_msg == existing_msg) {
2494 * We only take pwdLastSet from the existing object
2495 * otherwise we leave it as 0.
2497 io->u.pwdLastSet = samdb_result_nttime(info_msg, "pwdLastSet", 0);
2499 io->u.sAMAccountName = ldb_msg_find_attr_as_string(info_msg,
2500 "sAMAccountName", NULL);
2501 io->u.user_principal_name = ldb_msg_find_attr_as_string(info_msg,
2502 "userPrincipalName", NULL);
2503 io->u.is_computer = ldb_msg_check_string_attribute(info_msg, "objectClass", "computer");
2505 if (io->u.sAMAccountName == NULL) {
2506 ldb_asprintf_errstring(ldb,
2507 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2508 ldb_dn_get_linearized(info_msg->dn));
2510 return LDB_ERR_CONSTRAINT_VIOLATION;
2513 if (io->u.userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
2514 struct ldb_control *permit_trust = ldb_request_get_control(ac->req,
2515 DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID);
2517 if (permit_trust == NULL) {
2518 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2519 ldb_asprintf_errstring(ldb,
2520 "%08X: %s - setup_io: changing the interdomain trust password "
2521 "on %s not allowed via LDAP. Use LSA or NETLOGON",
2522 W_ERROR_V(WERR_ACCESS_DENIED),
2524 ldb_dn_get_linearized(info_msg->dn));
2529 /* Only non-trust accounts have restrictions (possibly this test is the
2530 * wrong way around, but we like to be restrictive if possible */
2531 io->u.restrictions = !(io->u.userAccountControl
2532 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
2533 | UF_SERVER_TRUST_ACCOUNT));
2535 if (ac->userPassword) {
2536 ret = msg_find_old_and_new_pwd_val(client_msg, "userPassword",
2538 &io->n.cleartext_utf8,
2539 &io->og.cleartext_utf8);
2540 if (ret != LDB_SUCCESS) {
2541 ldb_asprintf_errstring(ldb,
2543 "it's only allowed to set the old password once!");
2548 if (io->n.cleartext_utf8 != NULL) {
2549 struct ldb_val *cleartext_utf8_blob;
2552 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
2553 if (!cleartext_utf8_blob) {
2554 return ldb_oom(ldb);
2557 *cleartext_utf8_blob = *io->n.cleartext_utf8;
2559 /* make sure we have a null terminated string */
2560 p = talloc_strndup(cleartext_utf8_blob,
2561 (const char *)io->n.cleartext_utf8->data,
2562 io->n.cleartext_utf8->length);
2563 if ((p == NULL) && (io->n.cleartext_utf8->length > 0)) {
2564 return ldb_oom(ldb);
2566 cleartext_utf8_blob->data = (uint8_t *)p;
2568 io->n.cleartext_utf8 = cleartext_utf8_blob;
2571 ret = msg_find_old_and_new_pwd_val(client_msg, "clearTextPassword",
2573 &io->n.cleartext_utf16,
2574 &io->og.cleartext_utf16);
2575 if (ret != LDB_SUCCESS) {
2576 ldb_asprintf_errstring(ldb,
2578 "it's only allowed to set the old password once!");
2582 /* this rather strange looking piece of code is there to
2583 handle a ldap client setting a password remotely using the
2584 unicodePwd ldap field. The syntax is that the password is
2585 in UTF-16LE, with a " at either end. Unfortunately the
2586 unicodePwd field is also used to store the nt hashes
2587 internally in Samba, and is used in the nt hash format on
2588 the wire in DRS replication, so we have a single name for
2589 two distinct values. The code below leaves us with a small
2590 chance (less than 1 in 2^32) of a mixup, if someone manages
2591 to create a MD4 hash which starts and ends in 0x22 0x00, as
2592 that would then be treated as a UTF16 password rather than
2595 ret = msg_find_old_and_new_pwd_val(client_msg, "unicodePwd",
2599 if (ret != LDB_SUCCESS) {
2600 ldb_asprintf_errstring(ldb,
2602 "it's only allowed to set the old password once!");
2606 /* Checks and converts the actual "unicodePwd" attribute */
2607 if (!ac->hash_values &&
2609 quoted_utf16->length >= 4 &&
2610 quoted_utf16->data[0] == '"' &&
2611 quoted_utf16->data[1] == 0 &&
2612 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
2613 quoted_utf16->data[quoted_utf16->length-1] == 0) {
2614 struct ldb_val *quoted_utf16_2;
2616 if (io->n.cleartext_utf16) {
2617 /* refuse the change if someone wants to change with
2618 with both UTF16 possibilities at the same time... */
2619 ldb_asprintf_errstring(ldb,
2621 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2622 return LDB_ERR_UNWILLING_TO_PERFORM;
2626 * adapt the quoted UTF16 string to be a real
2629 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2630 if (quoted_utf16_2 == NULL) {
2631 return ldb_oom(ldb);
2634 quoted_utf16_2->data = quoted_utf16->data + 2;
2635 quoted_utf16_2->length = quoted_utf16->length-4;
2636 io->n.cleartext_utf16 = quoted_utf16_2;
2637 io->n.nt_hash = NULL;
2639 } else if (quoted_utf16) {
2640 /* We have only the hash available -> so no plaintext here */
2641 if (!ac->hash_values) {
2642 /* refuse the change if someone wants to change
2643 the hash without control specified... */
2644 ldb_asprintf_errstring(ldb,
2646 "it's not allowed to set the NT hash password directly'");
2647 /* this looks odd but this is what Windows does:
2648 returns "UNWILLING_TO_PERFORM" on wrong
2649 password sets and "CONSTRAINT_VIOLATION" on
2650 wrong password changes. */
2651 if (old_quoted_utf16 == NULL) {
2652 return LDB_ERR_UNWILLING_TO_PERFORM;
2655 return LDB_ERR_CONSTRAINT_VIOLATION;
2658 io->n.nt_hash = talloc(io->ac, struct samr_Password);
2659 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
2660 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
2663 /* Checks and converts the previous "unicodePwd" attribute */
2664 if (!ac->hash_values &&
2666 old_quoted_utf16->length >= 4 &&
2667 old_quoted_utf16->data[0] == '"' &&
2668 old_quoted_utf16->data[1] == 0 &&
2669 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
2670 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
2671 struct ldb_val *old_quoted_utf16_2;
2673 if (io->og.cleartext_utf16) {
2674 /* refuse the change if someone wants to change with
2675 both UTF16 possibilities at the same time... */
2676 ldb_asprintf_errstring(ldb,
2678 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2679 return LDB_ERR_UNWILLING_TO_PERFORM;
2683 * adapt the quoted UTF16 string to be a real
2686 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2687 if (old_quoted_utf16_2 == NULL) {
2688 return ldb_oom(ldb);
2691 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
2692 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
2694 io->og.cleartext_utf16 = old_quoted_utf16_2;
2695 io->og.nt_hash = NULL;
2696 } else if (old_quoted_utf16) {
2697 /* We have only the hash available -> so no plaintext here */
2698 if (!ac->hash_values) {
2699 /* refuse the change if someone wants to change
2700 the hash without control specified... */
2701 ldb_asprintf_errstring(ldb,
2703 "it's not allowed to set the NT hash password directly'");
2704 return LDB_ERR_UNWILLING_TO_PERFORM;
2707 io->og.nt_hash = talloc(io->ac, struct samr_Password);
2708 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
2709 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
2712 /* Handles the "dBCSPwd" attribute (LM hash) */
2713 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
2714 ret = msg_find_old_and_new_pwd_val(client_msg, "dBCSPwd",
2716 &lm_hash, &old_lm_hash);
2717 if (ret != LDB_SUCCESS) {
2718 ldb_asprintf_errstring(ldb,
2720 "it's only allowed to set the old password once!");
2724 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
2725 /* refuse the change if someone wants to change the hash
2726 without control specified... */
2727 ldb_asprintf_errstring(ldb,
2729 "it's not allowed to set the LM hash password directly'");
2730 return LDB_ERR_UNWILLING_TO_PERFORM;
2733 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
2734 io->n.lm_hash = talloc(io->ac, struct samr_Password);
2735 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
2736 sizeof(io->n.lm_hash->hash)));
2738 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
2739 io->og.lm_hash = talloc(io->ac, struct samr_Password);
2740 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
2741 sizeof(io->og.lm_hash->hash)));
2745 * Handles the password change control if it's specified. It has the
2746 * precedance and overrides already specified old password values of
2747 * change requests (but that shouldn't happen since the control is
2748 * fully internal and only used in conjunction with replace requests!).
2750 if (ac->change != NULL) {
2751 io->og.nt_hash = NULL;
2752 if (ac->change->old_nt_pwd_hash != NULL) {
2753 io->og.nt_hash = talloc_memdup(io->ac,
2754 ac->change->old_nt_pwd_hash,
2755 sizeof(struct samr_Password));
2757 io->og.lm_hash = NULL;
2758 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
2759 io->og.lm_hash = talloc_memdup(io->ac,
2760 ac->change->old_lm_pwd_hash,
2761 sizeof(struct samr_Password));
2765 /* refuse the change if someone wants to change the clear-
2766 text and supply his own hashes at the same time... */
2767 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
2768 && (io->n.nt_hash || io->n.lm_hash)) {
2769 ldb_asprintf_errstring(ldb,
2771 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2772 return LDB_ERR_UNWILLING_TO_PERFORM;
2775 /* refuse the change if someone wants to change the password
2776 using both plaintext methods (UTF8 and UTF16) at the same time... */
2777 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
2778 ldb_asprintf_errstring(ldb,
2780 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2781 return LDB_ERR_UNWILLING_TO_PERFORM;
2784 /* refuse the change if someone tries to set/change the password by
2785 * the lanman hash alone and we've deactivated that mechanism. This
2786 * would end in an account without any password! */
2787 if (io->ac->update_password
2788 && (!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
2789 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
2790 ldb_asprintf_errstring(ldb,
2792 "It's not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2793 /* on "userPassword" and "clearTextPassword" we've to return
2794 * something different, since these are virtual attributes */
2795 if ((ldb_msg_find_element(client_msg, "userPassword") != NULL) ||
2796 (ldb_msg_find_element(client_msg, "clearTextPassword") != NULL)) {
2797 return LDB_ERR_CONSTRAINT_VIOLATION;
2799 return LDB_ERR_UNWILLING_TO_PERFORM;
2802 /* refuse the change if someone wants to compare against a plaintext
2803 or hash at the same time for a "password modify" operation... */
2804 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
2805 && (io->og.nt_hash || io->og.lm_hash)) {
2806 ldb_asprintf_errstring(ldb,
2808 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2809 return LDB_ERR_UNWILLING_TO_PERFORM;
2812 /* refuse the change if someone wants to compare against both
2813 * plaintexts at the same time for a "password modify" operation... */
2814 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2815 ldb_asprintf_errstring(ldb,
2817 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2818 return LDB_ERR_UNWILLING_TO_PERFORM;
2821 /* Decides if we have a password modify or password reset operation */
2822 if (ac->req->operation == LDB_ADD) {
2823 /* On "add" we have only "password reset" */
2824 ac->pwd_reset = true;
2825 } else if (ac->req->operation == LDB_MODIFY) {
2826 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2827 || io->og.nt_hash || io->og.lm_hash) {
2828 /* If we have an old password specified then for sure it
2829 * is a user "password change" */
2830 ac->pwd_reset = false;
2832 /* Otherwise we have also here a "password reset" */
2833 ac->pwd_reset = true;
2836 /* this shouldn't happen */
2837 return ldb_operr(ldb);
2840 if (existing_msg != NULL) {
2843 if (ac->pwd_reset) {
2844 /* Get the old password from the database */
2845 status = samdb_result_passwords_no_lockout(ac,
2851 /* Get the old password from the database */
2852 status = samdb_result_passwords(ac,
2859 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
2860 return dsdb_module_werror(ac->module,
2861 LDB_ERR_CONSTRAINT_VIOLATION,
2862 WERR_ACCOUNT_LOCKED_OUT,
2863 "Password change not permitted,"
2864 " account locked out!");
2867 if (!NT_STATUS_IS_OK(status)) {
2869 * This only happens if the database has gone weird,
2870 * not if we are just missing the passwords
2872 return ldb_operr(ldb);
2875 io->o.nt_history_len = samdb_result_hashes(ac, existing_msg,
2878 io->o.lm_history_len = samdb_result_hashes(ac, existing_msg,
2881 io->o.supplemental = ldb_msg_find_ldb_val(existing_msg,
2882 "supplementalCredentials");
2884 if (io->o.supplemental != NULL) {
2885 enum ndr_err_code ndr_err;
2887 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
2889 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
2890 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2891 status = ndr_map_error2ntstatus(ndr_err);
2892 ldb_asprintf_errstring(ldb,
2893 "setup_io: failed to pull "
2894 "old supplementalCredentialsBlob: %s",
2896 return LDB_ERR_OPERATIONS_ERROR;
2904 static struct ph_context *ph_init_context(struct ldb_module *module,
2905 struct ldb_request *req,
2907 bool update_password)
2909 struct ldb_context *ldb;
2910 struct ph_context *ac;
2912 ldb = ldb_module_get_ctx(module);
2914 ac = talloc_zero(req, struct ph_context);
2916 ldb_set_errstring(ldb, "Out of Memory");
2920 ac->module = module;
2922 ac->userPassword = userPassword;
2923 ac->update_password = update_password;
2924 ac->update_lastset = true;
2929 static void ph_apply_controls(struct ph_context *ac)
2931 struct ldb_control *ctrl;
2933 ac->change_status = false;
2934 ctrl = ldb_request_get_control(ac->req,
2935 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2937 ac->change_status = true;
2939 /* Mark the "change status" control as uncritical (done) */
2940 ctrl->critical = false;
2943 ac->hash_values = false;
2944 ctrl = ldb_request_get_control(ac->req,
2945 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2947 ac->hash_values = true;
2949 /* Mark the "hash values" control as uncritical (done) */
2950 ctrl->critical = false;
2953 ctrl = ldb_request_get_control(ac->req,
2954 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2956 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2958 /* Mark the "change" control as uncritical (done) */
2959 ctrl->critical = false;
2962 ac->pwd_last_set_bypass = false;
2963 ctrl = ldb_request_get_control(ac->req,
2964 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID);
2966 ac->pwd_last_set_bypass = true;
2968 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2969 ctrl->critical = false;
2972 ac->pwd_last_set_default = false;
2973 ctrl = ldb_request_get_control(ac->req,
2974 DSDB_CONTROL_PASSWORD_DEFAULT_LAST_SET_OID);
2976 ac->pwd_last_set_default = true;
2978 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2979 ctrl->critical = false;
2983 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2985 struct ph_context *ac;
2987 ac = talloc_get_type(req->context, struct ph_context);
2990 return ldb_module_done(ac->req, NULL, NULL,
2991 LDB_ERR_OPERATIONS_ERROR);
2994 if (ares->type == LDB_REPLY_REFERRAL) {
2995 return ldb_module_send_referral(ac->req, ares->referral);
2998 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2999 /* On success and trivial errors a status control is being
3000 * added (used for example by the "samdb_set_password" call) */
3001 ldb_reply_add_control(ares,
3002 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
3007 if (ares->error != LDB_SUCCESS) {
3008 return ldb_module_done(ac->req, ares->controls,
3009 ares->response, ares->error);
3012 if (ares->type != LDB_REPLY_DONE) {
3014 return ldb_module_done(ac->req, NULL, NULL,
3015 LDB_ERR_OPERATIONS_ERROR);
3018 return ldb_module_done(ac->req, ares->controls,
3019 ares->response, ares->error);
3022 static int password_hash_add_do_add(struct ph_context *ac);
3023 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
3024 static int password_hash_mod_search_self(struct ph_context *ac);
3025 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
3026 static int password_hash_mod_do_mod(struct ph_context *ac);
3028 static int get_domain_data_callback(struct ldb_request *req,
3029 struct ldb_reply *ares)
3031 struct ldb_context *ldb;
3032 struct ph_context *ac;
3033 struct loadparm_context *lp_ctx;
3034 int ret = LDB_SUCCESS;
3036 ac = talloc_get_type(req->context, struct ph_context);
3037 ldb = ldb_module_get_ctx(ac->module);
3040 ret = LDB_ERR_OPERATIONS_ERROR;
3043 if (ares->error != LDB_SUCCESS) {
3044 return ldb_module_done(ac->req, ares->controls,
3045 ares->response, ares->error);
3048 switch (ares->type) {
3049 case LDB_REPLY_ENTRY:
3050 if (ac->status != NULL) {
3053 ldb_set_errstring(ldb, "Too many results");
3054 ret = LDB_ERR_OPERATIONS_ERROR;
3058 /* Setup the "status" structure (used as control later) */
3059 ac->status = talloc_zero(ac->req,
3060 struct dsdb_control_password_change_status);
3061 if (ac->status == NULL) {
3065 ret = LDB_ERR_OPERATIONS_ERROR;
3069 /* Setup the "domain data" structure */
3070 ac->status->domain_data.pwdProperties =
3071 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
3072 ac->status->domain_data.pwdHistoryLength =
3073 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
3074 ac->status->domain_data.maxPwdAge =
3075 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
3076 ac->status->domain_data.minPwdAge =
3077 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
3078 ac->status->domain_data.minPwdLength =
3079 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
3080 ac->status->domain_data.store_cleartext =
3081 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
3083 /* For a domain DN, this puts things in dotted notation */
3084 /* For builtin domains, this will give details for the host,
3085 * but that doesn't really matter, as it's just used for salt
3086 * and kerberos principals, which don't exist here */
3088 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3089 struct loadparm_context);
3091 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
3092 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
3093 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
3095 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
3097 if (ac->dom_res != NULL) {
3100 ldb_set_errstring(ldb, "Too many results");
3101 ret = LDB_ERR_OPERATIONS_ERROR;
3105 ac->dom_res = talloc_steal(ac, ares);
3109 case LDB_REPLY_REFERRAL:
3115 case LDB_REPLY_DONE:
3117 /* call the next step */
3118 switch (ac->req->operation) {
3120 ret = password_hash_add_do_add(ac);
3124 ret = password_hash_mod_do_mod(ac);
3128 ret = LDB_ERR_OPERATIONS_ERROR;
3135 if (ret != LDB_SUCCESS) {
3136 struct ldb_reply *new_ares;
3138 new_ares = talloc_zero(ac->req, struct ldb_reply);
3139 if (new_ares == NULL) {
3141 return ldb_module_done(ac->req, NULL, NULL,
3142 LDB_ERR_OPERATIONS_ERROR);
3145 new_ares->error = ret;
3146 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
3147 /* On success and trivial errors a status control is being
3148 * added (used for example by the "samdb_set_password" call) */
3149 ldb_reply_add_control(new_ares,
3150 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
3155 return ldb_module_done(ac->req, new_ares->controls,
3156 new_ares->response, new_ares->error);
3162 static int build_domain_data_request(struct ph_context *ac)
3164 /* attrs[] is returned from this function in
3165 ac->dom_req->op.search.attrs, so it must be static, as
3166 otherwise the compiler can put it on the stack */
3167 struct ldb_context *ldb;
3168 static const char * const attrs[] = { "pwdProperties",
3174 "lockOutObservationWindow",
3178 ldb = ldb_module_get_ctx(ac->module);
3180 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
3181 ldb_get_default_basedn(ldb),
3185 ac, get_domain_data_callback,
3187 LDB_REQ_SET_LOCATION(ac->dom_req);
3191 static int password_hash_needed(struct ldb_module *module,
3192 struct ldb_request *req,
3193 struct ph_context **_ac)
3195 struct ldb_context *ldb = ldb_module_get_ctx(module);
3196 const char *operation = NULL;
3197 const struct ldb_message *msg = NULL;
3198 struct ph_context *ac = NULL;
3199 const char *passwordAttrs[] = {
3201 "clearTextPassword",
3206 const char **a = NULL;
3207 unsigned int attr_cnt = 0;
3208 struct ldb_control *bypass = NULL;
3209 bool userPassword = dsdb_user_password_support(module, req, req);
3210 bool update_password = false;
3211 bool processing_needed = false;
3215 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_needed\n");
3217 switch (req->operation) {
3220 msg = req->op.add.message;
3223 operation = "modify";
3224 msg = req->op.mod.message;
3227 return ldb_next_request(module, req);
3230 if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
3231 return ldb_next_request(module, req);
3234 bypass = ldb_request_get_control(req,
3235 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
3236 if (bypass != NULL) {
3237 /* Mark the "bypass" control as uncritical (done) */
3238 bypass->critical = false;
3239 ldb_debug(ldb, LDB_DEBUG_TRACE,
3240 "password_hash_needed(%s) (bypassing)\n",
3242 return password_hash_bypass(module, req);
3245 /* nobody must touch password histories and 'supplementalCredentials' */
3246 if (ldb_msg_find_element(msg, "ntPwdHistory")) {
3247 return LDB_ERR_UNWILLING_TO_PERFORM;
3249 if (ldb_msg_find_element(msg, "lmPwdHistory")) {
3250 return LDB_ERR_UNWILLING_TO_PERFORM;
3252 if (ldb_msg_find_element(msg, "supplementalCredentials")) {
3253 return LDB_ERR_UNWILLING_TO_PERFORM;
3257 * If no part of this touches the 'userPassword' OR 'clearTextPassword'
3258 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
3259 * For password changes/set there should be a 'delete' or a 'modify'
3260 * on these attributes.
3262 for (a = passwordAttrs; *a != NULL; a++) {
3263 if ((!userPassword) && (ldb_attr_cmp(*a, "userPassword") == 0)) {
3267 if (ldb_msg_find_element(msg, *a) != NULL) {
3268 /* MS-ADTS 3.1.1.3.1.5.2 */
3269 if ((ldb_attr_cmp(*a, "userPassword") == 0) &&
3270 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
3271 return LDB_ERR_CONSTRAINT_VIOLATION;
3279 update_password = true;
3280 processing_needed = true;
3283 if (ldb_msg_find_element(msg, "pwdLastSet")) {
3284 processing_needed = true;
3287 if (!processing_needed) {
3288 return ldb_next_request(module, req);
3291 ac = ph_init_context(module, req, userPassword, update_password);
3293 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3294 return ldb_operr(ldb);
3296 ph_apply_controls(ac);
3299 * Make a copy in order to apply our modifications
3300 * to the final update
3302 ac->update_msg = ldb_msg_copy_shallow(ac, msg);
3303 if (ac->update_msg == NULL) {
3304 return ldb_oom(ldb);
3308 * Remove all password related attributes.
3310 if (ac->userPassword) {
3311 ldb_msg_remove_attr(ac->update_msg, "userPassword");
3313 ldb_msg_remove_attr(ac->update_msg, "clearTextPassword");
3314 ldb_msg_remove_attr(ac->update_msg, "unicodePwd");
3315 ldb_msg_remove_attr(ac->update_msg, "ntPwdHistory");
3316 ldb_msg_remove_attr(ac->update_msg, "dBCSPwd");
3317 ldb_msg_remove_attr(ac->update_msg, "lmPwdHistory");
3318 ldb_msg_remove_attr(ac->update_msg, "supplementalCredentials");
3319 ldb_msg_remove_attr(ac->update_msg, "pwdLastSet");
3325 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
3327 struct ldb_context *ldb = ldb_module_get_ctx(module);
3328 struct ph_context *ac = NULL;
3331 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
3333 ret = password_hash_needed(module, req, &ac);
3334 if (ret != LDB_SUCCESS) {
3341 /* Make sure we are performing the password set action on a (for us)
3342 * valid object. Those are instances of either "user" and/or
3343 * "inetOrgPerson". Otherwise continue with the submodules. */
3344 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
3345 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
3349 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
3350 ldb_set_errstring(ldb,
3351 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3352 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3355 return ldb_next_request(module, req);
3358 /* get user domain data */
3359 ret = build_domain_data_request(ac);
3360 if (ret != LDB_SUCCESS) {
3364 return ldb_next_request(module, ac->dom_req);
3367 static int password_hash_add_do_add(struct ph_context *ac)
3369 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3370 struct ldb_request *down_req;
3371 struct setup_password_fields_io io;
3374 /* Prepare the internal data structure containing the passwords */
3375 ret = setup_io(ac, ac->req->op.add.message, NULL, &io);
3376 if (ret != LDB_SUCCESS) {
3380 ret = setup_password_fields(&io);
3381 if (ret != LDB_SUCCESS) {
3385 ret = check_password_restrictions(&io);
3386 if (ret != LDB_SUCCESS) {
3390 ret = update_final_msg(&io);
3391 if (ret != LDB_SUCCESS) {
3395 ret = ldb_build_add_req(&down_req, ldb, ac,
3400 LDB_REQ_SET_LOCATION(down_req);
3401 if (ret != LDB_SUCCESS) {
3405 return ldb_next_request(ac->module, down_req);
3408 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
3410 struct ldb_context *ldb = ldb_module_get_ctx(module);
3411 struct ph_context *ac = NULL;
3412 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
3413 "unicodePwd", "dBCSPwd", NULL }, **l;
3414 unsigned int del_attr_cnt, add_attr_cnt, rep_attr_cnt;
3415 struct ldb_message_element *passwordAttr;
3416 struct ldb_message *msg;
3417 struct ldb_request *down_req;
3421 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
3423 ret = password_hash_needed(module, req, &ac);
3424 if (ret != LDB_SUCCESS) {
3431 /* use a new message structure so that we can modify it */
3432 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3434 return ldb_oom(ldb);
3437 /* - check for single-valued password attributes
3438 * (if not return "CONSTRAINT_VIOLATION")
3439 * - check that for a password change operation one add and one delete
3441 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3442 * - check that a password change and a password set operation cannot
3444 * (if not return "UNWILLING_TO_PERFORM")
3445 * - remove all password attributes modifications from the first change
3446 * operation (anything without the passwords) - we will make the real
3447 * modification later */
3451 for (l = passwordAttrs; *l != NULL; l++) {
3452 if ((!ac->userPassword) &&
3453 (ldb_attr_cmp(*l, "userPassword") == 0)) {
3457 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
3458 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
3461 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
3464 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
3467 if ((passwordAttr->num_values != 1) &&
3468 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
3470 ldb_asprintf_errstring(ldb,
3471 "'%s' attribute must have exactly one value on add operations!",
3473 return LDB_ERR_CONSTRAINT_VIOLATION;
3475 if ((passwordAttr->num_values > 1) &&
3476 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
3478 ldb_asprintf_errstring(ldb,
3479 "'%s' attribute must have zero or one value(s) on delete operations!",
3481 return LDB_ERR_CONSTRAINT_VIOLATION;
3483 ldb_msg_remove_element(msg, passwordAttr);
3486 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
3488 ldb_set_errstring(ldb,
3489 "Only the add action for a password change specified!");
3490 return LDB_ERR_UNWILLING_TO_PERFORM;
3492 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
3494 ldb_set_errstring(ldb,
3495 "Only one delete and one add action for a password change allowed!");
3496 return LDB_ERR_UNWILLING_TO_PERFORM;
3498 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
3500 ldb_set_errstring(ldb,
3501 "Either a password change or a password set operation is allowed!");
3502 return LDB_ERR_UNWILLING_TO_PERFORM;
3505 ldb_msg_remove_attr(msg, "pwdLastSet");
3507 /* if there was nothing else to be modified skip to next step */
3508 if (msg->num_elements == 0) {
3509 return password_hash_mod_search_self(ac);
3513 * Now we apply all changes remaining in msg
3514 * and remove them from our final update_msg
3517 for (i = 0; i < msg->num_elements; i++) {
3518 ldb_msg_remove_attr(ac->update_msg,
3519 msg->elements[i].name);
3522 ret = ldb_build_mod_req(&down_req, ldb, ac,
3525 ac, ph_modify_callback,
3527 LDB_REQ_SET_LOCATION(down_req);
3528 if (ret != LDB_SUCCESS) {
3532 return ldb_next_request(module, down_req);
3535 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3537 struct ph_context *ac;
3539 ac = talloc_get_type(req->context, struct ph_context);
3542 return ldb_module_done(ac->req, NULL, NULL,
3543 LDB_ERR_OPERATIONS_ERROR);
3546 if (ares->type == LDB_REPLY_REFERRAL) {
3547 return ldb_module_send_referral(ac->req, ares->referral);
3550 if (ares->error != LDB_SUCCESS) {
3551 return ldb_module_done(ac->req, ares->controls,
3552 ares->response, ares->error);
3555 if (ares->type != LDB_REPLY_DONE) {
3557 return ldb_module_done(ac->req, NULL, NULL,
3558 LDB_ERR_OPERATIONS_ERROR);
3563 return password_hash_mod_search_self(ac);
3566 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
3568 struct ldb_context *ldb;
3569 struct ph_context *ac;
3570 int ret = LDB_SUCCESS;
3572 ac = talloc_get_type(req->context, struct ph_context);
3573 ldb = ldb_module_get_ctx(ac->module);
3576 ret = LDB_ERR_OPERATIONS_ERROR;
3579 if (ares->error != LDB_SUCCESS) {
3580 return ldb_module_done(ac->req, ares->controls,
3581 ares->response, ares->error);
3584 /* we are interested only in the single reply (base search) */
3585 switch (ares->type) {
3586 case LDB_REPLY_ENTRY:
3587 /* Make sure we are performing the password change action on a
3588 * (for us) valid object. Those are instances of either "user"
3589 * and/or "inetOrgPerson". Otherwise continue with the
3591 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
3592 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
3595 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
3596 ldb_set_errstring(ldb,
3597 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3598 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
3602 ret = ldb_next_request(ac->module, ac->req);
3606 if (ac->search_res != NULL) {
3609 ldb_set_errstring(ldb, "Too many results");
3610 ret = LDB_ERR_OPERATIONS_ERROR;
3614 ac->search_res = talloc_steal(ac, ares);
3618 case LDB_REPLY_REFERRAL:
3619 /* ignore anything else for now */
3624 case LDB_REPLY_DONE:
3627 /* get user domain data */
3628 ret = build_domain_data_request(ac);
3629 if (ret != LDB_SUCCESS) {
3630 return ldb_module_done(ac->req, NULL, NULL, ret);
3633 ret = ldb_next_request(ac->module, ac->dom_req);
3638 if (ret != LDB_SUCCESS) {
3639 return ldb_module_done(ac->req, NULL, NULL, ret);
3645 static int password_hash_mod_search_self(struct ph_context *ac)
3647 struct ldb_context *ldb;
3648 static const char * const attrs[] = { "objectClass",
3649 "userAccountControl",
3650 "msDS-User-Account-Control-Computed",
3654 "userPrincipalName",
3655 "supplementalCredentials",
3664 struct ldb_request *search_req;
3667 ldb = ldb_module_get_ctx(ac->module);
3669 ret = ldb_build_search_req(&search_req, ldb, ac,
3670 ac->req->op.mod.message->dn,
3675 ac, ph_mod_search_callback,
3677 LDB_REQ_SET_LOCATION(search_req);
3678 if (ret != LDB_SUCCESS) {
3682 return ldb_next_request(ac->module, search_req);
3685 static int password_hash_mod_do_mod(struct ph_context *ac)
3687 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3688 struct ldb_request *mod_req;
3689 struct setup_password_fields_io io;
3692 /* Prepare the internal data structure containing the passwords */
3693 ret = setup_io(ac, ac->req->op.mod.message,
3694 ac->search_res->message, &io);
3695 if (ret != LDB_SUCCESS) {
3699 ret = setup_password_fields(&io);
3700 if (ret != LDB_SUCCESS) {
3704 ret = check_password_restrictions(&io);
3705 if (ret != LDB_SUCCESS) {
3709 ret = update_final_msg(&io);
3710 if (ret != LDB_SUCCESS) {
3714 ret = ldb_build_mod_req(&mod_req, ldb, ac,
3719 LDB_REQ_SET_LOCATION(mod_req);
3720 if (ret != LDB_SUCCESS) {
3724 return ldb_next_request(ac->module, mod_req);
3727 static const struct ldb_module_ops ldb_password_hash_module_ops = {
3728 .name = "password_hash",
3729 .add = password_hash_add,
3730 .modify = password_hash_modify
3733 int ldb_password_hash_module_init(const char *version)
3735 LDB_MODULE_CHECK_VERSION(version);
3736 return ldb_register_module(&ldb_password_hash_module_ops);