s4:dsdb/password_hash: move ldb_msg_add_empty() calls to update_final_msg()
[Samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blobebf0b804097e4c2511acd51f03cd8f8d88ff1594
1 /*
2 ldb database module
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/>.
25 * Name: ldb
27 * Component: ldb password_hash module
29 * Description: correctly handle AD password changes fields
31 * Author: Andrew Bartlett
32 * Author: Stefan Metzmacher
35 #include "includes.h"
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
81 * "samdb.h" */
83 struct ph_context {
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 dsdb_control_password_change_status *status;
93 struct dsdb_control_password_change *change;
95 bool pwd_reset;
96 bool change_status;
97 bool hash_values;
98 bool userPassword;
99 bool update_password;
100 bool update_lastset;
101 bool pwd_last_set_bypass;
105 struct setup_password_fields_io {
106 struct ph_context *ac;
108 struct smb_krb5_context *smb_krb5_context;
110 /* infos about the user account */
111 struct {
112 uint32_t userAccountControl;
113 NTTIME pwdLastSet;
114 const char *sAMAccountName;
115 const char *user_principal_name;
116 bool is_computer;
117 uint32_t restrictions;
118 } u;
120 /* new credentials and old given credentials */
121 struct setup_password_fields_given {
122 const struct ldb_val *cleartext_utf8;
123 const struct ldb_val *cleartext_utf16;
124 struct samr_Password *nt_hash;
125 struct samr_Password *lm_hash;
126 } n, og;
128 /* old credentials */
129 struct {
130 struct samr_Password *nt_hash;
131 struct samr_Password *lm_hash;
132 uint32_t nt_history_len;
133 struct samr_Password *nt_history;
134 uint32_t lm_history_len;
135 struct samr_Password *lm_history;
136 const struct ldb_val *supplemental;
137 struct supplementalCredentialsBlob scb;
138 } o;
140 /* generated credentials */
141 struct {
142 struct samr_Password *nt_hash;
143 struct samr_Password *lm_hash;
144 uint32_t nt_history_len;
145 struct samr_Password *nt_history;
146 uint32_t lm_history_len;
147 struct samr_Password *lm_history;
148 const char *salt;
149 DATA_BLOB aes_256;
150 DATA_BLOB aes_128;
151 DATA_BLOB des_md5;
152 DATA_BLOB des_crc;
153 struct ldb_val supplemental;
154 NTTIME last_set;
155 } g;
158 static int password_hash_bypass(struct ldb_module *module, struct ldb_request *request)
160 struct ldb_context *ldb = ldb_module_get_ctx(module);
161 const struct ldb_message *msg;
162 struct ldb_message_element *nte;
163 struct ldb_message_element *lme;
164 struct ldb_message_element *nthe;
165 struct ldb_message_element *lmhe;
166 struct ldb_message_element *sce;
168 switch (request->operation) {
169 case LDB_ADD:
170 msg = request->op.add.message;
171 break;
172 case LDB_MODIFY:
173 msg = request->op.mod.message;
174 break;
175 default:
176 return ldb_next_request(module, request);
179 /* nobody must touch password histories and 'supplementalCredentials' */
180 nte = dsdb_get_single_valued_attr(msg, "unicodePwd",
181 request->operation);
182 lme = dsdb_get_single_valued_attr(msg, "dBCSPwd",
183 request->operation);
184 nthe = dsdb_get_single_valued_attr(msg, "ntPwdHistory",
185 request->operation);
186 lmhe = dsdb_get_single_valued_attr(msg, "lmPwdHistory",
187 request->operation);
188 sce = dsdb_get_single_valued_attr(msg, "supplementalCredentials",
189 request->operation);
191 #define CHECK_HASH_ELEMENT(e, min, max) do {\
192 if (e && e->num_values) { \
193 unsigned int _count; \
194 if (e->num_values != 1) { \
195 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
196 "num_values != 1"); \
198 if ((e->values[0].length % 16) != 0) { \
199 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
200 "length % 16 != 0"); \
202 _count = e->values[0].length / 16; \
203 if (_count < min) { \
204 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
205 "count < min"); \
207 if (_count > max) { \
208 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
209 "count > max"); \
212 } while (0)
214 CHECK_HASH_ELEMENT(nte, 1, 1);
215 CHECK_HASH_ELEMENT(lme, 1, 1);
216 CHECK_HASH_ELEMENT(nthe, 1, INT32_MAX);
217 CHECK_HASH_ELEMENT(lmhe, 1, INT32_MAX);
219 if (sce && sce->num_values) {
220 enum ndr_err_code ndr_err;
221 struct supplementalCredentialsBlob *scb;
222 struct supplementalCredentialsPackage *scpp = NULL;
223 struct supplementalCredentialsPackage *scpk = NULL;
224 struct supplementalCredentialsPackage *scpkn = NULL;
225 struct supplementalCredentialsPackage *scpct = NULL;
226 DATA_BLOB scpbp = data_blob_null;
227 DATA_BLOB scpbk = data_blob_null;
228 DATA_BLOB scpbkn = data_blob_null;
229 DATA_BLOB scpbct = data_blob_null;
230 DATA_BLOB blob;
231 uint32_t i;
233 if (sce->num_values != 1) {
234 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
235 "num_values != 1");
238 scb = talloc_zero(request, struct supplementalCredentialsBlob);
239 if (!scb) {
240 return ldb_module_oom(module);
243 ndr_err = ndr_pull_struct_blob_all(&sce->values[0], scb, scb,
244 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
245 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
246 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
247 "ndr_pull_struct_blob_all");
250 if (scb->sub.num_packages < 2) {
251 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
252 "num_packages < 2");
255 for (i=0; i < scb->sub.num_packages; i++) {
256 DATA_BLOB subblob;
258 subblob = strhex_to_data_blob(scb, scb->sub.packages[i].data);
259 if (subblob.data == NULL) {
260 return ldb_module_oom(module);
263 if (strcmp(scb->sub.packages[i].name, "Packages") == 0) {
264 if (scpp) {
265 return ldb_error(ldb,
266 LDB_ERR_CONSTRAINT_VIOLATION,
267 "Packages twice");
269 scpp = &scb->sub.packages[i];
270 scpbp = subblob;
271 continue;
273 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos") == 0) {
274 if (scpk) {
275 return ldb_error(ldb,
276 LDB_ERR_CONSTRAINT_VIOLATION,
277 "Primary:Kerberos twice");
279 scpk = &scb->sub.packages[i];
280 scpbk = subblob;
281 continue;
283 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos-Newer-Keys") == 0) {
284 if (scpkn) {
285 return ldb_error(ldb,
286 LDB_ERR_CONSTRAINT_VIOLATION,
287 "Primary:Kerberos-Newer-Keys twice");
289 scpkn = &scb->sub.packages[i];
290 scpbkn = subblob;
291 continue;
293 if (strcmp(scb->sub.packages[i].name, "Primary:CLEARTEXT") == 0) {
294 if (scpct) {
295 return ldb_error(ldb,
296 LDB_ERR_CONSTRAINT_VIOLATION,
297 "Primary:CLEARTEXT twice");
299 scpct = &scb->sub.packages[i];
300 scpbct = subblob;
301 continue;
304 data_blob_free(&subblob);
307 if (scpp == NULL) {
308 return ldb_error(ldb,
309 LDB_ERR_CONSTRAINT_VIOLATION,
310 "Primary:Packages missing");
313 if (scpk == NULL) {
315 * If Primary:Kerberos is missing w2k8r2 reboots
316 * when a password is changed.
318 return ldb_error(ldb,
319 LDB_ERR_CONSTRAINT_VIOLATION,
320 "Primary:Kerberos missing");
323 if (scpp) {
324 struct package_PackagesBlob *p;
325 uint32_t n;
327 p = talloc_zero(scb, struct package_PackagesBlob);
328 if (p == NULL) {
329 return ldb_module_oom(module);
332 ndr_err = ndr_pull_struct_blob(&scpbp, p, p,
333 (ndr_pull_flags_fn_t)ndr_pull_package_PackagesBlob);
334 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
335 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
336 "ndr_pull_struct_blob Packages");
339 if (p->names == NULL) {
340 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
341 "Packages names == NULL");
344 for (n = 0; p->names[n]; n++) {
345 /* noop */
348 if (scb->sub.num_packages != (n + 1)) {
349 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
350 "Packages num_packages != num_names + 1");
353 talloc_free(p);
356 if (scpk) {
357 struct package_PrimaryKerberosBlob *k;
359 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
360 if (k == NULL) {
361 return ldb_module_oom(module);
364 ndr_err = ndr_pull_struct_blob(&scpbk, k, k,
365 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
366 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
367 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
368 "ndr_pull_struct_blob PrimaryKerberos");
371 if (k->version != 3) {
372 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
373 "PrimaryKerberos version != 3");
376 if (k->ctr.ctr3.salt.string == NULL) {
377 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
378 "PrimaryKerberos salt == NULL");
381 if (strlen(k->ctr.ctr3.salt.string) == 0) {
382 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
383 "PrimaryKerberos strlen(salt) == 0");
386 if (k->ctr.ctr3.num_keys != 2) {
387 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
388 "PrimaryKerberos num_keys != 2");
391 if (k->ctr.ctr3.num_old_keys > k->ctr.ctr3.num_keys) {
392 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
393 "PrimaryKerberos num_old_keys > num_keys");
396 if (k->ctr.ctr3.keys[0].keytype != ENCTYPE_DES_CBC_MD5) {
397 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
398 "PrimaryKerberos key[0] != DES_CBC_MD5");
400 if (k->ctr.ctr3.keys[1].keytype != ENCTYPE_DES_CBC_CRC) {
401 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
402 "PrimaryKerberos key[1] != DES_CBC_CRC");
405 if (k->ctr.ctr3.keys[0].value_len != 8) {
406 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
407 "PrimaryKerberos key[0] value_len != 8");
409 if (k->ctr.ctr3.keys[1].value_len != 8) {
410 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
411 "PrimaryKerberos key[1] value_len != 8");
414 for (i = 0; i < k->ctr.ctr3.num_old_keys; i++) {
415 if (k->ctr.ctr3.old_keys[i].keytype ==
416 k->ctr.ctr3.keys[i].keytype &&
417 k->ctr.ctr3.old_keys[i].value_len ==
418 k->ctr.ctr3.keys[i].value_len) {
419 continue;
422 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
423 "PrimaryKerberos old_keys type/value_len doesn't match");
426 talloc_free(k);
429 if (scpkn) {
430 struct package_PrimaryKerberosBlob *k;
432 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
433 if (k == NULL) {
434 return ldb_module_oom(module);
437 ndr_err = ndr_pull_struct_blob(&scpbkn, k, k,
438 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
439 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
440 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
441 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
444 if (k->version != 4) {
445 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
446 "KerberosNerverKeys version != 4");
449 if (k->ctr.ctr4.salt.string == NULL) {
450 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
451 "KerberosNewerKeys salt == NULL");
454 if (strlen(k->ctr.ctr4.salt.string) == 0) {
455 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
456 "KerberosNewerKeys strlen(salt) == 0");
459 if (k->ctr.ctr4.num_keys != 4) {
460 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
461 "KerberosNewerKeys num_keys != 2");
464 if (k->ctr.ctr4.num_old_keys > k->ctr.ctr4.num_keys) {
465 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
466 "KerberosNewerKeys num_old_keys > num_keys");
469 if (k->ctr.ctr4.num_older_keys > k->ctr.ctr4.num_old_keys) {
470 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
471 "KerberosNewerKeys num_older_keys > num_old_keys");
474 if (k->ctr.ctr4.keys[0].keytype != ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
475 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
476 "KerberosNewerKeys key[0] != AES256");
478 if (k->ctr.ctr4.keys[1].keytype != ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
479 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
480 "KerberosNewerKeys key[1] != AES128");
482 if (k->ctr.ctr4.keys[2].keytype != ENCTYPE_DES_CBC_MD5) {
483 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
484 "KerberosNewerKeys key[2] != DES_CBC_MD5");
486 if (k->ctr.ctr4.keys[3].keytype != ENCTYPE_DES_CBC_CRC) {
487 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
488 "KerberosNewerKeys key[3] != DES_CBC_CRC");
491 if (k->ctr.ctr4.keys[0].value_len != 32) {
492 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
493 "KerberosNewerKeys key[0] value_len != 32");
495 if (k->ctr.ctr4.keys[1].value_len != 16) {
496 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
497 "KerberosNewerKeys key[1] value_len != 16");
499 if (k->ctr.ctr4.keys[2].value_len != 8) {
500 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
501 "KerberosNewerKeys key[2] value_len != 8");
503 if (k->ctr.ctr4.keys[3].value_len != 8) {
504 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
505 "KerberosNewerKeys key[3] value_len != 8");
509 * TODO:
510 * Maybe we can check old and older keys here.
511 * But we need to do some tests, if the old keys
512 * can be taken from the PrimaryKerberos blob
513 * (with only des keys), when the domain was upgraded
514 * from w2k3 to w2k8.
517 talloc_free(k);
520 if (scpct) {
521 struct package_PrimaryCLEARTEXTBlob *ct;
523 ct = talloc_zero(scb, struct package_PrimaryCLEARTEXTBlob);
524 if (ct == NULL) {
525 return ldb_module_oom(module);
528 ndr_err = ndr_pull_struct_blob(&scpbct, ct, ct,
529 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryCLEARTEXTBlob);
530 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
531 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
532 "ndr_pull_struct_blob PrimaryCLEARTEXT");
535 if ((ct->cleartext.length % 2) != 0) {
536 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
537 "PrimaryCLEARTEXT length % 2 != 0");
540 talloc_free(ct);
543 ndr_err = ndr_push_struct_blob(&blob, scb, scb,
544 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
545 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
546 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
547 "ndr_pull_struct_blob_all");
550 if (sce->values[0].length != blob.length) {
551 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
552 "supplementalCredentialsBlob length differ");
555 if (memcmp(sce->values[0].data, blob.data, blob.length) != 0) {
556 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
557 "supplementalCredentialsBlob memcmp differ");
560 talloc_free(scb);
563 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_bypass - validated\n");
564 return ldb_next_request(module, request);
567 /* Get the NT hash, and fill it in as an entry in the password history,
568 and specify it into io->g.nt_hash */
570 static int setup_nt_fields(struct setup_password_fields_io *io)
572 struct ldb_context *ldb;
573 uint32_t i;
575 io->g.nt_hash = io->n.nt_hash;
576 ldb = ldb_module_get_ctx(io->ac->module);
578 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
579 return LDB_SUCCESS;
582 /* We might not have an old NT password */
583 io->g.nt_history = talloc_array(io->ac,
584 struct samr_Password,
585 io->ac->status->domain_data.pwdHistoryLength);
586 if (!io->g.nt_history) {
587 return ldb_oom(ldb);
590 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
591 io->o.nt_history_len); i++) {
592 io->g.nt_history[i+1] = io->o.nt_history[i];
594 io->g.nt_history_len = i + 1;
596 if (io->g.nt_hash) {
597 io->g.nt_history[0] = *io->g.nt_hash;
598 } else {
600 * TODO: is this correct?
601 * the simular behavior is correct for the lm history case
603 E_md4hash("", io->g.nt_history[0].hash);
606 return LDB_SUCCESS;
609 /* Get the LANMAN hash, and fill it in as an entry in the password history,
610 and specify it into io->g.lm_hash */
612 static int setup_lm_fields(struct setup_password_fields_io *io)
614 struct ldb_context *ldb;
615 uint32_t i;
617 io->g.lm_hash = io->n.lm_hash;
618 ldb = ldb_module_get_ctx(io->ac->module);
620 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
621 return LDB_SUCCESS;
624 /* We might not have an old LM password */
625 io->g.lm_history = talloc_array(io->ac,
626 struct samr_Password,
627 io->ac->status->domain_data.pwdHistoryLength);
628 if (!io->g.lm_history) {
629 return ldb_oom(ldb);
632 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
633 io->o.lm_history_len); i++) {
634 io->g.lm_history[i+1] = io->o.lm_history[i];
636 io->g.lm_history_len = i + 1;
638 if (io->g.lm_hash) {
639 io->g.lm_history[0] = *io->g.lm_hash;
640 } else {
641 E_deshash("", io->g.lm_history[0].hash);
644 return LDB_SUCCESS;
647 static int setup_kerberos_keys(struct setup_password_fields_io *io)
649 struct ldb_context *ldb;
650 krb5_error_code krb5_ret;
651 krb5_principal salt_principal;
652 krb5_data salt;
653 krb5_keyblock key;
654 krb5_data cleartext_data;
656 ldb = ldb_module_get_ctx(io->ac->module);
657 cleartext_data.data = (char *)io->n.cleartext_utf8->data;
658 cleartext_data.length = io->n.cleartext_utf8->length;
660 /* Many, many thanks to lukeh@padl.com for this
661 * algorithm, described in his Nov 10 2004 mail to
662 * samba-technical@lists.samba.org */
665 * Determine a salting principal
667 if (io->u.is_computer) {
668 char *name;
669 char *saltbody;
671 name = strlower_talloc(io->ac, io->u.sAMAccountName);
672 if (!name) {
673 return ldb_oom(ldb);
676 if (name[strlen(name)-1] == '$') {
677 name[strlen(name)-1] = '\0';
680 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
681 io->ac->status->domain_data.dns_domain);
682 if (!saltbody) {
683 return ldb_oom(ldb);
686 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
687 &salt_principal,
688 io->ac->status->domain_data.realm,
689 "host", saltbody, NULL);
690 } else if (io->u.user_principal_name) {
691 char *user_principal_name;
692 char *p;
694 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
695 if (!user_principal_name) {
696 return ldb_oom(ldb);
699 p = strchr(user_principal_name, '@');
700 if (p) {
701 p[0] = '\0';
704 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
705 &salt_principal,
706 io->ac->status->domain_data.realm,
707 user_principal_name, NULL);
708 } else {
709 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
710 &salt_principal,
711 io->ac->status->domain_data.realm,
712 io->u.sAMAccountName, NULL);
714 if (krb5_ret) {
715 ldb_asprintf_errstring(ldb,
716 "setup_kerberos_keys: "
717 "generation of a salting principal failed: %s",
718 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
719 krb5_ret, io->ac));
720 return LDB_ERR_OPERATIONS_ERROR;
724 * create salt from salt_principal
726 krb5_ret = smb_krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
727 salt_principal, &salt);
728 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
729 if (krb5_ret) {
730 ldb_asprintf_errstring(ldb,
731 "setup_kerberos_keys: "
732 "generation of krb5_salt failed: %s",
733 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
734 krb5_ret, io->ac));
735 return LDB_ERR_OPERATIONS_ERROR;
737 /* create a talloc copy */
738 io->g.salt = talloc_strndup(io->ac,
739 (char *)salt.data,
740 salt.length);
741 kerberos_free_data_contents(io->smb_krb5_context->krb5_context, &salt);
742 if (!io->g.salt) {
743 return ldb_oom(ldb);
745 /* now use the talloced copy of the salt */
746 salt.data = discard_const(io->g.salt);
747 salt.length = strlen(io->g.salt);
750 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
751 * the salt and the cleartext password
753 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
754 NULL,
755 &salt,
756 &cleartext_data,
757 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
758 &key);
759 if (krb5_ret) {
760 ldb_asprintf_errstring(ldb,
761 "setup_kerberos_keys: "
762 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
763 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
764 krb5_ret, io->ac));
765 return LDB_ERR_OPERATIONS_ERROR;
767 io->g.aes_256 = data_blob_talloc(io->ac,
768 KRB5_KEY_DATA(&key),
769 KRB5_KEY_LENGTH(&key));
770 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
771 if (!io->g.aes_256.data) {
772 return ldb_oom(ldb);
776 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
777 * the salt and the cleartext password
779 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
780 NULL,
781 &salt,
782 &cleartext_data,
783 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
784 &key);
785 if (krb5_ret) {
786 ldb_asprintf_errstring(ldb,
787 "setup_kerberos_keys: "
788 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
789 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
790 krb5_ret, io->ac));
791 return LDB_ERR_OPERATIONS_ERROR;
793 io->g.aes_128 = data_blob_talloc(io->ac,
794 KRB5_KEY_DATA(&key),
795 KRB5_KEY_LENGTH(&key));
796 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
797 if (!io->g.aes_128.data) {
798 return ldb_oom(ldb);
802 * create ENCTYPE_DES_CBC_MD5 key out of
803 * the salt and the cleartext password
805 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
806 NULL,
807 &salt,
808 &cleartext_data,
809 ENCTYPE_DES_CBC_MD5,
810 &key);
811 if (krb5_ret) {
812 ldb_asprintf_errstring(ldb,
813 "setup_kerberos_keys: "
814 "generation of a des-cbc-md5 key failed: %s",
815 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
816 krb5_ret, io->ac));
817 return LDB_ERR_OPERATIONS_ERROR;
819 io->g.des_md5 = data_blob_talloc(io->ac,
820 KRB5_KEY_DATA(&key),
821 KRB5_KEY_LENGTH(&key));
822 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
823 if (!io->g.des_md5.data) {
824 return ldb_oom(ldb);
828 * create ENCTYPE_DES_CBC_CRC key out of
829 * the salt and the cleartext password
831 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
832 NULL,
833 &salt,
834 &cleartext_data,
835 ENCTYPE_DES_CBC_CRC,
836 &key);
837 if (krb5_ret) {
838 ldb_asprintf_errstring(ldb,
839 "setup_kerberos_keys: "
840 "generation of a des-cbc-crc key failed: %s",
841 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
842 krb5_ret, io->ac));
843 return LDB_ERR_OPERATIONS_ERROR;
845 io->g.des_crc = data_blob_talloc(io->ac,
846 KRB5_KEY_DATA(&key),
847 KRB5_KEY_LENGTH(&key));
848 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
849 if (!io->g.des_crc.data) {
850 return ldb_oom(ldb);
853 return LDB_SUCCESS;
856 static int setup_primary_kerberos(struct setup_password_fields_io *io,
857 const struct supplementalCredentialsBlob *old_scb,
858 struct package_PrimaryKerberosBlob *pkb)
860 struct ldb_context *ldb;
861 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
862 struct supplementalCredentialsPackage *old_scp = NULL;
863 struct package_PrimaryKerberosBlob _old_pkb;
864 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
865 uint32_t i;
866 enum ndr_err_code ndr_err;
868 ldb = ldb_module_get_ctx(io->ac->module);
871 * prepare generation of keys
873 * ENCTYPE_DES_CBC_MD5
874 * ENCTYPE_DES_CBC_CRC
876 pkb->version = 3;
877 pkb3->salt.string = io->g.salt;
878 pkb3->num_keys = 2;
879 pkb3->keys = talloc_array(io->ac,
880 struct package_PrimaryKerberosKey3,
881 pkb3->num_keys);
882 if (!pkb3->keys) {
883 return ldb_oom(ldb);
886 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
887 pkb3->keys[0].value = &io->g.des_md5;
888 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
889 pkb3->keys[1].value = &io->g.des_crc;
891 /* initialize the old keys to zero */
892 pkb3->num_old_keys = 0;
893 pkb3->old_keys = NULL;
895 /* if there're no old keys, then we're done */
896 if (!old_scb) {
897 return LDB_SUCCESS;
900 for (i=0; i < old_scb->sub.num_packages; i++) {
901 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
902 continue;
905 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
906 continue;
909 old_scp = &old_scb->sub.packages[i];
910 break;
912 /* Primary:Kerberos element of supplementalCredentials */
913 if (old_scp) {
914 DATA_BLOB blob;
916 blob = strhex_to_data_blob(io->ac, old_scp->data);
917 if (!blob.data) {
918 return ldb_oom(ldb);
921 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
922 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
923 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
924 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
925 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
926 ldb_asprintf_errstring(ldb,
927 "setup_primary_kerberos: "
928 "failed to pull old package_PrimaryKerberosBlob: %s",
929 nt_errstr(status));
930 return LDB_ERR_OPERATIONS_ERROR;
933 if (_old_pkb.version != 3) {
934 ldb_asprintf_errstring(ldb,
935 "setup_primary_kerberos: "
936 "package_PrimaryKerberosBlob version[%u] expected[3]",
937 _old_pkb.version);
938 return LDB_ERR_OPERATIONS_ERROR;
941 old_pkb3 = &_old_pkb.ctr.ctr3;
944 /* if we didn't found the old keys we're done */
945 if (!old_pkb3) {
946 return LDB_SUCCESS;
949 /* fill in the old keys */
950 pkb3->num_old_keys = old_pkb3->num_keys;
951 pkb3->old_keys = old_pkb3->keys;
953 return LDB_SUCCESS;
956 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
957 const struct supplementalCredentialsBlob *old_scb,
958 struct package_PrimaryKerberosBlob *pkb)
960 struct ldb_context *ldb;
961 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
962 struct supplementalCredentialsPackage *old_scp = NULL;
963 struct package_PrimaryKerberosBlob _old_pkb;
964 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
965 uint32_t i;
966 enum ndr_err_code ndr_err;
968 ldb = ldb_module_get_ctx(io->ac->module);
971 * prepare generation of keys
973 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
974 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
975 * ENCTYPE_DES_CBC_MD5
976 * ENCTYPE_DES_CBC_CRC
978 pkb->version = 4;
979 pkb4->salt.string = io->g.salt;
980 pkb4->default_iteration_count = 4096;
981 pkb4->num_keys = 4;
983 pkb4->keys = talloc_array(io->ac,
984 struct package_PrimaryKerberosKey4,
985 pkb4->num_keys);
986 if (!pkb4->keys) {
987 return ldb_oom(ldb);
990 pkb4->keys[0].iteration_count = 4096;
991 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
992 pkb4->keys[0].value = &io->g.aes_256;
993 pkb4->keys[1].iteration_count = 4096;
994 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
995 pkb4->keys[1].value = &io->g.aes_128;
996 pkb4->keys[2].iteration_count = 4096;
997 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
998 pkb4->keys[2].value = &io->g.des_md5;
999 pkb4->keys[3].iteration_count = 4096;
1000 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
1001 pkb4->keys[3].value = &io->g.des_crc;
1003 /* initialize the old keys to zero */
1004 pkb4->num_old_keys = 0;
1005 pkb4->old_keys = NULL;
1006 pkb4->num_older_keys = 0;
1007 pkb4->older_keys = NULL;
1009 /* if there're no old keys, then we're done */
1010 if (!old_scb) {
1011 return LDB_SUCCESS;
1014 for (i=0; i < old_scb->sub.num_packages; i++) {
1015 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
1016 continue;
1019 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
1020 continue;
1023 old_scp = &old_scb->sub.packages[i];
1024 break;
1026 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1027 if (old_scp) {
1028 DATA_BLOB blob;
1030 blob = strhex_to_data_blob(io->ac, old_scp->data);
1031 if (!blob.data) {
1032 return ldb_oom(ldb);
1035 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1036 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
1037 &_old_pkb,
1038 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
1039 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1040 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1041 ldb_asprintf_errstring(ldb,
1042 "setup_primary_kerberos_newer: "
1043 "failed to pull old package_PrimaryKerberosBlob: %s",
1044 nt_errstr(status));
1045 return LDB_ERR_OPERATIONS_ERROR;
1048 if (_old_pkb.version != 4) {
1049 ldb_asprintf_errstring(ldb,
1050 "setup_primary_kerberos_newer: "
1051 "package_PrimaryKerberosBlob version[%u] expected[4]",
1052 _old_pkb.version);
1053 return LDB_ERR_OPERATIONS_ERROR;
1056 old_pkb4 = &_old_pkb.ctr.ctr4;
1059 /* if we didn't found the old keys we're done */
1060 if (!old_pkb4) {
1061 return LDB_SUCCESS;
1064 /* fill in the old keys */
1065 pkb4->num_old_keys = old_pkb4->num_keys;
1066 pkb4->old_keys = old_pkb4->keys;
1067 pkb4->num_older_keys = old_pkb4->num_old_keys;
1068 pkb4->older_keys = old_pkb4->old_keys;
1070 return LDB_SUCCESS;
1073 static int setup_primary_wdigest(struct setup_password_fields_io *io,
1074 const struct supplementalCredentialsBlob *old_scb,
1075 struct package_PrimaryWDigestBlob *pdb)
1077 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1078 DATA_BLOB sAMAccountName;
1079 DATA_BLOB sAMAccountName_l;
1080 DATA_BLOB sAMAccountName_u;
1081 const char *user_principal_name = io->u.user_principal_name;
1082 DATA_BLOB userPrincipalName;
1083 DATA_BLOB userPrincipalName_l;
1084 DATA_BLOB userPrincipalName_u;
1085 DATA_BLOB netbios_domain;
1086 DATA_BLOB netbios_domain_l;
1087 DATA_BLOB netbios_domain_u;
1088 DATA_BLOB dns_domain;
1089 DATA_BLOB dns_domain_l;
1090 DATA_BLOB dns_domain_u;
1091 DATA_BLOB digest;
1092 DATA_BLOB delim;
1093 DATA_BLOB backslash;
1094 uint8_t i;
1095 struct {
1096 DATA_BLOB *user;
1097 DATA_BLOB *realm;
1098 DATA_BLOB *nt4dom;
1099 } wdigest[] = {
1101 * See
1102 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1103 * for what precalculated hashes are supposed to be stored...
1105 * I can't reproduce all values which should contain "Digest" as realm,
1106 * am I doing something wrong or is w2k3 just broken...?
1108 * W2K3 fills in following for a user:
1110 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1111 * sAMAccountName: NewUser2Sam
1112 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1114 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1115 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1116 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1117 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1118 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1119 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1120 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1121 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1122 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1123 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1124 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1125 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1126 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1127 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1128 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1129 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1130 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1131 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1132 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1133 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1134 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1135 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1136 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1137 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1138 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1139 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1140 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1141 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1142 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1144 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1145 * sAMAccountName: NewUser2Sam
1147 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1148 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1149 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1150 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1151 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1152 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1153 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1154 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1155 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1156 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1157 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1158 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1159 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1160 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1161 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1162 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1163 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1164 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1165 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1166 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1167 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1168 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1169 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1170 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1171 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1172 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1173 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1174 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1175 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1179 * sAMAccountName, netbios_domain
1182 .user = &sAMAccountName,
1183 .realm = &netbios_domain,
1186 .user = &sAMAccountName_l,
1187 .realm = &netbios_domain_l,
1190 .user = &sAMAccountName_u,
1191 .realm = &netbios_domain_u,
1194 .user = &sAMAccountName,
1195 .realm = &netbios_domain_u,
1198 .user = &sAMAccountName,
1199 .realm = &netbios_domain_l,
1202 .user = &sAMAccountName_u,
1203 .realm = &netbios_domain_l,
1206 .user = &sAMAccountName_l,
1207 .realm = &netbios_domain_u,
1210 * sAMAccountName, dns_domain
1213 .user = &sAMAccountName,
1214 .realm = &dns_domain,
1217 .user = &sAMAccountName_l,
1218 .realm = &dns_domain_l,
1221 .user = &sAMAccountName_u,
1222 .realm = &dns_domain_u,
1225 .user = &sAMAccountName,
1226 .realm = &dns_domain_u,
1229 .user = &sAMAccountName,
1230 .realm = &dns_domain_l,
1233 .user = &sAMAccountName_u,
1234 .realm = &dns_domain_l,
1237 .user = &sAMAccountName_l,
1238 .realm = &dns_domain_u,
1241 * userPrincipalName, no realm
1244 .user = &userPrincipalName,
1248 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1249 * the fallback to the sAMAccountName based userPrincipalName is correct
1251 .user = &userPrincipalName_l,
1254 .user = &userPrincipalName_u,
1257 * nt4dom\sAMAccountName, no realm
1260 .user = &sAMAccountName,
1261 .nt4dom = &netbios_domain
1264 .user = &sAMAccountName_l,
1265 .nt4dom = &netbios_domain_l
1268 .user = &sAMAccountName_u,
1269 .nt4dom = &netbios_domain_u
1273 * the following ones are guessed depending on the technet2 article
1274 * but not reproducable on a w2k3 server
1276 /* sAMAccountName with "Digest" realm */
1278 .user = &sAMAccountName,
1279 .realm = &digest
1282 .user = &sAMAccountName_l,
1283 .realm = &digest
1286 .user = &sAMAccountName_u,
1287 .realm = &digest
1289 /* userPrincipalName with "Digest" realm */
1291 .user = &userPrincipalName,
1292 .realm = &digest
1295 .user = &userPrincipalName_l,
1296 .realm = &digest
1299 .user = &userPrincipalName_u,
1300 .realm = &digest
1302 /* nt4dom\\sAMAccountName with "Digest" realm */
1304 .user = &sAMAccountName,
1305 .nt4dom = &netbios_domain,
1306 .realm = &digest
1309 .user = &sAMAccountName_l,
1310 .nt4dom = &netbios_domain_l,
1311 .realm = &digest
1314 .user = &sAMAccountName_u,
1315 .nt4dom = &netbios_domain_u,
1316 .realm = &digest
1320 /* prepare DATA_BLOB's used in the combinations array */
1321 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
1322 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
1323 if (!sAMAccountName_l.data) {
1324 return ldb_oom(ldb);
1326 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
1327 if (!sAMAccountName_u.data) {
1328 return ldb_oom(ldb);
1331 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1332 if (!user_principal_name) {
1333 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
1334 io->u.sAMAccountName,
1335 io->ac->status->domain_data.dns_domain);
1336 if (!user_principal_name) {
1337 return ldb_oom(ldb);
1340 userPrincipalName = data_blob_string_const(user_principal_name);
1341 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
1342 if (!userPrincipalName_l.data) {
1343 return ldb_oom(ldb);
1345 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
1346 if (!userPrincipalName_u.data) {
1347 return ldb_oom(ldb);
1350 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
1351 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
1352 io->ac->status->domain_data.netbios_domain));
1353 if (!netbios_domain_l.data) {
1354 return ldb_oom(ldb);
1356 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
1357 io->ac->status->domain_data.netbios_domain));
1358 if (!netbios_domain_u.data) {
1359 return ldb_oom(ldb);
1362 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1363 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1364 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
1366 digest = data_blob_string_const("Digest");
1368 delim = data_blob_string_const(":");
1369 backslash = data_blob_string_const("\\");
1371 pdb->num_hashes = ARRAY_SIZE(wdigest);
1372 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
1373 pdb->num_hashes);
1374 if (!pdb->hashes) {
1375 return ldb_oom(ldb);
1378 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
1379 MD5_CTX md5;
1380 MD5Init(&md5);
1381 if (wdigest[i].nt4dom) {
1382 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
1383 MD5Update(&md5, backslash.data, backslash.length);
1385 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
1386 MD5Update(&md5, delim.data, delim.length);
1387 if (wdigest[i].realm) {
1388 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1390 MD5Update(&md5, delim.data, delim.length);
1391 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1392 MD5Final(pdb->hashes[i].hash, &md5);
1395 return LDB_SUCCESS;
1398 static int setup_supplemental_field(struct setup_password_fields_io *io)
1400 struct ldb_context *ldb;
1401 struct supplementalCredentialsBlob scb;
1402 struct supplementalCredentialsBlob *old_scb = NULL;
1403 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1404 uint32_t num_names = 0;
1405 const char *names[1+4];
1406 uint32_t num_packages = 0;
1407 struct supplementalCredentialsPackage packages[1+4];
1408 /* Packages */
1409 struct supplementalCredentialsPackage *pp = NULL;
1410 struct package_PackagesBlob pb;
1411 DATA_BLOB pb_blob;
1412 char *pb_hexstr;
1413 /* Primary:Kerberos-Newer-Keys */
1414 const char **nkn = NULL;
1415 struct supplementalCredentialsPackage *pkn = NULL;
1416 struct package_PrimaryKerberosBlob pknb;
1417 DATA_BLOB pknb_blob;
1418 char *pknb_hexstr;
1419 /* Primary:Kerberos */
1420 const char **nk = NULL;
1421 struct supplementalCredentialsPackage *pk = NULL;
1422 struct package_PrimaryKerberosBlob pkb;
1423 DATA_BLOB pkb_blob;
1424 char *pkb_hexstr;
1425 /* Primary:WDigest */
1426 const char **nd = NULL;
1427 struct supplementalCredentialsPackage *pd = NULL;
1428 struct package_PrimaryWDigestBlob pdb;
1429 DATA_BLOB pdb_blob;
1430 char *pdb_hexstr;
1431 /* Primary:CLEARTEXT */
1432 const char **nc = NULL;
1433 struct supplementalCredentialsPackage *pc = NULL;
1434 struct package_PrimaryCLEARTEXTBlob pcb;
1435 DATA_BLOB pcb_blob;
1436 char *pcb_hexstr;
1437 int ret;
1438 enum ndr_err_code ndr_err;
1439 uint8_t zero16[16];
1440 bool do_newer_keys = false;
1441 bool do_cleartext = false;
1443 ZERO_STRUCT(zero16);
1444 ZERO_STRUCT(names);
1446 ldb = ldb_module_get_ctx(io->ac->module);
1448 if (!io->n.cleartext_utf8) {
1450 * when we don't have a cleartext password
1451 * we can't setup a supplementalCredential value
1453 return LDB_SUCCESS;
1456 /* if there's an old supplementaCredentials blob then use it */
1457 if (io->o.supplemental) {
1458 if (io->o.scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1459 old_scb = &io->o.scb;
1460 } else {
1461 ldb_debug(ldb, LDB_DEBUG_ERROR,
1462 "setup_supplemental_field: "
1463 "supplementalCredentialsBlob "
1464 "signature[0x%04X] expected[0x%04X]",
1465 io->o.scb.sub.signature,
1466 SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1469 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1470 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1472 if (io->ac->status->domain_data.store_cleartext &&
1473 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1474 do_cleartext = true;
1478 * The ordering is this
1480 * Primary:Kerberos-Newer-Keys (optional)
1481 * Primary:Kerberos
1482 * Primary:WDigest
1483 * Primary:CLEARTEXT (optional)
1485 * And the 'Packages' package is insert before the last
1486 * other package.
1488 if (do_newer_keys) {
1489 /* Primary:Kerberos-Newer-Keys */
1490 nkn = &names[num_names++];
1491 pkn = &packages[num_packages++];
1494 /* Primary:Kerberos */
1495 nk = &names[num_names++];
1496 pk = &packages[num_packages++];
1498 if (!do_cleartext) {
1499 /* Packages */
1500 pp = &packages[num_packages++];
1503 /* Primary:WDigest */
1504 nd = &names[num_names++];
1505 pd = &packages[num_packages++];
1507 if (do_cleartext) {
1508 /* Packages */
1509 pp = &packages[num_packages++];
1511 /* Primary:CLEARTEXT */
1512 nc = &names[num_names++];
1513 pc = &packages[num_packages++];
1516 if (pkn) {
1518 * setup 'Primary:Kerberos-Newer-Keys' element
1520 *nkn = "Kerberos-Newer-Keys";
1522 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1523 if (ret != LDB_SUCCESS) {
1524 return ret;
1527 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1528 &pknb,
1529 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1530 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1531 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1532 ldb_asprintf_errstring(ldb,
1533 "setup_supplemental_field: "
1534 "failed to push package_PrimaryKerberosNeverBlob: %s",
1535 nt_errstr(status));
1536 return LDB_ERR_OPERATIONS_ERROR;
1538 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1539 if (!pknb_hexstr) {
1540 return ldb_oom(ldb);
1542 pkn->name = "Primary:Kerberos-Newer-Keys";
1543 pkn->reserved = 1;
1544 pkn->data = pknb_hexstr;
1548 * setup 'Primary:Kerberos' element
1550 *nk = "Kerberos";
1552 ret = setup_primary_kerberos(io, old_scb, &pkb);
1553 if (ret != LDB_SUCCESS) {
1554 return ret;
1557 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1558 &pkb,
1559 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1560 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1561 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1562 ldb_asprintf_errstring(ldb,
1563 "setup_supplemental_field: "
1564 "failed to push package_PrimaryKerberosBlob: %s",
1565 nt_errstr(status));
1566 return LDB_ERR_OPERATIONS_ERROR;
1568 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1569 if (!pkb_hexstr) {
1570 return ldb_oom(ldb);
1572 pk->name = "Primary:Kerberos";
1573 pk->reserved = 1;
1574 pk->data = pkb_hexstr;
1577 * setup 'Primary:WDigest' element
1579 *nd = "WDigest";
1581 ret = setup_primary_wdigest(io, old_scb, &pdb);
1582 if (ret != LDB_SUCCESS) {
1583 return ret;
1586 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1587 &pdb,
1588 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1589 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1590 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1591 ldb_asprintf_errstring(ldb,
1592 "setup_supplemental_field: "
1593 "failed to push package_PrimaryWDigestBlob: %s",
1594 nt_errstr(status));
1595 return LDB_ERR_OPERATIONS_ERROR;
1597 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1598 if (!pdb_hexstr) {
1599 return ldb_oom(ldb);
1601 pd->name = "Primary:WDigest";
1602 pd->reserved = 1;
1603 pd->data = pdb_hexstr;
1606 * setup 'Primary:CLEARTEXT' element
1608 if (pc) {
1609 *nc = "CLEARTEXT";
1611 pcb.cleartext = *io->n.cleartext_utf16;
1613 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1614 &pcb,
1615 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1616 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1617 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1618 ldb_asprintf_errstring(ldb,
1619 "setup_supplemental_field: "
1620 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1621 nt_errstr(status));
1622 return LDB_ERR_OPERATIONS_ERROR;
1624 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1625 if (!pcb_hexstr) {
1626 return ldb_oom(ldb);
1628 pc->name = "Primary:CLEARTEXT";
1629 pc->reserved = 1;
1630 pc->data = pcb_hexstr;
1634 * setup 'Packages' element
1636 pb.names = names;
1637 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1638 &pb,
1639 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1640 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1641 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1642 ldb_asprintf_errstring(ldb,
1643 "setup_supplemental_field: "
1644 "failed to push package_PackagesBlob: %s",
1645 nt_errstr(status));
1646 return LDB_ERR_OPERATIONS_ERROR;
1648 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1649 if (!pb_hexstr) {
1650 return ldb_oom(ldb);
1652 pp->name = "Packages";
1653 pp->reserved = 2;
1654 pp->data = pb_hexstr;
1657 * setup 'supplementalCredentials' value
1659 ZERO_STRUCT(scb);
1660 scb.sub.num_packages = num_packages;
1661 scb.sub.packages = packages;
1663 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1664 &scb,
1665 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1666 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1667 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1668 ldb_asprintf_errstring(ldb,
1669 "setup_supplemental_field: "
1670 "failed to push supplementalCredentialsBlob: %s",
1671 nt_errstr(status));
1672 return LDB_ERR_OPERATIONS_ERROR;
1675 return LDB_SUCCESS;
1678 static int setup_last_set_field(struct setup_password_fields_io *io)
1680 const struct ldb_message *msg = NULL;
1681 struct timeval tv = { .tv_sec = 0 };
1683 switch (io->ac->req->operation) {
1684 case LDB_ADD:
1685 msg = io->ac->req->op.add.message;
1686 break;
1687 case LDB_MODIFY:
1688 msg = io->ac->req->op.mod.message;
1689 break;
1690 default:
1691 return LDB_ERR_OPERATIONS_ERROR;
1692 break;
1695 if (io->ac->pwd_last_set_bypass) {
1696 struct ldb_message_element *el;
1698 if (msg == NULL) {
1699 return LDB_ERR_CONSTRAINT_VIOLATION;
1702 el = ldb_msg_find_element(msg, "pwdLastSet");
1703 if (el == NULL) {
1704 return LDB_ERR_CONSTRAINT_VIOLATION;
1707 io->g.last_set = samdb_result_nttime(msg, "pwdLastSet", 0);
1708 return LDB_SUCCESS;
1711 /* set it as now */
1712 GetTimeOfDay(&tv);
1713 io->g.last_set = timeval_to_nttime(&tv);
1715 return LDB_SUCCESS;
1718 static int setup_given_passwords(struct setup_password_fields_io *io,
1719 struct setup_password_fields_given *g)
1721 struct ldb_context *ldb;
1722 bool ok;
1724 ldb = ldb_module_get_ctx(io->ac->module);
1726 if (g->cleartext_utf8) {
1727 struct ldb_val *cleartext_utf16_blob;
1729 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1730 if (!cleartext_utf16_blob) {
1731 return ldb_oom(ldb);
1733 if (!convert_string_talloc(io->ac,
1734 CH_UTF8, CH_UTF16,
1735 g->cleartext_utf8->data,
1736 g->cleartext_utf8->length,
1737 (void *)&cleartext_utf16_blob->data,
1738 &cleartext_utf16_blob->length)) {
1739 if (g->cleartext_utf8->length != 0) {
1740 talloc_free(cleartext_utf16_blob);
1741 ldb_asprintf_errstring(ldb,
1742 "setup_password_fields: "
1743 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1744 io->u.sAMAccountName);
1745 return LDB_ERR_CONSTRAINT_VIOLATION;
1746 } else {
1747 /* passwords with length "0" are valid! */
1748 cleartext_utf16_blob->data = NULL;
1749 cleartext_utf16_blob->length = 0;
1752 g->cleartext_utf16 = cleartext_utf16_blob;
1753 } else if (g->cleartext_utf16) {
1754 struct ldb_val *cleartext_utf8_blob;
1756 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1757 if (!cleartext_utf8_blob) {
1758 return ldb_oom(ldb);
1760 if (!convert_string_talloc(io->ac,
1761 CH_UTF16MUNGED, CH_UTF8,
1762 g->cleartext_utf16->data,
1763 g->cleartext_utf16->length,
1764 (void *)&cleartext_utf8_blob->data,
1765 &cleartext_utf8_blob->length)) {
1766 if (g->cleartext_utf16->length != 0) {
1767 /* We must bail out here, the input wasn't even
1768 * a multiple of 2 bytes */
1769 talloc_free(cleartext_utf8_blob);
1770 ldb_asprintf_errstring(ldb,
1771 "setup_password_fields: "
1772 "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)!",
1773 io->u.sAMAccountName);
1774 return LDB_ERR_CONSTRAINT_VIOLATION;
1775 } else {
1776 /* passwords with length "0" are valid! */
1777 cleartext_utf8_blob->data = NULL;
1778 cleartext_utf8_blob->length = 0;
1781 g->cleartext_utf8 = cleartext_utf8_blob;
1784 if (g->cleartext_utf16) {
1785 struct samr_Password *nt_hash;
1787 nt_hash = talloc(io->ac, struct samr_Password);
1788 if (!nt_hash) {
1789 return ldb_oom(ldb);
1791 g->nt_hash = nt_hash;
1793 /* compute the new nt hash */
1794 mdfour(nt_hash->hash,
1795 g->cleartext_utf16->data,
1796 g->cleartext_utf16->length);
1799 if (g->cleartext_utf8) {
1800 struct samr_Password *lm_hash;
1802 lm_hash = talloc(io->ac, struct samr_Password);
1803 if (!lm_hash) {
1804 return ldb_oom(ldb);
1807 /* compute the new lm hash */
1808 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1809 if (ok) {
1810 g->lm_hash = lm_hash;
1811 } else {
1812 talloc_free(lm_hash);
1816 return LDB_SUCCESS;
1819 static int setup_password_fields(struct setup_password_fields_io *io)
1821 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1822 struct loadparm_context *lp_ctx =
1823 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1824 struct loadparm_context);
1825 int ret;
1827 /* transform the old password (for password changes) */
1828 ret = setup_given_passwords(io, &io->og);
1829 if (ret != LDB_SUCCESS) {
1830 return ret;
1833 /* transform the new password */
1834 ret = setup_given_passwords(io, &io->n);
1835 if (ret != LDB_SUCCESS) {
1836 return ret;
1839 if (io->n.cleartext_utf8) {
1840 ret = setup_kerberos_keys(io);
1841 if (ret != LDB_SUCCESS) {
1842 return ret;
1846 ret = setup_nt_fields(io);
1847 if (ret != LDB_SUCCESS) {
1848 return ret;
1851 if (lpcfg_lanman_auth(lp_ctx)) {
1852 ret = setup_lm_fields(io);
1853 if (ret != LDB_SUCCESS) {
1854 return ret;
1856 } else {
1857 io->g.lm_hash = NULL;
1858 io->g.lm_history_len = 0;
1861 ret = setup_supplemental_field(io);
1862 if (ret != LDB_SUCCESS) {
1863 return ret;
1866 ret = setup_last_set_field(io);
1867 if (ret != LDB_SUCCESS) {
1868 return ret;
1871 return LDB_SUCCESS;
1874 static int make_error_and_update_badPwdCount(struct setup_password_fields_io *io)
1876 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1877 struct ldb_message *mod_msg = NULL;
1878 NTSTATUS status;
1879 int ret;
1881 status = dsdb_update_bad_pwd_count(io->ac, ldb,
1882 io->ac->search_res->message,
1883 io->ac->dom_res->message,
1884 &mod_msg);
1885 if (!NT_STATUS_IS_OK(status)) {
1886 goto done;
1889 if (mod_msg == NULL) {
1890 goto done;
1894 * OK, horrible semantics ahead.
1896 * - We need to abort any existing transaction
1897 * - create a transaction arround the badPwdCount update
1898 * - re-open the transaction so the upper layer
1899 * doesn't know what happened.
1901 * This is needed because returning an error to the upper
1902 * layer will cancel the transaction and undo the badPwdCount
1903 * update.
1907 * Checking errors here is a bit pointless.
1908 * What can we do if we can't end the transaction?
1910 ret = ldb_next_del_trans(io->ac->module);
1911 if (ret != LDB_SUCCESS) {
1912 ldb_debug(ldb, LDB_DEBUG_FATAL,
1913 "Failed to abort transaction prior to update of badPwdCount of %s: %s",
1914 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1915 ldb_errstring(ldb));
1917 * just return the original error
1919 goto done;
1922 /* Likewise, what should we do if we can't open a new transaction? */
1923 ret = ldb_next_start_trans(io->ac->module);
1924 if (ret != LDB_SUCCESS) {
1925 ldb_debug(ldb, LDB_DEBUG_ERROR,
1926 "Failed to open transaction to update badPwdCount of %s: %s",
1927 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1928 ldb_errstring(ldb));
1930 * just return the original error
1932 goto done;
1935 ret = dsdb_module_modify(io->ac->module, mod_msg,
1936 DSDB_FLAG_NEXT_MODULE,
1937 io->ac->req);
1938 if (ret != LDB_SUCCESS) {
1939 ldb_debug(ldb, LDB_DEBUG_ERROR,
1940 "Failed to update badPwdCount of %s: %s",
1941 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1942 ldb_errstring(ldb));
1944 * We can only ignore this...
1948 ret = ldb_next_end_trans(io->ac->module);
1949 if (ret != LDB_SUCCESS) {
1950 ldb_debug(ldb, LDB_DEBUG_ERROR,
1951 "Failed to close transaction to update badPwdCount of %s: %s",
1952 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1953 ldb_errstring(ldb));
1955 * We can only ignore this...
1959 ret = ldb_next_start_trans(io->ac->module);
1960 if (ret != LDB_SUCCESS) {
1961 ldb_debug(ldb, LDB_DEBUG_ERROR,
1962 "Failed to open transaction after update of badPwdCount of %s: %s",
1963 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1964 ldb_errstring(ldb));
1966 * We can only ignore this...
1970 done:
1971 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1972 ldb_asprintf_errstring(ldb,
1973 "%08X: %s - check_password_restrictions: "
1974 "The old password specified doesn't match!",
1975 W_ERROR_V(WERR_INVALID_PASSWORD),
1976 ldb_strerror(ret));
1977 return ret;
1980 static int check_password_restrictions(struct setup_password_fields_io *io)
1982 struct ldb_context *ldb;
1983 int ret;
1985 ldb = ldb_module_get_ctx(io->ac->module);
1987 /* First check the old password is correct, for password changes */
1988 if (!io->ac->pwd_reset) {
1989 bool nt_hash_checked = false;
1991 /* we need the old nt or lm hash given by the client */
1992 if (!io->og.nt_hash && !io->og.lm_hash) {
1993 ldb_asprintf_errstring(ldb,
1994 "check_password_restrictions: "
1995 "You need to provide the old password in order "
1996 "to change it!");
1997 return LDB_ERR_UNWILLING_TO_PERFORM;
2000 /* The password modify through the NT hash is encouraged and
2001 has no problems at all */
2002 if (io->og.nt_hash) {
2003 if (!io->o.nt_hash || memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
2004 return make_error_and_update_badPwdCount(io);
2007 nt_hash_checked = true;
2010 /* But it is also possible to change a password by the LM hash
2011 * alone for compatibility reasons. This check is optional if
2012 * the NT hash was already checked - otherwise it's mandatory.
2013 * (as the SAMR operations request it). */
2014 if (io->og.lm_hash) {
2015 if ((!io->o.lm_hash && !nt_hash_checked)
2016 || (io->o.lm_hash && memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0)) {
2017 return make_error_and_update_badPwdCount(io);
2022 if (io->u.restrictions == 0) {
2023 /* FIXME: Is this right? */
2024 return LDB_SUCCESS;
2027 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2028 if ((io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) &&
2029 !io->ac->pwd_reset)
2031 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2032 ldb_asprintf_errstring(ldb,
2033 "%08X: %s - check_password_restrictions: "
2034 "password is too young to change!",
2035 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2036 ldb_strerror(ret));
2037 return ret;
2041 * Fundamental password checks done by the call
2042 * "samdb_check_password".
2043 * It is also in use by "dcesrv_samr_ValidatePassword".
2045 if (io->n.cleartext_utf8 != NULL) {
2046 enum samr_ValidationStatus vstat;
2047 vstat = samdb_check_password(io->n.cleartext_utf8,
2048 io->ac->status->domain_data.pwdProperties,
2049 io->ac->status->domain_data.minPwdLength);
2050 switch (vstat) {
2051 case SAMR_VALIDATION_STATUS_SUCCESS:
2052 /* perfect -> proceed! */
2053 break;
2055 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
2056 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2057 ldb_asprintf_errstring(ldb,
2058 "%08X: %s - check_password_restrictions: "
2059 "the password is too short. It should be equal or longer than %u characters!",
2060 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2061 ldb_strerror(ret),
2062 io->ac->status->domain_data.minPwdLength);
2063 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
2064 return ret;
2066 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
2067 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2068 ldb_asprintf_errstring(ldb,
2069 "%08X: %s - check_password_restrictions: "
2070 "the password does not meet the complexity criteria!",
2071 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2072 ldb_strerror(ret));
2073 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
2074 return ret;
2076 default:
2077 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2078 ldb_asprintf_errstring(ldb,
2079 "%08X: %s - check_password_restrictions: "
2080 "the password doesn't fit by a certain reason!",
2081 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2082 ldb_strerror(ret));
2083 return ret;
2087 if (io->ac->pwd_reset) {
2088 return LDB_SUCCESS;
2091 if (io->n.nt_hash) {
2092 uint32_t i;
2094 /* checks the NT hash password history */
2095 for (i = 0; i < io->o.nt_history_len; i++) {
2096 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
2097 if (ret == 0) {
2098 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2099 ldb_asprintf_errstring(ldb,
2100 "%08X: %s - check_password_restrictions: "
2101 "the password was already used (in history)!",
2102 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2103 ldb_strerror(ret));
2104 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2105 return ret;
2110 if (io->n.lm_hash) {
2111 uint32_t i;
2113 /* checks the LM hash password history */
2114 for (i = 0; i < io->o.lm_history_len; i++) {
2115 ret = memcmp(io->n.lm_hash, io->o.lm_history[i].hash, 16);
2116 if (ret == 0) {
2117 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2118 ldb_asprintf_errstring(ldb,
2119 "%08X: %s - check_password_restrictions: "
2120 "the password was already used (in history)!",
2121 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2122 ldb_strerror(ret));
2123 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2124 return ret;
2129 /* are all password changes disallowed? */
2130 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
2131 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2132 ldb_asprintf_errstring(ldb,
2133 "%08X: %s - check_password_restrictions: "
2134 "password changes disabled!",
2135 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2136 ldb_strerror(ret));
2137 return ret;
2140 /* can this user change the password? */
2141 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
2142 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2143 ldb_asprintf_errstring(ldb,
2144 "%08X: %s - check_password_restrictions: "
2145 "password can't be changed on this account!",
2146 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2147 ldb_strerror(ret));
2148 return ret;
2151 return LDB_SUCCESS;
2154 static int update_final_msg(struct setup_password_fields_io *io,
2155 struct ldb_message *msg)
2157 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
2158 int ret;
2159 int el_flags = 0;
2161 if (io->ac->req->operation == LDB_MODIFY) {
2162 el_flags |= LDB_FLAG_MOD_REPLACE;
2165 /* make sure we replace all the old attributes */
2166 if (io->ac->update_password && el_flags != 0) {
2167 ret = ldb_msg_add_empty(msg, "unicodePwd",
2168 el_flags, NULL);
2169 if (ret != LDB_SUCCESS) {
2170 return ret;
2172 ret = ldb_msg_add_empty(msg, "dBCSPwd",
2173 el_flags, NULL);
2174 if (ret != LDB_SUCCESS) {
2175 return ret;
2177 ret = ldb_msg_add_empty(msg, "ntPwdHistory",
2178 el_flags, NULL);
2179 if (ret != LDB_SUCCESS) {
2180 return ret;
2182 ret = ldb_msg_add_empty(msg, "lmPwdHistory",
2183 el_flags, NULL);
2184 if (ret != LDB_SUCCESS) {
2185 return ret;
2187 ret = ldb_msg_add_empty(msg, "supplementalCredentials",
2188 el_flags, NULL);
2189 if (ret != LDB_SUCCESS) {
2190 return ret;
2193 if (io->ac->update_lastset && el_flags != 0) {
2194 ret = ldb_msg_add_empty(msg, "pwdLastSet",
2195 el_flags, NULL);
2196 if (ret != LDB_SUCCESS) {
2197 return ret;
2201 if (io->g.nt_hash != NULL) {
2202 ret = samdb_msg_add_hash(ldb, io->ac, msg,
2203 "unicodePwd",
2204 io->g.nt_hash);
2205 if (ret != LDB_SUCCESS) {
2206 return ret;
2209 if (io->g.lm_hash != NULL) {
2210 ret = samdb_msg_add_hash(ldb, io->ac, msg,
2211 "dBCSPwd",
2212 io->g.lm_hash);
2213 if (ret != LDB_SUCCESS) {
2214 return ret;
2217 if (io->g.nt_history_len > 0) {
2218 ret = samdb_msg_add_hashes(ldb, io->ac, msg,
2219 "ntPwdHistory",
2220 io->g.nt_history,
2221 io->g.nt_history_len);
2222 if (ret != LDB_SUCCESS) {
2223 return ret;
2226 if (io->g.lm_history_len > 0) {
2227 ret = samdb_msg_add_hashes(ldb, io->ac, msg,
2228 "lmPwdHistory",
2229 io->g.lm_history,
2230 io->g.lm_history_len);
2231 if (ret != LDB_SUCCESS) {
2232 return ret;
2235 if (io->g.supplemental.length > 0) {
2236 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2237 &io->g.supplemental, NULL);
2238 if (ret != LDB_SUCCESS) {
2239 return ret;
2242 ret = samdb_msg_add_uint64(ldb, io->ac, msg,
2243 "pwdLastSet",
2244 io->g.last_set);
2245 if (ret != LDB_SUCCESS) {
2246 return ret;
2249 return LDB_SUCCESS;
2253 * This is intended for use by the "password_hash" module since there
2254 * password changes can be specified through one message element with the
2255 * new password (to set) and another one with the old password (to unset).
2257 * The first which sets a password (new value) can have flags
2258 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2259 * for entries). The latter (old value) has always specified
2260 * LDB_FLAG_MOD_DELETE.
2262 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2263 * matching message elements are malformed in respect to the set/change rules.
2264 * Otherwise it returns LDB_SUCCESS.
2266 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
2267 const char *name,
2268 enum ldb_request_type operation,
2269 const struct ldb_val **new_val,
2270 const struct ldb_val **old_val)
2272 unsigned int i;
2274 *new_val = NULL;
2275 *old_val = NULL;
2277 if (msg == NULL) {
2278 return LDB_SUCCESS;
2281 for (i = 0; i < msg->num_elements; i++) {
2282 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
2283 continue;
2286 if ((operation == LDB_MODIFY) &&
2287 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
2288 /* 0 values are allowed */
2289 if (msg->elements[i].num_values == 1) {
2290 *old_val = &msg->elements[i].values[0];
2291 } else if (msg->elements[i].num_values > 1) {
2292 return LDB_ERR_CONSTRAINT_VIOLATION;
2294 } else if ((operation == LDB_MODIFY) &&
2295 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
2296 if (msg->elements[i].num_values > 0) {
2297 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2298 } else {
2299 return LDB_ERR_UNWILLING_TO_PERFORM;
2301 } else {
2302 /* Add operations and LDB_FLAG_MOD_ADD */
2303 if (msg->elements[i].num_values > 0) {
2304 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2305 } else {
2306 return LDB_ERR_CONSTRAINT_VIOLATION;
2311 return LDB_SUCCESS;
2314 static int setup_io(struct ph_context *ac,
2315 const struct ldb_message *client_msg,
2316 const struct ldb_message *existing_msg,
2317 struct setup_password_fields_io *io)
2319 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
2320 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2321 struct loadparm_context *lp_ctx = talloc_get_type(
2322 ldb_get_opaque(ldb, "loadparm"), struct loadparm_context);
2323 int ret;
2324 const struct ldb_message *info_msg = NULL;
2326 ZERO_STRUCTP(io);
2328 /* Some operations below require kerberos contexts */
2330 if (existing_msg != NULL) {
2332 * This is a modify operation
2334 info_msg = existing_msg;
2335 } else {
2337 * This is an add operation
2339 info_msg = client_msg;
2342 if (smb_krb5_init_context(ac,
2343 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
2344 &io->smb_krb5_context) != 0) {
2345 return ldb_operr(ldb);
2348 io->ac = ac;
2350 io->u.userAccountControl = ldb_msg_find_attr_as_uint(info_msg,
2351 "userAccountControl", 0);
2352 if (info_msg == existing_msg) {
2354 * We only take pwdLastSet from the existing object
2355 * otherwise we leave it as 0.
2357 io->u.pwdLastSet = samdb_result_nttime(info_msg, "pwdLastSet", 0);
2359 io->u.sAMAccountName = ldb_msg_find_attr_as_string(info_msg,
2360 "sAMAccountName", NULL);
2361 io->u.user_principal_name = ldb_msg_find_attr_as_string(info_msg,
2362 "userPrincipalName", NULL);
2363 io->u.is_computer = ldb_msg_check_string_attribute(info_msg, "objectClass", "computer");
2365 if (io->u.sAMAccountName == NULL) {
2366 ldb_asprintf_errstring(ldb,
2367 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2368 ldb_dn_get_linearized(info_msg->dn));
2370 return LDB_ERR_CONSTRAINT_VIOLATION;
2373 if (io->u.userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
2374 struct ldb_control *permit_trust = ldb_request_get_control(ac->req,
2375 DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID);
2377 if (permit_trust == NULL) {
2378 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2379 ldb_asprintf_errstring(ldb,
2380 "%08X: %s - setup_io: changing the interdomain trust password "
2381 "on %s not allowed via LDAP. Use LSA or NETLOGON",
2382 W_ERROR_V(WERR_ACCESS_DENIED),
2383 ldb_strerror(ret),
2384 ldb_dn_get_linearized(info_msg->dn));
2385 return ret;
2389 /* Only non-trust accounts have restrictions (possibly this test is the
2390 * wrong way around, but we like to be restrictive if possible */
2391 io->u.restrictions = !(io->u.userAccountControl
2392 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
2393 | UF_SERVER_TRUST_ACCOUNT));
2395 if (ac->userPassword) {
2396 ret = msg_find_old_and_new_pwd_val(client_msg, "userPassword",
2397 ac->req->operation,
2398 &io->n.cleartext_utf8,
2399 &io->og.cleartext_utf8);
2400 if (ret != LDB_SUCCESS) {
2401 ldb_asprintf_errstring(ldb,
2402 "setup_io: "
2403 "it's only allowed to set the old password once!");
2404 return ret;
2408 if (io->n.cleartext_utf8 != NULL) {
2409 struct ldb_val *cleartext_utf8_blob;
2410 char *p;
2412 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
2413 if (!cleartext_utf8_blob) {
2414 return ldb_oom(ldb);
2417 *cleartext_utf8_blob = *io->n.cleartext_utf8;
2419 /* make sure we have a null terminated string */
2420 p = talloc_strndup(cleartext_utf8_blob,
2421 (const char *)io->n.cleartext_utf8->data,
2422 io->n.cleartext_utf8->length);
2423 if ((p == NULL) && (io->n.cleartext_utf8->length > 0)) {
2424 return ldb_oom(ldb);
2426 cleartext_utf8_blob->data = (uint8_t *)p;
2428 io->n.cleartext_utf8 = cleartext_utf8_blob;
2431 ret = msg_find_old_and_new_pwd_val(client_msg, "clearTextPassword",
2432 ac->req->operation,
2433 &io->n.cleartext_utf16,
2434 &io->og.cleartext_utf16);
2435 if (ret != LDB_SUCCESS) {
2436 ldb_asprintf_errstring(ldb,
2437 "setup_io: "
2438 "it's only allowed to set the old password once!");
2439 return ret;
2442 /* this rather strange looking piece of code is there to
2443 handle a ldap client setting a password remotely using the
2444 unicodePwd ldap field. The syntax is that the password is
2445 in UTF-16LE, with a " at either end. Unfortunately the
2446 unicodePwd field is also used to store the nt hashes
2447 internally in Samba, and is used in the nt hash format on
2448 the wire in DRS replication, so we have a single name for
2449 two distinct values. The code below leaves us with a small
2450 chance (less than 1 in 2^32) of a mixup, if someone manages
2451 to create a MD4 hash which starts and ends in 0x22 0x00, as
2452 that would then be treated as a UTF16 password rather than
2453 a nthash */
2455 ret = msg_find_old_and_new_pwd_val(client_msg, "unicodePwd",
2456 ac->req->operation,
2457 &quoted_utf16,
2458 &old_quoted_utf16);
2459 if (ret != LDB_SUCCESS) {
2460 ldb_asprintf_errstring(ldb,
2461 "setup_io: "
2462 "it's only allowed to set the old password once!");
2463 return ret;
2466 /* Checks and converts the actual "unicodePwd" attribute */
2467 if (!ac->hash_values &&
2468 quoted_utf16 &&
2469 quoted_utf16->length >= 4 &&
2470 quoted_utf16->data[0] == '"' &&
2471 quoted_utf16->data[1] == 0 &&
2472 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
2473 quoted_utf16->data[quoted_utf16->length-1] == 0) {
2474 struct ldb_val *quoted_utf16_2;
2476 if (io->n.cleartext_utf16) {
2477 /* refuse the change if someone wants to change with
2478 with both UTF16 possibilities at the same time... */
2479 ldb_asprintf_errstring(ldb,
2480 "setup_io: "
2481 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2482 return LDB_ERR_UNWILLING_TO_PERFORM;
2486 * adapt the quoted UTF16 string to be a real
2487 * cleartext one
2489 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2490 if (quoted_utf16_2 == NULL) {
2491 return ldb_oom(ldb);
2494 quoted_utf16_2->data = quoted_utf16->data + 2;
2495 quoted_utf16_2->length = quoted_utf16->length-4;
2496 io->n.cleartext_utf16 = quoted_utf16_2;
2497 io->n.nt_hash = NULL;
2499 } else if (quoted_utf16) {
2500 /* We have only the hash available -> so no plaintext here */
2501 if (!ac->hash_values) {
2502 /* refuse the change if someone wants to change
2503 the hash without control specified... */
2504 ldb_asprintf_errstring(ldb,
2505 "setup_io: "
2506 "it's not allowed to set the NT hash password directly'");
2507 /* this looks odd but this is what Windows does:
2508 returns "UNWILLING_TO_PERFORM" on wrong
2509 password sets and "CONSTRAINT_VIOLATION" on
2510 wrong password changes. */
2511 if (old_quoted_utf16 == NULL) {
2512 return LDB_ERR_UNWILLING_TO_PERFORM;
2515 return LDB_ERR_CONSTRAINT_VIOLATION;
2518 io->n.nt_hash = talloc(io->ac, struct samr_Password);
2519 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
2520 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
2523 /* Checks and converts the previous "unicodePwd" attribute */
2524 if (!ac->hash_values &&
2525 old_quoted_utf16 &&
2526 old_quoted_utf16->length >= 4 &&
2527 old_quoted_utf16->data[0] == '"' &&
2528 old_quoted_utf16->data[1] == 0 &&
2529 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
2530 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
2531 struct ldb_val *old_quoted_utf16_2;
2533 if (io->og.cleartext_utf16) {
2534 /* refuse the change if someone wants to change with
2535 both UTF16 possibilities at the same time... */
2536 ldb_asprintf_errstring(ldb,
2537 "setup_io: "
2538 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2539 return LDB_ERR_UNWILLING_TO_PERFORM;
2543 * adapt the quoted UTF16 string to be a real
2544 * cleartext one
2546 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2547 if (old_quoted_utf16_2 == NULL) {
2548 return ldb_oom(ldb);
2551 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
2552 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
2554 io->og.cleartext_utf16 = old_quoted_utf16_2;
2555 io->og.nt_hash = NULL;
2556 } else if (old_quoted_utf16) {
2557 /* We have only the hash available -> so no plaintext here */
2558 if (!ac->hash_values) {
2559 /* refuse the change if someone wants to change
2560 the hash without control specified... */
2561 ldb_asprintf_errstring(ldb,
2562 "setup_io: "
2563 "it's not allowed to set the NT hash password directly'");
2564 return LDB_ERR_UNWILLING_TO_PERFORM;
2567 io->og.nt_hash = talloc(io->ac, struct samr_Password);
2568 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
2569 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
2572 /* Handles the "dBCSPwd" attribute (LM hash) */
2573 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
2574 ret = msg_find_old_and_new_pwd_val(client_msg, "dBCSPwd",
2575 ac->req->operation,
2576 &lm_hash, &old_lm_hash);
2577 if (ret != LDB_SUCCESS) {
2578 ldb_asprintf_errstring(ldb,
2579 "setup_io: "
2580 "it's only allowed to set the old password once!");
2581 return ret;
2584 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
2585 /* refuse the change if someone wants to change the hash
2586 without control specified... */
2587 ldb_asprintf_errstring(ldb,
2588 "setup_io: "
2589 "it's not allowed to set the LM hash password directly'");
2590 return LDB_ERR_UNWILLING_TO_PERFORM;
2593 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
2594 io->n.lm_hash = talloc(io->ac, struct samr_Password);
2595 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
2596 sizeof(io->n.lm_hash->hash)));
2598 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
2599 io->og.lm_hash = talloc(io->ac, struct samr_Password);
2600 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
2601 sizeof(io->og.lm_hash->hash)));
2605 * Handles the password change control if it's specified. It has the
2606 * precedance and overrides already specified old password values of
2607 * change requests (but that shouldn't happen since the control is
2608 * fully internal and only used in conjunction with replace requests!).
2610 if (ac->change != NULL) {
2611 io->og.nt_hash = NULL;
2612 if (ac->change->old_nt_pwd_hash != NULL) {
2613 io->og.nt_hash = talloc_memdup(io->ac,
2614 ac->change->old_nt_pwd_hash,
2615 sizeof(struct samr_Password));
2617 io->og.lm_hash = NULL;
2618 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
2619 io->og.lm_hash = talloc_memdup(io->ac,
2620 ac->change->old_lm_pwd_hash,
2621 sizeof(struct samr_Password));
2625 /* refuse the change if someone wants to change the clear-
2626 text and supply his own hashes at the same time... */
2627 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
2628 && (io->n.nt_hash || io->n.lm_hash)) {
2629 ldb_asprintf_errstring(ldb,
2630 "setup_io: "
2631 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2632 return LDB_ERR_UNWILLING_TO_PERFORM;
2635 /* refuse the change if someone wants to change the password
2636 using both plaintext methods (UTF8 and UTF16) at the same time... */
2637 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
2638 ldb_asprintf_errstring(ldb,
2639 "setup_io: "
2640 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2641 return LDB_ERR_UNWILLING_TO_PERFORM;
2644 /* refuse the change if someone tries to set/change the password by
2645 * the lanman hash alone and we've deactivated that mechanism. This
2646 * would end in an account without any password! */
2647 if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
2648 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
2649 ldb_asprintf_errstring(ldb,
2650 "setup_io: "
2651 "It's not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2652 /* on "userPassword" and "clearTextPassword" we've to return
2653 * something different, since these are virtual attributes */
2654 if ((ldb_msg_find_element(client_msg, "userPassword") != NULL) ||
2655 (ldb_msg_find_element(client_msg, "clearTextPassword") != NULL)) {
2656 return LDB_ERR_CONSTRAINT_VIOLATION;
2658 return LDB_ERR_UNWILLING_TO_PERFORM;
2661 /* refuse the change if someone wants to compare against a plaintext
2662 or hash at the same time for a "password modify" operation... */
2663 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
2664 && (io->og.nt_hash || io->og.lm_hash)) {
2665 ldb_asprintf_errstring(ldb,
2666 "setup_io: "
2667 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2668 return LDB_ERR_UNWILLING_TO_PERFORM;
2671 /* refuse the change if someone wants to compare against both
2672 * plaintexts at the same time for a "password modify" operation... */
2673 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2674 ldb_asprintf_errstring(ldb,
2675 "setup_io: "
2676 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2677 return LDB_ERR_UNWILLING_TO_PERFORM;
2680 /* Decides if we have a password modify or password reset operation */
2681 if (ac->req->operation == LDB_ADD) {
2682 /* On "add" we have only "password reset" */
2683 ac->pwd_reset = true;
2684 } else if (ac->req->operation == LDB_MODIFY) {
2685 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2686 || io->og.nt_hash || io->og.lm_hash) {
2687 /* If we have an old password specified then for sure it
2688 * is a user "password change" */
2689 ac->pwd_reset = false;
2690 } else {
2691 /* Otherwise we have also here a "password reset" */
2692 ac->pwd_reset = true;
2694 } else {
2695 /* this shouldn't happen */
2696 return ldb_operr(ldb);
2699 if (existing_msg != NULL) {
2700 NTSTATUS status;
2702 if (ac->pwd_reset) {
2703 /* Get the old password from the database */
2704 status = samdb_result_passwords_no_lockout(ac,
2705 lp_ctx,
2706 existing_msg,
2707 &io->o.lm_hash,
2708 &io->o.nt_hash);
2709 } else {
2710 /* Get the old password from the database */
2711 status = samdb_result_passwords(ac,
2712 lp_ctx,
2713 existing_msg,
2714 &io->o.lm_hash,
2715 &io->o.nt_hash);
2718 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
2719 return dsdb_module_werror(ac->module,
2720 LDB_ERR_CONSTRAINT_VIOLATION,
2721 WERR_ACCOUNT_LOCKED_OUT,
2722 "Password change not permitted,"
2723 " account locked out!");
2726 if (!NT_STATUS_IS_OK(status)) {
2728 * This only happens if the database has gone weird,
2729 * not if we are just missing the passwords
2731 return ldb_operr(ldb);
2734 io->o.nt_history_len = samdb_result_hashes(ac, existing_msg,
2735 "ntPwdHistory",
2736 &io->o.nt_history);
2737 io->o.lm_history_len = samdb_result_hashes(ac, existing_msg,
2738 "lmPwdHistory",
2739 &io->o.lm_history);
2740 io->o.supplemental = ldb_msg_find_ldb_val(existing_msg,
2741 "supplementalCredentials");
2743 if (io->o.supplemental != NULL) {
2744 enum ndr_err_code ndr_err;
2746 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
2747 &io->o.scb,
2748 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
2749 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2750 status = ndr_map_error2ntstatus(ndr_err);
2751 ldb_asprintf_errstring(ldb,
2752 "setup_io: failed to pull "
2753 "old supplementalCredentialsBlob: %s",
2754 nt_errstr(status));
2755 return LDB_ERR_OPERATIONS_ERROR;
2760 return LDB_SUCCESS;
2763 static struct ph_context *ph_init_context(struct ldb_module *module,
2764 struct ldb_request *req,
2765 bool userPassword,
2766 bool update_password)
2768 struct ldb_context *ldb;
2769 struct ph_context *ac;
2771 ldb = ldb_module_get_ctx(module);
2773 ac = talloc_zero(req, struct ph_context);
2774 if (ac == NULL) {
2775 ldb_set_errstring(ldb, "Out of Memory");
2776 return NULL;
2779 ac->module = module;
2780 ac->req = req;
2781 ac->userPassword = userPassword;
2782 ac->update_password = update_password;
2783 ac->update_lastset = true;
2785 return ac;
2788 static void ph_apply_controls(struct ph_context *ac)
2790 struct ldb_control *ctrl;
2792 ac->change_status = false;
2793 ctrl = ldb_request_get_control(ac->req,
2794 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2795 if (ctrl != NULL) {
2796 ac->change_status = true;
2798 /* Mark the "change status" control as uncritical (done) */
2799 ctrl->critical = false;
2802 ac->hash_values = false;
2803 ctrl = ldb_request_get_control(ac->req,
2804 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2805 if (ctrl != NULL) {
2806 ac->hash_values = true;
2808 /* Mark the "hash values" control as uncritical (done) */
2809 ctrl->critical = false;
2812 ctrl = ldb_request_get_control(ac->req,
2813 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2814 if (ctrl != NULL) {
2815 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2817 /* Mark the "change" control as uncritical (done) */
2818 ctrl->critical = false;
2821 ac->pwd_last_set_bypass = false;
2822 ctrl = ldb_request_get_control(ac->req,
2823 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID);
2824 if (ctrl != NULL) {
2825 ac->pwd_last_set_bypass = true;
2827 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2828 ctrl->critical = false;
2832 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2834 struct ph_context *ac;
2836 ac = talloc_get_type(req->context, struct ph_context);
2838 if (!ares) {
2839 return ldb_module_done(ac->req, NULL, NULL,
2840 LDB_ERR_OPERATIONS_ERROR);
2843 if (ares->type == LDB_REPLY_REFERRAL) {
2844 return ldb_module_send_referral(ac->req, ares->referral);
2847 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2848 /* On success and trivial errors a status control is being
2849 * added (used for example by the "samdb_set_password" call) */
2850 ldb_reply_add_control(ares,
2851 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2852 false,
2853 ac->status);
2856 if (ares->error != LDB_SUCCESS) {
2857 return ldb_module_done(ac->req, ares->controls,
2858 ares->response, ares->error);
2861 if (ares->type != LDB_REPLY_DONE) {
2862 talloc_free(ares);
2863 return ldb_module_done(ac->req, NULL, NULL,
2864 LDB_ERR_OPERATIONS_ERROR);
2867 return ldb_module_done(ac->req, ares->controls,
2868 ares->response, ares->error);
2871 static int password_hash_add_do_add(struct ph_context *ac);
2872 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2873 static int password_hash_mod_search_self(struct ph_context *ac);
2874 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2875 static int password_hash_mod_do_mod(struct ph_context *ac);
2877 static int get_domain_data_callback(struct ldb_request *req,
2878 struct ldb_reply *ares)
2880 struct ldb_context *ldb;
2881 struct ph_context *ac;
2882 struct loadparm_context *lp_ctx;
2883 int ret = LDB_SUCCESS;
2885 ac = talloc_get_type(req->context, struct ph_context);
2886 ldb = ldb_module_get_ctx(ac->module);
2888 if (!ares) {
2889 ret = LDB_ERR_OPERATIONS_ERROR;
2890 goto done;
2892 if (ares->error != LDB_SUCCESS) {
2893 return ldb_module_done(ac->req, ares->controls,
2894 ares->response, ares->error);
2897 switch (ares->type) {
2898 case LDB_REPLY_ENTRY:
2899 if (ac->status != NULL) {
2900 talloc_free(ares);
2902 ldb_set_errstring(ldb, "Too many results");
2903 ret = LDB_ERR_OPERATIONS_ERROR;
2904 goto done;
2907 /* Setup the "status" structure (used as control later) */
2908 ac->status = talloc_zero(ac->req,
2909 struct dsdb_control_password_change_status);
2910 if (ac->status == NULL) {
2911 talloc_free(ares);
2913 ldb_oom(ldb);
2914 ret = LDB_ERR_OPERATIONS_ERROR;
2915 goto done;
2918 /* Setup the "domain data" structure */
2919 ac->status->domain_data.pwdProperties =
2920 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
2921 ac->status->domain_data.pwdHistoryLength =
2922 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
2923 ac->status->domain_data.maxPwdAge =
2924 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
2925 ac->status->domain_data.minPwdAge =
2926 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
2927 ac->status->domain_data.minPwdLength =
2928 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
2929 ac->status->domain_data.store_cleartext =
2930 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2932 /* For a domain DN, this puts things in dotted notation */
2933 /* For builtin domains, this will give details for the host,
2934 * but that doesn't really matter, as it's just used for salt
2935 * and kerberos principals, which don't exist here */
2937 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2938 struct loadparm_context);
2940 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
2941 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
2942 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
2944 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2946 if (ac->dom_res != NULL) {
2947 talloc_free(ares);
2949 ldb_set_errstring(ldb, "Too many results");
2950 ret = LDB_ERR_OPERATIONS_ERROR;
2951 goto done;
2954 ac->dom_res = talloc_steal(ac, ares);
2955 ret = LDB_SUCCESS;
2956 break;
2958 case LDB_REPLY_REFERRAL:
2959 /* ignore */
2960 talloc_free(ares);
2961 ret = LDB_SUCCESS;
2962 break;
2964 case LDB_REPLY_DONE:
2965 talloc_free(ares);
2966 /* call the next step */
2967 switch (ac->req->operation) {
2968 case LDB_ADD:
2969 ret = password_hash_add_do_add(ac);
2970 break;
2972 case LDB_MODIFY:
2973 ret = password_hash_mod_do_mod(ac);
2974 break;
2976 default:
2977 ret = LDB_ERR_OPERATIONS_ERROR;
2978 break;
2980 break;
2983 done:
2984 if (ret != LDB_SUCCESS) {
2985 struct ldb_reply *new_ares;
2987 new_ares = talloc_zero(ac->req, struct ldb_reply);
2988 if (new_ares == NULL) {
2989 ldb_oom(ldb);
2990 return ldb_module_done(ac->req, NULL, NULL,
2991 LDB_ERR_OPERATIONS_ERROR);
2994 new_ares->error = ret;
2995 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2996 /* On success and trivial errors a status control is being
2997 * added (used for example by the "samdb_set_password" call) */
2998 ldb_reply_add_control(new_ares,
2999 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
3000 false,
3001 ac->status);
3004 return ldb_module_done(ac->req, new_ares->controls,
3005 new_ares->response, new_ares->error);
3008 return LDB_SUCCESS;
3011 static int build_domain_data_request(struct ph_context *ac)
3013 /* attrs[] is returned from this function in
3014 ac->dom_req->op.search.attrs, so it must be static, as
3015 otherwise the compiler can put it on the stack */
3016 struct ldb_context *ldb;
3017 static const char * const attrs[] = { "pwdProperties",
3018 "pwdHistoryLength",
3019 "maxPwdAge",
3020 "minPwdAge",
3021 "minPwdLength",
3022 "lockoutThreshold",
3023 "lockOutObservationWindow",
3024 NULL };
3025 int ret;
3027 ldb = ldb_module_get_ctx(ac->module);
3029 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
3030 ldb_get_default_basedn(ldb),
3031 LDB_SCOPE_BASE,
3032 NULL, attrs,
3033 NULL,
3034 ac, get_domain_data_callback,
3035 ac->req);
3036 LDB_REQ_SET_LOCATION(ac->dom_req);
3037 return ret;
3040 static int password_hash_needed(struct ldb_module *module,
3041 struct ldb_request *req,
3042 struct ph_context **_ac)
3044 struct ldb_context *ldb = ldb_module_get_ctx(module);
3045 const char *operation = NULL;
3046 const struct ldb_message *msg = NULL;
3047 struct ph_context *ac = NULL;
3048 const char *passwordAttrs[] = {
3049 "userPassword",
3050 "clearTextPassword",
3051 "unicodePwd",
3052 "dBCSPwd",
3053 NULL
3055 const char **a = NULL;
3056 unsigned int attr_cnt = 0;
3057 struct ldb_control *bypass = NULL;
3058 bool userPassword = dsdb_user_password_support(module, req, req);
3059 bool update_password = false;
3061 *_ac = NULL;
3063 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_needed\n");
3065 switch (req->operation) {
3066 case LDB_ADD:
3067 operation = "add";
3068 msg = req->op.add.message;
3069 break;
3070 case LDB_MODIFY:
3071 operation = "modify";
3072 msg = req->op.mod.message;
3073 break;
3074 default:
3075 return ldb_next_request(module, req);
3078 if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
3079 return ldb_next_request(module, req);
3082 bypass = ldb_request_get_control(req,
3083 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
3084 if (bypass != NULL) {
3085 /* Mark the "bypass" control as uncritical (done) */
3086 bypass->critical = false;
3087 ldb_debug(ldb, LDB_DEBUG_TRACE,
3088 "password_hash_needed(%s) (bypassing)\n",
3089 operation);
3090 return password_hash_bypass(module, req);
3093 /* nobody must touch password histories and 'supplementalCredentials' */
3094 if (ldb_msg_find_element(msg, "ntPwdHistory")) {
3095 return LDB_ERR_UNWILLING_TO_PERFORM;
3097 if (ldb_msg_find_element(msg, "lmPwdHistory")) {
3098 return LDB_ERR_UNWILLING_TO_PERFORM;
3100 if (ldb_msg_find_element(msg, "supplementalCredentials")) {
3101 return LDB_ERR_UNWILLING_TO_PERFORM;
3105 * If no part of this touches the 'userPassword' OR 'clearTextPassword'
3106 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
3107 * For password changes/set there should be a 'delete' or a 'modify'
3108 * on these attributes.
3110 for (a = passwordAttrs; *a != NULL; a++) {
3111 if ((!userPassword) && (ldb_attr_cmp(*a, "userPassword") == 0)) {
3112 continue;
3115 if (ldb_msg_find_element(msg, *a) != NULL) {
3116 /* MS-ADTS 3.1.1.3.1.5.2 */
3117 if ((ldb_attr_cmp(*a, "userPassword") == 0) &&
3118 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
3119 return LDB_ERR_CONSTRAINT_VIOLATION;
3122 ++attr_cnt;
3126 if (attr_cnt > 0) {
3127 update_password = true;
3130 if (!update_password) {
3131 return ldb_next_request(module, req);
3134 ac = ph_init_context(module, req, userPassword, update_password);
3135 if (!ac) {
3136 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3137 return ldb_operr(ldb);
3139 ph_apply_controls(ac);
3141 *_ac = ac;
3142 return LDB_SUCCESS;
3145 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
3147 struct ldb_context *ldb = ldb_module_get_ctx(module);
3148 struct ph_context *ac = NULL;
3149 int ret;
3151 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
3153 ret = password_hash_needed(module, req, &ac);
3154 if (ret != LDB_SUCCESS) {
3155 return ret;
3157 if (ac == NULL) {
3158 return ret;
3161 /* Make sure we are performing the password set action on a (for us)
3162 * valid object. Those are instances of either "user" and/or
3163 * "inetOrgPerson". Otherwise continue with the submodules. */
3164 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
3165 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
3167 TALLOC_FREE(ac);
3169 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
3170 ldb_set_errstring(ldb,
3171 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3172 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3175 return ldb_next_request(module, req);
3178 /* get user domain data */
3179 ret = build_domain_data_request(ac);
3180 if (ret != LDB_SUCCESS) {
3181 return ret;
3184 return ldb_next_request(module, ac->dom_req);
3187 static int password_hash_add_do_add(struct ph_context *ac)
3189 struct ldb_context *ldb;
3190 struct ldb_request *down_req;
3191 struct ldb_message *msg;
3192 struct setup_password_fields_io io;
3193 int ret;
3195 /* Prepare the internal data structure containing the passwords */
3196 ret = setup_io(ac, ac->req->op.add.message, NULL, &io);
3197 if (ret != LDB_SUCCESS) {
3198 return ret;
3201 ldb = ldb_module_get_ctx(ac->module);
3203 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
3204 if (msg == NULL) {
3205 return ldb_operr(ldb);
3208 /* remove attributes that we just read into 'io' */
3209 if (ac->userPassword) {
3210 ldb_msg_remove_attr(msg, "userPassword");
3212 ldb_msg_remove_attr(msg, "clearTextPassword");
3213 ldb_msg_remove_attr(msg, "unicodePwd");
3214 ldb_msg_remove_attr(msg, "dBCSPwd");
3215 ldb_msg_remove_attr(msg, "pwdLastSet");
3217 ret = setup_password_fields(&io);
3218 if (ret != LDB_SUCCESS) {
3219 return ret;
3222 ret = check_password_restrictions(&io);
3223 if (ret != LDB_SUCCESS) {
3224 return ret;
3227 ret = update_final_msg(&io, msg);
3228 if (ret != LDB_SUCCESS) {
3229 return ret;
3232 ret = ldb_build_add_req(&down_req, ldb, ac,
3233 msg,
3234 ac->req->controls,
3235 ac, ph_op_callback,
3236 ac->req);
3237 LDB_REQ_SET_LOCATION(down_req);
3238 if (ret != LDB_SUCCESS) {
3239 return ret;
3242 return ldb_next_request(ac->module, down_req);
3245 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
3247 struct ldb_context *ldb = ldb_module_get_ctx(module);
3248 struct ph_context *ac = NULL;
3249 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
3250 "unicodePwd", "dBCSPwd", NULL }, **l;
3251 unsigned int del_attr_cnt, add_attr_cnt, rep_attr_cnt;
3252 struct ldb_message_element *passwordAttr;
3253 struct ldb_message *msg;
3254 struct ldb_request *down_req;
3255 int ret;
3257 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
3259 ret = password_hash_needed(module, req, &ac);
3260 if (ret != LDB_SUCCESS) {
3261 return ret;
3263 if (ac == NULL) {
3264 return ret;
3267 /* use a new message structure so that we can modify it */
3268 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3269 if (msg == NULL) {
3270 return ldb_oom(ldb);
3273 /* - check for single-valued password attributes
3274 * (if not return "CONSTRAINT_VIOLATION")
3275 * - check that for a password change operation one add and one delete
3276 * operation exists
3277 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3278 * - check that a password change and a password set operation cannot
3279 * be mixed
3280 * (if not return "UNWILLING_TO_PERFORM")
3281 * - remove all password attributes modifications from the first change
3282 * operation (anything without the passwords) - we will make the real
3283 * modification later */
3284 del_attr_cnt = 0;
3285 add_attr_cnt = 0;
3286 rep_attr_cnt = 0;
3287 for (l = passwordAttrs; *l != NULL; l++) {
3288 if ((!ac->userPassword) &&
3289 (ldb_attr_cmp(*l, "userPassword") == 0)) {
3290 continue;
3293 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
3294 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
3295 ++del_attr_cnt;
3297 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
3298 ++add_attr_cnt;
3300 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
3301 ++rep_attr_cnt;
3303 if ((passwordAttr->num_values != 1) &&
3304 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
3305 talloc_free(ac);
3306 ldb_asprintf_errstring(ldb,
3307 "'%s' attribute must have exactly one value on add operations!",
3308 *l);
3309 return LDB_ERR_CONSTRAINT_VIOLATION;
3311 if ((passwordAttr->num_values > 1) &&
3312 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
3313 talloc_free(ac);
3314 ldb_asprintf_errstring(ldb,
3315 "'%s' attribute must have zero or one value(s) on delete operations!",
3316 *l);
3317 return LDB_ERR_CONSTRAINT_VIOLATION;
3319 ldb_msg_remove_element(msg, passwordAttr);
3322 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
3323 talloc_free(ac);
3324 ldb_set_errstring(ldb,
3325 "Only the add action for a password change specified!");
3326 return LDB_ERR_UNWILLING_TO_PERFORM;
3328 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
3329 talloc_free(ac);
3330 ldb_set_errstring(ldb,
3331 "Only one delete and one add action for a password change allowed!");
3332 return LDB_ERR_UNWILLING_TO_PERFORM;
3334 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
3335 talloc_free(ac);
3336 ldb_set_errstring(ldb,
3337 "Either a password change or a password set operation is allowed!");
3338 return LDB_ERR_UNWILLING_TO_PERFORM;
3341 /* if there was nothing else to be modified skip to next step */
3342 if (msg->num_elements == 0) {
3343 return password_hash_mod_search_self(ac);
3346 ret = ldb_build_mod_req(&down_req, ldb, ac,
3347 msg,
3348 req->controls,
3349 ac, ph_modify_callback,
3350 req);
3351 LDB_REQ_SET_LOCATION(down_req);
3352 if (ret != LDB_SUCCESS) {
3353 return ret;
3356 return ldb_next_request(module, down_req);
3359 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3361 struct ph_context *ac;
3363 ac = talloc_get_type(req->context, struct ph_context);
3365 if (!ares) {
3366 return ldb_module_done(ac->req, NULL, NULL,
3367 LDB_ERR_OPERATIONS_ERROR);
3370 if (ares->type == LDB_REPLY_REFERRAL) {
3371 return ldb_module_send_referral(ac->req, ares->referral);
3374 if (ares->error != LDB_SUCCESS) {
3375 return ldb_module_done(ac->req, ares->controls,
3376 ares->response, ares->error);
3379 if (ares->type != LDB_REPLY_DONE) {
3380 talloc_free(ares);
3381 return ldb_module_done(ac->req, NULL, NULL,
3382 LDB_ERR_OPERATIONS_ERROR);
3385 talloc_free(ares);
3387 return password_hash_mod_search_self(ac);
3390 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
3392 struct ldb_context *ldb;
3393 struct ph_context *ac;
3394 int ret = LDB_SUCCESS;
3396 ac = talloc_get_type(req->context, struct ph_context);
3397 ldb = ldb_module_get_ctx(ac->module);
3399 if (!ares) {
3400 ret = LDB_ERR_OPERATIONS_ERROR;
3401 goto done;
3403 if (ares->error != LDB_SUCCESS) {
3404 return ldb_module_done(ac->req, ares->controls,
3405 ares->response, ares->error);
3408 /* we are interested only in the single reply (base search) */
3409 switch (ares->type) {
3410 case LDB_REPLY_ENTRY:
3411 /* Make sure we are performing the password change action on a
3412 * (for us) valid object. Those are instances of either "user"
3413 * and/or "inetOrgPerson". Otherwise continue with the
3414 * submodules. */
3415 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
3416 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
3417 talloc_free(ares);
3419 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
3420 ldb_set_errstring(ldb,
3421 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3422 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
3423 goto done;
3426 ret = ldb_next_request(ac->module, ac->req);
3427 goto done;
3430 if (ac->search_res != NULL) {
3431 talloc_free(ares);
3433 ldb_set_errstring(ldb, "Too many results");
3434 ret = LDB_ERR_OPERATIONS_ERROR;
3435 goto done;
3438 ac->search_res = talloc_steal(ac, ares);
3439 ret = LDB_SUCCESS;
3440 break;
3442 case LDB_REPLY_REFERRAL:
3443 /* ignore anything else for now */
3444 talloc_free(ares);
3445 ret = LDB_SUCCESS;
3446 break;
3448 case LDB_REPLY_DONE:
3449 talloc_free(ares);
3451 /* get user domain data */
3452 ret = build_domain_data_request(ac);
3453 if (ret != LDB_SUCCESS) {
3454 return ldb_module_done(ac->req, NULL, NULL, ret);
3457 ret = ldb_next_request(ac->module, ac->dom_req);
3458 break;
3461 done:
3462 if (ret != LDB_SUCCESS) {
3463 return ldb_module_done(ac->req, NULL, NULL, ret);
3466 return LDB_SUCCESS;
3469 static int password_hash_mod_search_self(struct ph_context *ac)
3471 struct ldb_context *ldb;
3472 static const char * const attrs[] = { "objectClass",
3473 "userAccountControl",
3474 "msDS-User-Account-Control-Computed",
3475 "pwdLastSet",
3476 "sAMAccountName",
3477 "objectSid",
3478 "userPrincipalName",
3479 "supplementalCredentials",
3480 "lmPwdHistory",
3481 "ntPwdHistory",
3482 "dBCSPwd",
3483 "unicodePwd",
3484 "badPasswordTime",
3485 "badPwdCount",
3486 "lockoutTime",
3487 NULL };
3488 struct ldb_request *search_req;
3489 int ret;
3491 ldb = ldb_module_get_ctx(ac->module);
3493 ret = ldb_build_search_req(&search_req, ldb, ac,
3494 ac->req->op.mod.message->dn,
3495 LDB_SCOPE_BASE,
3496 "(objectclass=*)",
3497 attrs,
3498 NULL,
3499 ac, ph_mod_search_callback,
3500 ac->req);
3501 LDB_REQ_SET_LOCATION(search_req);
3502 if (ret != LDB_SUCCESS) {
3503 return ret;
3506 return ldb_next_request(ac->module, search_req);
3509 static int password_hash_mod_do_mod(struct ph_context *ac)
3511 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3512 struct ldb_request *mod_req;
3513 struct ldb_message *msg;
3514 struct setup_password_fields_io io;
3515 int ret;
3517 /* use a new message structure so that we can modify it */
3518 msg = ldb_msg_new(ac);
3519 if (msg == NULL) {
3520 return ldb_operr(ldb);
3523 /* modify dn */
3524 msg->dn = ac->req->op.mod.message->dn;
3526 /* Prepare the internal data structure containing the passwords */
3527 ret = setup_io(ac, ac->req->op.mod.message,
3528 ac->search_res->message, &io);
3529 if (ret != LDB_SUCCESS) {
3530 return ret;
3533 ret = setup_password_fields(&io);
3534 if (ret != LDB_SUCCESS) {
3535 return ret;
3538 ret = check_password_restrictions(&io);
3539 if (ret != LDB_SUCCESS) {
3540 return ret;
3543 ret = update_final_msg(&io, msg);
3544 if (ret != LDB_SUCCESS) {
3545 return ret;
3548 ret = ldb_build_mod_req(&mod_req, ldb, ac,
3549 msg,
3550 ac->req->controls,
3551 ac, ph_op_callback,
3552 ac->req);
3553 LDB_REQ_SET_LOCATION(mod_req);
3554 if (ret != LDB_SUCCESS) {
3555 return ret;
3558 return ldb_next_request(ac->module, mod_req);
3561 static const struct ldb_module_ops ldb_password_hash_module_ops = {
3562 .name = "password_hash",
3563 .add = password_hash_add,
3564 .modify = password_hash_modify
3567 int ldb_password_hash_module_init(const char *version)
3569 LDB_MODULE_CHECK_VERSION(version);
3570 return ldb_register_module(&ldb_password_hash_module_ops);