s4:password_hash: correctly update pwdLastSet on deleted objects.
[Samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blobd52ad2d7f556b088cbd2b40e7320d90af94a5e1f
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 ldb_message *update_msg;
94 struct dsdb_control_password_change_status *status;
95 struct dsdb_control_password_change *change;
97 bool pwd_reset;
98 bool change_status;
99 bool hash_values;
100 bool userPassword;
101 bool update_password;
102 bool update_lastset;
103 bool pwd_last_set_bypass;
104 bool pwd_last_set_default;
108 struct setup_password_fields_io {
109 struct ph_context *ac;
111 struct smb_krb5_context *smb_krb5_context;
113 /* infos about the user account */
114 struct {
115 uint32_t userAccountControl;
116 NTTIME pwdLastSet;
117 const char *sAMAccountName;
118 const char *user_principal_name;
119 bool is_computer;
120 uint32_t restrictions;
121 } u;
123 /* new credentials and old given credentials */
124 struct setup_password_fields_given {
125 const struct ldb_val *cleartext_utf8;
126 const struct ldb_val *cleartext_utf16;
127 struct samr_Password *nt_hash;
128 struct samr_Password *lm_hash;
129 } n, og;
131 /* old credentials */
132 struct {
133 struct samr_Password *nt_hash;
134 struct samr_Password *lm_hash;
135 uint32_t nt_history_len;
136 struct samr_Password *nt_history;
137 uint32_t lm_history_len;
138 struct samr_Password *lm_history;
139 const struct ldb_val *supplemental;
140 struct supplementalCredentialsBlob scb;
141 } o;
143 /* generated credentials */
144 struct {
145 struct samr_Password *nt_hash;
146 struct samr_Password *lm_hash;
147 uint32_t nt_history_len;
148 struct samr_Password *nt_history;
149 uint32_t lm_history_len;
150 struct samr_Password *lm_history;
151 const char *salt;
152 DATA_BLOB aes_256;
153 DATA_BLOB aes_128;
154 DATA_BLOB des_md5;
155 DATA_BLOB des_crc;
156 struct ldb_val supplemental;
157 NTTIME last_set;
158 } g;
161 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
162 const char *name,
163 enum ldb_request_type operation,
164 const struct ldb_val **new_val,
165 const struct ldb_val **old_val);
167 static int password_hash_bypass(struct ldb_module *module, struct ldb_request *request)
169 struct ldb_context *ldb = ldb_module_get_ctx(module);
170 const struct ldb_message *msg;
171 struct ldb_message_element *nte;
172 struct ldb_message_element *lme;
173 struct ldb_message_element *nthe;
174 struct ldb_message_element *lmhe;
175 struct ldb_message_element *sce;
177 switch (request->operation) {
178 case LDB_ADD:
179 msg = request->op.add.message;
180 break;
181 case LDB_MODIFY:
182 msg = request->op.mod.message;
183 break;
184 default:
185 return ldb_next_request(module, request);
188 /* nobody must touch password histories and 'supplementalCredentials' */
189 nte = dsdb_get_single_valued_attr(msg, "unicodePwd",
190 request->operation);
191 lme = dsdb_get_single_valued_attr(msg, "dBCSPwd",
192 request->operation);
193 nthe = dsdb_get_single_valued_attr(msg, "ntPwdHistory",
194 request->operation);
195 lmhe = dsdb_get_single_valued_attr(msg, "lmPwdHistory",
196 request->operation);
197 sce = dsdb_get_single_valued_attr(msg, "supplementalCredentials",
198 request->operation);
200 #define CHECK_HASH_ELEMENT(e, min, max) do {\
201 if (e && e->num_values) { \
202 unsigned int _count; \
203 if (e->num_values != 1) { \
204 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
205 "num_values != 1"); \
207 if ((e->values[0].length % 16) != 0) { \
208 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
209 "length % 16 != 0"); \
211 _count = e->values[0].length / 16; \
212 if (_count < min) { \
213 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
214 "count < min"); \
216 if (_count > max) { \
217 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
218 "count > max"); \
221 } while (0)
223 CHECK_HASH_ELEMENT(nte, 1, 1);
224 CHECK_HASH_ELEMENT(lme, 1, 1);
225 CHECK_HASH_ELEMENT(nthe, 1, INT32_MAX);
226 CHECK_HASH_ELEMENT(lmhe, 1, INT32_MAX);
228 if (sce && sce->num_values) {
229 enum ndr_err_code ndr_err;
230 struct supplementalCredentialsBlob *scb;
231 struct supplementalCredentialsPackage *scpp = NULL;
232 struct supplementalCredentialsPackage *scpk = NULL;
233 struct supplementalCredentialsPackage *scpkn = NULL;
234 struct supplementalCredentialsPackage *scpct = NULL;
235 DATA_BLOB scpbp = data_blob_null;
236 DATA_BLOB scpbk = data_blob_null;
237 DATA_BLOB scpbkn = data_blob_null;
238 DATA_BLOB scpbct = data_blob_null;
239 DATA_BLOB blob;
240 uint32_t i;
242 if (sce->num_values != 1) {
243 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
244 "num_values != 1");
247 scb = talloc_zero(request, struct supplementalCredentialsBlob);
248 if (!scb) {
249 return ldb_module_oom(module);
252 ndr_err = ndr_pull_struct_blob_all(&sce->values[0], scb, scb,
253 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
255 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
256 "ndr_pull_struct_blob_all");
259 if (scb->sub.num_packages < 2) {
260 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
261 "num_packages < 2");
264 for (i=0; i < scb->sub.num_packages; i++) {
265 DATA_BLOB subblob;
267 subblob = strhex_to_data_blob(scb, scb->sub.packages[i].data);
268 if (subblob.data == NULL) {
269 return ldb_module_oom(module);
272 if (strcmp(scb->sub.packages[i].name, "Packages") == 0) {
273 if (scpp) {
274 return ldb_error(ldb,
275 LDB_ERR_CONSTRAINT_VIOLATION,
276 "Packages twice");
278 scpp = &scb->sub.packages[i];
279 scpbp = subblob;
280 continue;
282 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos") == 0) {
283 if (scpk) {
284 return ldb_error(ldb,
285 LDB_ERR_CONSTRAINT_VIOLATION,
286 "Primary:Kerberos twice");
288 scpk = &scb->sub.packages[i];
289 scpbk = subblob;
290 continue;
292 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos-Newer-Keys") == 0) {
293 if (scpkn) {
294 return ldb_error(ldb,
295 LDB_ERR_CONSTRAINT_VIOLATION,
296 "Primary:Kerberos-Newer-Keys twice");
298 scpkn = &scb->sub.packages[i];
299 scpbkn = subblob;
300 continue;
302 if (strcmp(scb->sub.packages[i].name, "Primary:CLEARTEXT") == 0) {
303 if (scpct) {
304 return ldb_error(ldb,
305 LDB_ERR_CONSTRAINT_VIOLATION,
306 "Primary:CLEARTEXT twice");
308 scpct = &scb->sub.packages[i];
309 scpbct = subblob;
310 continue;
313 data_blob_free(&subblob);
316 if (scpp == NULL) {
317 return ldb_error(ldb,
318 LDB_ERR_CONSTRAINT_VIOLATION,
319 "Primary:Packages missing");
322 if (scpk == NULL) {
324 * If Primary:Kerberos is missing w2k8r2 reboots
325 * when a password is changed.
327 return ldb_error(ldb,
328 LDB_ERR_CONSTRAINT_VIOLATION,
329 "Primary:Kerberos missing");
332 if (scpp) {
333 struct package_PackagesBlob *p;
334 uint32_t n;
336 p = talloc_zero(scb, struct package_PackagesBlob);
337 if (p == NULL) {
338 return ldb_module_oom(module);
341 ndr_err = ndr_pull_struct_blob(&scpbp, p, p,
342 (ndr_pull_flags_fn_t)ndr_pull_package_PackagesBlob);
343 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
344 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
345 "ndr_pull_struct_blob Packages");
348 if (p->names == NULL) {
349 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
350 "Packages names == NULL");
353 for (n = 0; p->names[n]; n++) {
354 /* noop */
357 if (scb->sub.num_packages != (n + 1)) {
358 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
359 "Packages num_packages != num_names + 1");
362 talloc_free(p);
365 if (scpk) {
366 struct package_PrimaryKerberosBlob *k;
368 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
369 if (k == NULL) {
370 return ldb_module_oom(module);
373 ndr_err = ndr_pull_struct_blob(&scpbk, k, k,
374 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
375 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
376 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
377 "ndr_pull_struct_blob PrimaryKerberos");
380 if (k->version != 3) {
381 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
382 "PrimaryKerberos version != 3");
385 if (k->ctr.ctr3.salt.string == NULL) {
386 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
387 "PrimaryKerberos salt == NULL");
390 if (strlen(k->ctr.ctr3.salt.string) == 0) {
391 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
392 "PrimaryKerberos strlen(salt) == 0");
395 if (k->ctr.ctr3.num_keys != 2) {
396 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
397 "PrimaryKerberos num_keys != 2");
400 if (k->ctr.ctr3.num_old_keys > k->ctr.ctr3.num_keys) {
401 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
402 "PrimaryKerberos num_old_keys > num_keys");
405 if (k->ctr.ctr3.keys[0].keytype != ENCTYPE_DES_CBC_MD5) {
406 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
407 "PrimaryKerberos key[0] != DES_CBC_MD5");
409 if (k->ctr.ctr3.keys[1].keytype != ENCTYPE_DES_CBC_CRC) {
410 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
411 "PrimaryKerberos key[1] != DES_CBC_CRC");
414 if (k->ctr.ctr3.keys[0].value_len != 8) {
415 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
416 "PrimaryKerberos key[0] value_len != 8");
418 if (k->ctr.ctr3.keys[1].value_len != 8) {
419 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
420 "PrimaryKerberos key[1] value_len != 8");
423 for (i = 0; i < k->ctr.ctr3.num_old_keys; i++) {
424 if (k->ctr.ctr3.old_keys[i].keytype ==
425 k->ctr.ctr3.keys[i].keytype &&
426 k->ctr.ctr3.old_keys[i].value_len ==
427 k->ctr.ctr3.keys[i].value_len) {
428 continue;
431 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
432 "PrimaryKerberos old_keys type/value_len doesn't match");
435 talloc_free(k);
438 if (scpkn) {
439 struct package_PrimaryKerberosBlob *k;
441 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
442 if (k == NULL) {
443 return ldb_module_oom(module);
446 ndr_err = ndr_pull_struct_blob(&scpbkn, k, k,
447 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
448 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
449 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
450 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
453 if (k->version != 4) {
454 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
455 "KerberosNerverKeys version != 4");
458 if (k->ctr.ctr4.salt.string == NULL) {
459 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
460 "KerberosNewerKeys salt == NULL");
463 if (strlen(k->ctr.ctr4.salt.string) == 0) {
464 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
465 "KerberosNewerKeys strlen(salt) == 0");
468 if (k->ctr.ctr4.num_keys != 4) {
469 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
470 "KerberosNewerKeys num_keys != 2");
473 if (k->ctr.ctr4.num_old_keys > k->ctr.ctr4.num_keys) {
474 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
475 "KerberosNewerKeys num_old_keys > num_keys");
478 if (k->ctr.ctr4.num_older_keys > k->ctr.ctr4.num_old_keys) {
479 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
480 "KerberosNewerKeys num_older_keys > num_old_keys");
483 if (k->ctr.ctr4.keys[0].keytype != ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
484 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
485 "KerberosNewerKeys key[0] != AES256");
487 if (k->ctr.ctr4.keys[1].keytype != ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
488 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
489 "KerberosNewerKeys key[1] != AES128");
491 if (k->ctr.ctr4.keys[2].keytype != ENCTYPE_DES_CBC_MD5) {
492 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
493 "KerberosNewerKeys key[2] != DES_CBC_MD5");
495 if (k->ctr.ctr4.keys[3].keytype != ENCTYPE_DES_CBC_CRC) {
496 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
497 "KerberosNewerKeys key[3] != DES_CBC_CRC");
500 if (k->ctr.ctr4.keys[0].value_len != 32) {
501 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
502 "KerberosNewerKeys key[0] value_len != 32");
504 if (k->ctr.ctr4.keys[1].value_len != 16) {
505 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
506 "KerberosNewerKeys key[1] value_len != 16");
508 if (k->ctr.ctr4.keys[2].value_len != 8) {
509 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
510 "KerberosNewerKeys key[2] value_len != 8");
512 if (k->ctr.ctr4.keys[3].value_len != 8) {
513 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
514 "KerberosNewerKeys key[3] value_len != 8");
518 * TODO:
519 * Maybe we can check old and older keys here.
520 * But we need to do some tests, if the old keys
521 * can be taken from the PrimaryKerberos blob
522 * (with only des keys), when the domain was upgraded
523 * from w2k3 to w2k8.
526 talloc_free(k);
529 if (scpct) {
530 struct package_PrimaryCLEARTEXTBlob *ct;
532 ct = talloc_zero(scb, struct package_PrimaryCLEARTEXTBlob);
533 if (ct == NULL) {
534 return ldb_module_oom(module);
537 ndr_err = ndr_pull_struct_blob(&scpbct, ct, ct,
538 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryCLEARTEXTBlob);
539 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
540 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
541 "ndr_pull_struct_blob PrimaryCLEARTEXT");
544 if ((ct->cleartext.length % 2) != 0) {
545 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
546 "PrimaryCLEARTEXT length % 2 != 0");
549 talloc_free(ct);
552 ndr_err = ndr_push_struct_blob(&blob, scb, scb,
553 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
554 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
555 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
556 "ndr_pull_struct_blob_all");
559 if (sce->values[0].length != blob.length) {
560 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
561 "supplementalCredentialsBlob length differ");
564 if (memcmp(sce->values[0].data, blob.data, blob.length) != 0) {
565 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
566 "supplementalCredentialsBlob memcmp differ");
569 talloc_free(scb);
572 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_bypass - validated\n");
573 return ldb_next_request(module, request);
576 /* Get the NT hash, and fill it in as an entry in the password history,
577 and specify it into io->g.nt_hash */
579 static int setup_nt_fields(struct setup_password_fields_io *io)
581 struct ldb_context *ldb;
582 uint32_t i;
584 io->g.nt_hash = io->n.nt_hash;
585 ldb = ldb_module_get_ctx(io->ac->module);
587 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
588 return LDB_SUCCESS;
591 /* We might not have an old NT password */
592 io->g.nt_history = talloc_array(io->ac,
593 struct samr_Password,
594 io->ac->status->domain_data.pwdHistoryLength);
595 if (!io->g.nt_history) {
596 return ldb_oom(ldb);
599 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
600 io->o.nt_history_len); i++) {
601 io->g.nt_history[i+1] = io->o.nt_history[i];
603 io->g.nt_history_len = i + 1;
605 if (io->g.nt_hash) {
606 io->g.nt_history[0] = *io->g.nt_hash;
607 } else {
609 * TODO: is this correct?
610 * the simular behavior is correct for the lm history case
612 E_md4hash("", io->g.nt_history[0].hash);
615 return LDB_SUCCESS;
618 /* Get the LANMAN hash, and fill it in as an entry in the password history,
619 and specify it into io->g.lm_hash */
621 static int setup_lm_fields(struct setup_password_fields_io *io)
623 struct ldb_context *ldb;
624 uint32_t i;
626 io->g.lm_hash = io->n.lm_hash;
627 ldb = ldb_module_get_ctx(io->ac->module);
629 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
630 return LDB_SUCCESS;
633 /* We might not have an old LM password */
634 io->g.lm_history = talloc_array(io->ac,
635 struct samr_Password,
636 io->ac->status->domain_data.pwdHistoryLength);
637 if (!io->g.lm_history) {
638 return ldb_oom(ldb);
641 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
642 io->o.lm_history_len); i++) {
643 io->g.lm_history[i+1] = io->o.lm_history[i];
645 io->g.lm_history_len = i + 1;
647 if (io->g.lm_hash) {
648 io->g.lm_history[0] = *io->g.lm_hash;
649 } else {
650 E_deshash("", io->g.lm_history[0].hash);
653 return LDB_SUCCESS;
656 static int setup_kerberos_keys(struct setup_password_fields_io *io)
658 struct ldb_context *ldb;
659 krb5_error_code krb5_ret;
660 krb5_principal salt_principal;
661 krb5_data salt;
662 krb5_keyblock key;
663 krb5_data cleartext_data;
665 ldb = ldb_module_get_ctx(io->ac->module);
666 cleartext_data.data = (char *)io->n.cleartext_utf8->data;
667 cleartext_data.length = io->n.cleartext_utf8->length;
669 /* Many, many thanks to lukeh@padl.com for this
670 * algorithm, described in his Nov 10 2004 mail to
671 * samba-technical@lists.samba.org */
674 * Determine a salting principal
676 if (io->u.is_computer) {
677 char *name;
678 char *saltbody;
680 name = strlower_talloc(io->ac, io->u.sAMAccountName);
681 if (!name) {
682 return ldb_oom(ldb);
685 if (name[strlen(name)-1] == '$') {
686 name[strlen(name)-1] = '\0';
689 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
690 io->ac->status->domain_data.dns_domain);
691 if (!saltbody) {
692 return ldb_oom(ldb);
695 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
696 &salt_principal,
697 io->ac->status->domain_data.realm,
698 "host", saltbody, NULL);
699 } else if (io->u.user_principal_name) {
700 char *user_principal_name;
701 char *p;
703 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
704 if (!user_principal_name) {
705 return ldb_oom(ldb);
708 p = strchr(user_principal_name, '@');
709 if (p) {
710 p[0] = '\0';
713 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
714 &salt_principal,
715 io->ac->status->domain_data.realm,
716 user_principal_name, NULL);
717 } else {
718 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
719 &salt_principal,
720 io->ac->status->domain_data.realm,
721 io->u.sAMAccountName, NULL);
723 if (krb5_ret) {
724 ldb_asprintf_errstring(ldb,
725 "setup_kerberos_keys: "
726 "generation of a salting principal failed: %s",
727 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
728 krb5_ret, io->ac));
729 return LDB_ERR_OPERATIONS_ERROR;
733 * create salt from salt_principal
735 krb5_ret = smb_krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
736 salt_principal, &salt);
737 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
738 if (krb5_ret) {
739 ldb_asprintf_errstring(ldb,
740 "setup_kerberos_keys: "
741 "generation of krb5_salt failed: %s",
742 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
743 krb5_ret, io->ac));
744 return LDB_ERR_OPERATIONS_ERROR;
746 /* create a talloc copy */
747 io->g.salt = talloc_strndup(io->ac,
748 (char *)salt.data,
749 salt.length);
750 kerberos_free_data_contents(io->smb_krb5_context->krb5_context, &salt);
751 if (!io->g.salt) {
752 return ldb_oom(ldb);
754 /* now use the talloced copy of the salt */
755 salt.data = discard_const(io->g.salt);
756 salt.length = strlen(io->g.salt);
759 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
760 * the salt and the cleartext password
762 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
763 NULL,
764 &salt,
765 &cleartext_data,
766 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
767 &key);
768 if (krb5_ret) {
769 ldb_asprintf_errstring(ldb,
770 "setup_kerberos_keys: "
771 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
772 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
773 krb5_ret, io->ac));
774 return LDB_ERR_OPERATIONS_ERROR;
776 io->g.aes_256 = data_blob_talloc(io->ac,
777 KRB5_KEY_DATA(&key),
778 KRB5_KEY_LENGTH(&key));
779 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
780 if (!io->g.aes_256.data) {
781 return ldb_oom(ldb);
785 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
786 * the salt and the cleartext password
788 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
789 NULL,
790 &salt,
791 &cleartext_data,
792 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
793 &key);
794 if (krb5_ret) {
795 ldb_asprintf_errstring(ldb,
796 "setup_kerberos_keys: "
797 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
798 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
799 krb5_ret, io->ac));
800 return LDB_ERR_OPERATIONS_ERROR;
802 io->g.aes_128 = data_blob_talloc(io->ac,
803 KRB5_KEY_DATA(&key),
804 KRB5_KEY_LENGTH(&key));
805 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
806 if (!io->g.aes_128.data) {
807 return ldb_oom(ldb);
811 * create ENCTYPE_DES_CBC_MD5 key out of
812 * the salt and the cleartext password
814 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
815 NULL,
816 &salt,
817 &cleartext_data,
818 ENCTYPE_DES_CBC_MD5,
819 &key);
820 if (krb5_ret) {
821 ldb_asprintf_errstring(ldb,
822 "setup_kerberos_keys: "
823 "generation of a des-cbc-md5 key failed: %s",
824 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
825 krb5_ret, io->ac));
826 return LDB_ERR_OPERATIONS_ERROR;
828 io->g.des_md5 = data_blob_talloc(io->ac,
829 KRB5_KEY_DATA(&key),
830 KRB5_KEY_LENGTH(&key));
831 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
832 if (!io->g.des_md5.data) {
833 return ldb_oom(ldb);
837 * create ENCTYPE_DES_CBC_CRC key out of
838 * the salt and the cleartext password
840 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
841 NULL,
842 &salt,
843 &cleartext_data,
844 ENCTYPE_DES_CBC_CRC,
845 &key);
846 if (krb5_ret) {
847 ldb_asprintf_errstring(ldb,
848 "setup_kerberos_keys: "
849 "generation of a des-cbc-crc key failed: %s",
850 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
851 krb5_ret, io->ac));
852 return LDB_ERR_OPERATIONS_ERROR;
854 io->g.des_crc = data_blob_talloc(io->ac,
855 KRB5_KEY_DATA(&key),
856 KRB5_KEY_LENGTH(&key));
857 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
858 if (!io->g.des_crc.data) {
859 return ldb_oom(ldb);
862 return LDB_SUCCESS;
865 static int setup_primary_kerberos(struct setup_password_fields_io *io,
866 const struct supplementalCredentialsBlob *old_scb,
867 struct package_PrimaryKerberosBlob *pkb)
869 struct ldb_context *ldb;
870 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
871 struct supplementalCredentialsPackage *old_scp = NULL;
872 struct package_PrimaryKerberosBlob _old_pkb;
873 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
874 uint32_t i;
875 enum ndr_err_code ndr_err;
877 ldb = ldb_module_get_ctx(io->ac->module);
880 * prepare generation of keys
882 * ENCTYPE_DES_CBC_MD5
883 * ENCTYPE_DES_CBC_CRC
885 pkb->version = 3;
886 pkb3->salt.string = io->g.salt;
887 pkb3->num_keys = 2;
888 pkb3->keys = talloc_array(io->ac,
889 struct package_PrimaryKerberosKey3,
890 pkb3->num_keys);
891 if (!pkb3->keys) {
892 return ldb_oom(ldb);
895 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
896 pkb3->keys[0].value = &io->g.des_md5;
897 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
898 pkb3->keys[1].value = &io->g.des_crc;
900 /* initialize the old keys to zero */
901 pkb3->num_old_keys = 0;
902 pkb3->old_keys = NULL;
904 /* if there're no old keys, then we're done */
905 if (!old_scb) {
906 return LDB_SUCCESS;
909 for (i=0; i < old_scb->sub.num_packages; i++) {
910 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
911 continue;
914 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
915 continue;
918 old_scp = &old_scb->sub.packages[i];
919 break;
921 /* Primary:Kerberos element of supplementalCredentials */
922 if (old_scp) {
923 DATA_BLOB blob;
925 blob = strhex_to_data_blob(io->ac, old_scp->data);
926 if (!blob.data) {
927 return ldb_oom(ldb);
930 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
931 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
932 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
933 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
934 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
935 ldb_asprintf_errstring(ldb,
936 "setup_primary_kerberos: "
937 "failed to pull old package_PrimaryKerberosBlob: %s",
938 nt_errstr(status));
939 return LDB_ERR_OPERATIONS_ERROR;
942 if (_old_pkb.version != 3) {
943 ldb_asprintf_errstring(ldb,
944 "setup_primary_kerberos: "
945 "package_PrimaryKerberosBlob version[%u] expected[3]",
946 _old_pkb.version);
947 return LDB_ERR_OPERATIONS_ERROR;
950 old_pkb3 = &_old_pkb.ctr.ctr3;
953 /* if we didn't found the old keys we're done */
954 if (!old_pkb3) {
955 return LDB_SUCCESS;
958 /* fill in the old keys */
959 pkb3->num_old_keys = old_pkb3->num_keys;
960 pkb3->old_keys = old_pkb3->keys;
962 return LDB_SUCCESS;
965 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
966 const struct supplementalCredentialsBlob *old_scb,
967 struct package_PrimaryKerberosBlob *pkb)
969 struct ldb_context *ldb;
970 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
971 struct supplementalCredentialsPackage *old_scp = NULL;
972 struct package_PrimaryKerberosBlob _old_pkb;
973 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
974 uint32_t i;
975 enum ndr_err_code ndr_err;
977 ldb = ldb_module_get_ctx(io->ac->module);
980 * prepare generation of keys
982 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
983 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
984 * ENCTYPE_DES_CBC_MD5
985 * ENCTYPE_DES_CBC_CRC
987 pkb->version = 4;
988 pkb4->salt.string = io->g.salt;
989 pkb4->default_iteration_count = 4096;
990 pkb4->num_keys = 4;
992 pkb4->keys = talloc_array(io->ac,
993 struct package_PrimaryKerberosKey4,
994 pkb4->num_keys);
995 if (!pkb4->keys) {
996 return ldb_oom(ldb);
999 pkb4->keys[0].iteration_count = 4096;
1000 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
1001 pkb4->keys[0].value = &io->g.aes_256;
1002 pkb4->keys[1].iteration_count = 4096;
1003 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
1004 pkb4->keys[1].value = &io->g.aes_128;
1005 pkb4->keys[2].iteration_count = 4096;
1006 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
1007 pkb4->keys[2].value = &io->g.des_md5;
1008 pkb4->keys[3].iteration_count = 4096;
1009 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
1010 pkb4->keys[3].value = &io->g.des_crc;
1012 /* initialize the old keys to zero */
1013 pkb4->num_old_keys = 0;
1014 pkb4->old_keys = NULL;
1015 pkb4->num_older_keys = 0;
1016 pkb4->older_keys = NULL;
1018 /* if there're no old keys, then we're done */
1019 if (!old_scb) {
1020 return LDB_SUCCESS;
1023 for (i=0; i < old_scb->sub.num_packages; i++) {
1024 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
1025 continue;
1028 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
1029 continue;
1032 old_scp = &old_scb->sub.packages[i];
1033 break;
1035 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1036 if (old_scp) {
1037 DATA_BLOB blob;
1039 blob = strhex_to_data_blob(io->ac, old_scp->data);
1040 if (!blob.data) {
1041 return ldb_oom(ldb);
1044 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1045 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
1046 &_old_pkb,
1047 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
1048 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1049 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1050 ldb_asprintf_errstring(ldb,
1051 "setup_primary_kerberos_newer: "
1052 "failed to pull old package_PrimaryKerberosBlob: %s",
1053 nt_errstr(status));
1054 return LDB_ERR_OPERATIONS_ERROR;
1057 if (_old_pkb.version != 4) {
1058 ldb_asprintf_errstring(ldb,
1059 "setup_primary_kerberos_newer: "
1060 "package_PrimaryKerberosBlob version[%u] expected[4]",
1061 _old_pkb.version);
1062 return LDB_ERR_OPERATIONS_ERROR;
1065 old_pkb4 = &_old_pkb.ctr.ctr4;
1068 /* if we didn't found the old keys we're done */
1069 if (!old_pkb4) {
1070 return LDB_SUCCESS;
1073 /* fill in the old keys */
1074 pkb4->num_old_keys = old_pkb4->num_keys;
1075 pkb4->old_keys = old_pkb4->keys;
1076 pkb4->num_older_keys = old_pkb4->num_old_keys;
1077 pkb4->older_keys = old_pkb4->old_keys;
1079 return LDB_SUCCESS;
1082 static int setup_primary_wdigest(struct setup_password_fields_io *io,
1083 const struct supplementalCredentialsBlob *old_scb,
1084 struct package_PrimaryWDigestBlob *pdb)
1086 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1087 DATA_BLOB sAMAccountName;
1088 DATA_BLOB sAMAccountName_l;
1089 DATA_BLOB sAMAccountName_u;
1090 const char *user_principal_name = io->u.user_principal_name;
1091 DATA_BLOB userPrincipalName;
1092 DATA_BLOB userPrincipalName_l;
1093 DATA_BLOB userPrincipalName_u;
1094 DATA_BLOB netbios_domain;
1095 DATA_BLOB netbios_domain_l;
1096 DATA_BLOB netbios_domain_u;
1097 DATA_BLOB dns_domain;
1098 DATA_BLOB dns_domain_l;
1099 DATA_BLOB dns_domain_u;
1100 DATA_BLOB digest;
1101 DATA_BLOB delim;
1102 DATA_BLOB backslash;
1103 uint8_t i;
1104 struct {
1105 DATA_BLOB *user;
1106 DATA_BLOB *realm;
1107 DATA_BLOB *nt4dom;
1108 } wdigest[] = {
1110 * See
1111 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1112 * for what precalculated hashes are supposed to be stored...
1114 * I can't reproduce all values which should contain "Digest" as realm,
1115 * am I doing something wrong or is w2k3 just broken...?
1117 * W2K3 fills in following for a user:
1119 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1120 * sAMAccountName: NewUser2Sam
1121 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1123 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1124 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1125 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1126 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1127 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1128 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1129 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1130 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1131 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1132 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1133 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1134 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1135 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1136 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1137 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1138 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1139 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1140 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1141 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1142 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1143 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1144 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1145 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1146 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1147 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1148 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1149 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1150 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1151 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1153 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1154 * sAMAccountName: NewUser2Sam
1156 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1157 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1158 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1159 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1160 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1161 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1162 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1163 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1164 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1165 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1166 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1167 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1168 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1169 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1170 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1171 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1172 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1173 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1174 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1175 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1176 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1177 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1178 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1179 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1180 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1181 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1182 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1183 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1184 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1188 * sAMAccountName, netbios_domain
1191 .user = &sAMAccountName,
1192 .realm = &netbios_domain,
1195 .user = &sAMAccountName_l,
1196 .realm = &netbios_domain_l,
1199 .user = &sAMAccountName_u,
1200 .realm = &netbios_domain_u,
1203 .user = &sAMAccountName,
1204 .realm = &netbios_domain_u,
1207 .user = &sAMAccountName,
1208 .realm = &netbios_domain_l,
1211 .user = &sAMAccountName_u,
1212 .realm = &netbios_domain_l,
1215 .user = &sAMAccountName_l,
1216 .realm = &netbios_domain_u,
1219 * sAMAccountName, dns_domain
1222 .user = &sAMAccountName,
1223 .realm = &dns_domain,
1226 .user = &sAMAccountName_l,
1227 .realm = &dns_domain_l,
1230 .user = &sAMAccountName_u,
1231 .realm = &dns_domain_u,
1234 .user = &sAMAccountName,
1235 .realm = &dns_domain_u,
1238 .user = &sAMAccountName,
1239 .realm = &dns_domain_l,
1242 .user = &sAMAccountName_u,
1243 .realm = &dns_domain_l,
1246 .user = &sAMAccountName_l,
1247 .realm = &dns_domain_u,
1250 * userPrincipalName, no realm
1253 .user = &userPrincipalName,
1257 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1258 * the fallback to the sAMAccountName based userPrincipalName is correct
1260 .user = &userPrincipalName_l,
1263 .user = &userPrincipalName_u,
1266 * nt4dom\sAMAccountName, no realm
1269 .user = &sAMAccountName,
1270 .nt4dom = &netbios_domain
1273 .user = &sAMAccountName_l,
1274 .nt4dom = &netbios_domain_l
1277 .user = &sAMAccountName_u,
1278 .nt4dom = &netbios_domain_u
1282 * the following ones are guessed depending on the technet2 article
1283 * but not reproducable on a w2k3 server
1285 /* sAMAccountName with "Digest" realm */
1287 .user = &sAMAccountName,
1288 .realm = &digest
1291 .user = &sAMAccountName_l,
1292 .realm = &digest
1295 .user = &sAMAccountName_u,
1296 .realm = &digest
1298 /* userPrincipalName with "Digest" realm */
1300 .user = &userPrincipalName,
1301 .realm = &digest
1304 .user = &userPrincipalName_l,
1305 .realm = &digest
1308 .user = &userPrincipalName_u,
1309 .realm = &digest
1311 /* nt4dom\\sAMAccountName with "Digest" realm */
1313 .user = &sAMAccountName,
1314 .nt4dom = &netbios_domain,
1315 .realm = &digest
1318 .user = &sAMAccountName_l,
1319 .nt4dom = &netbios_domain_l,
1320 .realm = &digest
1323 .user = &sAMAccountName_u,
1324 .nt4dom = &netbios_domain_u,
1325 .realm = &digest
1329 /* prepare DATA_BLOB's used in the combinations array */
1330 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
1331 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
1332 if (!sAMAccountName_l.data) {
1333 return ldb_oom(ldb);
1335 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
1336 if (!sAMAccountName_u.data) {
1337 return ldb_oom(ldb);
1340 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1341 if (!user_principal_name) {
1342 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
1343 io->u.sAMAccountName,
1344 io->ac->status->domain_data.dns_domain);
1345 if (!user_principal_name) {
1346 return ldb_oom(ldb);
1349 userPrincipalName = data_blob_string_const(user_principal_name);
1350 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
1351 if (!userPrincipalName_l.data) {
1352 return ldb_oom(ldb);
1354 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
1355 if (!userPrincipalName_u.data) {
1356 return ldb_oom(ldb);
1359 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
1360 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
1361 io->ac->status->domain_data.netbios_domain));
1362 if (!netbios_domain_l.data) {
1363 return ldb_oom(ldb);
1365 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
1366 io->ac->status->domain_data.netbios_domain));
1367 if (!netbios_domain_u.data) {
1368 return ldb_oom(ldb);
1371 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1372 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1373 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
1375 digest = data_blob_string_const("Digest");
1377 delim = data_blob_string_const(":");
1378 backslash = data_blob_string_const("\\");
1380 pdb->num_hashes = ARRAY_SIZE(wdigest);
1381 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
1382 pdb->num_hashes);
1383 if (!pdb->hashes) {
1384 return ldb_oom(ldb);
1387 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
1388 MD5_CTX md5;
1389 MD5Init(&md5);
1390 if (wdigest[i].nt4dom) {
1391 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
1392 MD5Update(&md5, backslash.data, backslash.length);
1394 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
1395 MD5Update(&md5, delim.data, delim.length);
1396 if (wdigest[i].realm) {
1397 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1399 MD5Update(&md5, delim.data, delim.length);
1400 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1401 MD5Final(pdb->hashes[i].hash, &md5);
1404 return LDB_SUCCESS;
1407 static int setup_supplemental_field(struct setup_password_fields_io *io)
1409 struct ldb_context *ldb;
1410 struct supplementalCredentialsBlob scb;
1411 struct supplementalCredentialsBlob *old_scb = NULL;
1412 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1413 uint32_t num_names = 0;
1414 const char *names[1+4];
1415 uint32_t num_packages = 0;
1416 struct supplementalCredentialsPackage packages[1+4];
1417 /* Packages */
1418 struct supplementalCredentialsPackage *pp = NULL;
1419 struct package_PackagesBlob pb;
1420 DATA_BLOB pb_blob;
1421 char *pb_hexstr;
1422 /* Primary:Kerberos-Newer-Keys */
1423 const char **nkn = NULL;
1424 struct supplementalCredentialsPackage *pkn = NULL;
1425 struct package_PrimaryKerberosBlob pknb;
1426 DATA_BLOB pknb_blob;
1427 char *pknb_hexstr;
1428 /* Primary:Kerberos */
1429 const char **nk = NULL;
1430 struct supplementalCredentialsPackage *pk = NULL;
1431 struct package_PrimaryKerberosBlob pkb;
1432 DATA_BLOB pkb_blob;
1433 char *pkb_hexstr;
1434 /* Primary:WDigest */
1435 const char **nd = NULL;
1436 struct supplementalCredentialsPackage *pd = NULL;
1437 struct package_PrimaryWDigestBlob pdb;
1438 DATA_BLOB pdb_blob;
1439 char *pdb_hexstr;
1440 /* Primary:CLEARTEXT */
1441 const char **nc = NULL;
1442 struct supplementalCredentialsPackage *pc = NULL;
1443 struct package_PrimaryCLEARTEXTBlob pcb;
1444 DATA_BLOB pcb_blob;
1445 char *pcb_hexstr;
1446 int ret;
1447 enum ndr_err_code ndr_err;
1448 uint8_t zero16[16];
1449 bool do_newer_keys = false;
1450 bool do_cleartext = false;
1452 ZERO_STRUCT(zero16);
1453 ZERO_STRUCT(names);
1455 ldb = ldb_module_get_ctx(io->ac->module);
1457 if (!io->n.cleartext_utf8) {
1459 * when we don't have a cleartext password
1460 * we can't setup a supplementalCredential value
1462 return LDB_SUCCESS;
1465 /* if there's an old supplementaCredentials blob then use it */
1466 if (io->o.supplemental) {
1467 if (io->o.scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1468 old_scb = &io->o.scb;
1469 } else {
1470 ldb_debug(ldb, LDB_DEBUG_ERROR,
1471 "setup_supplemental_field: "
1472 "supplementalCredentialsBlob "
1473 "signature[0x%04X] expected[0x%04X]",
1474 io->o.scb.sub.signature,
1475 SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1478 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1479 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1481 if (io->ac->status->domain_data.store_cleartext &&
1482 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1483 do_cleartext = true;
1487 * The ordering is this
1489 * Primary:Kerberos-Newer-Keys (optional)
1490 * Primary:Kerberos
1491 * Primary:WDigest
1492 * Primary:CLEARTEXT (optional)
1494 * And the 'Packages' package is insert before the last
1495 * other package.
1497 if (do_newer_keys) {
1498 /* Primary:Kerberos-Newer-Keys */
1499 nkn = &names[num_names++];
1500 pkn = &packages[num_packages++];
1503 /* Primary:Kerberos */
1504 nk = &names[num_names++];
1505 pk = &packages[num_packages++];
1507 if (!do_cleartext) {
1508 /* Packages */
1509 pp = &packages[num_packages++];
1512 /* Primary:WDigest */
1513 nd = &names[num_names++];
1514 pd = &packages[num_packages++];
1516 if (do_cleartext) {
1517 /* Packages */
1518 pp = &packages[num_packages++];
1520 /* Primary:CLEARTEXT */
1521 nc = &names[num_names++];
1522 pc = &packages[num_packages++];
1525 if (pkn) {
1527 * setup 'Primary:Kerberos-Newer-Keys' element
1529 *nkn = "Kerberos-Newer-Keys";
1531 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1532 if (ret != LDB_SUCCESS) {
1533 return ret;
1536 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1537 &pknb,
1538 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1539 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1540 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1541 ldb_asprintf_errstring(ldb,
1542 "setup_supplemental_field: "
1543 "failed to push package_PrimaryKerberosNeverBlob: %s",
1544 nt_errstr(status));
1545 return LDB_ERR_OPERATIONS_ERROR;
1547 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1548 if (!pknb_hexstr) {
1549 return ldb_oom(ldb);
1551 pkn->name = "Primary:Kerberos-Newer-Keys";
1552 pkn->reserved = 1;
1553 pkn->data = pknb_hexstr;
1557 * setup 'Primary:Kerberos' element
1559 *nk = "Kerberos";
1561 ret = setup_primary_kerberos(io, old_scb, &pkb);
1562 if (ret != LDB_SUCCESS) {
1563 return ret;
1566 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1567 &pkb,
1568 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1569 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1570 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1571 ldb_asprintf_errstring(ldb,
1572 "setup_supplemental_field: "
1573 "failed to push package_PrimaryKerberosBlob: %s",
1574 nt_errstr(status));
1575 return LDB_ERR_OPERATIONS_ERROR;
1577 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1578 if (!pkb_hexstr) {
1579 return ldb_oom(ldb);
1581 pk->name = "Primary:Kerberos";
1582 pk->reserved = 1;
1583 pk->data = pkb_hexstr;
1586 * setup 'Primary:WDigest' element
1588 *nd = "WDigest";
1590 ret = setup_primary_wdigest(io, old_scb, &pdb);
1591 if (ret != LDB_SUCCESS) {
1592 return ret;
1595 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1596 &pdb,
1597 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1598 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1599 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1600 ldb_asprintf_errstring(ldb,
1601 "setup_supplemental_field: "
1602 "failed to push package_PrimaryWDigestBlob: %s",
1603 nt_errstr(status));
1604 return LDB_ERR_OPERATIONS_ERROR;
1606 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1607 if (!pdb_hexstr) {
1608 return ldb_oom(ldb);
1610 pd->name = "Primary:WDigest";
1611 pd->reserved = 1;
1612 pd->data = pdb_hexstr;
1615 * setup 'Primary:CLEARTEXT' element
1617 if (pc) {
1618 *nc = "CLEARTEXT";
1620 pcb.cleartext = *io->n.cleartext_utf16;
1622 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1623 &pcb,
1624 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1625 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1626 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1627 ldb_asprintf_errstring(ldb,
1628 "setup_supplemental_field: "
1629 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1630 nt_errstr(status));
1631 return LDB_ERR_OPERATIONS_ERROR;
1633 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1634 if (!pcb_hexstr) {
1635 return ldb_oom(ldb);
1637 pc->name = "Primary:CLEARTEXT";
1638 pc->reserved = 1;
1639 pc->data = pcb_hexstr;
1643 * setup 'Packages' element
1645 pb.names = names;
1646 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1647 &pb,
1648 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1649 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1650 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1651 ldb_asprintf_errstring(ldb,
1652 "setup_supplemental_field: "
1653 "failed to push package_PackagesBlob: %s",
1654 nt_errstr(status));
1655 return LDB_ERR_OPERATIONS_ERROR;
1657 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1658 if (!pb_hexstr) {
1659 return ldb_oom(ldb);
1661 pp->name = "Packages";
1662 pp->reserved = 2;
1663 pp->data = pb_hexstr;
1666 * setup 'supplementalCredentials' value
1668 ZERO_STRUCT(scb);
1669 scb.sub.num_packages = num_packages;
1670 scb.sub.packages = packages;
1672 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1673 &scb,
1674 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1675 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1676 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1677 ldb_asprintf_errstring(ldb,
1678 "setup_supplemental_field: "
1679 "failed to push supplementalCredentialsBlob: %s",
1680 nt_errstr(status));
1681 return LDB_ERR_OPERATIONS_ERROR;
1684 return LDB_SUCCESS;
1687 static int setup_last_set_field(struct setup_password_fields_io *io)
1689 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1690 const struct ldb_message *msg = NULL;
1691 struct timeval tv = { .tv_sec = 0 };
1692 const struct ldb_val *old_val = NULL;
1693 const struct ldb_val *new_val = NULL;
1694 int ret;
1696 switch (io->ac->req->operation) {
1697 case LDB_ADD:
1698 msg = io->ac->req->op.add.message;
1699 break;
1700 case LDB_MODIFY:
1701 msg = io->ac->req->op.mod.message;
1702 break;
1703 default:
1704 return LDB_ERR_OPERATIONS_ERROR;
1705 break;
1708 if (io->ac->pwd_last_set_bypass) {
1709 struct ldb_message_element *el1 = NULL;
1710 struct ldb_message_element *el2 = NULL;
1712 if (msg == NULL) {
1713 return LDB_ERR_CONSTRAINT_VIOLATION;
1716 el1 = dsdb_get_single_valued_attr(msg, "pwdLastSet",
1717 io->ac->req->operation);
1718 if (el1 == NULL) {
1719 return LDB_ERR_CONSTRAINT_VIOLATION;
1721 el2 = ldb_msg_find_element(msg, "pwdLastSet");
1722 if (el2 == NULL) {
1723 return LDB_ERR_CONSTRAINT_VIOLATION;
1725 if (el1 != el2) {
1726 return LDB_ERR_CONSTRAINT_VIOLATION;
1729 io->g.last_set = samdb_result_nttime(msg, "pwdLastSet", 0);
1730 return LDB_SUCCESS;
1733 ret = msg_find_old_and_new_pwd_val(msg, "pwdLastSet",
1734 io->ac->req->operation,
1735 &new_val, &old_val);
1736 if (ret != LDB_SUCCESS) {
1737 return ret;
1740 if (old_val != NULL && new_val == NULL) {
1741 ldb_set_errstring(ldb,
1742 "'pwdLastSet' deletion is not allowed!");
1743 return LDB_ERR_UNWILLING_TO_PERFORM;
1746 io->g.last_set = UINT64_MAX;
1747 if (new_val != NULL) {
1748 struct ldb_message *tmp_msg = NULL;
1750 tmp_msg = ldb_msg_new(io->ac);
1751 if (tmp_msg == NULL) {
1752 return ldb_module_oom(io->ac->module);
1755 if (old_val != NULL) {
1756 NTTIME old_last_set = 0;
1758 ret = ldb_msg_add_value(tmp_msg, "oldval",
1759 old_val, NULL);
1760 if (ret != LDB_SUCCESS) {
1761 return ret;
1764 old_last_set = samdb_result_nttime(tmp_msg,
1765 "oldval",
1767 if (io->u.pwdLastSet != old_last_set) {
1768 return dsdb_module_werror(io->ac->module,
1769 LDB_ERR_NO_SUCH_ATTRIBUTE,
1770 WERR_DS_CANT_REM_MISSING_ATT_VAL,
1771 "setup_last_set_field: old pwdLastSet "
1772 "value not found!");
1776 ret = ldb_msg_add_value(tmp_msg, "newval",
1777 new_val, NULL);
1778 if (ret != LDB_SUCCESS) {
1779 return ret;
1782 io->g.last_set = samdb_result_nttime(tmp_msg,
1783 "newval",
1785 } else if (ldb_msg_find_element(msg, "pwdLastSet")) {
1786 ldb_set_errstring(ldb,
1787 "'pwdLastSet' deletion is not allowed!");
1788 return LDB_ERR_UNWILLING_TO_PERFORM;
1791 /* only 0 or -1 (0xFFFFFFFFFFFFFFFF) are allowed */
1792 switch (io->g.last_set) {
1793 case 0:
1794 if (!io->ac->pwd_last_set_default) {
1795 break;
1797 if (!io->ac->update_password) {
1798 break;
1800 /* fall through */
1801 case UINT64_MAX:
1802 if (!io->ac->update_password &&
1803 io->u.pwdLastSet != 0 &&
1804 io->u.pwdLastSet != UINT64_MAX)
1807 * Just setting pwdLastSet to -1, while not changing
1808 * any password field has no effect if pwdLastSet
1809 * is already non-zero.
1811 io->ac->update_lastset = false;
1812 break;
1814 /* -1 means set it as now */
1815 GetTimeOfDay(&tv);
1816 io->g.last_set = timeval_to_nttime(&tv);
1817 break;
1818 default:
1819 return dsdb_module_werror(io->ac->module,
1820 LDB_ERR_OTHER,
1821 WERR_INVALID_PARAM,
1822 "setup_last_set_field: "
1823 "pwdLastSet must be 0 or -1 only!");
1826 if (io->ac->req->operation == LDB_ADD) {
1828 * We always need to store the value on add
1829 * operations.
1831 return LDB_SUCCESS;
1834 if (io->g.last_set == io->u.pwdLastSet) {
1836 * Just setting pwdLastSet to 0, is no-op if it's already 0.
1838 io->ac->update_lastset = false;
1841 return LDB_SUCCESS;
1844 static int setup_given_passwords(struct setup_password_fields_io *io,
1845 struct setup_password_fields_given *g)
1847 struct ldb_context *ldb;
1848 bool ok;
1850 ldb = ldb_module_get_ctx(io->ac->module);
1852 if (g->cleartext_utf8) {
1853 struct ldb_val *cleartext_utf16_blob;
1855 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1856 if (!cleartext_utf16_blob) {
1857 return ldb_oom(ldb);
1859 if (!convert_string_talloc(io->ac,
1860 CH_UTF8, CH_UTF16,
1861 g->cleartext_utf8->data,
1862 g->cleartext_utf8->length,
1863 (void *)&cleartext_utf16_blob->data,
1864 &cleartext_utf16_blob->length)) {
1865 if (g->cleartext_utf8->length != 0) {
1866 talloc_free(cleartext_utf16_blob);
1867 ldb_asprintf_errstring(ldb,
1868 "setup_password_fields: "
1869 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1870 io->u.sAMAccountName);
1871 return LDB_ERR_CONSTRAINT_VIOLATION;
1872 } else {
1873 /* passwords with length "0" are valid! */
1874 cleartext_utf16_blob->data = NULL;
1875 cleartext_utf16_blob->length = 0;
1878 g->cleartext_utf16 = cleartext_utf16_blob;
1879 } else if (g->cleartext_utf16) {
1880 struct ldb_val *cleartext_utf8_blob;
1882 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1883 if (!cleartext_utf8_blob) {
1884 return ldb_oom(ldb);
1886 if (!convert_string_talloc(io->ac,
1887 CH_UTF16MUNGED, CH_UTF8,
1888 g->cleartext_utf16->data,
1889 g->cleartext_utf16->length,
1890 (void *)&cleartext_utf8_blob->data,
1891 &cleartext_utf8_blob->length)) {
1892 if (g->cleartext_utf16->length != 0) {
1893 /* We must bail out here, the input wasn't even
1894 * a multiple of 2 bytes */
1895 talloc_free(cleartext_utf8_blob);
1896 ldb_asprintf_errstring(ldb,
1897 "setup_password_fields: "
1898 "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)!",
1899 io->u.sAMAccountName);
1900 return LDB_ERR_CONSTRAINT_VIOLATION;
1901 } else {
1902 /* passwords with length "0" are valid! */
1903 cleartext_utf8_blob->data = NULL;
1904 cleartext_utf8_blob->length = 0;
1907 g->cleartext_utf8 = cleartext_utf8_blob;
1910 if (g->cleartext_utf16) {
1911 struct samr_Password *nt_hash;
1913 nt_hash = talloc(io->ac, struct samr_Password);
1914 if (!nt_hash) {
1915 return ldb_oom(ldb);
1917 g->nt_hash = nt_hash;
1919 /* compute the new nt hash */
1920 mdfour(nt_hash->hash,
1921 g->cleartext_utf16->data,
1922 g->cleartext_utf16->length);
1925 if (g->cleartext_utf8) {
1926 struct samr_Password *lm_hash;
1928 lm_hash = talloc(io->ac, struct samr_Password);
1929 if (!lm_hash) {
1930 return ldb_oom(ldb);
1933 /* compute the new lm hash */
1934 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1935 if (ok) {
1936 g->lm_hash = lm_hash;
1937 } else {
1938 talloc_free(lm_hash);
1942 return LDB_SUCCESS;
1945 static int setup_password_fields(struct setup_password_fields_io *io)
1947 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1948 struct loadparm_context *lp_ctx =
1949 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1950 struct loadparm_context);
1951 int ret;
1953 ret = setup_last_set_field(io);
1954 if (ret != LDB_SUCCESS) {
1955 return ret;
1958 if (!io->ac->update_password) {
1959 return LDB_SUCCESS;
1962 /* transform the old password (for password changes) */
1963 ret = setup_given_passwords(io, &io->og);
1964 if (ret != LDB_SUCCESS) {
1965 return ret;
1968 /* transform the new password */
1969 ret = setup_given_passwords(io, &io->n);
1970 if (ret != LDB_SUCCESS) {
1971 return ret;
1974 if (io->n.cleartext_utf8) {
1975 ret = setup_kerberos_keys(io);
1976 if (ret != LDB_SUCCESS) {
1977 return ret;
1981 ret = setup_nt_fields(io);
1982 if (ret != LDB_SUCCESS) {
1983 return ret;
1986 if (lpcfg_lanman_auth(lp_ctx)) {
1987 ret = setup_lm_fields(io);
1988 if (ret != LDB_SUCCESS) {
1989 return ret;
1991 } else {
1992 io->g.lm_hash = NULL;
1993 io->g.lm_history_len = 0;
1996 ret = setup_supplemental_field(io);
1997 if (ret != LDB_SUCCESS) {
1998 return ret;
2001 return LDB_SUCCESS;
2004 static int make_error_and_update_badPwdCount(struct setup_password_fields_io *io)
2006 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
2007 struct ldb_message *mod_msg = NULL;
2008 NTSTATUS status;
2009 int ret;
2011 status = dsdb_update_bad_pwd_count(io->ac, ldb,
2012 io->ac->search_res->message,
2013 io->ac->dom_res->message,
2014 &mod_msg);
2015 if (!NT_STATUS_IS_OK(status)) {
2016 goto done;
2019 if (mod_msg == NULL) {
2020 goto done;
2024 * OK, horrible semantics ahead.
2026 * - We need to abort any existing transaction
2027 * - create a transaction arround the badPwdCount update
2028 * - re-open the transaction so the upper layer
2029 * doesn't know what happened.
2031 * This is needed because returning an error to the upper
2032 * layer will cancel the transaction and undo the badPwdCount
2033 * update.
2037 * Checking errors here is a bit pointless.
2038 * What can we do if we can't end the transaction?
2040 ret = ldb_next_del_trans(io->ac->module);
2041 if (ret != LDB_SUCCESS) {
2042 ldb_debug(ldb, LDB_DEBUG_FATAL,
2043 "Failed to abort transaction prior to update of badPwdCount of %s: %s",
2044 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2045 ldb_errstring(ldb));
2047 * just return the original error
2049 goto done;
2052 /* Likewise, what should we do if we can't open a new transaction? */
2053 ret = ldb_next_start_trans(io->ac->module);
2054 if (ret != LDB_SUCCESS) {
2055 ldb_debug(ldb, LDB_DEBUG_ERROR,
2056 "Failed to open transaction to update badPwdCount of %s: %s",
2057 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2058 ldb_errstring(ldb));
2060 * just return the original error
2062 goto done;
2065 ret = dsdb_module_modify(io->ac->module, mod_msg,
2066 DSDB_FLAG_NEXT_MODULE,
2067 io->ac->req);
2068 if (ret != LDB_SUCCESS) {
2069 ldb_debug(ldb, LDB_DEBUG_ERROR,
2070 "Failed to update badPwdCount of %s: %s",
2071 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2072 ldb_errstring(ldb));
2074 * We can only ignore this...
2078 ret = ldb_next_end_trans(io->ac->module);
2079 if (ret != LDB_SUCCESS) {
2080 ldb_debug(ldb, LDB_DEBUG_ERROR,
2081 "Failed to close transaction to update badPwdCount of %s: %s",
2082 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2083 ldb_errstring(ldb));
2085 * We can only ignore this...
2089 ret = ldb_next_start_trans(io->ac->module);
2090 if (ret != LDB_SUCCESS) {
2091 ldb_debug(ldb, LDB_DEBUG_ERROR,
2092 "Failed to open transaction after update of badPwdCount of %s: %s",
2093 ldb_dn_get_linearized(io->ac->search_res->message->dn),
2094 ldb_errstring(ldb));
2096 * We can only ignore this...
2100 done:
2101 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2102 ldb_asprintf_errstring(ldb,
2103 "%08X: %s - check_password_restrictions: "
2104 "The old password specified doesn't match!",
2105 W_ERROR_V(WERR_INVALID_PASSWORD),
2106 ldb_strerror(ret));
2107 return ret;
2110 static int check_password_restrictions(struct setup_password_fields_io *io)
2112 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
2113 int ret;
2114 struct loadparm_context *lp_ctx =
2115 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2116 struct loadparm_context);
2118 if (!io->ac->update_password) {
2119 return LDB_SUCCESS;
2122 /* First check the old password is correct, for password changes */
2123 if (!io->ac->pwd_reset) {
2124 bool nt_hash_checked = false;
2126 /* we need the old nt or lm hash given by the client */
2127 if (!io->og.nt_hash && !io->og.lm_hash) {
2128 ldb_asprintf_errstring(ldb,
2129 "check_password_restrictions: "
2130 "You need to provide the old password in order "
2131 "to change it!");
2132 return LDB_ERR_UNWILLING_TO_PERFORM;
2135 /* The password modify through the NT hash is encouraged and
2136 has no problems at all */
2137 if (io->og.nt_hash) {
2138 if (!io->o.nt_hash || memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
2139 return make_error_and_update_badPwdCount(io);
2142 nt_hash_checked = true;
2145 /* But it is also possible to change a password by the LM hash
2146 * alone for compatibility reasons. This check is optional if
2147 * the NT hash was already checked - otherwise it's mandatory.
2148 * (as the SAMR operations request it). */
2149 if (io->og.lm_hash) {
2150 if ((!io->o.lm_hash && !nt_hash_checked)
2151 || (io->o.lm_hash && memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0)) {
2152 return make_error_and_update_badPwdCount(io);
2157 if (io->u.restrictions == 0) {
2158 /* FIXME: Is this right? */
2159 return LDB_SUCCESS;
2162 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2163 if ((io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) &&
2164 !io->ac->pwd_reset)
2166 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2167 ldb_asprintf_errstring(ldb,
2168 "%08X: %s - check_password_restrictions: "
2169 "password is too young to change!",
2170 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2171 ldb_strerror(ret));
2172 return ret;
2176 * Fundamental password checks done by the call
2177 * "samdb_check_password".
2178 * It is also in use by "dcesrv_samr_ValidatePassword".
2180 if (io->n.cleartext_utf8 != NULL) {
2181 enum samr_ValidationStatus vstat;
2182 vstat = samdb_check_password(io->ac, lp_ctx,
2183 io->n.cleartext_utf8,
2184 io->ac->status->domain_data.pwdProperties,
2185 io->ac->status->domain_data.minPwdLength);
2186 switch (vstat) {
2187 case SAMR_VALIDATION_STATUS_SUCCESS:
2188 /* perfect -> proceed! */
2189 break;
2191 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
2192 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2193 ldb_asprintf_errstring(ldb,
2194 "%08X: %s - check_password_restrictions: "
2195 "the password is too short. It should be equal or longer than %u characters!",
2196 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2197 ldb_strerror(ret),
2198 io->ac->status->domain_data.minPwdLength);
2199 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
2200 return ret;
2202 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
2203 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2204 ldb_asprintf_errstring(ldb,
2205 "%08X: %s - check_password_restrictions: "
2206 "the password does not meet the complexity criteria!",
2207 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2208 ldb_strerror(ret));
2209 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
2210 return ret;
2212 default:
2213 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2214 ldb_asprintf_errstring(ldb,
2215 "%08X: %s - check_password_restrictions: "
2216 "the password doesn't fit due to a miscellaneous restriction!",
2217 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2218 ldb_strerror(ret));
2219 return ret;
2223 if (io->ac->pwd_reset) {
2224 return LDB_SUCCESS;
2227 if (io->n.nt_hash) {
2228 uint32_t i;
2230 /* checks the NT hash password history */
2231 for (i = 0; i < io->o.nt_history_len; i++) {
2232 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
2233 if (ret == 0) {
2234 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2235 ldb_asprintf_errstring(ldb,
2236 "%08X: %s - check_password_restrictions: "
2237 "the password was already used (in history)!",
2238 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2239 ldb_strerror(ret));
2240 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2241 return ret;
2246 if (io->n.lm_hash) {
2247 uint32_t i;
2249 /* checks the LM hash password history */
2250 for (i = 0; i < io->o.lm_history_len; i++) {
2251 ret = memcmp(io->n.lm_hash, io->o.lm_history[i].hash, 16);
2252 if (ret == 0) {
2253 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2254 ldb_asprintf_errstring(ldb,
2255 "%08X: %s - check_password_restrictions: "
2256 "the password was already used (in history)!",
2257 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2258 ldb_strerror(ret));
2259 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2260 return ret;
2265 /* are all password changes disallowed? */
2266 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
2267 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2268 ldb_asprintf_errstring(ldb,
2269 "%08X: %s - check_password_restrictions: "
2270 "password changes disabled!",
2271 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2272 ldb_strerror(ret));
2273 return ret;
2276 /* can this user change the password? */
2277 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
2278 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2279 ldb_asprintf_errstring(ldb,
2280 "%08X: %s - check_password_restrictions: "
2281 "password can't be changed on this account!",
2282 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2283 ldb_strerror(ret));
2284 return ret;
2287 return LDB_SUCCESS;
2290 static int update_final_msg(struct setup_password_fields_io *io)
2292 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
2293 int ret;
2294 int el_flags = 0;
2295 bool update_password = io->ac->update_password;
2296 bool update_scb = io->ac->update_password;
2299 * If we add a user without initial password,
2300 * we need to add replication meta data for
2301 * following attributes:
2302 * - unicodePwd
2303 * - dBCSPwd
2304 * - ntPwdHistory
2305 * - lmPwdHistory
2307 * If we add a user with initial password or a
2308 * password is changed of an existing user,
2309 * we need to replace the following attributes
2310 * with a forced meta data update, e.g. also
2311 * when updating an empty attribute with an empty value:
2312 * - unicodePwd
2313 * - dBCSPwd
2314 * - ntPwdHistory
2315 * - lmPwdHistory
2316 * - supplementalCredentials
2319 switch (io->ac->req->operation) {
2320 case LDB_ADD:
2321 update_password = true;
2322 el_flags |= DSDB_FLAG_INTERNAL_FORCE_META_DATA;
2323 break;
2324 case LDB_MODIFY:
2325 el_flags |= LDB_FLAG_MOD_REPLACE;
2326 el_flags |= DSDB_FLAG_INTERNAL_FORCE_META_DATA;
2327 break;
2328 default:
2329 return ldb_module_operr(io->ac->module);
2332 if (update_password) {
2333 ret = ldb_msg_add_empty(io->ac->update_msg,
2334 "unicodePwd",
2335 el_flags, NULL);
2336 if (ret != LDB_SUCCESS) {
2337 return ret;
2339 ret = ldb_msg_add_empty(io->ac->update_msg,
2340 "dBCSPwd",
2341 el_flags, NULL);
2342 if (ret != LDB_SUCCESS) {
2343 return ret;
2345 ret = ldb_msg_add_empty(io->ac->update_msg,
2346 "ntPwdHistory",
2347 el_flags, NULL);
2348 if (ret != LDB_SUCCESS) {
2349 return ret;
2351 ret = ldb_msg_add_empty(io->ac->update_msg,
2352 "lmPwdHistory",
2353 el_flags, NULL);
2354 if (ret != LDB_SUCCESS) {
2355 return ret;
2358 if (update_scb) {
2359 ret = ldb_msg_add_empty(io->ac->update_msg,
2360 "supplementalCredentials",
2361 el_flags, NULL);
2362 if (ret != LDB_SUCCESS) {
2363 return ret;
2366 if (io->ac->update_lastset) {
2367 ret = ldb_msg_add_empty(io->ac->update_msg,
2368 "pwdLastSet",
2369 el_flags, NULL);
2370 if (ret != LDB_SUCCESS) {
2371 return ret;
2375 if (io->g.nt_hash != NULL) {
2376 ret = samdb_msg_add_hash(ldb, io->ac,
2377 io->ac->update_msg,
2378 "unicodePwd",
2379 io->g.nt_hash);
2380 if (ret != LDB_SUCCESS) {
2381 return ret;
2384 if (io->g.lm_hash != NULL) {
2385 ret = samdb_msg_add_hash(ldb, io->ac,
2386 io->ac->update_msg,
2387 "dBCSPwd",
2388 io->g.lm_hash);
2389 if (ret != LDB_SUCCESS) {
2390 return ret;
2393 if (io->g.nt_history_len > 0) {
2394 ret = samdb_msg_add_hashes(ldb, io->ac,
2395 io->ac->update_msg,
2396 "ntPwdHistory",
2397 io->g.nt_history,
2398 io->g.nt_history_len);
2399 if (ret != LDB_SUCCESS) {
2400 return ret;
2403 if (io->g.lm_history_len > 0) {
2404 ret = samdb_msg_add_hashes(ldb, io->ac,
2405 io->ac->update_msg,
2406 "lmPwdHistory",
2407 io->g.lm_history,
2408 io->g.lm_history_len);
2409 if (ret != LDB_SUCCESS) {
2410 return ret;
2413 if (io->g.supplemental.length > 0) {
2414 ret = ldb_msg_add_value(io->ac->update_msg,
2415 "supplementalCredentials",
2416 &io->g.supplemental, NULL);
2417 if (ret != LDB_SUCCESS) {
2418 return ret;
2421 if (io->ac->update_lastset) {
2422 ret = samdb_msg_add_uint64(ldb, io->ac,
2423 io->ac->update_msg,
2424 "pwdLastSet",
2425 io->g.last_set);
2426 if (ret != LDB_SUCCESS) {
2427 return ret;
2431 return LDB_SUCCESS;
2435 * This is intended for use by the "password_hash" module since there
2436 * password changes can be specified through one message element with the
2437 * new password (to set) and another one with the old password (to unset).
2439 * The first which sets a password (new value) can have flags
2440 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2441 * for entries). The latter (old value) has always specified
2442 * LDB_FLAG_MOD_DELETE.
2444 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2445 * matching message elements are malformed in respect to the set/change rules.
2446 * Otherwise it returns LDB_SUCCESS.
2448 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
2449 const char *name,
2450 enum ldb_request_type operation,
2451 const struct ldb_val **new_val,
2452 const struct ldb_val **old_val)
2454 unsigned int i;
2456 *new_val = NULL;
2457 *old_val = NULL;
2459 if (msg == NULL) {
2460 return LDB_SUCCESS;
2463 for (i = 0; i < msg->num_elements; i++) {
2464 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
2465 continue;
2468 if ((operation == LDB_MODIFY) &&
2469 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
2470 /* 0 values are allowed */
2471 if (msg->elements[i].num_values == 1) {
2472 *old_val = &msg->elements[i].values[0];
2473 } else if (msg->elements[i].num_values > 1) {
2474 return LDB_ERR_CONSTRAINT_VIOLATION;
2476 } else if ((operation == LDB_MODIFY) &&
2477 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
2478 if (msg->elements[i].num_values > 0) {
2479 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2480 } else {
2481 return LDB_ERR_UNWILLING_TO_PERFORM;
2483 } else {
2484 /* Add operations and LDB_FLAG_MOD_ADD */
2485 if (msg->elements[i].num_values > 0) {
2486 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2487 } else {
2488 return LDB_ERR_CONSTRAINT_VIOLATION;
2493 return LDB_SUCCESS;
2496 static int setup_io(struct ph_context *ac,
2497 const struct ldb_message *client_msg,
2498 const struct ldb_message *existing_msg,
2499 struct setup_password_fields_io *io)
2501 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
2502 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2503 struct loadparm_context *lp_ctx = talloc_get_type(
2504 ldb_get_opaque(ldb, "loadparm"), struct loadparm_context);
2505 int ret;
2506 const struct ldb_message *info_msg = NULL;
2508 ZERO_STRUCTP(io);
2510 /* Some operations below require kerberos contexts */
2512 if (existing_msg != NULL) {
2514 * This is a modify operation
2516 info_msg = existing_msg;
2517 } else {
2519 * This is an add operation
2521 info_msg = client_msg;
2524 if (smb_krb5_init_context(ac,
2525 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
2526 &io->smb_krb5_context) != 0) {
2527 return ldb_operr(ldb);
2530 io->ac = ac;
2532 io->u.userAccountControl = ldb_msg_find_attr_as_uint(info_msg,
2533 "userAccountControl", 0);
2534 if (info_msg == existing_msg) {
2536 * We only take pwdLastSet from the existing object
2537 * otherwise we leave it as 0.
2539 * If no attribute is available, e.g. on deleted objects
2540 * we remember that as UINT64_MAX.
2542 io->u.pwdLastSet = samdb_result_nttime(info_msg, "pwdLastSet",
2543 UINT64_MAX);
2545 io->u.sAMAccountName = ldb_msg_find_attr_as_string(info_msg,
2546 "sAMAccountName", NULL);
2547 io->u.user_principal_name = ldb_msg_find_attr_as_string(info_msg,
2548 "userPrincipalName", NULL);
2549 io->u.is_computer = ldb_msg_check_string_attribute(info_msg, "objectClass", "computer");
2551 if (io->u.sAMAccountName == NULL) {
2552 ldb_asprintf_errstring(ldb,
2553 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2554 ldb_dn_get_linearized(info_msg->dn));
2556 return LDB_ERR_CONSTRAINT_VIOLATION;
2559 if (io->u.userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
2560 struct ldb_control *permit_trust = ldb_request_get_control(ac->req,
2561 DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID);
2563 if (permit_trust == NULL) {
2564 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2565 ldb_asprintf_errstring(ldb,
2566 "%08X: %s - setup_io: changing the interdomain trust password "
2567 "on %s not allowed via LDAP. Use LSA or NETLOGON",
2568 W_ERROR_V(WERR_ACCESS_DENIED),
2569 ldb_strerror(ret),
2570 ldb_dn_get_linearized(info_msg->dn));
2571 return ret;
2575 /* Only non-trust accounts have restrictions (possibly this test is the
2576 * wrong way around, but we like to be restrictive if possible */
2577 io->u.restrictions = !(io->u.userAccountControl
2578 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
2579 | UF_SERVER_TRUST_ACCOUNT));
2581 if (ac->userPassword) {
2582 ret = msg_find_old_and_new_pwd_val(client_msg, "userPassword",
2583 ac->req->operation,
2584 &io->n.cleartext_utf8,
2585 &io->og.cleartext_utf8);
2586 if (ret != LDB_SUCCESS) {
2587 ldb_asprintf_errstring(ldb,
2588 "setup_io: "
2589 "it's only allowed to set the old password once!");
2590 return ret;
2594 if (io->n.cleartext_utf8 != NULL) {
2595 struct ldb_val *cleartext_utf8_blob;
2596 char *p;
2598 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
2599 if (!cleartext_utf8_blob) {
2600 return ldb_oom(ldb);
2603 *cleartext_utf8_blob = *io->n.cleartext_utf8;
2605 /* make sure we have a null terminated string */
2606 p = talloc_strndup(cleartext_utf8_blob,
2607 (const char *)io->n.cleartext_utf8->data,
2608 io->n.cleartext_utf8->length);
2609 if ((p == NULL) && (io->n.cleartext_utf8->length > 0)) {
2610 return ldb_oom(ldb);
2612 cleartext_utf8_blob->data = (uint8_t *)p;
2614 io->n.cleartext_utf8 = cleartext_utf8_blob;
2617 ret = msg_find_old_and_new_pwd_val(client_msg, "clearTextPassword",
2618 ac->req->operation,
2619 &io->n.cleartext_utf16,
2620 &io->og.cleartext_utf16);
2621 if (ret != LDB_SUCCESS) {
2622 ldb_asprintf_errstring(ldb,
2623 "setup_io: "
2624 "it's only allowed to set the old password once!");
2625 return ret;
2628 /* this rather strange looking piece of code is there to
2629 handle a ldap client setting a password remotely using the
2630 unicodePwd ldap field. The syntax is that the password is
2631 in UTF-16LE, with a " at either end. Unfortunately the
2632 unicodePwd field is also used to store the nt hashes
2633 internally in Samba, and is used in the nt hash format on
2634 the wire in DRS replication, so we have a single name for
2635 two distinct values. The code below leaves us with a small
2636 chance (less than 1 in 2^32) of a mixup, if someone manages
2637 to create a MD4 hash which starts and ends in 0x22 0x00, as
2638 that would then be treated as a UTF16 password rather than
2639 a nthash */
2641 ret = msg_find_old_and_new_pwd_val(client_msg, "unicodePwd",
2642 ac->req->operation,
2643 &quoted_utf16,
2644 &old_quoted_utf16);
2645 if (ret != LDB_SUCCESS) {
2646 ldb_asprintf_errstring(ldb,
2647 "setup_io: "
2648 "it's only allowed to set the old password once!");
2649 return ret;
2652 /* Checks and converts the actual "unicodePwd" attribute */
2653 if (!ac->hash_values &&
2654 quoted_utf16 &&
2655 quoted_utf16->length >= 4 &&
2656 quoted_utf16->data[0] == '"' &&
2657 quoted_utf16->data[1] == 0 &&
2658 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
2659 quoted_utf16->data[quoted_utf16->length-1] == 0) {
2660 struct ldb_val *quoted_utf16_2;
2662 if (io->n.cleartext_utf16) {
2663 /* refuse the change if someone wants to change with
2664 with both UTF16 possibilities at the same time... */
2665 ldb_asprintf_errstring(ldb,
2666 "setup_io: "
2667 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2668 return LDB_ERR_UNWILLING_TO_PERFORM;
2672 * adapt the quoted UTF16 string to be a real
2673 * cleartext one
2675 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2676 if (quoted_utf16_2 == NULL) {
2677 return ldb_oom(ldb);
2680 quoted_utf16_2->data = quoted_utf16->data + 2;
2681 quoted_utf16_2->length = quoted_utf16->length-4;
2682 io->n.cleartext_utf16 = quoted_utf16_2;
2683 io->n.nt_hash = NULL;
2685 } else if (quoted_utf16) {
2686 /* We have only the hash available -> so no plaintext here */
2687 if (!ac->hash_values) {
2688 /* refuse the change if someone wants to change
2689 the hash without control specified... */
2690 ldb_asprintf_errstring(ldb,
2691 "setup_io: "
2692 "it's not allowed to set the NT hash password directly'");
2693 /* this looks odd but this is what Windows does:
2694 returns "UNWILLING_TO_PERFORM" on wrong
2695 password sets and "CONSTRAINT_VIOLATION" on
2696 wrong password changes. */
2697 if (old_quoted_utf16 == NULL) {
2698 return LDB_ERR_UNWILLING_TO_PERFORM;
2701 return LDB_ERR_CONSTRAINT_VIOLATION;
2704 io->n.nt_hash = talloc(io->ac, struct samr_Password);
2705 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
2706 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
2709 /* Checks and converts the previous "unicodePwd" attribute */
2710 if (!ac->hash_values &&
2711 old_quoted_utf16 &&
2712 old_quoted_utf16->length >= 4 &&
2713 old_quoted_utf16->data[0] == '"' &&
2714 old_quoted_utf16->data[1] == 0 &&
2715 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
2716 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
2717 struct ldb_val *old_quoted_utf16_2;
2719 if (io->og.cleartext_utf16) {
2720 /* refuse the change if someone wants to change with
2721 both UTF16 possibilities at the same time... */
2722 ldb_asprintf_errstring(ldb,
2723 "setup_io: "
2724 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2725 return LDB_ERR_UNWILLING_TO_PERFORM;
2729 * adapt the quoted UTF16 string to be a real
2730 * cleartext one
2732 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2733 if (old_quoted_utf16_2 == NULL) {
2734 return ldb_oom(ldb);
2737 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
2738 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
2740 io->og.cleartext_utf16 = old_quoted_utf16_2;
2741 io->og.nt_hash = NULL;
2742 } else if (old_quoted_utf16) {
2743 /* We have only the hash available -> so no plaintext here */
2744 if (!ac->hash_values) {
2745 /* refuse the change if someone wants to change
2746 the hash without control specified... */
2747 ldb_asprintf_errstring(ldb,
2748 "setup_io: "
2749 "it's not allowed to set the NT hash password directly'");
2750 return LDB_ERR_UNWILLING_TO_PERFORM;
2753 io->og.nt_hash = talloc(io->ac, struct samr_Password);
2754 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
2755 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
2758 /* Handles the "dBCSPwd" attribute (LM hash) */
2759 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
2760 ret = msg_find_old_and_new_pwd_val(client_msg, "dBCSPwd",
2761 ac->req->operation,
2762 &lm_hash, &old_lm_hash);
2763 if (ret != LDB_SUCCESS) {
2764 ldb_asprintf_errstring(ldb,
2765 "setup_io: "
2766 "it's only allowed to set the old password once!");
2767 return ret;
2770 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
2771 /* refuse the change if someone wants to change the hash
2772 without control specified... */
2773 ldb_asprintf_errstring(ldb,
2774 "setup_io: "
2775 "it's not allowed to set the LM hash password directly'");
2776 return LDB_ERR_UNWILLING_TO_PERFORM;
2779 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
2780 io->n.lm_hash = talloc(io->ac, struct samr_Password);
2781 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
2782 sizeof(io->n.lm_hash->hash)));
2784 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
2785 io->og.lm_hash = talloc(io->ac, struct samr_Password);
2786 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
2787 sizeof(io->og.lm_hash->hash)));
2791 * Handles the password change control if it's specified. It has the
2792 * precedance and overrides already specified old password values of
2793 * change requests (but that shouldn't happen since the control is
2794 * fully internal and only used in conjunction with replace requests!).
2796 if (ac->change != NULL) {
2797 io->og.nt_hash = NULL;
2798 if (ac->change->old_nt_pwd_hash != NULL) {
2799 io->og.nt_hash = talloc_memdup(io->ac,
2800 ac->change->old_nt_pwd_hash,
2801 sizeof(struct samr_Password));
2803 io->og.lm_hash = NULL;
2804 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
2805 io->og.lm_hash = talloc_memdup(io->ac,
2806 ac->change->old_lm_pwd_hash,
2807 sizeof(struct samr_Password));
2811 /* refuse the change if someone wants to change the clear-
2812 text and supply his own hashes at the same time... */
2813 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
2814 && (io->n.nt_hash || io->n.lm_hash)) {
2815 ldb_asprintf_errstring(ldb,
2816 "setup_io: "
2817 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2818 return LDB_ERR_UNWILLING_TO_PERFORM;
2821 /* refuse the change if someone wants to change the password
2822 using both plaintext methods (UTF8 and UTF16) at the same time... */
2823 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
2824 ldb_asprintf_errstring(ldb,
2825 "setup_io: "
2826 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2827 return LDB_ERR_UNWILLING_TO_PERFORM;
2830 /* refuse the change if someone tries to set/change the password by
2831 * the lanman hash alone and we've deactivated that mechanism. This
2832 * would end in an account without any password! */
2833 if (io->ac->update_password
2834 && (!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
2835 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
2836 ldb_asprintf_errstring(ldb,
2837 "setup_io: "
2838 "It's not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2839 /* on "userPassword" and "clearTextPassword" we've to return
2840 * something different, since these are virtual attributes */
2841 if ((ldb_msg_find_element(client_msg, "userPassword") != NULL) ||
2842 (ldb_msg_find_element(client_msg, "clearTextPassword") != NULL)) {
2843 return LDB_ERR_CONSTRAINT_VIOLATION;
2845 return LDB_ERR_UNWILLING_TO_PERFORM;
2848 /* refuse the change if someone wants to compare against a plaintext
2849 or hash at the same time for a "password modify" operation... */
2850 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
2851 && (io->og.nt_hash || io->og.lm_hash)) {
2852 ldb_asprintf_errstring(ldb,
2853 "setup_io: "
2854 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2855 return LDB_ERR_UNWILLING_TO_PERFORM;
2858 /* refuse the change if someone wants to compare against both
2859 * plaintexts at the same time for a "password modify" operation... */
2860 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2861 ldb_asprintf_errstring(ldb,
2862 "setup_io: "
2863 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2864 return LDB_ERR_UNWILLING_TO_PERFORM;
2867 /* Decides if we have a password modify or password reset operation */
2868 if (ac->req->operation == LDB_ADD) {
2869 /* On "add" we have only "password reset" */
2870 ac->pwd_reset = true;
2871 } else if (ac->req->operation == LDB_MODIFY) {
2872 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2873 || io->og.nt_hash || io->og.lm_hash) {
2874 /* If we have an old password specified then for sure it
2875 * is a user "password change" */
2876 ac->pwd_reset = false;
2877 } else {
2878 /* Otherwise we have also here a "password reset" */
2879 ac->pwd_reset = true;
2881 } else {
2882 /* this shouldn't happen */
2883 return ldb_operr(ldb);
2886 if (existing_msg != NULL) {
2887 NTSTATUS status;
2889 if (ac->pwd_reset) {
2890 /* Get the old password from the database */
2891 status = samdb_result_passwords_no_lockout(ac,
2892 lp_ctx,
2893 existing_msg,
2894 &io->o.lm_hash,
2895 &io->o.nt_hash);
2896 } else {
2897 /* Get the old password from the database */
2898 status = samdb_result_passwords(ac,
2899 lp_ctx,
2900 existing_msg,
2901 &io->o.lm_hash,
2902 &io->o.nt_hash);
2905 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
2906 return dsdb_module_werror(ac->module,
2907 LDB_ERR_CONSTRAINT_VIOLATION,
2908 WERR_ACCOUNT_LOCKED_OUT,
2909 "Password change not permitted,"
2910 " account locked out!");
2913 if (!NT_STATUS_IS_OK(status)) {
2915 * This only happens if the database has gone weird,
2916 * not if we are just missing the passwords
2918 return ldb_operr(ldb);
2921 io->o.nt_history_len = samdb_result_hashes(ac, existing_msg,
2922 "ntPwdHistory",
2923 &io->o.nt_history);
2924 io->o.lm_history_len = samdb_result_hashes(ac, existing_msg,
2925 "lmPwdHistory",
2926 &io->o.lm_history);
2927 io->o.supplemental = ldb_msg_find_ldb_val(existing_msg,
2928 "supplementalCredentials");
2930 if (io->o.supplemental != NULL) {
2931 enum ndr_err_code ndr_err;
2933 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
2934 &io->o.scb,
2935 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
2936 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2937 status = ndr_map_error2ntstatus(ndr_err);
2938 ldb_asprintf_errstring(ldb,
2939 "setup_io: failed to pull "
2940 "old supplementalCredentialsBlob: %s",
2941 nt_errstr(status));
2942 return LDB_ERR_OPERATIONS_ERROR;
2947 return LDB_SUCCESS;
2950 static struct ph_context *ph_init_context(struct ldb_module *module,
2951 struct ldb_request *req,
2952 bool userPassword,
2953 bool update_password)
2955 struct ldb_context *ldb;
2956 struct ph_context *ac;
2958 ldb = ldb_module_get_ctx(module);
2960 ac = talloc_zero(req, struct ph_context);
2961 if (ac == NULL) {
2962 ldb_set_errstring(ldb, "Out of Memory");
2963 return NULL;
2966 ac->module = module;
2967 ac->req = req;
2968 ac->userPassword = userPassword;
2969 ac->update_password = update_password;
2970 ac->update_lastset = true;
2972 return ac;
2975 static void ph_apply_controls(struct ph_context *ac)
2977 struct ldb_control *ctrl;
2979 ac->change_status = false;
2980 ctrl = ldb_request_get_control(ac->req,
2981 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2982 if (ctrl != NULL) {
2983 ac->change_status = true;
2985 /* Mark the "change status" control as uncritical (done) */
2986 ctrl->critical = false;
2989 ac->hash_values = false;
2990 ctrl = ldb_request_get_control(ac->req,
2991 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2992 if (ctrl != NULL) {
2993 ac->hash_values = true;
2995 /* Mark the "hash values" control as uncritical (done) */
2996 ctrl->critical = false;
2999 ctrl = ldb_request_get_control(ac->req,
3000 DSDB_CONTROL_PASSWORD_CHANGE_OID);
3001 if (ctrl != NULL) {
3002 ac->change = (struct dsdb_control_password_change *) ctrl->data;
3004 /* Mark the "change" control as uncritical (done) */
3005 ctrl->critical = false;
3008 ac->pwd_last_set_bypass = false;
3009 ctrl = ldb_request_get_control(ac->req,
3010 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID);
3011 if (ctrl != NULL) {
3012 ac->pwd_last_set_bypass = true;
3014 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
3015 ctrl->critical = false;
3018 ac->pwd_last_set_default = false;
3019 ctrl = ldb_request_get_control(ac->req,
3020 DSDB_CONTROL_PASSWORD_DEFAULT_LAST_SET_OID);
3021 if (ctrl != NULL) {
3022 ac->pwd_last_set_default = true;
3024 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
3025 ctrl->critical = false;
3029 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
3031 struct ph_context *ac;
3033 ac = talloc_get_type(req->context, struct ph_context);
3035 if (!ares) {
3036 return ldb_module_done(ac->req, NULL, NULL,
3037 LDB_ERR_OPERATIONS_ERROR);
3040 if (ares->type == LDB_REPLY_REFERRAL) {
3041 return ldb_module_send_referral(ac->req, ares->referral);
3044 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
3045 /* On success and trivial errors a status control is being
3046 * added (used for example by the "samdb_set_password" call) */
3047 ldb_reply_add_control(ares,
3048 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
3049 false,
3050 ac->status);
3053 if (ares->error != LDB_SUCCESS) {
3054 return ldb_module_done(ac->req, ares->controls,
3055 ares->response, ares->error);
3058 if (ares->type != LDB_REPLY_DONE) {
3059 talloc_free(ares);
3060 return ldb_module_done(ac->req, NULL, NULL,
3061 LDB_ERR_OPERATIONS_ERROR);
3064 return ldb_module_done(ac->req, ares->controls,
3065 ares->response, ares->error);
3068 static int password_hash_add_do_add(struct ph_context *ac);
3069 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
3070 static int password_hash_mod_search_self(struct ph_context *ac);
3071 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
3072 static int password_hash_mod_do_mod(struct ph_context *ac);
3074 static int get_domain_data_callback(struct ldb_request *req,
3075 struct ldb_reply *ares)
3077 struct ldb_context *ldb;
3078 struct ph_context *ac;
3079 struct loadparm_context *lp_ctx;
3080 int ret = LDB_SUCCESS;
3082 ac = talloc_get_type(req->context, struct ph_context);
3083 ldb = ldb_module_get_ctx(ac->module);
3085 if (!ares) {
3086 ret = LDB_ERR_OPERATIONS_ERROR;
3087 goto done;
3089 if (ares->error != LDB_SUCCESS) {
3090 return ldb_module_done(ac->req, ares->controls,
3091 ares->response, ares->error);
3094 switch (ares->type) {
3095 case LDB_REPLY_ENTRY:
3096 if (ac->status != NULL) {
3097 talloc_free(ares);
3099 ldb_set_errstring(ldb, "Too many results");
3100 ret = LDB_ERR_OPERATIONS_ERROR;
3101 goto done;
3104 /* Setup the "status" structure (used as control later) */
3105 ac->status = talloc_zero(ac->req,
3106 struct dsdb_control_password_change_status);
3107 if (ac->status == NULL) {
3108 talloc_free(ares);
3110 ldb_oom(ldb);
3111 ret = LDB_ERR_OPERATIONS_ERROR;
3112 goto done;
3115 /* Setup the "domain data" structure */
3116 ac->status->domain_data.pwdProperties =
3117 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
3118 ac->status->domain_data.pwdHistoryLength =
3119 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
3120 ac->status->domain_data.maxPwdAge =
3121 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
3122 ac->status->domain_data.minPwdAge =
3123 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
3124 ac->status->domain_data.minPwdLength =
3125 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
3126 ac->status->domain_data.store_cleartext =
3127 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
3129 /* For a domain DN, this puts things in dotted notation */
3130 /* For builtin domains, this will give details for the host,
3131 * but that doesn't really matter, as it's just used for salt
3132 * and kerberos principals, which don't exist here */
3134 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3135 struct loadparm_context);
3137 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
3138 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
3139 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
3141 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
3143 if (ac->dom_res != NULL) {
3144 talloc_free(ares);
3146 ldb_set_errstring(ldb, "Too many results");
3147 ret = LDB_ERR_OPERATIONS_ERROR;
3148 goto done;
3151 ac->dom_res = talloc_steal(ac, ares);
3152 ret = LDB_SUCCESS;
3153 break;
3155 case LDB_REPLY_REFERRAL:
3156 /* ignore */
3157 talloc_free(ares);
3158 ret = LDB_SUCCESS;
3159 break;
3161 case LDB_REPLY_DONE:
3162 talloc_free(ares);
3163 /* call the next step */
3164 switch (ac->req->operation) {
3165 case LDB_ADD:
3166 ret = password_hash_add_do_add(ac);
3167 break;
3169 case LDB_MODIFY:
3170 ret = password_hash_mod_do_mod(ac);
3171 break;
3173 default:
3174 ret = LDB_ERR_OPERATIONS_ERROR;
3175 break;
3177 break;
3180 done:
3181 if (ret != LDB_SUCCESS) {
3182 struct ldb_reply *new_ares;
3184 new_ares = talloc_zero(ac->req, struct ldb_reply);
3185 if (new_ares == NULL) {
3186 ldb_oom(ldb);
3187 return ldb_module_done(ac->req, NULL, NULL,
3188 LDB_ERR_OPERATIONS_ERROR);
3191 new_ares->error = ret;
3192 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
3193 /* On success and trivial errors a status control is being
3194 * added (used for example by the "samdb_set_password" call) */
3195 ldb_reply_add_control(new_ares,
3196 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
3197 false,
3198 ac->status);
3201 return ldb_module_done(ac->req, new_ares->controls,
3202 new_ares->response, new_ares->error);
3205 return LDB_SUCCESS;
3208 static int build_domain_data_request(struct ph_context *ac)
3210 /* attrs[] is returned from this function in
3211 ac->dom_req->op.search.attrs, so it must be static, as
3212 otherwise the compiler can put it on the stack */
3213 struct ldb_context *ldb;
3214 static const char * const attrs[] = { "pwdProperties",
3215 "pwdHistoryLength",
3216 "maxPwdAge",
3217 "minPwdAge",
3218 "minPwdLength",
3219 "lockoutThreshold",
3220 "lockOutObservationWindow",
3221 NULL };
3222 int ret;
3224 ldb = ldb_module_get_ctx(ac->module);
3226 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
3227 ldb_get_default_basedn(ldb),
3228 LDB_SCOPE_BASE,
3229 NULL, attrs,
3230 NULL,
3231 ac, get_domain_data_callback,
3232 ac->req);
3233 LDB_REQ_SET_LOCATION(ac->dom_req);
3234 return ret;
3237 static int password_hash_needed(struct ldb_module *module,
3238 struct ldb_request *req,
3239 struct ph_context **_ac)
3241 struct ldb_context *ldb = ldb_module_get_ctx(module);
3242 const char *operation = NULL;
3243 const struct ldb_message *msg = NULL;
3244 struct ph_context *ac = NULL;
3245 const char *passwordAttrs[] = {
3246 "userPassword",
3247 "clearTextPassword",
3248 "unicodePwd",
3249 "dBCSPwd",
3250 NULL
3252 const char **a = NULL;
3253 unsigned int attr_cnt = 0;
3254 struct ldb_control *bypass = NULL;
3255 bool userPassword = dsdb_user_password_support(module, req, req);
3256 bool update_password = false;
3257 bool processing_needed = false;
3259 *_ac = NULL;
3261 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_needed\n");
3263 switch (req->operation) {
3264 case LDB_ADD:
3265 operation = "add";
3266 msg = req->op.add.message;
3267 break;
3268 case LDB_MODIFY:
3269 operation = "modify";
3270 msg = req->op.mod.message;
3271 break;
3272 default:
3273 return ldb_next_request(module, req);
3276 if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
3277 return ldb_next_request(module, req);
3280 bypass = ldb_request_get_control(req,
3281 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
3282 if (bypass != NULL) {
3283 /* Mark the "bypass" control as uncritical (done) */
3284 bypass->critical = false;
3285 ldb_debug(ldb, LDB_DEBUG_TRACE,
3286 "password_hash_needed(%s) (bypassing)\n",
3287 operation);
3288 return password_hash_bypass(module, req);
3291 /* nobody must touch password histories and 'supplementalCredentials' */
3292 if (ldb_msg_find_element(msg, "ntPwdHistory")) {
3293 return LDB_ERR_UNWILLING_TO_PERFORM;
3295 if (ldb_msg_find_element(msg, "lmPwdHistory")) {
3296 return LDB_ERR_UNWILLING_TO_PERFORM;
3298 if (ldb_msg_find_element(msg, "supplementalCredentials")) {
3299 return LDB_ERR_UNWILLING_TO_PERFORM;
3303 * If no part of this touches the 'userPassword' OR 'clearTextPassword'
3304 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
3305 * For password changes/set there should be a 'delete' or a 'modify'
3306 * on these attributes.
3308 for (a = passwordAttrs; *a != NULL; a++) {
3309 if ((!userPassword) && (ldb_attr_cmp(*a, "userPassword") == 0)) {
3310 continue;
3313 if (ldb_msg_find_element(msg, *a) != NULL) {
3314 /* MS-ADTS 3.1.1.3.1.5.2 */
3315 if ((ldb_attr_cmp(*a, "userPassword") == 0) &&
3316 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
3317 return LDB_ERR_CONSTRAINT_VIOLATION;
3320 ++attr_cnt;
3324 if (attr_cnt > 0) {
3325 update_password = true;
3326 processing_needed = true;
3329 if (ldb_msg_find_element(msg, "pwdLastSet")) {
3330 processing_needed = true;
3333 if (!processing_needed) {
3334 return ldb_next_request(module, req);
3337 ac = ph_init_context(module, req, userPassword, update_password);
3338 if (!ac) {
3339 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3340 return ldb_operr(ldb);
3342 ph_apply_controls(ac);
3345 * Make a copy in order to apply our modifications
3346 * to the final update
3348 ac->update_msg = ldb_msg_copy_shallow(ac, msg);
3349 if (ac->update_msg == NULL) {
3350 return ldb_oom(ldb);
3354 * Remove all password related attributes.
3356 if (ac->userPassword) {
3357 ldb_msg_remove_attr(ac->update_msg, "userPassword");
3359 ldb_msg_remove_attr(ac->update_msg, "clearTextPassword");
3360 ldb_msg_remove_attr(ac->update_msg, "unicodePwd");
3361 ldb_msg_remove_attr(ac->update_msg, "ntPwdHistory");
3362 ldb_msg_remove_attr(ac->update_msg, "dBCSPwd");
3363 ldb_msg_remove_attr(ac->update_msg, "lmPwdHistory");
3364 ldb_msg_remove_attr(ac->update_msg, "supplementalCredentials");
3365 ldb_msg_remove_attr(ac->update_msg, "pwdLastSet");
3367 *_ac = ac;
3368 return LDB_SUCCESS;
3371 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
3373 struct ldb_context *ldb = ldb_module_get_ctx(module);
3374 struct ph_context *ac = NULL;
3375 int ret;
3377 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
3379 ret = password_hash_needed(module, req, &ac);
3380 if (ret != LDB_SUCCESS) {
3381 return ret;
3383 if (ac == NULL) {
3384 return ret;
3387 /* Make sure we are performing the password set action on a (for us)
3388 * valid object. Those are instances of either "user" and/or
3389 * "inetOrgPerson". Otherwise continue with the submodules. */
3390 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
3391 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
3393 TALLOC_FREE(ac);
3395 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
3396 ldb_set_errstring(ldb,
3397 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3398 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3401 return ldb_next_request(module, req);
3404 /* get user domain data */
3405 ret = build_domain_data_request(ac);
3406 if (ret != LDB_SUCCESS) {
3407 return ret;
3410 return ldb_next_request(module, ac->dom_req);
3413 static int password_hash_add_do_add(struct ph_context *ac)
3415 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3416 struct ldb_request *down_req;
3417 struct setup_password_fields_io io;
3418 int ret;
3420 /* Prepare the internal data structure containing the passwords */
3421 ret = setup_io(ac, ac->req->op.add.message, NULL, &io);
3422 if (ret != LDB_SUCCESS) {
3423 return ret;
3426 ret = setup_password_fields(&io);
3427 if (ret != LDB_SUCCESS) {
3428 return ret;
3431 ret = check_password_restrictions(&io);
3432 if (ret != LDB_SUCCESS) {
3433 return ret;
3436 ret = update_final_msg(&io);
3437 if (ret != LDB_SUCCESS) {
3438 return ret;
3441 ret = ldb_build_add_req(&down_req, ldb, ac,
3442 ac->update_msg,
3443 ac->req->controls,
3444 ac, ph_op_callback,
3445 ac->req);
3446 LDB_REQ_SET_LOCATION(down_req);
3447 if (ret != LDB_SUCCESS) {
3448 return ret;
3451 return ldb_next_request(ac->module, down_req);
3454 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
3456 struct ldb_context *ldb = ldb_module_get_ctx(module);
3457 struct ph_context *ac = NULL;
3458 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
3459 "unicodePwd", "dBCSPwd", NULL }, **l;
3460 unsigned int del_attr_cnt, add_attr_cnt, rep_attr_cnt;
3461 struct ldb_message_element *passwordAttr;
3462 struct ldb_message *msg;
3463 struct ldb_request *down_req;
3464 struct ldb_control *restore = NULL;
3465 int ret;
3466 unsigned int i = 0;
3468 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
3470 ret = password_hash_needed(module, req, &ac);
3471 if (ret != LDB_SUCCESS) {
3472 return ret;
3474 if (ac == NULL) {
3475 return ret;
3478 /* use a new message structure so that we can modify it */
3479 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3480 if (msg == NULL) {
3481 return ldb_oom(ldb);
3484 /* - check for single-valued password attributes
3485 * (if not return "CONSTRAINT_VIOLATION")
3486 * - check that for a password change operation one add and one delete
3487 * operation exists
3488 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3489 * - check that a password change and a password set operation cannot
3490 * be mixed
3491 * (if not return "UNWILLING_TO_PERFORM")
3492 * - remove all password attributes modifications from the first change
3493 * operation (anything without the passwords) - we will make the real
3494 * modification later */
3495 del_attr_cnt = 0;
3496 add_attr_cnt = 0;
3497 rep_attr_cnt = 0;
3498 for (l = passwordAttrs; *l != NULL; l++) {
3499 if ((!ac->userPassword) &&
3500 (ldb_attr_cmp(*l, "userPassword") == 0)) {
3501 continue;
3504 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
3505 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
3506 ++del_attr_cnt;
3508 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
3509 ++add_attr_cnt;
3511 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
3512 ++rep_attr_cnt;
3514 if ((passwordAttr->num_values != 1) &&
3515 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
3516 talloc_free(ac);
3517 ldb_asprintf_errstring(ldb,
3518 "'%s' attribute must have exactly one value on add operations!",
3519 *l);
3520 return LDB_ERR_CONSTRAINT_VIOLATION;
3522 if ((passwordAttr->num_values > 1) &&
3523 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
3524 talloc_free(ac);
3525 ldb_asprintf_errstring(ldb,
3526 "'%s' attribute must have zero or one value(s) on delete operations!",
3527 *l);
3528 return LDB_ERR_CONSTRAINT_VIOLATION;
3530 ldb_msg_remove_element(msg, passwordAttr);
3533 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
3534 talloc_free(ac);
3535 ldb_set_errstring(ldb,
3536 "Only the add action for a password change specified!");
3537 return LDB_ERR_UNWILLING_TO_PERFORM;
3539 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
3540 talloc_free(ac);
3541 ldb_set_errstring(ldb,
3542 "Only one delete and one add action for a password change allowed!");
3543 return LDB_ERR_UNWILLING_TO_PERFORM;
3545 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
3546 talloc_free(ac);
3547 ldb_set_errstring(ldb,
3548 "Either a password change or a password set operation is allowed!");
3549 return LDB_ERR_UNWILLING_TO_PERFORM;
3552 restore = ldb_request_get_control(req,
3553 DSDB_CONTROL_RESTORE_TOMBSTONE_OID);
3554 if (restore == NULL) {
3556 * A tomstone reanimation generates a double update
3557 * of pwdLastSet.
3559 * So we only remove it without the
3560 * DSDB_CONTROL_RESTORE_TOMBSTONE_OID control.
3562 ldb_msg_remove_attr(msg, "pwdLastSet");
3566 /* if there was nothing else to be modified skip to next step */
3567 if (msg->num_elements == 0) {
3568 return password_hash_mod_search_self(ac);
3572 * Now we apply all changes remaining in msg
3573 * and remove them from our final update_msg
3576 for (i = 0; i < msg->num_elements; i++) {
3577 ldb_msg_remove_attr(ac->update_msg,
3578 msg->elements[i].name);
3581 ret = ldb_build_mod_req(&down_req, ldb, ac,
3582 msg,
3583 req->controls,
3584 ac, ph_modify_callback,
3585 req);
3586 LDB_REQ_SET_LOCATION(down_req);
3587 if (ret != LDB_SUCCESS) {
3588 return ret;
3591 return ldb_next_request(module, down_req);
3594 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3596 struct ph_context *ac;
3598 ac = talloc_get_type(req->context, struct ph_context);
3600 if (!ares) {
3601 return ldb_module_done(ac->req, NULL, NULL,
3602 LDB_ERR_OPERATIONS_ERROR);
3605 if (ares->type == LDB_REPLY_REFERRAL) {
3606 return ldb_module_send_referral(ac->req, ares->referral);
3609 if (ares->error != LDB_SUCCESS) {
3610 return ldb_module_done(ac->req, ares->controls,
3611 ares->response, ares->error);
3614 if (ares->type != LDB_REPLY_DONE) {
3615 talloc_free(ares);
3616 return ldb_module_done(ac->req, NULL, NULL,
3617 LDB_ERR_OPERATIONS_ERROR);
3620 talloc_free(ares);
3622 return password_hash_mod_search_self(ac);
3625 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
3627 struct ldb_context *ldb;
3628 struct ph_context *ac;
3629 int ret = LDB_SUCCESS;
3631 ac = talloc_get_type(req->context, struct ph_context);
3632 ldb = ldb_module_get_ctx(ac->module);
3634 if (!ares) {
3635 ret = LDB_ERR_OPERATIONS_ERROR;
3636 goto done;
3638 if (ares->error != LDB_SUCCESS) {
3639 return ldb_module_done(ac->req, ares->controls,
3640 ares->response, ares->error);
3643 /* we are interested only in the single reply (base search) */
3644 switch (ares->type) {
3645 case LDB_REPLY_ENTRY:
3646 /* Make sure we are performing the password change action on a
3647 * (for us) valid object. Those are instances of either "user"
3648 * and/or "inetOrgPerson". Otherwise continue with the
3649 * submodules. */
3650 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
3651 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
3652 talloc_free(ares);
3654 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
3655 ldb_set_errstring(ldb,
3656 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3657 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
3658 goto done;
3661 ret = ldb_next_request(ac->module, ac->req);
3662 goto done;
3665 if (ac->search_res != NULL) {
3666 talloc_free(ares);
3668 ldb_set_errstring(ldb, "Too many results");
3669 ret = LDB_ERR_OPERATIONS_ERROR;
3670 goto done;
3673 ac->search_res = talloc_steal(ac, ares);
3674 ret = LDB_SUCCESS;
3675 break;
3677 case LDB_REPLY_REFERRAL:
3678 /* ignore anything else for now */
3679 talloc_free(ares);
3680 ret = LDB_SUCCESS;
3681 break;
3683 case LDB_REPLY_DONE:
3684 talloc_free(ares);
3686 /* get user domain data */
3687 ret = build_domain_data_request(ac);
3688 if (ret != LDB_SUCCESS) {
3689 return ldb_module_done(ac->req, NULL, NULL, ret);
3692 ret = ldb_next_request(ac->module, ac->dom_req);
3693 break;
3696 done:
3697 if (ret != LDB_SUCCESS) {
3698 return ldb_module_done(ac->req, NULL, NULL, ret);
3701 return LDB_SUCCESS;
3704 static int password_hash_mod_search_self(struct ph_context *ac)
3706 struct ldb_context *ldb;
3707 static const char * const attrs[] = { "objectClass",
3708 "userAccountControl",
3709 "msDS-User-Account-Control-Computed",
3710 "pwdLastSet",
3711 "sAMAccountName",
3712 "objectSid",
3713 "userPrincipalName",
3714 "supplementalCredentials",
3715 "lmPwdHistory",
3716 "ntPwdHistory",
3717 "dBCSPwd",
3718 "unicodePwd",
3719 "badPasswordTime",
3720 "badPwdCount",
3721 "lockoutTime",
3722 NULL };
3723 struct ldb_request *search_req;
3724 int ret;
3726 ldb = ldb_module_get_ctx(ac->module);
3728 ret = ldb_build_search_req(&search_req, ldb, ac,
3729 ac->req->op.mod.message->dn,
3730 LDB_SCOPE_BASE,
3731 "(objectclass=*)",
3732 attrs,
3733 NULL,
3734 ac, ph_mod_search_callback,
3735 ac->req);
3736 LDB_REQ_SET_LOCATION(search_req);
3737 if (ret != LDB_SUCCESS) {
3738 return ret;
3741 return ldb_next_request(ac->module, search_req);
3744 static int password_hash_mod_do_mod(struct ph_context *ac)
3746 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3747 struct ldb_request *mod_req;
3748 struct setup_password_fields_io io;
3749 int ret;
3751 /* Prepare the internal data structure containing the passwords */
3752 ret = setup_io(ac, ac->req->op.mod.message,
3753 ac->search_res->message, &io);
3754 if (ret != LDB_SUCCESS) {
3755 return ret;
3758 ret = setup_password_fields(&io);
3759 if (ret != LDB_SUCCESS) {
3760 return ret;
3763 ret = check_password_restrictions(&io);
3764 if (ret != LDB_SUCCESS) {
3765 return ret;
3768 ret = update_final_msg(&io);
3769 if (ret != LDB_SUCCESS) {
3770 return ret;
3773 ret = ldb_build_mod_req(&mod_req, ldb, ac,
3774 ac->update_msg,
3775 ac->req->controls,
3776 ac, ph_op_callback,
3777 ac->req);
3778 LDB_REQ_SET_LOCATION(mod_req);
3779 if (ret != LDB_SUCCESS) {
3780 return ret;
3783 return ldb_next_request(ac->module, mod_req);
3786 static const struct ldb_module_ops ldb_password_hash_module_ops = {
3787 .name = "password_hash",
3788 .add = password_hash_add,
3789 .modify = password_hash_modify
3792 int ldb_password_hash_module_init(const char *version)
3794 LDB_MODULE_CHECK_VERSION(version);
3795 return ldb_register_module(&ldb_password_hash_module_ops);