s4:dsdb/password_hash: reject interdomain trust password changes via LDAP
[Samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blob9ac3551ea3f56150d6a1ec5fe2bf5ebecd20094d
1 /*
2 ldb database module
4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Stefan Metzmacher 2007-2010
8 Copyright (C) Matthias Dieter Wallnöfer 2009-2010
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * Name: ldb
27 * Component: ldb password_hash module
29 * Description: correctly handle AD password changes fields
31 * Author: Andrew Bartlett
32 * Author: Stefan Metzmacher
35 #include "includes.h"
36 #include "ldb_module.h"
37 #include "libcli/auth/libcli_auth.h"
38 #include "system/kerberos.h"
39 #include "auth/kerberos/kerberos.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/samdb/ldb_modules/util.h"
42 #include "dsdb/samdb/ldb_modules/password_modules.h"
43 #include "librpc/gen_ndr/ndr_drsblobs.h"
44 #include "../lib/crypto/crypto.h"
45 #include "param/param.h"
46 #include "lib/krb5_wrap/krb5_samba.h"
48 /* If we have decided there is a reason to work on this request, then
49 * setup all the password hash types correctly.
51 * If we haven't the hashes yet but the password given as plain-text (attributes
52 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
53 * the constraints. Once this is done, we calculate the password hashes.
55 * Notice: unlike the real AD which only supports the UTF16 special based
56 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
57 * understand also a UTF16 based 'clearTextPassword' one.
58 * The latter is also accessible through LDAP so it can also be set by external
59 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
61 * Also when the module receives only the password hashes (possible through
62 * specifying an internal LDB control - for security reasons) some checks are
63 * performed depending on the operation mode (see below) (e.g. if the password
64 * has been in use before if the password memory policy was activated).
66 * Attention: There is a difference between "modify" and "reset" operations
67 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
68 * operation for a password attribute we thread this as a "modify"; if it sends
69 * only a "replace" one we have an (administrative) reset.
71 * Finally, if the administrator has requested that a password history
72 * be maintained, then this should also be written out.
76 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
77 * - Check for right connection encryption
80 /* Notice: Definition of "dsdb_control_password_change_status" moved into
81 * "samdb.h" */
83 struct ph_context {
84 struct ldb_module *module;
85 struct ldb_request *req;
87 struct ldb_request *dom_req;
88 struct ldb_reply *dom_res;
90 struct ldb_reply *search_res;
92 struct dsdb_control_password_change_status *status;
93 struct dsdb_control_password_change *change;
95 bool pwd_reset;
96 bool change_status;
97 bool hash_values;
98 bool userPassword;
99 bool pwd_last_set_bypass;
103 struct setup_password_fields_io {
104 struct ph_context *ac;
106 struct smb_krb5_context *smb_krb5_context;
108 /* infos about the user account */
109 struct {
110 uint32_t userAccountControl;
111 NTTIME pwdLastSet;
112 const char *sAMAccountName;
113 const char *user_principal_name;
114 bool is_computer;
115 uint32_t restrictions;
116 } u;
118 /* new credentials and old given credentials */
119 struct setup_password_fields_given {
120 const struct ldb_val *cleartext_utf8;
121 const struct ldb_val *cleartext_utf16;
122 struct samr_Password *nt_hash;
123 struct samr_Password *lm_hash;
124 } n, og;
126 /* old credentials */
127 struct {
128 struct samr_Password *nt_hash;
129 struct samr_Password *lm_hash;
130 uint32_t nt_history_len;
131 struct samr_Password *nt_history;
132 uint32_t lm_history_len;
133 struct samr_Password *lm_history;
134 const struct ldb_val *supplemental;
135 struct supplementalCredentialsBlob scb;
136 } o;
138 /* generated credentials */
139 struct {
140 struct samr_Password *nt_hash;
141 struct samr_Password *lm_hash;
142 uint32_t nt_history_len;
143 struct samr_Password *nt_history;
144 uint32_t lm_history_len;
145 struct samr_Password *lm_history;
146 const char *salt;
147 DATA_BLOB aes_256;
148 DATA_BLOB aes_128;
149 DATA_BLOB des_md5;
150 DATA_BLOB des_crc;
151 struct ldb_val supplemental;
152 NTTIME last_set;
153 } g;
156 static int password_hash_bypass(struct ldb_module *module, struct ldb_request *request)
158 struct ldb_context *ldb = ldb_module_get_ctx(module);
159 const struct ldb_message *msg;
160 struct ldb_message_element *nte;
161 struct ldb_message_element *lme;
162 struct ldb_message_element *nthe;
163 struct ldb_message_element *lmhe;
164 struct ldb_message_element *sce;
166 switch (request->operation) {
167 case LDB_ADD:
168 msg = request->op.add.message;
169 break;
170 case LDB_MODIFY:
171 msg = request->op.mod.message;
172 break;
173 default:
174 return ldb_next_request(module, request);
177 /* nobody must touch password histories and 'supplementalCredentials' */
178 nte = dsdb_get_single_valued_attr(msg, "unicodePwd",
179 request->operation);
180 lme = dsdb_get_single_valued_attr(msg, "dBCSPwd",
181 request->operation);
182 nthe = dsdb_get_single_valued_attr(msg, "ntPwdHistory",
183 request->operation);
184 lmhe = dsdb_get_single_valued_attr(msg, "lmPwdHistory",
185 request->operation);
186 sce = dsdb_get_single_valued_attr(msg, "supplementalCredentials",
187 request->operation);
189 #define CHECK_HASH_ELEMENT(e, min, max) do {\
190 if (e && e->num_values) { \
191 unsigned int _count; \
192 if (e->num_values != 1) { \
193 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
194 "num_values != 1"); \
196 if ((e->values[0].length % 16) != 0) { \
197 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
198 "length % 16 != 0"); \
200 _count = e->values[0].length / 16; \
201 if (_count < min) { \
202 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
203 "count < min"); \
205 if (_count > max) { \
206 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
207 "count > max"); \
210 } while (0)
212 CHECK_HASH_ELEMENT(nte, 1, 1);
213 CHECK_HASH_ELEMENT(lme, 1, 1);
214 CHECK_HASH_ELEMENT(nthe, 1, INT32_MAX);
215 CHECK_HASH_ELEMENT(lmhe, 1, INT32_MAX);
217 if (sce && sce->num_values) {
218 enum ndr_err_code ndr_err;
219 struct supplementalCredentialsBlob *scb;
220 struct supplementalCredentialsPackage *scpp = NULL;
221 struct supplementalCredentialsPackage *scpk = NULL;
222 struct supplementalCredentialsPackage *scpkn = NULL;
223 struct supplementalCredentialsPackage *scpct = NULL;
224 DATA_BLOB scpbp = data_blob_null;
225 DATA_BLOB scpbk = data_blob_null;
226 DATA_BLOB scpbkn = data_blob_null;
227 DATA_BLOB scpbct = data_blob_null;
228 DATA_BLOB blob;
229 uint32_t i;
231 if (sce->num_values != 1) {
232 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
233 "num_values != 1");
236 scb = talloc_zero(request, struct supplementalCredentialsBlob);
237 if (!scb) {
238 return ldb_module_oom(module);
241 ndr_err = ndr_pull_struct_blob_all(&sce->values[0], scb, scb,
242 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
243 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
244 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
245 "ndr_pull_struct_blob_all");
248 if (scb->sub.num_packages < 2) {
249 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
250 "num_packages < 2");
253 for (i=0; i < scb->sub.num_packages; i++) {
254 DATA_BLOB subblob;
256 subblob = strhex_to_data_blob(scb, scb->sub.packages[i].data);
257 if (subblob.data == NULL) {
258 return ldb_module_oom(module);
261 if (strcmp(scb->sub.packages[i].name, "Packages") == 0) {
262 if (scpp) {
263 return ldb_error(ldb,
264 LDB_ERR_CONSTRAINT_VIOLATION,
265 "Packages twice");
267 scpp = &scb->sub.packages[i];
268 scpbp = subblob;
269 continue;
271 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos") == 0) {
272 if (scpk) {
273 return ldb_error(ldb,
274 LDB_ERR_CONSTRAINT_VIOLATION,
275 "Primary:Kerberos twice");
277 scpk = &scb->sub.packages[i];
278 scpbk = subblob;
279 continue;
281 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos-Newer-Keys") == 0) {
282 if (scpkn) {
283 return ldb_error(ldb,
284 LDB_ERR_CONSTRAINT_VIOLATION,
285 "Primary:Kerberos-Newer-Keys twice");
287 scpkn = &scb->sub.packages[i];
288 scpbkn = subblob;
289 continue;
291 if (strcmp(scb->sub.packages[i].name, "Primary:CLEARTEXT") == 0) {
292 if (scpct) {
293 return ldb_error(ldb,
294 LDB_ERR_CONSTRAINT_VIOLATION,
295 "Primary:CLEARTEXT twice");
297 scpct = &scb->sub.packages[i];
298 scpbct = subblob;
299 continue;
302 data_blob_free(&subblob);
305 if (scpp == NULL) {
306 return ldb_error(ldb,
307 LDB_ERR_CONSTRAINT_VIOLATION,
308 "Primary:Packages missing");
311 if (scpk == NULL) {
313 * If Primary:Kerberos is missing w2k8r2 reboots
314 * when a password is changed.
316 return ldb_error(ldb,
317 LDB_ERR_CONSTRAINT_VIOLATION,
318 "Primary:Kerberos missing");
321 if (scpp) {
322 struct package_PackagesBlob *p;
323 uint32_t n;
325 p = talloc_zero(scb, struct package_PackagesBlob);
326 if (p == NULL) {
327 return ldb_module_oom(module);
330 ndr_err = ndr_pull_struct_blob(&scpbp, p, p,
331 (ndr_pull_flags_fn_t)ndr_pull_package_PackagesBlob);
332 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
333 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
334 "ndr_pull_struct_blob Packages");
337 if (p->names == NULL) {
338 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
339 "Packages names == NULL");
342 for (n = 0; p->names[n]; n++) {
343 /* noop */
346 if (scb->sub.num_packages != (n + 1)) {
347 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
348 "Packages num_packages != num_names + 1");
351 talloc_free(p);
354 if (scpk) {
355 struct package_PrimaryKerberosBlob *k;
357 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
358 if (k == NULL) {
359 return ldb_module_oom(module);
362 ndr_err = ndr_pull_struct_blob(&scpbk, k, k,
363 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
364 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
365 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
366 "ndr_pull_struct_blob PrimaryKerberos");
369 if (k->version != 3) {
370 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
371 "PrimaryKerberos version != 3");
374 if (k->ctr.ctr3.salt.string == NULL) {
375 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
376 "PrimaryKerberos salt == NULL");
379 if (strlen(k->ctr.ctr3.salt.string) == 0) {
380 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
381 "PrimaryKerberos strlen(salt) == 0");
384 if (k->ctr.ctr3.num_keys != 2) {
385 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
386 "PrimaryKerberos num_keys != 2");
389 if (k->ctr.ctr3.num_old_keys > k->ctr.ctr3.num_keys) {
390 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
391 "PrimaryKerberos num_old_keys > num_keys");
394 if (k->ctr.ctr3.keys[0].keytype != ENCTYPE_DES_CBC_MD5) {
395 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
396 "PrimaryKerberos key[0] != DES_CBC_MD5");
398 if (k->ctr.ctr3.keys[1].keytype != ENCTYPE_DES_CBC_CRC) {
399 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
400 "PrimaryKerberos key[1] != DES_CBC_CRC");
403 if (k->ctr.ctr3.keys[0].value_len != 8) {
404 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
405 "PrimaryKerberos key[0] value_len != 8");
407 if (k->ctr.ctr3.keys[1].value_len != 8) {
408 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
409 "PrimaryKerberos key[1] value_len != 8");
412 for (i = 0; i < k->ctr.ctr3.num_old_keys; i++) {
413 if (k->ctr.ctr3.old_keys[i].keytype ==
414 k->ctr.ctr3.keys[i].keytype &&
415 k->ctr.ctr3.old_keys[i].value_len ==
416 k->ctr.ctr3.keys[i].value_len) {
417 continue;
420 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
421 "PrimaryKerberos old_keys type/value_len doesn't match");
424 talloc_free(k);
427 if (scpkn) {
428 struct package_PrimaryKerberosBlob *k;
430 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
431 if (k == NULL) {
432 return ldb_module_oom(module);
435 ndr_err = ndr_pull_struct_blob(&scpbkn, k, k,
436 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
437 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
438 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
439 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
442 if (k->version != 4) {
443 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
444 "KerberosNerverKeys version != 4");
447 if (k->ctr.ctr4.salt.string == NULL) {
448 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
449 "KerberosNewerKeys salt == NULL");
452 if (strlen(k->ctr.ctr4.salt.string) == 0) {
453 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
454 "KerberosNewerKeys strlen(salt) == 0");
457 if (k->ctr.ctr4.num_keys != 4) {
458 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
459 "KerberosNewerKeys num_keys != 2");
462 if (k->ctr.ctr4.num_old_keys > k->ctr.ctr4.num_keys) {
463 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
464 "KerberosNewerKeys num_old_keys > num_keys");
467 if (k->ctr.ctr4.num_older_keys > k->ctr.ctr4.num_old_keys) {
468 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
469 "KerberosNewerKeys num_older_keys > num_old_keys");
472 if (k->ctr.ctr4.keys[0].keytype != ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
473 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
474 "KerberosNewerKeys key[0] != AES256");
476 if (k->ctr.ctr4.keys[1].keytype != ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
477 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
478 "KerberosNewerKeys key[1] != AES128");
480 if (k->ctr.ctr4.keys[2].keytype != ENCTYPE_DES_CBC_MD5) {
481 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
482 "KerberosNewerKeys key[2] != DES_CBC_MD5");
484 if (k->ctr.ctr4.keys[3].keytype != ENCTYPE_DES_CBC_CRC) {
485 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
486 "KerberosNewerKeys key[3] != DES_CBC_CRC");
489 if (k->ctr.ctr4.keys[0].value_len != 32) {
490 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
491 "KerberosNewerKeys key[0] value_len != 32");
493 if (k->ctr.ctr4.keys[1].value_len != 16) {
494 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
495 "KerberosNewerKeys key[1] value_len != 16");
497 if (k->ctr.ctr4.keys[2].value_len != 8) {
498 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
499 "KerberosNewerKeys key[2] value_len != 8");
501 if (k->ctr.ctr4.keys[3].value_len != 8) {
502 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
503 "KerberosNewerKeys key[3] value_len != 8");
507 * TODO:
508 * Maybe we can check old and older keys here.
509 * But we need to do some tests, if the old keys
510 * can be taken from the PrimaryKerberos blob
511 * (with only des keys), when the domain was upgraded
512 * from w2k3 to w2k8.
515 talloc_free(k);
518 if (scpct) {
519 struct package_PrimaryCLEARTEXTBlob *ct;
521 ct = talloc_zero(scb, struct package_PrimaryCLEARTEXTBlob);
522 if (ct == NULL) {
523 return ldb_module_oom(module);
526 ndr_err = ndr_pull_struct_blob(&scpbct, ct, ct,
527 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryCLEARTEXTBlob);
528 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
529 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
530 "ndr_pull_struct_blob PrimaryCLEARTEXT");
533 if ((ct->cleartext.length % 2) != 0) {
534 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
535 "PrimaryCLEARTEXT length % 2 != 0");
538 talloc_free(ct);
541 ndr_err = ndr_push_struct_blob(&blob, scb, scb,
542 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
543 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
544 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
545 "ndr_pull_struct_blob_all");
548 if (sce->values[0].length != blob.length) {
549 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
550 "supplementalCredentialsBlob length differ");
553 if (memcmp(sce->values[0].data, blob.data, blob.length) != 0) {
554 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
555 "supplementalCredentialsBlob memcmp differ");
558 talloc_free(scb);
561 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_bypass - validated\n");
562 return ldb_next_request(module, request);
565 /* Get the NT hash, and fill it in as an entry in the password history,
566 and specify it into io->g.nt_hash */
568 static int setup_nt_fields(struct setup_password_fields_io *io)
570 struct ldb_context *ldb;
571 uint32_t i;
573 io->g.nt_hash = io->n.nt_hash;
574 ldb = ldb_module_get_ctx(io->ac->module);
576 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
577 return LDB_SUCCESS;
580 /* We might not have an old NT password */
581 io->g.nt_history = talloc_array(io->ac,
582 struct samr_Password,
583 io->ac->status->domain_data.pwdHistoryLength);
584 if (!io->g.nt_history) {
585 return ldb_oom(ldb);
588 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
589 io->o.nt_history_len); i++) {
590 io->g.nt_history[i+1] = io->o.nt_history[i];
592 io->g.nt_history_len = i + 1;
594 if (io->g.nt_hash) {
595 io->g.nt_history[0] = *io->g.nt_hash;
596 } else {
598 * TODO: is this correct?
599 * the simular behavior is correct for the lm history case
601 E_md4hash("", io->g.nt_history[0].hash);
604 return LDB_SUCCESS;
607 /* Get the LANMAN hash, and fill it in as an entry in the password history,
608 and specify it into io->g.lm_hash */
610 static int setup_lm_fields(struct setup_password_fields_io *io)
612 struct ldb_context *ldb;
613 uint32_t i;
615 io->g.lm_hash = io->n.lm_hash;
616 ldb = ldb_module_get_ctx(io->ac->module);
618 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
619 return LDB_SUCCESS;
622 /* We might not have an old LM password */
623 io->g.lm_history = talloc_array(io->ac,
624 struct samr_Password,
625 io->ac->status->domain_data.pwdHistoryLength);
626 if (!io->g.lm_history) {
627 return ldb_oom(ldb);
630 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
631 io->o.lm_history_len); i++) {
632 io->g.lm_history[i+1] = io->o.lm_history[i];
634 io->g.lm_history_len = i + 1;
636 if (io->g.lm_hash) {
637 io->g.lm_history[0] = *io->g.lm_hash;
638 } else {
639 E_deshash("", io->g.lm_history[0].hash);
642 return LDB_SUCCESS;
645 static int setup_kerberos_keys(struct setup_password_fields_io *io)
647 struct ldb_context *ldb;
648 krb5_error_code krb5_ret;
649 krb5_principal salt_principal;
650 krb5_data salt;
651 krb5_keyblock key;
652 krb5_data cleartext_data;
654 ldb = ldb_module_get_ctx(io->ac->module);
655 cleartext_data.data = io->n.cleartext_utf8->data;
656 cleartext_data.length = io->n.cleartext_utf8->length;
658 /* Many, many thanks to lukeh@padl.com for this
659 * algorithm, described in his Nov 10 2004 mail to
660 * samba-technical@lists.samba.org */
663 * Determine a salting principal
665 if (io->u.is_computer) {
666 char *name;
667 char *saltbody;
669 name = strlower_talloc(io->ac, io->u.sAMAccountName);
670 if (!name) {
671 return ldb_oom(ldb);
674 if (name[strlen(name)-1] == '$') {
675 name[strlen(name)-1] = '\0';
678 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
679 io->ac->status->domain_data.dns_domain);
680 if (!saltbody) {
681 return ldb_oom(ldb);
684 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
685 &salt_principal,
686 io->ac->status->domain_data.realm,
687 "host", saltbody, NULL);
688 } else if (io->u.user_principal_name) {
689 char *user_principal_name;
690 char *p;
692 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
693 if (!user_principal_name) {
694 return ldb_oom(ldb);
697 p = strchr(user_principal_name, '@');
698 if (p) {
699 p[0] = '\0';
702 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
703 &salt_principal,
704 io->ac->status->domain_data.realm,
705 user_principal_name, NULL);
706 } else {
707 krb5_ret = smb_krb5_make_principal(io->smb_krb5_context->krb5_context,
708 &salt_principal,
709 io->ac->status->domain_data.realm,
710 io->u.sAMAccountName, NULL);
712 if (krb5_ret) {
713 ldb_asprintf_errstring(ldb,
714 "setup_kerberos_keys: "
715 "generation of a salting principal failed: %s",
716 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
717 krb5_ret, io->ac));
718 return LDB_ERR_OPERATIONS_ERROR;
722 * create salt from salt_principal
724 krb5_ret = smb_krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
725 salt_principal, &salt);
726 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
727 if (krb5_ret) {
728 ldb_asprintf_errstring(ldb,
729 "setup_kerberos_keys: "
730 "generation of krb5_salt failed: %s",
731 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
732 krb5_ret, io->ac));
733 return LDB_ERR_OPERATIONS_ERROR;
735 /* create a talloc copy */
736 io->g.salt = talloc_strndup(io->ac,
737 (char *)salt.data,
738 salt.length);
739 kerberos_free_data_contents(io->smb_krb5_context->krb5_context, &salt);
740 if (!io->g.salt) {
741 return ldb_oom(ldb);
743 /* now use the talloced copy of the salt */
744 salt.data = discard_const(io->g.salt);
745 salt.length = strlen(io->g.salt);
748 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
749 * the salt and the cleartext password
751 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
752 NULL,
753 &salt,
754 &cleartext_data,
755 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
756 &key);
757 if (krb5_ret) {
758 ldb_asprintf_errstring(ldb,
759 "setup_kerberos_keys: "
760 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
761 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
762 krb5_ret, io->ac));
763 return LDB_ERR_OPERATIONS_ERROR;
765 io->g.aes_256 = data_blob_talloc(io->ac,
766 KRB5_KEY_DATA(&key),
767 KRB5_KEY_LENGTH(&key));
768 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
769 if (!io->g.aes_256.data) {
770 return ldb_oom(ldb);
774 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
775 * the salt and the cleartext password
777 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
778 NULL,
779 &salt,
780 &cleartext_data,
781 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
782 &key);
783 if (krb5_ret) {
784 ldb_asprintf_errstring(ldb,
785 "setup_kerberos_keys: "
786 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
787 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
788 krb5_ret, io->ac));
789 return LDB_ERR_OPERATIONS_ERROR;
791 io->g.aes_128 = data_blob_talloc(io->ac,
792 KRB5_KEY_DATA(&key),
793 KRB5_KEY_LENGTH(&key));
794 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
795 if (!io->g.aes_128.data) {
796 return ldb_oom(ldb);
800 * create ENCTYPE_DES_CBC_MD5 key out of
801 * the salt and the cleartext password
803 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
804 NULL,
805 &salt,
806 &cleartext_data,
807 ENCTYPE_DES_CBC_MD5,
808 &key);
809 if (krb5_ret) {
810 ldb_asprintf_errstring(ldb,
811 "setup_kerberos_keys: "
812 "generation of a des-cbc-md5 key failed: %s",
813 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
814 krb5_ret, io->ac));
815 return LDB_ERR_OPERATIONS_ERROR;
817 io->g.des_md5 = data_blob_talloc(io->ac,
818 KRB5_KEY_DATA(&key),
819 KRB5_KEY_LENGTH(&key));
820 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
821 if (!io->g.des_md5.data) {
822 return ldb_oom(ldb);
826 * create ENCTYPE_DES_CBC_CRC key out of
827 * the salt and the cleartext password
829 krb5_ret = smb_krb5_create_key_from_string(io->smb_krb5_context->krb5_context,
830 NULL,
831 &salt,
832 &cleartext_data,
833 ENCTYPE_DES_CBC_CRC,
834 &key);
835 if (krb5_ret) {
836 ldb_asprintf_errstring(ldb,
837 "setup_kerberos_keys: "
838 "generation of a des-cbc-crc key failed: %s",
839 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
840 krb5_ret, io->ac));
841 return LDB_ERR_OPERATIONS_ERROR;
843 io->g.des_crc = data_blob_talloc(io->ac,
844 KRB5_KEY_DATA(&key),
845 KRB5_KEY_LENGTH(&key));
846 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
847 if (!io->g.des_crc.data) {
848 return ldb_oom(ldb);
851 return LDB_SUCCESS;
854 static int setup_primary_kerberos(struct setup_password_fields_io *io,
855 const struct supplementalCredentialsBlob *old_scb,
856 struct package_PrimaryKerberosBlob *pkb)
858 struct ldb_context *ldb;
859 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
860 struct supplementalCredentialsPackage *old_scp = NULL;
861 struct package_PrimaryKerberosBlob _old_pkb;
862 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
863 uint32_t i;
864 enum ndr_err_code ndr_err;
866 ldb = ldb_module_get_ctx(io->ac->module);
869 * prepare generation of keys
871 * ENCTYPE_DES_CBC_MD5
872 * ENCTYPE_DES_CBC_CRC
874 pkb->version = 3;
875 pkb3->salt.string = io->g.salt;
876 pkb3->num_keys = 2;
877 pkb3->keys = talloc_array(io->ac,
878 struct package_PrimaryKerberosKey3,
879 pkb3->num_keys);
880 if (!pkb3->keys) {
881 return ldb_oom(ldb);
884 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
885 pkb3->keys[0].value = &io->g.des_md5;
886 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
887 pkb3->keys[1].value = &io->g.des_crc;
889 /* initialize the old keys to zero */
890 pkb3->num_old_keys = 0;
891 pkb3->old_keys = NULL;
893 /* if there're no old keys, then we're done */
894 if (!old_scb) {
895 return LDB_SUCCESS;
898 for (i=0; i < old_scb->sub.num_packages; i++) {
899 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
900 continue;
903 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
904 continue;
907 old_scp = &old_scb->sub.packages[i];
908 break;
910 /* Primary:Kerberos element of supplementalCredentials */
911 if (old_scp) {
912 DATA_BLOB blob;
914 blob = strhex_to_data_blob(io->ac, old_scp->data);
915 if (!blob.data) {
916 return ldb_oom(ldb);
919 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
920 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
921 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
922 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
923 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
924 ldb_asprintf_errstring(ldb,
925 "setup_primary_kerberos: "
926 "failed to pull old package_PrimaryKerberosBlob: %s",
927 nt_errstr(status));
928 return LDB_ERR_OPERATIONS_ERROR;
931 if (_old_pkb.version != 3) {
932 ldb_asprintf_errstring(ldb,
933 "setup_primary_kerberos: "
934 "package_PrimaryKerberosBlob version[%u] expected[3]",
935 _old_pkb.version);
936 return LDB_ERR_OPERATIONS_ERROR;
939 old_pkb3 = &_old_pkb.ctr.ctr3;
942 /* if we didn't found the old keys we're done */
943 if (!old_pkb3) {
944 return LDB_SUCCESS;
947 /* fill in the old keys */
948 pkb3->num_old_keys = old_pkb3->num_keys;
949 pkb3->old_keys = old_pkb3->keys;
951 return LDB_SUCCESS;
954 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
955 const struct supplementalCredentialsBlob *old_scb,
956 struct package_PrimaryKerberosBlob *pkb)
958 struct ldb_context *ldb;
959 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
960 struct supplementalCredentialsPackage *old_scp = NULL;
961 struct package_PrimaryKerberosBlob _old_pkb;
962 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
963 uint32_t i;
964 enum ndr_err_code ndr_err;
966 ldb = ldb_module_get_ctx(io->ac->module);
969 * prepare generation of keys
971 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
972 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
973 * ENCTYPE_DES_CBC_MD5
974 * ENCTYPE_DES_CBC_CRC
976 pkb->version = 4;
977 pkb4->salt.string = io->g.salt;
978 pkb4->default_iteration_count = 4096;
979 pkb4->num_keys = 4;
981 pkb4->keys = talloc_array(io->ac,
982 struct package_PrimaryKerberosKey4,
983 pkb4->num_keys);
984 if (!pkb4->keys) {
985 return ldb_oom(ldb);
988 pkb4->keys[0].iteration_count = 4096;
989 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
990 pkb4->keys[0].value = &io->g.aes_256;
991 pkb4->keys[1].iteration_count = 4096;
992 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
993 pkb4->keys[1].value = &io->g.aes_128;
994 pkb4->keys[2].iteration_count = 4096;
995 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
996 pkb4->keys[2].value = &io->g.des_md5;
997 pkb4->keys[3].iteration_count = 4096;
998 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
999 pkb4->keys[3].value = &io->g.des_crc;
1001 /* initialize the old keys to zero */
1002 pkb4->num_old_keys = 0;
1003 pkb4->old_keys = NULL;
1004 pkb4->num_older_keys = 0;
1005 pkb4->older_keys = NULL;
1007 /* if there're no old keys, then we're done */
1008 if (!old_scb) {
1009 return LDB_SUCCESS;
1012 for (i=0; i < old_scb->sub.num_packages; i++) {
1013 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
1014 continue;
1017 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
1018 continue;
1021 old_scp = &old_scb->sub.packages[i];
1022 break;
1024 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1025 if (old_scp) {
1026 DATA_BLOB blob;
1028 blob = strhex_to_data_blob(io->ac, old_scp->data);
1029 if (!blob.data) {
1030 return ldb_oom(ldb);
1033 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1034 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
1035 &_old_pkb,
1036 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
1037 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1038 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1039 ldb_asprintf_errstring(ldb,
1040 "setup_primary_kerberos_newer: "
1041 "failed to pull old package_PrimaryKerberosBlob: %s",
1042 nt_errstr(status));
1043 return LDB_ERR_OPERATIONS_ERROR;
1046 if (_old_pkb.version != 4) {
1047 ldb_asprintf_errstring(ldb,
1048 "setup_primary_kerberos_newer: "
1049 "package_PrimaryKerberosBlob version[%u] expected[4]",
1050 _old_pkb.version);
1051 return LDB_ERR_OPERATIONS_ERROR;
1054 old_pkb4 = &_old_pkb.ctr.ctr4;
1057 /* if we didn't found the old keys we're done */
1058 if (!old_pkb4) {
1059 return LDB_SUCCESS;
1062 /* fill in the old keys */
1063 pkb4->num_old_keys = old_pkb4->num_keys;
1064 pkb4->old_keys = old_pkb4->keys;
1065 pkb4->num_older_keys = old_pkb4->num_old_keys;
1066 pkb4->older_keys = old_pkb4->old_keys;
1068 return LDB_SUCCESS;
1071 static int setup_primary_wdigest(struct setup_password_fields_io *io,
1072 const struct supplementalCredentialsBlob *old_scb,
1073 struct package_PrimaryWDigestBlob *pdb)
1075 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1076 DATA_BLOB sAMAccountName;
1077 DATA_BLOB sAMAccountName_l;
1078 DATA_BLOB sAMAccountName_u;
1079 const char *user_principal_name = io->u.user_principal_name;
1080 DATA_BLOB userPrincipalName;
1081 DATA_BLOB userPrincipalName_l;
1082 DATA_BLOB userPrincipalName_u;
1083 DATA_BLOB netbios_domain;
1084 DATA_BLOB netbios_domain_l;
1085 DATA_BLOB netbios_domain_u;
1086 DATA_BLOB dns_domain;
1087 DATA_BLOB dns_domain_l;
1088 DATA_BLOB dns_domain_u;
1089 DATA_BLOB digest;
1090 DATA_BLOB delim;
1091 DATA_BLOB backslash;
1092 uint8_t i;
1093 struct {
1094 DATA_BLOB *user;
1095 DATA_BLOB *realm;
1096 DATA_BLOB *nt4dom;
1097 } wdigest[] = {
1099 * See
1100 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1101 * for what precalculated hashes are supposed to be stored...
1103 * I can't reproduce all values which should contain "Digest" as realm,
1104 * am I doing something wrong or is w2k3 just broken...?
1106 * W2K3 fills in following for a user:
1108 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1109 * sAMAccountName: NewUser2Sam
1110 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1112 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1113 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1114 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1115 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1116 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1117 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1118 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1119 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1120 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1121 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1122 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1123 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1124 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1125 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1126 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1127 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1128 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1129 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1130 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1131 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1132 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1133 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1134 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1135 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1136 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1137 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1138 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1139 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1140 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1142 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1143 * sAMAccountName: NewUser2Sam
1145 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1146 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1147 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1148 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1149 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1150 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1151 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1152 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1153 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1154 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1155 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1156 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1157 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1158 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1159 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1160 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1161 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1162 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1163 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1164 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1165 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1166 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1167 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1168 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1169 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1170 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1171 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1172 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1173 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1177 * sAMAccountName, netbios_domain
1180 .user = &sAMAccountName,
1181 .realm = &netbios_domain,
1184 .user = &sAMAccountName_l,
1185 .realm = &netbios_domain_l,
1188 .user = &sAMAccountName_u,
1189 .realm = &netbios_domain_u,
1192 .user = &sAMAccountName,
1193 .realm = &netbios_domain_u,
1196 .user = &sAMAccountName,
1197 .realm = &netbios_domain_l,
1200 .user = &sAMAccountName_u,
1201 .realm = &netbios_domain_l,
1204 .user = &sAMAccountName_l,
1205 .realm = &netbios_domain_u,
1208 * sAMAccountName, dns_domain
1211 .user = &sAMAccountName,
1212 .realm = &dns_domain,
1215 .user = &sAMAccountName_l,
1216 .realm = &dns_domain_l,
1219 .user = &sAMAccountName_u,
1220 .realm = &dns_domain_u,
1223 .user = &sAMAccountName,
1224 .realm = &dns_domain_u,
1227 .user = &sAMAccountName,
1228 .realm = &dns_domain_l,
1231 .user = &sAMAccountName_u,
1232 .realm = &dns_domain_l,
1235 .user = &sAMAccountName_l,
1236 .realm = &dns_domain_u,
1239 * userPrincipalName, no realm
1242 .user = &userPrincipalName,
1246 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1247 * the fallback to the sAMAccountName based userPrincipalName is correct
1249 .user = &userPrincipalName_l,
1252 .user = &userPrincipalName_u,
1255 * nt4dom\sAMAccountName, no realm
1258 .user = &sAMAccountName,
1259 .nt4dom = &netbios_domain
1262 .user = &sAMAccountName_l,
1263 .nt4dom = &netbios_domain_l
1266 .user = &sAMAccountName_u,
1267 .nt4dom = &netbios_domain_u
1271 * the following ones are guessed depending on the technet2 article
1272 * but not reproducable on a w2k3 server
1274 /* sAMAccountName with "Digest" realm */
1276 .user = &sAMAccountName,
1277 .realm = &digest
1280 .user = &sAMAccountName_l,
1281 .realm = &digest
1284 .user = &sAMAccountName_u,
1285 .realm = &digest
1287 /* userPrincipalName with "Digest" realm */
1289 .user = &userPrincipalName,
1290 .realm = &digest
1293 .user = &userPrincipalName_l,
1294 .realm = &digest
1297 .user = &userPrincipalName_u,
1298 .realm = &digest
1300 /* nt4dom\\sAMAccountName with "Digest" realm */
1302 .user = &sAMAccountName,
1303 .nt4dom = &netbios_domain,
1304 .realm = &digest
1307 .user = &sAMAccountName_l,
1308 .nt4dom = &netbios_domain_l,
1309 .realm = &digest
1312 .user = &sAMAccountName_u,
1313 .nt4dom = &netbios_domain_u,
1314 .realm = &digest
1318 /* prepare DATA_BLOB's used in the combinations array */
1319 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
1320 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
1321 if (!sAMAccountName_l.data) {
1322 return ldb_oom(ldb);
1324 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
1325 if (!sAMAccountName_u.data) {
1326 return ldb_oom(ldb);
1329 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1330 if (!user_principal_name) {
1331 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
1332 io->u.sAMAccountName,
1333 io->ac->status->domain_data.dns_domain);
1334 if (!user_principal_name) {
1335 return ldb_oom(ldb);
1338 userPrincipalName = data_blob_string_const(user_principal_name);
1339 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
1340 if (!userPrincipalName_l.data) {
1341 return ldb_oom(ldb);
1343 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
1344 if (!userPrincipalName_u.data) {
1345 return ldb_oom(ldb);
1348 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
1349 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
1350 io->ac->status->domain_data.netbios_domain));
1351 if (!netbios_domain_l.data) {
1352 return ldb_oom(ldb);
1354 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
1355 io->ac->status->domain_data.netbios_domain));
1356 if (!netbios_domain_u.data) {
1357 return ldb_oom(ldb);
1360 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1361 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1362 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
1364 digest = data_blob_string_const("Digest");
1366 delim = data_blob_string_const(":");
1367 backslash = data_blob_string_const("\\");
1369 pdb->num_hashes = ARRAY_SIZE(wdigest);
1370 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
1371 pdb->num_hashes);
1372 if (!pdb->hashes) {
1373 return ldb_oom(ldb);
1376 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
1377 MD5_CTX md5;
1378 MD5Init(&md5);
1379 if (wdigest[i].nt4dom) {
1380 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
1381 MD5Update(&md5, backslash.data, backslash.length);
1383 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
1384 MD5Update(&md5, delim.data, delim.length);
1385 if (wdigest[i].realm) {
1386 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1388 MD5Update(&md5, delim.data, delim.length);
1389 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1390 MD5Final(pdb->hashes[i].hash, &md5);
1393 return LDB_SUCCESS;
1396 static int setup_supplemental_field(struct setup_password_fields_io *io)
1398 struct ldb_context *ldb;
1399 struct supplementalCredentialsBlob scb;
1400 struct supplementalCredentialsBlob _old_scb;
1401 struct supplementalCredentialsBlob *old_scb = NULL;
1402 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1403 uint32_t num_names = 0;
1404 const char *names[1+4];
1405 uint32_t num_packages = 0;
1406 struct supplementalCredentialsPackage packages[1+4];
1407 /* Packages */
1408 struct supplementalCredentialsPackage *pp = NULL;
1409 struct package_PackagesBlob pb;
1410 DATA_BLOB pb_blob;
1411 char *pb_hexstr;
1412 /* Primary:Kerberos-Newer-Keys */
1413 const char **nkn = NULL;
1414 struct supplementalCredentialsPackage *pkn = NULL;
1415 struct package_PrimaryKerberosBlob pknb;
1416 DATA_BLOB pknb_blob;
1417 char *pknb_hexstr;
1418 /* Primary:Kerberos */
1419 const char **nk = NULL;
1420 struct supplementalCredentialsPackage *pk = NULL;
1421 struct package_PrimaryKerberosBlob pkb;
1422 DATA_BLOB pkb_blob;
1423 char *pkb_hexstr;
1424 /* Primary:WDigest */
1425 const char **nd = NULL;
1426 struct supplementalCredentialsPackage *pd = NULL;
1427 struct package_PrimaryWDigestBlob pdb;
1428 DATA_BLOB pdb_blob;
1429 char *pdb_hexstr;
1430 /* Primary:CLEARTEXT */
1431 const char **nc = NULL;
1432 struct supplementalCredentialsPackage *pc = NULL;
1433 struct package_PrimaryCLEARTEXTBlob pcb;
1434 DATA_BLOB pcb_blob;
1435 char *pcb_hexstr;
1436 int ret;
1437 enum ndr_err_code ndr_err;
1438 uint8_t zero16[16];
1439 bool do_newer_keys = false;
1440 bool do_cleartext = false;
1442 ZERO_STRUCT(zero16);
1443 ZERO_STRUCT(names);
1445 ldb = ldb_module_get_ctx(io->ac->module);
1447 if (!io->n.cleartext_utf8) {
1449 * when we don't have a cleartext password
1450 * we can't setup a supplementalCredential value
1452 return LDB_SUCCESS;
1455 /* if there's an old supplementaCredentials blob then parse it */
1456 if (io->o.supplemental) {
1457 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1458 &_old_scb,
1459 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1461 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1462 ldb_asprintf_errstring(ldb,
1463 "setup_supplemental_field: "
1464 "failed to pull old supplementalCredentialsBlob: %s",
1465 nt_errstr(status));
1466 return LDB_ERR_OPERATIONS_ERROR;
1469 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1470 old_scb = &_old_scb;
1471 } else {
1472 ldb_debug(ldb, LDB_DEBUG_ERROR,
1473 "setup_supplemental_field: "
1474 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1475 _old_scb.sub.signature, 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 const struct ldb_message *msg = NULL;
1691 switch (io->ac->req->operation) {
1692 case LDB_ADD:
1693 msg = io->ac->req->op.add.message;
1694 break;
1695 case LDB_MODIFY:
1696 msg = io->ac->req->op.mod.message;
1697 break;
1698 default:
1699 return LDB_ERR_OPERATIONS_ERROR;
1700 break;
1703 if (io->ac->pwd_last_set_bypass) {
1704 struct ldb_message_element *el;
1706 if (msg == NULL) {
1707 return LDB_ERR_CONSTRAINT_VIOLATION;
1710 el = ldb_msg_find_element(msg, "pwdLastSet");
1711 if (el == NULL) {
1712 return LDB_ERR_CONSTRAINT_VIOLATION;
1715 io->g.last_set = samdb_result_nttime(msg, "pwdLastSet", 0);
1716 return LDB_SUCCESS;
1719 /* set it as now */
1720 unix_to_nt_time(&io->g.last_set, time(NULL));
1722 return LDB_SUCCESS;
1725 static int setup_given_passwords(struct setup_password_fields_io *io,
1726 struct setup_password_fields_given *g)
1728 struct ldb_context *ldb;
1729 bool ok;
1731 ldb = ldb_module_get_ctx(io->ac->module);
1733 if (g->cleartext_utf8) {
1734 struct ldb_val *cleartext_utf16_blob;
1736 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1737 if (!cleartext_utf16_blob) {
1738 return ldb_oom(ldb);
1740 if (!convert_string_talloc(io->ac,
1741 CH_UTF8, CH_UTF16,
1742 g->cleartext_utf8->data,
1743 g->cleartext_utf8->length,
1744 (void *)&cleartext_utf16_blob->data,
1745 &cleartext_utf16_blob->length)) {
1746 if (g->cleartext_utf8->length != 0) {
1747 talloc_free(cleartext_utf16_blob);
1748 ldb_asprintf_errstring(ldb,
1749 "setup_password_fields: "
1750 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1751 io->u.sAMAccountName);
1752 return LDB_ERR_CONSTRAINT_VIOLATION;
1753 } else {
1754 /* passwords with length "0" are valid! */
1755 cleartext_utf16_blob->data = NULL;
1756 cleartext_utf16_blob->length = 0;
1759 g->cleartext_utf16 = cleartext_utf16_blob;
1760 } else if (g->cleartext_utf16) {
1761 struct ldb_val *cleartext_utf8_blob;
1763 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1764 if (!cleartext_utf8_blob) {
1765 return ldb_oom(ldb);
1767 if (!convert_string_talloc(io->ac,
1768 CH_UTF16MUNGED, CH_UTF8,
1769 g->cleartext_utf16->data,
1770 g->cleartext_utf16->length,
1771 (void *)&cleartext_utf8_blob->data,
1772 &cleartext_utf8_blob->length)) {
1773 if (g->cleartext_utf16->length != 0) {
1774 /* We must bail out here, the input wasn't even
1775 * a multiple of 2 bytes */
1776 talloc_free(cleartext_utf8_blob);
1777 ldb_asprintf_errstring(ldb,
1778 "setup_password_fields: "
1779 "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)!",
1780 io->u.sAMAccountName);
1781 return LDB_ERR_CONSTRAINT_VIOLATION;
1782 } else {
1783 /* passwords with length "0" are valid! */
1784 cleartext_utf8_blob->data = NULL;
1785 cleartext_utf8_blob->length = 0;
1788 g->cleartext_utf8 = cleartext_utf8_blob;
1791 if (g->cleartext_utf16) {
1792 struct samr_Password *nt_hash;
1794 nt_hash = talloc(io->ac, struct samr_Password);
1795 if (!nt_hash) {
1796 return ldb_oom(ldb);
1798 g->nt_hash = nt_hash;
1800 /* compute the new nt hash */
1801 mdfour(nt_hash->hash,
1802 g->cleartext_utf16->data,
1803 g->cleartext_utf16->length);
1806 if (g->cleartext_utf8) {
1807 struct samr_Password *lm_hash;
1809 lm_hash = talloc(io->ac, struct samr_Password);
1810 if (!lm_hash) {
1811 return ldb_oom(ldb);
1814 /* compute the new lm hash */
1815 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1816 if (ok) {
1817 g->lm_hash = lm_hash;
1818 } else {
1819 talloc_free(lm_hash);
1823 return LDB_SUCCESS;
1826 static int setup_password_fields(struct setup_password_fields_io *io)
1828 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1829 struct loadparm_context *lp_ctx =
1830 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1831 struct loadparm_context);
1832 int ret;
1834 /* transform the old password (for password changes) */
1835 ret = setup_given_passwords(io, &io->og);
1836 if (ret != LDB_SUCCESS) {
1837 return ret;
1840 /* transform the new password */
1841 ret = setup_given_passwords(io, &io->n);
1842 if (ret != LDB_SUCCESS) {
1843 return ret;
1846 if (io->n.cleartext_utf8) {
1847 ret = setup_kerberos_keys(io);
1848 if (ret != LDB_SUCCESS) {
1849 return ret;
1853 ret = setup_nt_fields(io);
1854 if (ret != LDB_SUCCESS) {
1855 return ret;
1858 if (lpcfg_lanman_auth(lp_ctx)) {
1859 ret = setup_lm_fields(io);
1860 if (ret != LDB_SUCCESS) {
1861 return ret;
1863 } else {
1864 io->g.lm_hash = NULL;
1865 io->g.lm_history_len = 0;
1868 ret = setup_supplemental_field(io);
1869 if (ret != LDB_SUCCESS) {
1870 return ret;
1873 ret = setup_last_set_field(io);
1874 if (ret != LDB_SUCCESS) {
1875 return ret;
1878 return LDB_SUCCESS;
1881 static int make_error_and_update_badPwdCount(struct setup_password_fields_io *io)
1883 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1884 struct ldb_message *mod_msg = NULL;
1885 NTSTATUS status;
1886 int ret;
1888 status = dsdb_update_bad_pwd_count(io->ac, ldb,
1889 io->ac->search_res->message,
1890 io->ac->dom_res->message,
1891 &mod_msg);
1892 if (!NT_STATUS_IS_OK(status)) {
1893 goto done;
1896 if (mod_msg == NULL) {
1897 goto done;
1901 * OK, horrible semantics ahead.
1903 * - We need to abort any existing transaction
1904 * - create a transaction arround the badPwdCount update
1905 * - re-open the transaction so the upper layer
1906 * doesn't know what happened.
1908 * This is needed because returning an error to the upper
1909 * layer will cancel the transaction and undo the badPwdCount
1910 * update.
1914 * Checking errors here is a bit pointless.
1915 * What can we do if we can't end the transaction?
1917 ret = ldb_next_del_trans(io->ac->module);
1918 if (ret != LDB_SUCCESS) {
1919 ldb_debug(ldb, LDB_DEBUG_FATAL,
1920 "Failed to abort transaction prior to update of badPwdCount of %s: %s",
1921 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1922 ldb_errstring(ldb));
1924 * just return the original error
1926 goto done;
1929 /* Likewise, what should we do if we can't open a new transaction? */
1930 ret = ldb_next_start_trans(io->ac->module);
1931 if (ret != LDB_SUCCESS) {
1932 ldb_debug(ldb, LDB_DEBUG_ERROR,
1933 "Failed to open transaction to update badPwdCount of %s: %s",
1934 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1935 ldb_errstring(ldb));
1937 * just return the original error
1939 goto done;
1942 ret = dsdb_module_modify(io->ac->module, mod_msg,
1943 DSDB_FLAG_NEXT_MODULE,
1944 io->ac->req);
1945 if (ret != LDB_SUCCESS) {
1946 ldb_debug(ldb, LDB_DEBUG_ERROR,
1947 "Failed to update badPwdCount of %s: %s",
1948 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1949 ldb_errstring(ldb));
1951 * We can only ignore this...
1955 ret = ldb_next_end_trans(io->ac->module);
1956 if (ret != LDB_SUCCESS) {
1957 ldb_debug(ldb, LDB_DEBUG_ERROR,
1958 "Failed to close transaction to update badPwdCount of %s: %s",
1959 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1960 ldb_errstring(ldb));
1962 * We can only ignore this...
1966 ret = ldb_next_start_trans(io->ac->module);
1967 if (ret != LDB_SUCCESS) {
1968 ldb_debug(ldb, LDB_DEBUG_ERROR,
1969 "Failed to open transaction after update of badPwdCount of %s: %s",
1970 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1971 ldb_errstring(ldb));
1973 * We can only ignore this...
1977 done:
1978 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1979 ldb_asprintf_errstring(ldb,
1980 "%08X: %s - check_password_restrictions: "
1981 "The old password specified doesn't match!",
1982 W_ERROR_V(WERR_INVALID_PASSWORD),
1983 ldb_strerror(ret));
1984 return ret;
1987 static int check_password_restrictions(struct setup_password_fields_io *io)
1989 struct ldb_context *ldb;
1990 int ret;
1992 ldb = ldb_module_get_ctx(io->ac->module);
1994 /* First check the old password is correct, for password changes */
1995 if (!io->ac->pwd_reset) {
1996 bool nt_hash_checked = false;
1998 /* we need the old nt or lm hash given by the client */
1999 if (!io->og.nt_hash && !io->og.lm_hash) {
2000 ldb_asprintf_errstring(ldb,
2001 "check_password_restrictions: "
2002 "You need to provide the old password in order "
2003 "to change it!");
2004 return LDB_ERR_UNWILLING_TO_PERFORM;
2007 /* The password modify through the NT hash is encouraged and
2008 has no problems at all */
2009 if (io->og.nt_hash) {
2010 if (!io->o.nt_hash || memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
2011 return make_error_and_update_badPwdCount(io);
2014 nt_hash_checked = true;
2017 /* But it is also possible to change a password by the LM hash
2018 * alone for compatibility reasons. This check is optional if
2019 * the NT hash was already checked - otherwise it's mandatory.
2020 * (as the SAMR operations request it). */
2021 if (io->og.lm_hash) {
2022 if ((!io->o.lm_hash && !nt_hash_checked)
2023 || (io->o.lm_hash && memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0)) {
2024 return make_error_and_update_badPwdCount(io);
2029 if (io->u.restrictions == 0) {
2030 /* FIXME: Is this right? */
2031 return LDB_SUCCESS;
2034 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2035 if ((io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) &&
2036 !io->ac->pwd_reset)
2038 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2039 ldb_asprintf_errstring(ldb,
2040 "%08X: %s - check_password_restrictions: "
2041 "password is too young to change!",
2042 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2043 ldb_strerror(ret));
2044 return ret;
2048 * Fundamental password checks done by the call
2049 * "samdb_check_password".
2050 * It is also in use by "dcesrv_samr_ValidatePassword".
2052 if (io->n.cleartext_utf8 != NULL) {
2053 enum samr_ValidationStatus vstat;
2054 vstat = samdb_check_password(io->n.cleartext_utf8,
2055 io->ac->status->domain_data.pwdProperties,
2056 io->ac->status->domain_data.minPwdLength);
2057 switch (vstat) {
2058 case SAMR_VALIDATION_STATUS_SUCCESS:
2059 /* perfect -> proceed! */
2060 break;
2062 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
2063 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2064 ldb_asprintf_errstring(ldb,
2065 "%08X: %s - check_password_restrictions: "
2066 "the password is too short. It should be equal or longer than %u characters!",
2067 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2068 ldb_strerror(ret),
2069 io->ac->status->domain_data.minPwdLength);
2070 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
2071 return ret;
2073 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
2074 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2075 ldb_asprintf_errstring(ldb,
2076 "%08X: %s - check_password_restrictions: "
2077 "the password does not meet the complexity criteria!",
2078 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2079 ldb_strerror(ret));
2080 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
2081 return ret;
2083 default:
2084 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2085 ldb_asprintf_errstring(ldb,
2086 "%08X: %s - check_password_restrictions: "
2087 "the password doesn't fit by a certain reason!",
2088 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2089 ldb_strerror(ret));
2090 return ret;
2094 if (io->ac->pwd_reset) {
2095 return LDB_SUCCESS;
2098 if (io->n.nt_hash) {
2099 uint32_t i;
2101 /* checks the NT hash password history */
2102 for (i = 0; i < io->o.nt_history_len; i++) {
2103 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
2104 if (ret == 0) {
2105 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2106 ldb_asprintf_errstring(ldb,
2107 "%08X: %s - check_password_restrictions: "
2108 "the password was already used (in history)!",
2109 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2110 ldb_strerror(ret));
2111 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2112 return ret;
2117 if (io->n.lm_hash) {
2118 uint32_t i;
2120 /* checks the LM hash password history */
2121 for (i = 0; i < io->o.lm_history_len; i++) {
2122 ret = memcmp(io->n.lm_hash, io->o.lm_history[i].hash, 16);
2123 if (ret == 0) {
2124 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2125 ldb_asprintf_errstring(ldb,
2126 "%08X: %s - check_password_restrictions: "
2127 "the password was already used (in history)!",
2128 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2129 ldb_strerror(ret));
2130 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2131 return ret;
2136 /* are all password changes disallowed? */
2137 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
2138 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2139 ldb_asprintf_errstring(ldb,
2140 "%08X: %s - check_password_restrictions: "
2141 "password changes disabled!",
2142 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2143 ldb_strerror(ret));
2144 return ret;
2147 /* can this user change the password? */
2148 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
2149 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2150 ldb_asprintf_errstring(ldb,
2151 "%08X: %s - check_password_restrictions: "
2152 "password can't be changed on this account!",
2153 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2154 ldb_strerror(ret));
2155 return ret;
2158 return LDB_SUCCESS;
2162 * This is intended for use by the "password_hash" module since there
2163 * password changes can be specified through one message element with the
2164 * new password (to set) and another one with the old password (to unset).
2166 * The first which sets a password (new value) can have flags
2167 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2168 * for entries). The latter (old value) has always specified
2169 * LDB_FLAG_MOD_DELETE.
2171 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2172 * matching message elements are malformed in respect to the set/change rules.
2173 * Otherwise it returns LDB_SUCCESS.
2175 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
2176 const char *name,
2177 enum ldb_request_type operation,
2178 const struct ldb_val **new_val,
2179 const struct ldb_val **old_val)
2181 unsigned int i;
2183 *new_val = NULL;
2184 *old_val = NULL;
2186 if (msg == NULL) {
2187 return LDB_SUCCESS;
2190 for (i = 0; i < msg->num_elements; i++) {
2191 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
2192 continue;
2195 if ((operation == LDB_MODIFY) &&
2196 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
2197 /* 0 values are allowed */
2198 if (msg->elements[i].num_values == 1) {
2199 *old_val = &msg->elements[i].values[0];
2200 } else if (msg->elements[i].num_values > 1) {
2201 return LDB_ERR_CONSTRAINT_VIOLATION;
2203 } else if ((operation == LDB_MODIFY) &&
2204 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
2205 if (msg->elements[i].num_values > 0) {
2206 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2207 } else {
2208 return LDB_ERR_UNWILLING_TO_PERFORM;
2210 } else {
2211 /* Add operations and LDB_FLAG_MOD_ADD */
2212 if (msg->elements[i].num_values > 0) {
2213 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2214 } else {
2215 return LDB_ERR_CONSTRAINT_VIOLATION;
2220 return LDB_SUCCESS;
2223 static int setup_io(struct ph_context *ac,
2224 const struct ldb_message *orig_msg,
2225 const struct ldb_message *searched_msg,
2226 struct setup_password_fields_io *io)
2228 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
2229 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2230 struct loadparm_context *lp_ctx = talloc_get_type(
2231 ldb_get_opaque(ldb, "loadparm"), struct loadparm_context);
2232 int ret;
2234 ZERO_STRUCTP(io);
2236 /* Some operations below require kerberos contexts */
2238 if (smb_krb5_init_context(ac,
2239 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
2240 &io->smb_krb5_context) != 0) {
2241 return ldb_operr(ldb);
2244 io->ac = ac;
2246 io->u.userAccountControl = ldb_msg_find_attr_as_uint(searched_msg,
2247 "userAccountControl", 0);
2248 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
2249 io->u.sAMAccountName = ldb_msg_find_attr_as_string(searched_msg,
2250 "sAMAccountName", NULL);
2251 io->u.user_principal_name = ldb_msg_find_attr_as_string(searched_msg,
2252 "userPrincipalName", NULL);
2253 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
2255 if (io->u.sAMAccountName == NULL) {
2256 ldb_asprintf_errstring(ldb,
2257 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2258 ldb_dn_get_linearized(searched_msg->dn));
2260 return LDB_ERR_CONSTRAINT_VIOLATION;
2263 if (io->u.userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
2264 struct ldb_control *permit_trust = ldb_request_get_control(ac->req,
2265 DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID);
2267 if (permit_trust == NULL) {
2268 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2269 ldb_asprintf_errstring(ldb,
2270 "%08X: %s - setup_io: changing the interdomain trust password "
2271 "on %s not allowed via LDAP. Use LSA or NETLOGON",
2272 W_ERROR_V(WERR_ACCESS_DENIED),
2273 ldb_strerror(ret),
2274 ldb_dn_get_linearized(searched_msg->dn));
2275 return ret;
2279 /* Only non-trust accounts have restrictions (possibly this test is the
2280 * wrong way around, but we like to be restrictive if possible */
2281 io->u.restrictions = !(io->u.userAccountControl
2282 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
2283 | UF_SERVER_TRUST_ACCOUNT));
2285 if (ac->userPassword) {
2286 ret = msg_find_old_and_new_pwd_val(orig_msg, "userPassword",
2287 ac->req->operation,
2288 &io->n.cleartext_utf8,
2289 &io->og.cleartext_utf8);
2290 if (ret != LDB_SUCCESS) {
2291 ldb_asprintf_errstring(ldb,
2292 "setup_io: "
2293 "it's only allowed to set the old password once!");
2294 return ret;
2298 if (io->n.cleartext_utf8 != NULL) {
2299 struct ldb_val *cleartext_utf8_blob;
2300 char *p;
2302 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
2303 if (!cleartext_utf8_blob) {
2304 return ldb_oom(ldb);
2307 *cleartext_utf8_blob = *io->n.cleartext_utf8;
2309 /* make sure we have a null terminated string */
2310 p = talloc_strndup(cleartext_utf8_blob,
2311 (const char *)io->n.cleartext_utf8->data,
2312 io->n.cleartext_utf8->length);
2313 if ((p == NULL) && (io->n.cleartext_utf8->length > 0)) {
2314 return ldb_oom(ldb);
2316 cleartext_utf8_blob->data = (uint8_t *)p;
2318 io->n.cleartext_utf8 = cleartext_utf8_blob;
2321 ret = msg_find_old_and_new_pwd_val(orig_msg, "clearTextPassword",
2322 ac->req->operation,
2323 &io->n.cleartext_utf16,
2324 &io->og.cleartext_utf16);
2325 if (ret != LDB_SUCCESS) {
2326 ldb_asprintf_errstring(ldb,
2327 "setup_io: "
2328 "it's only allowed to set the old password once!");
2329 return ret;
2332 /* this rather strange looking piece of code is there to
2333 handle a ldap client setting a password remotely using the
2334 unicodePwd ldap field. The syntax is that the password is
2335 in UTF-16LE, with a " at either end. Unfortunately the
2336 unicodePwd field is also used to store the nt hashes
2337 internally in Samba, and is used in the nt hash format on
2338 the wire in DRS replication, so we have a single name for
2339 two distinct values. The code below leaves us with a small
2340 chance (less than 1 in 2^32) of a mixup, if someone manages
2341 to create a MD4 hash which starts and ends in 0x22 0x00, as
2342 that would then be treated as a UTF16 password rather than
2343 a nthash */
2345 ret = msg_find_old_and_new_pwd_val(orig_msg, "unicodePwd",
2346 ac->req->operation,
2347 &quoted_utf16,
2348 &old_quoted_utf16);
2349 if (ret != LDB_SUCCESS) {
2350 ldb_asprintf_errstring(ldb,
2351 "setup_io: "
2352 "it's only allowed to set the old password once!");
2353 return ret;
2356 /* Checks and converts the actual "unicodePwd" attribute */
2357 if (!ac->hash_values &&
2358 quoted_utf16 &&
2359 quoted_utf16->length >= 4 &&
2360 quoted_utf16->data[0] == '"' &&
2361 quoted_utf16->data[1] == 0 &&
2362 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
2363 quoted_utf16->data[quoted_utf16->length-1] == 0) {
2364 struct ldb_val *quoted_utf16_2;
2366 if (io->n.cleartext_utf16) {
2367 /* refuse the change if someone wants to change with
2368 with both UTF16 possibilities at the same time... */
2369 ldb_asprintf_errstring(ldb,
2370 "setup_io: "
2371 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2372 return LDB_ERR_UNWILLING_TO_PERFORM;
2376 * adapt the quoted UTF16 string to be a real
2377 * cleartext one
2379 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2380 if (quoted_utf16_2 == NULL) {
2381 return ldb_oom(ldb);
2384 quoted_utf16_2->data = quoted_utf16->data + 2;
2385 quoted_utf16_2->length = quoted_utf16->length-4;
2386 io->n.cleartext_utf16 = quoted_utf16_2;
2387 io->n.nt_hash = NULL;
2389 } else if (quoted_utf16) {
2390 /* We have only the hash available -> so no plaintext here */
2391 if (!ac->hash_values) {
2392 /* refuse the change if someone wants to change
2393 the hash without control specified... */
2394 ldb_asprintf_errstring(ldb,
2395 "setup_io: "
2396 "it's not allowed to set the NT hash password directly'");
2397 /* this looks odd but this is what Windows does:
2398 returns "UNWILLING_TO_PERFORM" on wrong
2399 password sets and "CONSTRAINT_VIOLATION" on
2400 wrong password changes. */
2401 if (old_quoted_utf16 == NULL) {
2402 return LDB_ERR_UNWILLING_TO_PERFORM;
2405 return LDB_ERR_CONSTRAINT_VIOLATION;
2408 io->n.nt_hash = talloc(io->ac, struct samr_Password);
2409 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
2410 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
2413 /* Checks and converts the previous "unicodePwd" attribute */
2414 if (!ac->hash_values &&
2415 old_quoted_utf16 &&
2416 old_quoted_utf16->length >= 4 &&
2417 old_quoted_utf16->data[0] == '"' &&
2418 old_quoted_utf16->data[1] == 0 &&
2419 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
2420 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
2421 struct ldb_val *old_quoted_utf16_2;
2423 if (io->og.cleartext_utf16) {
2424 /* refuse the change if someone wants to change with
2425 both UTF16 possibilities at the same time... */
2426 ldb_asprintf_errstring(ldb,
2427 "setup_io: "
2428 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2429 return LDB_ERR_UNWILLING_TO_PERFORM;
2433 * adapt the quoted UTF16 string to be a real
2434 * cleartext one
2436 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2437 if (old_quoted_utf16_2 == NULL) {
2438 return ldb_oom(ldb);
2441 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
2442 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
2444 io->og.cleartext_utf16 = old_quoted_utf16_2;
2445 io->og.nt_hash = NULL;
2446 } else if (old_quoted_utf16) {
2447 /* We have only the hash available -> so no plaintext here */
2448 if (!ac->hash_values) {
2449 /* refuse the change if someone wants to change
2450 the hash without control specified... */
2451 ldb_asprintf_errstring(ldb,
2452 "setup_io: "
2453 "it's not allowed to set the NT hash password directly'");
2454 return LDB_ERR_UNWILLING_TO_PERFORM;
2457 io->og.nt_hash = talloc(io->ac, struct samr_Password);
2458 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
2459 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
2462 /* Handles the "dBCSPwd" attribute (LM hash) */
2463 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
2464 ret = msg_find_old_and_new_pwd_val(orig_msg, "dBCSPwd",
2465 ac->req->operation,
2466 &lm_hash, &old_lm_hash);
2467 if (ret != LDB_SUCCESS) {
2468 ldb_asprintf_errstring(ldb,
2469 "setup_io: "
2470 "it's only allowed to set the old password once!");
2471 return ret;
2474 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
2475 /* refuse the change if someone wants to change the hash
2476 without control specified... */
2477 ldb_asprintf_errstring(ldb,
2478 "setup_io: "
2479 "it's not allowed to set the LM hash password directly'");
2480 return LDB_ERR_UNWILLING_TO_PERFORM;
2483 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
2484 io->n.lm_hash = talloc(io->ac, struct samr_Password);
2485 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
2486 sizeof(io->n.lm_hash->hash)));
2488 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
2489 io->og.lm_hash = talloc(io->ac, struct samr_Password);
2490 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
2491 sizeof(io->og.lm_hash->hash)));
2495 * Handles the password change control if it's specified. It has the
2496 * precedance and overrides already specified old password values of
2497 * change requests (but that shouldn't happen since the control is
2498 * fully internal and only used in conjunction with replace requests!).
2500 if (ac->change != NULL) {
2501 io->og.nt_hash = NULL;
2502 if (ac->change->old_nt_pwd_hash != NULL) {
2503 io->og.nt_hash = talloc_memdup(io->ac,
2504 ac->change->old_nt_pwd_hash,
2505 sizeof(struct samr_Password));
2507 io->og.lm_hash = NULL;
2508 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
2509 io->og.lm_hash = talloc_memdup(io->ac,
2510 ac->change->old_lm_pwd_hash,
2511 sizeof(struct samr_Password));
2515 /* refuse the change if someone wants to change the clear-
2516 text and supply his own hashes at the same time... */
2517 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
2518 && (io->n.nt_hash || io->n.lm_hash)) {
2519 ldb_asprintf_errstring(ldb,
2520 "setup_io: "
2521 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2522 return LDB_ERR_UNWILLING_TO_PERFORM;
2525 /* refuse the change if someone wants to change the password
2526 using both plaintext methods (UTF8 and UTF16) at the same time... */
2527 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
2528 ldb_asprintf_errstring(ldb,
2529 "setup_io: "
2530 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2531 return LDB_ERR_UNWILLING_TO_PERFORM;
2534 /* refuse the change if someone tries to set/change the password by
2535 * the lanman hash alone and we've deactivated that mechanism. This
2536 * would end in an account without any password! */
2537 if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
2538 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
2539 ldb_asprintf_errstring(ldb,
2540 "setup_io: "
2541 "It' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2542 /* on "userPassword" and "clearTextPassword" we've to return
2543 * something different, since these are virtual attributes */
2544 if ((ldb_msg_find_element(orig_msg, "userPassword") != NULL) ||
2545 (ldb_msg_find_element(orig_msg, "clearTextPassword") != NULL)) {
2546 return LDB_ERR_CONSTRAINT_VIOLATION;
2548 return LDB_ERR_UNWILLING_TO_PERFORM;
2551 /* refuse the change if someone wants to compare against a plaintext
2552 or hash at the same time for a "password modify" operation... */
2553 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
2554 && (io->og.nt_hash || io->og.lm_hash)) {
2555 ldb_asprintf_errstring(ldb,
2556 "setup_io: "
2557 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2558 return LDB_ERR_UNWILLING_TO_PERFORM;
2561 /* refuse the change if someone wants to compare against both
2562 * plaintexts at the same time for a "password modify" operation... */
2563 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2564 ldb_asprintf_errstring(ldb,
2565 "setup_io: "
2566 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2567 return LDB_ERR_UNWILLING_TO_PERFORM;
2570 /* Decides if we have a password modify or password reset operation */
2571 if (ac->req->operation == LDB_ADD) {
2572 /* On "add" we have only "password reset" */
2573 ac->pwd_reset = true;
2574 } else if (ac->req->operation == LDB_MODIFY) {
2575 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2576 || io->og.nt_hash || io->og.lm_hash) {
2577 /* If we have an old password specified then for sure it
2578 * is a user "password change" */
2579 ac->pwd_reset = false;
2580 } else {
2581 /* Otherwise we have also here a "password reset" */
2582 ac->pwd_reset = true;
2584 } else {
2585 /* this shouldn't happen */
2586 return ldb_operr(ldb);
2589 return LDB_SUCCESS;
2592 static struct ph_context *ph_init_context(struct ldb_module *module,
2593 struct ldb_request *req,
2594 bool userPassword)
2596 struct ldb_context *ldb;
2597 struct ph_context *ac;
2599 ldb = ldb_module_get_ctx(module);
2601 ac = talloc_zero(req, struct ph_context);
2602 if (ac == NULL) {
2603 ldb_set_errstring(ldb, "Out of Memory");
2604 return NULL;
2607 ac->module = module;
2608 ac->req = req;
2609 ac->userPassword = userPassword;
2611 return ac;
2614 static void ph_apply_controls(struct ph_context *ac)
2616 struct ldb_control *ctrl;
2618 ac->change_status = false;
2619 ctrl = ldb_request_get_control(ac->req,
2620 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2621 if (ctrl != NULL) {
2622 ac->change_status = true;
2624 /* Mark the "change status" control as uncritical (done) */
2625 ctrl->critical = false;
2628 ac->hash_values = false;
2629 ctrl = ldb_request_get_control(ac->req,
2630 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2631 if (ctrl != NULL) {
2632 ac->hash_values = true;
2634 /* Mark the "hash values" control as uncritical (done) */
2635 ctrl->critical = false;
2638 ctrl = ldb_request_get_control(ac->req,
2639 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2640 if (ctrl != NULL) {
2641 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2643 /* Mark the "change" control as uncritical (done) */
2644 ctrl->critical = false;
2647 ac->pwd_last_set_bypass = false;
2648 ctrl = ldb_request_get_control(ac->req,
2649 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID);
2650 if (ctrl != NULL) {
2651 ac->pwd_last_set_bypass = true;
2653 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2654 ctrl->critical = false;
2658 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2660 struct ph_context *ac;
2662 ac = talloc_get_type(req->context, struct ph_context);
2664 if (!ares) {
2665 return ldb_module_done(ac->req, NULL, NULL,
2666 LDB_ERR_OPERATIONS_ERROR);
2669 if (ares->type == LDB_REPLY_REFERRAL) {
2670 return ldb_module_send_referral(ac->req, ares->referral);
2673 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2674 /* On success and trivial errors a status control is being
2675 * added (used for example by the "samdb_set_password" call) */
2676 ldb_reply_add_control(ares,
2677 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2678 false,
2679 ac->status);
2682 if (ares->error != LDB_SUCCESS) {
2683 return ldb_module_done(ac->req, ares->controls,
2684 ares->response, ares->error);
2687 if (ares->type != LDB_REPLY_DONE) {
2688 talloc_free(ares);
2689 return ldb_module_done(ac->req, NULL, NULL,
2690 LDB_ERR_OPERATIONS_ERROR);
2693 return ldb_module_done(ac->req, ares->controls,
2694 ares->response, ares->error);
2697 static int password_hash_add_do_add(struct ph_context *ac);
2698 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2699 static int password_hash_mod_search_self(struct ph_context *ac);
2700 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2701 static int password_hash_mod_do_mod(struct ph_context *ac);
2703 static int get_domain_data_callback(struct ldb_request *req,
2704 struct ldb_reply *ares)
2706 struct ldb_context *ldb;
2707 struct ph_context *ac;
2708 struct loadparm_context *lp_ctx;
2709 int ret = LDB_SUCCESS;
2711 ac = talloc_get_type(req->context, struct ph_context);
2712 ldb = ldb_module_get_ctx(ac->module);
2714 if (!ares) {
2715 ret = LDB_ERR_OPERATIONS_ERROR;
2716 goto done;
2718 if (ares->error != LDB_SUCCESS) {
2719 return ldb_module_done(ac->req, ares->controls,
2720 ares->response, ares->error);
2723 switch (ares->type) {
2724 case LDB_REPLY_ENTRY:
2725 if (ac->status != NULL) {
2726 talloc_free(ares);
2728 ldb_set_errstring(ldb, "Too many results");
2729 ret = LDB_ERR_OPERATIONS_ERROR;
2730 goto done;
2733 /* Setup the "status" structure (used as control later) */
2734 ac->status = talloc_zero(ac->req,
2735 struct dsdb_control_password_change_status);
2736 if (ac->status == NULL) {
2737 talloc_free(ares);
2739 ldb_oom(ldb);
2740 ret = LDB_ERR_OPERATIONS_ERROR;
2741 goto done;
2744 /* Setup the "domain data" structure */
2745 ac->status->domain_data.pwdProperties =
2746 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
2747 ac->status->domain_data.pwdHistoryLength =
2748 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
2749 ac->status->domain_data.maxPwdAge =
2750 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
2751 ac->status->domain_data.minPwdAge =
2752 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
2753 ac->status->domain_data.minPwdLength =
2754 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
2755 ac->status->domain_data.store_cleartext =
2756 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2758 /* For a domain DN, this puts things in dotted notation */
2759 /* For builtin domains, this will give details for the host,
2760 * but that doesn't really matter, as it's just used for salt
2761 * and kerberos principals, which don't exist here */
2763 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2764 struct loadparm_context);
2766 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
2767 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
2768 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
2770 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2772 if (ac->dom_res != NULL) {
2773 talloc_free(ares);
2775 ldb_set_errstring(ldb, "Too many results");
2776 ret = LDB_ERR_OPERATIONS_ERROR;
2777 goto done;
2780 ac->dom_res = talloc_steal(ac, ares);
2781 ret = LDB_SUCCESS;
2782 break;
2784 case LDB_REPLY_REFERRAL:
2785 /* ignore */
2786 talloc_free(ares);
2787 ret = LDB_SUCCESS;
2788 break;
2790 case LDB_REPLY_DONE:
2791 talloc_free(ares);
2792 /* call the next step */
2793 switch (ac->req->operation) {
2794 case LDB_ADD:
2795 ret = password_hash_add_do_add(ac);
2796 break;
2798 case LDB_MODIFY:
2799 ret = password_hash_mod_do_mod(ac);
2800 break;
2802 default:
2803 ret = LDB_ERR_OPERATIONS_ERROR;
2804 break;
2806 break;
2809 done:
2810 if (ret != LDB_SUCCESS) {
2811 struct ldb_reply *new_ares;
2813 new_ares = talloc_zero(ac->req, struct ldb_reply);
2814 if (new_ares == NULL) {
2815 ldb_oom(ldb);
2816 return ldb_module_done(ac->req, NULL, NULL,
2817 LDB_ERR_OPERATIONS_ERROR);
2820 new_ares->error = ret;
2821 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2822 /* On success and trivial errors a status control is being
2823 * added (used for example by the "samdb_set_password" call) */
2824 ldb_reply_add_control(new_ares,
2825 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2826 false,
2827 ac->status);
2830 return ldb_module_done(ac->req, new_ares->controls,
2831 new_ares->response, new_ares->error);
2834 return LDB_SUCCESS;
2837 static int build_domain_data_request(struct ph_context *ac)
2839 /* attrs[] is returned from this function in
2840 ac->dom_req->op.search.attrs, so it must be static, as
2841 otherwise the compiler can put it on the stack */
2842 struct ldb_context *ldb;
2843 static const char * const attrs[] = { "pwdProperties",
2844 "pwdHistoryLength",
2845 "maxPwdAge",
2846 "minPwdAge",
2847 "minPwdLength",
2848 "lockoutThreshold",
2849 "lockOutObservationWindow",
2850 NULL };
2851 int ret;
2853 ldb = ldb_module_get_ctx(ac->module);
2855 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
2856 ldb_get_default_basedn(ldb),
2857 LDB_SCOPE_BASE,
2858 NULL, attrs,
2859 NULL,
2860 ac, get_domain_data_callback,
2861 ac->req);
2862 LDB_REQ_SET_LOCATION(ac->dom_req);
2863 return ret;
2866 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2868 struct ldb_context *ldb;
2869 struct ph_context *ac;
2870 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2871 *ntAttr, *lmAttr;
2872 int ret;
2873 struct ldb_control *bypass = NULL;
2874 bool userPassword = dsdb_user_password_support(module, req, req);
2876 ldb = ldb_module_get_ctx(module);
2878 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2880 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2881 return ldb_next_request(module, req);
2884 bypass = ldb_request_get_control(req,
2885 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2886 if (bypass != NULL) {
2887 /* Mark the "bypass" control as uncritical (done) */
2888 bypass->critical = false;
2889 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
2890 return password_hash_bypass(module, req);
2893 /* nobody must touch password histories and 'supplementalCredentials' */
2894 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2895 return LDB_ERR_UNWILLING_TO_PERFORM;
2897 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2898 return LDB_ERR_UNWILLING_TO_PERFORM;
2900 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2901 return LDB_ERR_UNWILLING_TO_PERFORM;
2904 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2905 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2907 userPasswordAttr = NULL;
2908 if (userPassword) {
2909 userPasswordAttr = ldb_msg_find_element(req->op.add.message,
2910 "userPassword");
2911 /* MS-ADTS 3.1.1.3.1.5.2 */
2912 if ((userPasswordAttr != NULL) &&
2913 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2914 return LDB_ERR_CONSTRAINT_VIOLATION;
2917 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2918 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2919 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2921 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2922 return ldb_next_request(module, req);
2925 /* Make sure we are performing the password set action on a (for us)
2926 * valid object. Those are instances of either "user" and/or
2927 * "inetOrgPerson". Otherwise continue with the submodules. */
2928 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2929 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2931 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2932 ldb_set_errstring(ldb,
2933 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2934 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2937 return ldb_next_request(module, req);
2940 ac = ph_init_context(module, req, userPassword);
2941 if (ac == NULL) {
2942 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2943 return ldb_operr(ldb);
2945 ph_apply_controls(ac);
2947 /* get user domain data */
2948 ret = build_domain_data_request(ac);
2949 if (ret != LDB_SUCCESS) {
2950 return ret;
2953 return ldb_next_request(module, ac->dom_req);
2956 static int password_hash_add_do_add(struct ph_context *ac)
2958 struct ldb_context *ldb;
2959 struct ldb_request *down_req;
2960 struct ldb_message *msg;
2961 struct setup_password_fields_io io;
2962 int ret;
2964 /* Prepare the internal data structure containing the passwords */
2965 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2966 if (ret != LDB_SUCCESS) {
2967 return ret;
2970 ldb = ldb_module_get_ctx(ac->module);
2972 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2973 if (msg == NULL) {
2974 return ldb_operr(ldb);
2977 /* remove attributes that we just read into 'io' */
2978 if (ac->userPassword) {
2979 ldb_msg_remove_attr(msg, "userPassword");
2981 ldb_msg_remove_attr(msg, "clearTextPassword");
2982 ldb_msg_remove_attr(msg, "unicodePwd");
2983 ldb_msg_remove_attr(msg, "dBCSPwd");
2984 ldb_msg_remove_attr(msg, "pwdLastSet");
2986 ret = setup_password_fields(&io);
2987 if (ret != LDB_SUCCESS) {
2988 return ret;
2991 ret = check_password_restrictions(&io);
2992 if (ret != LDB_SUCCESS) {
2993 return ret;
2996 if (io.g.nt_hash) {
2997 ret = samdb_msg_add_hash(ldb, ac, msg,
2998 "unicodePwd", io.g.nt_hash);
2999 if (ret != LDB_SUCCESS) {
3000 return ret;
3003 if (io.g.lm_hash) {
3004 ret = samdb_msg_add_hash(ldb, ac, msg,
3005 "dBCSPwd", io.g.lm_hash);
3006 if (ret != LDB_SUCCESS) {
3007 return ret;
3010 if (io.g.nt_history_len > 0) {
3011 ret = samdb_msg_add_hashes(ldb, ac, msg,
3012 "ntPwdHistory",
3013 io.g.nt_history,
3014 io.g.nt_history_len);
3015 if (ret != LDB_SUCCESS) {
3016 return ret;
3019 if (io.g.lm_history_len > 0) {
3020 ret = samdb_msg_add_hashes(ldb, ac, msg,
3021 "lmPwdHistory",
3022 io.g.lm_history,
3023 io.g.lm_history_len);
3024 if (ret != LDB_SUCCESS) {
3025 return ret;
3028 if (io.g.supplemental.length > 0) {
3029 ret = ldb_msg_add_value(msg, "supplementalCredentials",
3030 &io.g.supplemental, NULL);
3031 if (ret != LDB_SUCCESS) {
3032 return ret;
3035 ret = samdb_msg_add_uint64(ldb, ac, msg,
3036 "pwdLastSet",
3037 io.g.last_set);
3038 if (ret != LDB_SUCCESS) {
3039 return ret;
3042 ret = ldb_build_add_req(&down_req, ldb, ac,
3043 msg,
3044 ac->req->controls,
3045 ac, ph_op_callback,
3046 ac->req);
3047 LDB_REQ_SET_LOCATION(down_req);
3048 if (ret != LDB_SUCCESS) {
3049 return ret;
3052 return ldb_next_request(ac->module, down_req);
3055 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
3057 struct ldb_context *ldb;
3058 struct ph_context *ac;
3059 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
3060 "unicodePwd", "dBCSPwd", NULL }, **l;
3061 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
3062 struct ldb_message_element *passwordAttr;
3063 struct ldb_message *msg;
3064 struct ldb_request *down_req;
3065 int ret;
3066 struct ldb_control *bypass = NULL;
3067 bool userPassword = dsdb_user_password_support(module, req, req);
3069 ldb = ldb_module_get_ctx(module);
3071 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
3073 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
3074 return ldb_next_request(module, req);
3077 bypass = ldb_request_get_control(req,
3078 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
3079 if (bypass != NULL) {
3080 /* Mark the "bypass" control as uncritical (done) */
3081 bypass->critical = false;
3082 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
3083 return password_hash_bypass(module, req);
3086 /* nobody must touch password histories and 'supplementalCredentials' */
3087 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
3088 return LDB_ERR_UNWILLING_TO_PERFORM;
3090 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
3091 return LDB_ERR_UNWILLING_TO_PERFORM;
3093 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
3094 return LDB_ERR_UNWILLING_TO_PERFORM;
3097 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
3098 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
3099 * For password changes/set there should be a 'delete' or a 'modify'
3100 * on these attributes. */
3101 attr_cnt = 0;
3102 for (l = passwordAttrs; *l != NULL; l++) {
3103 if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) {
3104 continue;
3107 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
3108 /* MS-ADTS 3.1.1.3.1.5.2 */
3109 if ((ldb_attr_cmp(*l, "userPassword") == 0) &&
3110 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
3111 return LDB_ERR_CONSTRAINT_VIOLATION;
3114 ++attr_cnt;
3117 if (attr_cnt == 0) {
3118 return ldb_next_request(module, req);
3121 ac = ph_init_context(module, req, userPassword);
3122 if (!ac) {
3123 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3124 return ldb_operr(ldb);
3126 ph_apply_controls(ac);
3128 /* use a new message structure so that we can modify it */
3129 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3130 if (msg == NULL) {
3131 return ldb_oom(ldb);
3134 /* - check for single-valued password attributes
3135 * (if not return "CONSTRAINT_VIOLATION")
3136 * - check that for a password change operation one add and one delete
3137 * operation exists
3138 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3139 * - check that a password change and a password set operation cannot
3140 * be mixed
3141 * (if not return "UNWILLING_TO_PERFORM")
3142 * - remove all password attributes modifications from the first change
3143 * operation (anything without the passwords) - we will make the real
3144 * modification later */
3145 del_attr_cnt = 0;
3146 add_attr_cnt = 0;
3147 rep_attr_cnt = 0;
3148 for (l = passwordAttrs; *l != NULL; l++) {
3149 if ((!ac->userPassword) &&
3150 (ldb_attr_cmp(*l, "userPassword") == 0)) {
3151 continue;
3154 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
3155 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
3156 ++del_attr_cnt;
3158 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
3159 ++add_attr_cnt;
3161 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
3162 ++rep_attr_cnt;
3164 if ((passwordAttr->num_values != 1) &&
3165 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
3166 talloc_free(ac);
3167 ldb_asprintf_errstring(ldb,
3168 "'%s' attribute must have exactly one value on add operations!",
3169 *l);
3170 return LDB_ERR_CONSTRAINT_VIOLATION;
3172 if ((passwordAttr->num_values > 1) &&
3173 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
3174 talloc_free(ac);
3175 ldb_asprintf_errstring(ldb,
3176 "'%s' attribute must have zero or one value(s) on delete operations!",
3177 *l);
3178 return LDB_ERR_CONSTRAINT_VIOLATION;
3180 ldb_msg_remove_element(msg, passwordAttr);
3183 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
3184 talloc_free(ac);
3185 ldb_set_errstring(ldb,
3186 "Only the add action for a password change specified!");
3187 return LDB_ERR_UNWILLING_TO_PERFORM;
3189 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
3190 talloc_free(ac);
3191 ldb_set_errstring(ldb,
3192 "Only one delete and one add action for a password change allowed!");
3193 return LDB_ERR_UNWILLING_TO_PERFORM;
3195 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
3196 talloc_free(ac);
3197 ldb_set_errstring(ldb,
3198 "Either a password change or a password set operation is allowed!");
3199 return LDB_ERR_UNWILLING_TO_PERFORM;
3202 /* if there was nothing else to be modified skip to next step */
3203 if (msg->num_elements == 0) {
3204 return password_hash_mod_search_self(ac);
3207 ret = ldb_build_mod_req(&down_req, ldb, ac,
3208 msg,
3209 req->controls,
3210 ac, ph_modify_callback,
3211 req);
3212 LDB_REQ_SET_LOCATION(down_req);
3213 if (ret != LDB_SUCCESS) {
3214 return ret;
3217 return ldb_next_request(module, down_req);
3220 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3222 struct ph_context *ac;
3224 ac = talloc_get_type(req->context, struct ph_context);
3226 if (!ares) {
3227 return ldb_module_done(ac->req, NULL, NULL,
3228 LDB_ERR_OPERATIONS_ERROR);
3231 if (ares->type == LDB_REPLY_REFERRAL) {
3232 return ldb_module_send_referral(ac->req, ares->referral);
3235 if (ares->error != LDB_SUCCESS) {
3236 return ldb_module_done(ac->req, ares->controls,
3237 ares->response, ares->error);
3240 if (ares->type != LDB_REPLY_DONE) {
3241 talloc_free(ares);
3242 return ldb_module_done(ac->req, NULL, NULL,
3243 LDB_ERR_OPERATIONS_ERROR);
3246 talloc_free(ares);
3248 return password_hash_mod_search_self(ac);
3251 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
3253 struct ldb_context *ldb;
3254 struct ph_context *ac;
3255 int ret = LDB_SUCCESS;
3257 ac = talloc_get_type(req->context, struct ph_context);
3258 ldb = ldb_module_get_ctx(ac->module);
3260 if (!ares) {
3261 ret = LDB_ERR_OPERATIONS_ERROR;
3262 goto done;
3264 if (ares->error != LDB_SUCCESS) {
3265 return ldb_module_done(ac->req, ares->controls,
3266 ares->response, ares->error);
3269 /* we are interested only in the single reply (base search) */
3270 switch (ares->type) {
3271 case LDB_REPLY_ENTRY:
3272 /* Make sure we are performing the password change action on a
3273 * (for us) valid object. Those are instances of either "user"
3274 * and/or "inetOrgPerson". Otherwise continue with the
3275 * submodules. */
3276 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
3277 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
3278 talloc_free(ares);
3280 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
3281 ldb_set_errstring(ldb,
3282 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3283 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
3284 goto done;
3287 ret = ldb_next_request(ac->module, ac->req);
3288 goto done;
3291 if (ac->search_res != NULL) {
3292 talloc_free(ares);
3294 ldb_set_errstring(ldb, "Too many results");
3295 ret = LDB_ERR_OPERATIONS_ERROR;
3296 goto done;
3299 ac->search_res = talloc_steal(ac, ares);
3300 ret = LDB_SUCCESS;
3301 break;
3303 case LDB_REPLY_REFERRAL:
3304 /* ignore anything else for now */
3305 talloc_free(ares);
3306 ret = LDB_SUCCESS;
3307 break;
3309 case LDB_REPLY_DONE:
3310 talloc_free(ares);
3312 /* get user domain data */
3313 ret = build_domain_data_request(ac);
3314 if (ret != LDB_SUCCESS) {
3315 return ldb_module_done(ac->req, NULL, NULL, ret);
3318 ret = ldb_next_request(ac->module, ac->dom_req);
3319 break;
3322 done:
3323 if (ret != LDB_SUCCESS) {
3324 return ldb_module_done(ac->req, NULL, NULL, ret);
3327 return LDB_SUCCESS;
3330 static int password_hash_mod_search_self(struct ph_context *ac)
3332 struct ldb_context *ldb;
3333 static const char * const attrs[] = { "objectClass",
3334 "userAccountControl",
3335 "msDS-User-Account-Control-Computed",
3336 "pwdLastSet",
3337 "sAMAccountName",
3338 "objectSid",
3339 "userPrincipalName",
3340 "supplementalCredentials",
3341 "lmPwdHistory",
3342 "ntPwdHistory",
3343 "dBCSPwd",
3344 "unicodePwd",
3345 "badPasswordTime",
3346 "badPwdCount",
3347 "lockoutTime",
3348 NULL };
3349 struct ldb_request *search_req;
3350 int ret;
3352 ldb = ldb_module_get_ctx(ac->module);
3354 ret = ldb_build_search_req(&search_req, ldb, ac,
3355 ac->req->op.mod.message->dn,
3356 LDB_SCOPE_BASE,
3357 "(objectclass=*)",
3358 attrs,
3359 NULL,
3360 ac, ph_mod_search_callback,
3361 ac->req);
3362 LDB_REQ_SET_LOCATION(search_req);
3363 if (ret != LDB_SUCCESS) {
3364 return ret;
3367 return ldb_next_request(ac->module, search_req);
3370 static int password_hash_mod_do_mod(struct ph_context *ac)
3372 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3373 struct loadparm_context *lp_ctx =
3374 talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3375 struct loadparm_context);
3376 struct ldb_request *mod_req;
3377 struct ldb_message *msg;
3378 const struct ldb_message *orig_msg, *searched_msg;
3379 struct setup_password_fields_io io;
3380 int ret;
3381 NTSTATUS status;
3383 /* use a new message structure so that we can modify it */
3384 msg = ldb_msg_new(ac);
3385 if (msg == NULL) {
3386 return ldb_operr(ldb);
3389 /* modify dn */
3390 msg->dn = ac->req->op.mod.message->dn;
3392 orig_msg = ac->req->op.mod.message;
3393 searched_msg = ac->search_res->message;
3395 /* Prepare the internal data structure containing the passwords */
3396 ret = setup_io(ac, orig_msg, searched_msg, &io);
3397 if (ret != LDB_SUCCESS) {
3398 return ret;
3401 if (io.ac->pwd_reset) {
3402 /* Get the old password from the database */
3403 status = samdb_result_passwords_no_lockout(io.ac,
3404 lp_ctx,
3405 discard_const_p(struct ldb_message, searched_msg),
3406 &io.o.lm_hash,
3407 &io.o.nt_hash);
3408 } else {
3409 /* Get the old password from the database */
3410 status = samdb_result_passwords(io.ac,
3411 lp_ctx,
3412 discard_const_p(struct ldb_message, searched_msg),
3413 &io.o.lm_hash, &io.o.nt_hash);
3416 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
3417 ldb_asprintf_errstring(ldb,
3418 "%08X: check_password: "
3419 "Password change not permitted, account locked out!",
3420 W_ERROR_V(WERR_ACCOUNT_LOCKED_OUT));
3421 return LDB_ERR_CONSTRAINT_VIOLATION;
3424 if (!NT_STATUS_IS_OK(status)) {
3426 * This only happens if the database has gone weird,
3427 * not if we are just missing the passwords
3429 return ldb_operr(ldb);
3432 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
3433 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
3434 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
3436 ret = setup_password_fields(&io);
3437 if (ret != LDB_SUCCESS) {
3438 return ret;
3441 ret = check_password_restrictions(&io);
3442 if (ret != LDB_SUCCESS) {
3443 return ret;
3446 /* make sure we replace all the old attributes */
3447 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
3448 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
3449 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3450 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3451 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
3452 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
3454 if (io.g.nt_hash) {
3455 ret = samdb_msg_add_hash(ldb, ac, msg,
3456 "unicodePwd", io.g.nt_hash);
3457 if (ret != LDB_SUCCESS) {
3458 return ret;
3461 if (io.g.lm_hash) {
3462 ret = samdb_msg_add_hash(ldb, ac, msg,
3463 "dBCSPwd", io.g.lm_hash);
3464 if (ret != LDB_SUCCESS) {
3465 return ret;
3468 if (io.g.nt_history_len > 0) {
3469 ret = samdb_msg_add_hashes(ldb, ac, msg,
3470 "ntPwdHistory",
3471 io.g.nt_history,
3472 io.g.nt_history_len);
3473 if (ret != LDB_SUCCESS) {
3474 return ret;
3477 if (io.g.lm_history_len > 0) {
3478 ret = samdb_msg_add_hashes(ldb, ac, msg,
3479 "lmPwdHistory",
3480 io.g.lm_history,
3481 io.g.lm_history_len);
3482 if (ret != LDB_SUCCESS) {
3483 return ret;
3486 if (io.g.supplemental.length > 0) {
3487 ret = ldb_msg_add_value(msg, "supplementalCredentials",
3488 &io.g.supplemental, NULL);
3489 if (ret != LDB_SUCCESS) {
3490 return ret;
3493 ret = samdb_msg_add_uint64(ldb, ac, msg,
3494 "pwdLastSet",
3495 io.g.last_set);
3496 if (ret != LDB_SUCCESS) {
3497 return ret;
3500 ret = ldb_build_mod_req(&mod_req, ldb, ac,
3501 msg,
3502 ac->req->controls,
3503 ac, ph_op_callback,
3504 ac->req);
3505 LDB_REQ_SET_LOCATION(mod_req);
3506 if (ret != LDB_SUCCESS) {
3507 return ret;
3510 return ldb_next_request(ac->module, mod_req);
3513 static const struct ldb_module_ops ldb_password_hash_module_ops = {
3514 .name = "password_hash",
3515 .add = password_hash_add,
3516 .modify = password_hash_modify
3519 int ldb_password_hash_module_init(const char *version)
3521 LDB_MODULE_CHECK_VERSION(version);
3522 return ldb_register_module(&ldb_password_hash_module_ops);