Update mailing list references to point at lists.samba.org
[Samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blobd304038346b93f5e443d88292cc234774c169987
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_salt 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 = 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.saltvalue.data,
738 salt.saltvalue.length);
739 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
740 if (!io->g.salt) {
741 return ldb_oom(ldb);
743 salt.saltvalue.data = discard_const(io->g.salt);
744 salt.saltvalue.length = strlen(io->g.salt);
747 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
748 * the salt and the cleartext password
750 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
751 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
752 cleartext_data,
753 salt,
754 &key);
755 if (krb5_ret) {
756 ldb_asprintf_errstring(ldb,
757 "setup_kerberos_keys: "
758 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
759 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
760 krb5_ret, io->ac));
761 return LDB_ERR_OPERATIONS_ERROR;
763 io->g.aes_256 = data_blob_talloc(io->ac,
764 KRB5_KEY_DATA(&key),
765 KRB5_KEY_LENGTH(&key));
766 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
767 if (!io->g.aes_256.data) {
768 return ldb_oom(ldb);
772 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
773 * the salt and the cleartext password
775 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
776 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
777 cleartext_data,
778 salt,
779 &key);
780 if (krb5_ret) {
781 ldb_asprintf_errstring(ldb,
782 "setup_kerberos_keys: "
783 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
784 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
785 krb5_ret, io->ac));
786 return LDB_ERR_OPERATIONS_ERROR;
788 io->g.aes_128 = data_blob_talloc(io->ac,
789 KRB5_KEY_DATA(&key),
790 KRB5_KEY_LENGTH(&key));
791 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
792 if (!io->g.aes_128.data) {
793 return ldb_oom(ldb);
797 * create ENCTYPE_DES_CBC_MD5 key out of
798 * the salt and the cleartext password
800 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
801 ENCTYPE_DES_CBC_MD5,
802 cleartext_data,
803 salt,
804 &key);
805 if (krb5_ret) {
806 ldb_asprintf_errstring(ldb,
807 "setup_kerberos_keys: "
808 "generation of a des-cbc-md5 key failed: %s",
809 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
810 krb5_ret, io->ac));
811 return LDB_ERR_OPERATIONS_ERROR;
813 io->g.des_md5 = data_blob_talloc(io->ac,
814 KRB5_KEY_DATA(&key),
815 KRB5_KEY_LENGTH(&key));
816 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
817 if (!io->g.des_md5.data) {
818 return ldb_oom(ldb);
822 * create ENCTYPE_DES_CBC_CRC key out of
823 * the salt and the cleartext password
825 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
826 ENCTYPE_DES_CBC_CRC,
827 cleartext_data,
828 salt,
829 &key);
830 if (krb5_ret) {
831 ldb_asprintf_errstring(ldb,
832 "setup_kerberos_keys: "
833 "generation of a des-cbc-crc key failed: %s",
834 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
835 krb5_ret, io->ac));
836 return LDB_ERR_OPERATIONS_ERROR;
838 io->g.des_crc = data_blob_talloc(io->ac,
839 KRB5_KEY_DATA(&key),
840 KRB5_KEY_LENGTH(&key));
841 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
842 if (!io->g.des_crc.data) {
843 return ldb_oom(ldb);
846 return LDB_SUCCESS;
849 static int setup_primary_kerberos(struct setup_password_fields_io *io,
850 const struct supplementalCredentialsBlob *old_scb,
851 struct package_PrimaryKerberosBlob *pkb)
853 struct ldb_context *ldb;
854 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
855 struct supplementalCredentialsPackage *old_scp = NULL;
856 struct package_PrimaryKerberosBlob _old_pkb;
857 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
858 uint32_t i;
859 enum ndr_err_code ndr_err;
861 ldb = ldb_module_get_ctx(io->ac->module);
864 * prepare generation of keys
866 * ENCTYPE_DES_CBC_MD5
867 * ENCTYPE_DES_CBC_CRC
869 pkb->version = 3;
870 pkb3->salt.string = io->g.salt;
871 pkb3->num_keys = 2;
872 pkb3->keys = talloc_array(io->ac,
873 struct package_PrimaryKerberosKey3,
874 pkb3->num_keys);
875 if (!pkb3->keys) {
876 return ldb_oom(ldb);
879 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
880 pkb3->keys[0].value = &io->g.des_md5;
881 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
882 pkb3->keys[1].value = &io->g.des_crc;
884 /* initialize the old keys to zero */
885 pkb3->num_old_keys = 0;
886 pkb3->old_keys = NULL;
888 /* if there're no old keys, then we're done */
889 if (!old_scb) {
890 return LDB_SUCCESS;
893 for (i=0; i < old_scb->sub.num_packages; i++) {
894 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
895 continue;
898 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
899 continue;
902 old_scp = &old_scb->sub.packages[i];
903 break;
905 /* Primary:Kerberos element of supplementalCredentials */
906 if (old_scp) {
907 DATA_BLOB blob;
909 blob = strhex_to_data_blob(io->ac, old_scp->data);
910 if (!blob.data) {
911 return ldb_oom(ldb);
914 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
915 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
916 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
917 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
918 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
919 ldb_asprintf_errstring(ldb,
920 "setup_primary_kerberos: "
921 "failed to pull old package_PrimaryKerberosBlob: %s",
922 nt_errstr(status));
923 return LDB_ERR_OPERATIONS_ERROR;
926 if (_old_pkb.version != 3) {
927 ldb_asprintf_errstring(ldb,
928 "setup_primary_kerberos: "
929 "package_PrimaryKerberosBlob version[%u] expected[3]",
930 _old_pkb.version);
931 return LDB_ERR_OPERATIONS_ERROR;
934 old_pkb3 = &_old_pkb.ctr.ctr3;
937 /* if we didn't found the old keys we're done */
938 if (!old_pkb3) {
939 return LDB_SUCCESS;
942 /* fill in the old keys */
943 pkb3->num_old_keys = old_pkb3->num_keys;
944 pkb3->old_keys = old_pkb3->keys;
946 return LDB_SUCCESS;
949 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
950 const struct supplementalCredentialsBlob *old_scb,
951 struct package_PrimaryKerberosBlob *pkb)
953 struct ldb_context *ldb;
954 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
955 struct supplementalCredentialsPackage *old_scp = NULL;
956 struct package_PrimaryKerberosBlob _old_pkb;
957 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
958 uint32_t i;
959 enum ndr_err_code ndr_err;
961 ldb = ldb_module_get_ctx(io->ac->module);
964 * prepare generation of keys
966 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
967 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
968 * ENCTYPE_DES_CBC_MD5
969 * ENCTYPE_DES_CBC_CRC
971 pkb->version = 4;
972 pkb4->salt.string = io->g.salt;
973 pkb4->default_iteration_count = 4096;
974 pkb4->num_keys = 4;
976 pkb4->keys = talloc_array(io->ac,
977 struct package_PrimaryKerberosKey4,
978 pkb4->num_keys);
979 if (!pkb4->keys) {
980 return ldb_oom(ldb);
983 pkb4->keys[0].iteration_count = 4096;
984 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
985 pkb4->keys[0].value = &io->g.aes_256;
986 pkb4->keys[1].iteration_count = 4096;
987 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
988 pkb4->keys[1].value = &io->g.aes_128;
989 pkb4->keys[2].iteration_count = 4096;
990 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
991 pkb4->keys[2].value = &io->g.des_md5;
992 pkb4->keys[3].iteration_count = 4096;
993 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
994 pkb4->keys[3].value = &io->g.des_crc;
996 /* initialize the old keys to zero */
997 pkb4->num_old_keys = 0;
998 pkb4->old_keys = NULL;
999 pkb4->num_older_keys = 0;
1000 pkb4->older_keys = NULL;
1002 /* if there're no old keys, then we're done */
1003 if (!old_scb) {
1004 return LDB_SUCCESS;
1007 for (i=0; i < old_scb->sub.num_packages; i++) {
1008 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
1009 continue;
1012 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
1013 continue;
1016 old_scp = &old_scb->sub.packages[i];
1017 break;
1019 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1020 if (old_scp) {
1021 DATA_BLOB blob;
1023 blob = strhex_to_data_blob(io->ac, old_scp->data);
1024 if (!blob.data) {
1025 return ldb_oom(ldb);
1028 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1029 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
1030 &_old_pkb,
1031 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
1032 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1033 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1034 ldb_asprintf_errstring(ldb,
1035 "setup_primary_kerberos_newer: "
1036 "failed to pull old package_PrimaryKerberosBlob: %s",
1037 nt_errstr(status));
1038 return LDB_ERR_OPERATIONS_ERROR;
1041 if (_old_pkb.version != 4) {
1042 ldb_asprintf_errstring(ldb,
1043 "setup_primary_kerberos_newer: "
1044 "package_PrimaryKerberosBlob version[%u] expected[4]",
1045 _old_pkb.version);
1046 return LDB_ERR_OPERATIONS_ERROR;
1049 old_pkb4 = &_old_pkb.ctr.ctr4;
1052 /* if we didn't found the old keys we're done */
1053 if (!old_pkb4) {
1054 return LDB_SUCCESS;
1057 /* fill in the old keys */
1058 pkb4->num_old_keys = old_pkb4->num_keys;
1059 pkb4->old_keys = old_pkb4->keys;
1060 pkb4->num_older_keys = old_pkb4->num_old_keys;
1061 pkb4->older_keys = old_pkb4->old_keys;
1063 return LDB_SUCCESS;
1066 static int setup_primary_wdigest(struct setup_password_fields_io *io,
1067 const struct supplementalCredentialsBlob *old_scb,
1068 struct package_PrimaryWDigestBlob *pdb)
1070 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1071 DATA_BLOB sAMAccountName;
1072 DATA_BLOB sAMAccountName_l;
1073 DATA_BLOB sAMAccountName_u;
1074 const char *user_principal_name = io->u.user_principal_name;
1075 DATA_BLOB userPrincipalName;
1076 DATA_BLOB userPrincipalName_l;
1077 DATA_BLOB userPrincipalName_u;
1078 DATA_BLOB netbios_domain;
1079 DATA_BLOB netbios_domain_l;
1080 DATA_BLOB netbios_domain_u;
1081 DATA_BLOB dns_domain;
1082 DATA_BLOB dns_domain_l;
1083 DATA_BLOB dns_domain_u;
1084 DATA_BLOB digest;
1085 DATA_BLOB delim;
1086 DATA_BLOB backslash;
1087 uint8_t i;
1088 struct {
1089 DATA_BLOB *user;
1090 DATA_BLOB *realm;
1091 DATA_BLOB *nt4dom;
1092 } wdigest[] = {
1094 * See
1095 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1096 * for what precalculated hashes are supposed to be stored...
1098 * I can't reproduce all values which should contain "Digest" as realm,
1099 * am I doing something wrong or is w2k3 just broken...?
1101 * W2K3 fills in following for a user:
1103 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1104 * sAMAccountName: NewUser2Sam
1105 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1107 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1108 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1109 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1110 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1111 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1112 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1113 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1114 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1115 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1116 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1117 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1118 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1119 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1120 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1121 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1122 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1123 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1124 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1125 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1126 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1127 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1128 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1129 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1130 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1131 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1132 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1133 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1134 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1135 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1137 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1138 * sAMAccountName: NewUser2Sam
1140 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1141 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1142 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1143 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1144 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1145 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1146 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1147 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1148 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1149 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1150 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1151 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1152 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1153 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1154 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1155 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1156 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1157 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1158 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1159 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1160 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1161 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1162 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1163 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1164 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1165 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1166 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1167 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1168 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1172 * sAMAccountName, netbios_domain
1175 .user = &sAMAccountName,
1176 .realm = &netbios_domain,
1179 .user = &sAMAccountName_l,
1180 .realm = &netbios_domain_l,
1183 .user = &sAMAccountName_u,
1184 .realm = &netbios_domain_u,
1187 .user = &sAMAccountName,
1188 .realm = &netbios_domain_u,
1191 .user = &sAMAccountName,
1192 .realm = &netbios_domain_l,
1195 .user = &sAMAccountName_u,
1196 .realm = &netbios_domain_l,
1199 .user = &sAMAccountName_l,
1200 .realm = &netbios_domain_u,
1203 * sAMAccountName, dns_domain
1206 .user = &sAMAccountName,
1207 .realm = &dns_domain,
1210 .user = &sAMAccountName_l,
1211 .realm = &dns_domain_l,
1214 .user = &sAMAccountName_u,
1215 .realm = &dns_domain_u,
1218 .user = &sAMAccountName,
1219 .realm = &dns_domain_u,
1222 .user = &sAMAccountName,
1223 .realm = &dns_domain_l,
1226 .user = &sAMAccountName_u,
1227 .realm = &dns_domain_l,
1230 .user = &sAMAccountName_l,
1231 .realm = &dns_domain_u,
1234 * userPrincipalName, no realm
1237 .user = &userPrincipalName,
1241 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1242 * the fallback to the sAMAccountName based userPrincipalName is correct
1244 .user = &userPrincipalName_l,
1247 .user = &userPrincipalName_u,
1250 * nt4dom\sAMAccountName, no realm
1253 .user = &sAMAccountName,
1254 .nt4dom = &netbios_domain
1257 .user = &sAMAccountName_l,
1258 .nt4dom = &netbios_domain_l
1261 .user = &sAMAccountName_u,
1262 .nt4dom = &netbios_domain_u
1266 * the following ones are guessed depending on the technet2 article
1267 * but not reproducable on a w2k3 server
1269 /* sAMAccountName with "Digest" realm */
1271 .user = &sAMAccountName,
1272 .realm = &digest
1275 .user = &sAMAccountName_l,
1276 .realm = &digest
1279 .user = &sAMAccountName_u,
1280 .realm = &digest
1282 /* userPrincipalName with "Digest" realm */
1284 .user = &userPrincipalName,
1285 .realm = &digest
1288 .user = &userPrincipalName_l,
1289 .realm = &digest
1292 .user = &userPrincipalName_u,
1293 .realm = &digest
1295 /* nt4dom\\sAMAccountName with "Digest" realm */
1297 .user = &sAMAccountName,
1298 .nt4dom = &netbios_domain,
1299 .realm = &digest
1302 .user = &sAMAccountName_l,
1303 .nt4dom = &netbios_domain_l,
1304 .realm = &digest
1307 .user = &sAMAccountName_u,
1308 .nt4dom = &netbios_domain_u,
1309 .realm = &digest
1313 /* prepare DATA_BLOB's used in the combinations array */
1314 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
1315 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
1316 if (!sAMAccountName_l.data) {
1317 return ldb_oom(ldb);
1319 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
1320 if (!sAMAccountName_u.data) {
1321 return ldb_oom(ldb);
1324 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1325 if (!user_principal_name) {
1326 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
1327 io->u.sAMAccountName,
1328 io->ac->status->domain_data.dns_domain);
1329 if (!user_principal_name) {
1330 return ldb_oom(ldb);
1333 userPrincipalName = data_blob_string_const(user_principal_name);
1334 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
1335 if (!userPrincipalName_l.data) {
1336 return ldb_oom(ldb);
1338 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
1339 if (!userPrincipalName_u.data) {
1340 return ldb_oom(ldb);
1343 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
1344 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
1345 io->ac->status->domain_data.netbios_domain));
1346 if (!netbios_domain_l.data) {
1347 return ldb_oom(ldb);
1349 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
1350 io->ac->status->domain_data.netbios_domain));
1351 if (!netbios_domain_u.data) {
1352 return ldb_oom(ldb);
1355 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1356 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1357 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
1359 digest = data_blob_string_const("Digest");
1361 delim = data_blob_string_const(":");
1362 backslash = data_blob_string_const("\\");
1364 pdb->num_hashes = ARRAY_SIZE(wdigest);
1365 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
1366 pdb->num_hashes);
1367 if (!pdb->hashes) {
1368 return ldb_oom(ldb);
1371 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
1372 MD5_CTX md5;
1373 MD5Init(&md5);
1374 if (wdigest[i].nt4dom) {
1375 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
1376 MD5Update(&md5, backslash.data, backslash.length);
1378 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
1379 MD5Update(&md5, delim.data, delim.length);
1380 if (wdigest[i].realm) {
1381 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1383 MD5Update(&md5, delim.data, delim.length);
1384 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1385 MD5Final(pdb->hashes[i].hash, &md5);
1388 return LDB_SUCCESS;
1391 static int setup_supplemental_field(struct setup_password_fields_io *io)
1393 struct ldb_context *ldb;
1394 struct supplementalCredentialsBlob scb;
1395 struct supplementalCredentialsBlob _old_scb;
1396 struct supplementalCredentialsBlob *old_scb = NULL;
1397 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1398 uint32_t num_names = 0;
1399 const char *names[1+4];
1400 uint32_t num_packages = 0;
1401 struct supplementalCredentialsPackage packages[1+4];
1402 /* Packages */
1403 struct supplementalCredentialsPackage *pp = NULL;
1404 struct package_PackagesBlob pb;
1405 DATA_BLOB pb_blob;
1406 char *pb_hexstr;
1407 /* Primary:Kerberos-Newer-Keys */
1408 const char **nkn = NULL;
1409 struct supplementalCredentialsPackage *pkn = NULL;
1410 struct package_PrimaryKerberosBlob pknb;
1411 DATA_BLOB pknb_blob;
1412 char *pknb_hexstr;
1413 /* Primary:Kerberos */
1414 const char **nk = NULL;
1415 struct supplementalCredentialsPackage *pk = NULL;
1416 struct package_PrimaryKerberosBlob pkb;
1417 DATA_BLOB pkb_blob;
1418 char *pkb_hexstr;
1419 /* Primary:WDigest */
1420 const char **nd = NULL;
1421 struct supplementalCredentialsPackage *pd = NULL;
1422 struct package_PrimaryWDigestBlob pdb;
1423 DATA_BLOB pdb_blob;
1424 char *pdb_hexstr;
1425 /* Primary:CLEARTEXT */
1426 const char **nc = NULL;
1427 struct supplementalCredentialsPackage *pc = NULL;
1428 struct package_PrimaryCLEARTEXTBlob pcb;
1429 DATA_BLOB pcb_blob;
1430 char *pcb_hexstr;
1431 int ret;
1432 enum ndr_err_code ndr_err;
1433 uint8_t zero16[16];
1434 bool do_newer_keys = false;
1435 bool do_cleartext = false;
1437 ZERO_STRUCT(zero16);
1438 ZERO_STRUCT(names);
1440 ldb = ldb_module_get_ctx(io->ac->module);
1442 if (!io->n.cleartext_utf8) {
1444 * when we don't have a cleartext password
1445 * we can't setup a supplementalCredential value
1447 return LDB_SUCCESS;
1450 /* if there's an old supplementaCredentials blob then parse it */
1451 if (io->o.supplemental) {
1452 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1453 &_old_scb,
1454 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1455 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1456 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1457 ldb_asprintf_errstring(ldb,
1458 "setup_supplemental_field: "
1459 "failed to pull old supplementalCredentialsBlob: %s",
1460 nt_errstr(status));
1461 return LDB_ERR_OPERATIONS_ERROR;
1464 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1465 old_scb = &_old_scb;
1466 } else {
1467 ldb_debug(ldb, LDB_DEBUG_ERROR,
1468 "setup_supplemental_field: "
1469 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1470 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1473 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1474 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1476 if (io->ac->status->domain_data.store_cleartext &&
1477 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1478 do_cleartext = true;
1482 * The ordering is this
1484 * Primary:Kerberos-Newer-Keys (optional)
1485 * Primary:Kerberos
1486 * Primary:WDigest
1487 * Primary:CLEARTEXT (optional)
1489 * And the 'Packages' package is insert before the last
1490 * other package.
1492 if (do_newer_keys) {
1493 /* Primary:Kerberos-Newer-Keys */
1494 nkn = &names[num_names++];
1495 pkn = &packages[num_packages++];
1498 /* Primary:Kerberos */
1499 nk = &names[num_names++];
1500 pk = &packages[num_packages++];
1502 if (!do_cleartext) {
1503 /* Packages */
1504 pp = &packages[num_packages++];
1507 /* Primary:WDigest */
1508 nd = &names[num_names++];
1509 pd = &packages[num_packages++];
1511 if (do_cleartext) {
1512 /* Packages */
1513 pp = &packages[num_packages++];
1515 /* Primary:CLEARTEXT */
1516 nc = &names[num_names++];
1517 pc = &packages[num_packages++];
1520 if (pkn) {
1522 * setup 'Primary:Kerberos-Newer-Keys' element
1524 *nkn = "Kerberos-Newer-Keys";
1526 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1527 if (ret != LDB_SUCCESS) {
1528 return ret;
1531 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1532 &pknb,
1533 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1534 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1535 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1536 ldb_asprintf_errstring(ldb,
1537 "setup_supplemental_field: "
1538 "failed to push package_PrimaryKerberosNeverBlob: %s",
1539 nt_errstr(status));
1540 return LDB_ERR_OPERATIONS_ERROR;
1542 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1543 if (!pknb_hexstr) {
1544 return ldb_oom(ldb);
1546 pkn->name = "Primary:Kerberos-Newer-Keys";
1547 pkn->reserved = 1;
1548 pkn->data = pknb_hexstr;
1552 * setup 'Primary:Kerberos' element
1554 *nk = "Kerberos";
1556 ret = setup_primary_kerberos(io, old_scb, &pkb);
1557 if (ret != LDB_SUCCESS) {
1558 return ret;
1561 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1562 &pkb,
1563 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1564 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1565 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1566 ldb_asprintf_errstring(ldb,
1567 "setup_supplemental_field: "
1568 "failed to push package_PrimaryKerberosBlob: %s",
1569 nt_errstr(status));
1570 return LDB_ERR_OPERATIONS_ERROR;
1572 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1573 if (!pkb_hexstr) {
1574 return ldb_oom(ldb);
1576 pk->name = "Primary:Kerberos";
1577 pk->reserved = 1;
1578 pk->data = pkb_hexstr;
1581 * setup 'Primary:WDigest' element
1583 *nd = "WDigest";
1585 ret = setup_primary_wdigest(io, old_scb, &pdb);
1586 if (ret != LDB_SUCCESS) {
1587 return ret;
1590 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1591 &pdb,
1592 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1593 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1594 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1595 ldb_asprintf_errstring(ldb,
1596 "setup_supplemental_field: "
1597 "failed to push package_PrimaryWDigestBlob: %s",
1598 nt_errstr(status));
1599 return LDB_ERR_OPERATIONS_ERROR;
1601 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1602 if (!pdb_hexstr) {
1603 return ldb_oom(ldb);
1605 pd->name = "Primary:WDigest";
1606 pd->reserved = 1;
1607 pd->data = pdb_hexstr;
1610 * setup 'Primary:CLEARTEXT' element
1612 if (pc) {
1613 *nc = "CLEARTEXT";
1615 pcb.cleartext = *io->n.cleartext_utf16;
1617 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1618 &pcb,
1619 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1620 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1621 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1622 ldb_asprintf_errstring(ldb,
1623 "setup_supplemental_field: "
1624 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1625 nt_errstr(status));
1626 return LDB_ERR_OPERATIONS_ERROR;
1628 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1629 if (!pcb_hexstr) {
1630 return ldb_oom(ldb);
1632 pc->name = "Primary:CLEARTEXT";
1633 pc->reserved = 1;
1634 pc->data = pcb_hexstr;
1638 * setup 'Packages' element
1640 pb.names = names;
1641 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1642 &pb,
1643 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1644 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1645 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1646 ldb_asprintf_errstring(ldb,
1647 "setup_supplemental_field: "
1648 "failed to push package_PackagesBlob: %s",
1649 nt_errstr(status));
1650 return LDB_ERR_OPERATIONS_ERROR;
1652 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1653 if (!pb_hexstr) {
1654 return ldb_oom(ldb);
1656 pp->name = "Packages";
1657 pp->reserved = 2;
1658 pp->data = pb_hexstr;
1661 * setup 'supplementalCredentials' value
1663 ZERO_STRUCT(scb);
1664 scb.sub.num_packages = num_packages;
1665 scb.sub.packages = packages;
1667 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1668 &scb,
1669 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1670 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1671 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1672 ldb_asprintf_errstring(ldb,
1673 "setup_supplemental_field: "
1674 "failed to push supplementalCredentialsBlob: %s",
1675 nt_errstr(status));
1676 return LDB_ERR_OPERATIONS_ERROR;
1679 return LDB_SUCCESS;
1682 static int setup_last_set_field(struct setup_password_fields_io *io)
1684 const struct ldb_message *msg = NULL;
1686 switch (io->ac->req->operation) {
1687 case LDB_ADD:
1688 msg = io->ac->req->op.add.message;
1689 break;
1690 case LDB_MODIFY:
1691 msg = io->ac->req->op.mod.message;
1692 break;
1693 default:
1694 return LDB_ERR_OPERATIONS_ERROR;
1695 break;
1698 if (io->ac->pwd_last_set_bypass) {
1699 struct ldb_message_element *el;
1701 if (msg == NULL) {
1702 return LDB_ERR_CONSTRAINT_VIOLATION;
1705 el = ldb_msg_find_element(msg, "pwdLastSet");
1706 if (el == NULL) {
1707 return LDB_ERR_CONSTRAINT_VIOLATION;
1710 io->g.last_set = samdb_result_nttime(msg, "pwdLastSet", 0);
1711 return LDB_SUCCESS;
1714 /* set it as now */
1715 unix_to_nt_time(&io->g.last_set, time(NULL));
1717 return LDB_SUCCESS;
1720 static int setup_given_passwords(struct setup_password_fields_io *io,
1721 struct setup_password_fields_given *g)
1723 struct ldb_context *ldb;
1724 bool ok;
1726 ldb = ldb_module_get_ctx(io->ac->module);
1728 if (g->cleartext_utf8) {
1729 struct ldb_val *cleartext_utf16_blob;
1731 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1732 if (!cleartext_utf16_blob) {
1733 return ldb_oom(ldb);
1735 if (!convert_string_talloc(io->ac,
1736 CH_UTF8, CH_UTF16,
1737 g->cleartext_utf8->data,
1738 g->cleartext_utf8->length,
1739 (void *)&cleartext_utf16_blob->data,
1740 &cleartext_utf16_blob->length)) {
1741 if (g->cleartext_utf8->length != 0) {
1742 talloc_free(cleartext_utf16_blob);
1743 ldb_asprintf_errstring(ldb,
1744 "setup_password_fields: "
1745 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1746 io->u.sAMAccountName);
1747 return LDB_ERR_CONSTRAINT_VIOLATION;
1748 } else {
1749 /* passwords with length "0" are valid! */
1750 cleartext_utf16_blob->data = NULL;
1751 cleartext_utf16_blob->length = 0;
1754 g->cleartext_utf16 = cleartext_utf16_blob;
1755 } else if (g->cleartext_utf16) {
1756 struct ldb_val *cleartext_utf8_blob;
1758 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1759 if (!cleartext_utf8_blob) {
1760 return ldb_oom(ldb);
1762 if (!convert_string_talloc(io->ac,
1763 CH_UTF16MUNGED, CH_UTF8,
1764 g->cleartext_utf16->data,
1765 g->cleartext_utf16->length,
1766 (void *)&cleartext_utf8_blob->data,
1767 &cleartext_utf8_blob->length)) {
1768 if (g->cleartext_utf16->length != 0) {
1769 /* We must bail out here, the input wasn't even
1770 * a multiple of 2 bytes */
1771 talloc_free(cleartext_utf8_blob);
1772 ldb_asprintf_errstring(ldb,
1773 "setup_password_fields: "
1774 "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)!",
1775 io->u.sAMAccountName);
1776 return LDB_ERR_CONSTRAINT_VIOLATION;
1777 } else {
1778 /* passwords with length "0" are valid! */
1779 cleartext_utf8_blob->data = NULL;
1780 cleartext_utf8_blob->length = 0;
1783 g->cleartext_utf8 = cleartext_utf8_blob;
1786 if (g->cleartext_utf16) {
1787 struct samr_Password *nt_hash;
1789 nt_hash = talloc(io->ac, struct samr_Password);
1790 if (!nt_hash) {
1791 return ldb_oom(ldb);
1793 g->nt_hash = nt_hash;
1795 /* compute the new nt hash */
1796 mdfour(nt_hash->hash,
1797 g->cleartext_utf16->data,
1798 g->cleartext_utf16->length);
1801 if (g->cleartext_utf8) {
1802 struct samr_Password *lm_hash;
1804 lm_hash = talloc(io->ac, struct samr_Password);
1805 if (!lm_hash) {
1806 return ldb_oom(ldb);
1809 /* compute the new lm hash */
1810 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1811 if (ok) {
1812 g->lm_hash = lm_hash;
1813 } else {
1814 talloc_free(lm_hash);
1818 return LDB_SUCCESS;
1821 static int setup_password_fields(struct setup_password_fields_io *io)
1823 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1824 struct loadparm_context *lp_ctx =
1825 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1826 struct loadparm_context);
1827 int ret;
1829 /* transform the old password (for password changes) */
1830 ret = setup_given_passwords(io, &io->og);
1831 if (ret != LDB_SUCCESS) {
1832 return ret;
1835 /* transform the new password */
1836 ret = setup_given_passwords(io, &io->n);
1837 if (ret != LDB_SUCCESS) {
1838 return ret;
1841 if (io->n.cleartext_utf8) {
1842 ret = setup_kerberos_keys(io);
1843 if (ret != LDB_SUCCESS) {
1844 return ret;
1848 ret = setup_nt_fields(io);
1849 if (ret != LDB_SUCCESS) {
1850 return ret;
1853 if (lpcfg_lanman_auth(lp_ctx)) {
1854 ret = setup_lm_fields(io);
1855 if (ret != LDB_SUCCESS) {
1856 return ret;
1858 } else {
1859 io->g.lm_hash = NULL;
1860 io->g.lm_history_len = 0;
1863 ret = setup_supplemental_field(io);
1864 if (ret != LDB_SUCCESS) {
1865 return ret;
1868 ret = setup_last_set_field(io);
1869 if (ret != LDB_SUCCESS) {
1870 return ret;
1873 return LDB_SUCCESS;
1876 static int make_error_and_update_badPwdCount(struct setup_password_fields_io *io)
1878 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1879 struct ldb_message *mod_msg = NULL;
1880 NTSTATUS status;
1881 int ret;
1883 status = dsdb_update_bad_pwd_count(io->ac, ldb,
1884 io->ac->search_res->message,
1885 io->ac->dom_res->message,
1886 &mod_msg);
1887 if (!NT_STATUS_IS_OK(status)) {
1888 goto done;
1891 if (mod_msg == NULL) {
1892 goto done;
1896 * OK, horrible semantics ahead.
1898 * - We need to abort any existing transaction
1899 * - create a transaction arround the badPwdCount update
1900 * - re-open the transaction so the upper layer
1901 * doesn't know what happened.
1903 * This is needed because returning an error to the upper
1904 * layer will cancel the transaction and undo the badPwdCount
1905 * update.
1909 * Checking errors here is a bit pointless.
1910 * What can we do if we can't end the transaction?
1912 ret = ldb_next_del_trans(io->ac->module);
1913 if (ret != LDB_SUCCESS) {
1914 ldb_debug(ldb, LDB_DEBUG_FATAL,
1915 "Failed to abort transaction prior to update of badPwdCount of %s: %s",
1916 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1917 ldb_errstring(ldb));
1919 * just return the original error
1921 goto done;
1924 /* Likewise, what should we do if we can't open a new transaction? */
1925 ret = ldb_next_start_trans(io->ac->module);
1926 if (ret != LDB_SUCCESS) {
1927 ldb_debug(ldb, LDB_DEBUG_ERROR,
1928 "Failed to open transaction to update badPwdCount of %s: %s",
1929 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1930 ldb_errstring(ldb));
1932 * just return the original error
1934 goto done;
1937 ret = dsdb_module_modify(io->ac->module, mod_msg,
1938 DSDB_FLAG_NEXT_MODULE,
1939 io->ac->req);
1940 if (ret != LDB_SUCCESS) {
1941 ldb_debug(ldb, LDB_DEBUG_ERROR,
1942 "Failed to update badPwdCount of %s: %s",
1943 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1944 ldb_errstring(ldb));
1946 * We can only ignore this...
1950 ret = ldb_next_end_trans(io->ac->module);
1951 if (ret != LDB_SUCCESS) {
1952 ldb_debug(ldb, LDB_DEBUG_ERROR,
1953 "Failed to close transaction to update badPwdCount of %s: %s",
1954 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1955 ldb_errstring(ldb));
1957 * We can only ignore this...
1961 ret = ldb_next_start_trans(io->ac->module);
1962 if (ret != LDB_SUCCESS) {
1963 ldb_debug(ldb, LDB_DEBUG_ERROR,
1964 "Failed to open transaction after update of badPwdCount of %s: %s",
1965 ldb_dn_get_linearized(io->ac->search_res->message->dn),
1966 ldb_errstring(ldb));
1968 * We can only ignore this...
1972 done:
1973 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1974 ldb_asprintf_errstring(ldb,
1975 "%08X: %s - check_password_restrictions: "
1976 "The old password specified doesn't match!",
1977 W_ERROR_V(WERR_INVALID_PASSWORD),
1978 ldb_strerror(ret));
1979 return ret;
1982 static int check_password_restrictions(struct setup_password_fields_io *io)
1984 struct ldb_context *ldb;
1985 int ret;
1987 ldb = ldb_module_get_ctx(io->ac->module);
1989 /* First check the old password is correct, for password changes */
1990 if (!io->ac->pwd_reset) {
1991 bool nt_hash_checked = false;
1993 /* we need the old nt or lm hash given by the client */
1994 if (!io->og.nt_hash && !io->og.lm_hash) {
1995 ldb_asprintf_errstring(ldb,
1996 "check_password_restrictions: "
1997 "You need to provide the old password in order "
1998 "to change it!");
1999 return LDB_ERR_UNWILLING_TO_PERFORM;
2002 /* The password modify through the NT hash is encouraged and
2003 has no problems at all */
2004 if (io->og.nt_hash) {
2005 if (!io->o.nt_hash || memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
2006 return make_error_and_update_badPwdCount(io);
2009 nt_hash_checked = true;
2012 /* But it is also possible to change a password by the LM hash
2013 * alone for compatibility reasons. This check is optional if
2014 * the NT hash was already checked - otherwise it's mandatory.
2015 * (as the SAMR operations request it). */
2016 if (io->og.lm_hash) {
2017 if ((!io->o.lm_hash && !nt_hash_checked)
2018 || (io->o.lm_hash && memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0)) {
2019 return make_error_and_update_badPwdCount(io);
2024 if (io->u.restrictions == 0) {
2025 /* FIXME: Is this right? */
2026 return LDB_SUCCESS;
2029 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2030 if ((io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) &&
2031 !io->ac->pwd_reset)
2033 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2034 ldb_asprintf_errstring(ldb,
2035 "%08X: %s - check_password_restrictions: "
2036 "password is too young to change!",
2037 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2038 ldb_strerror(ret));
2039 return ret;
2043 * Fundamental password checks done by the call
2044 * "samdb_check_password".
2045 * It is also in use by "dcesrv_samr_ValidatePassword".
2047 if (io->n.cleartext_utf8 != NULL) {
2048 enum samr_ValidationStatus vstat;
2049 vstat = samdb_check_password(io->n.cleartext_utf8,
2050 io->ac->status->domain_data.pwdProperties,
2051 io->ac->status->domain_data.minPwdLength);
2052 switch (vstat) {
2053 case SAMR_VALIDATION_STATUS_SUCCESS:
2054 /* perfect -> proceed! */
2055 break;
2057 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
2058 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2059 ldb_asprintf_errstring(ldb,
2060 "%08X: %s - check_password_restrictions: "
2061 "the password is too short. It should be equal or longer than %u characters!",
2062 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2063 ldb_strerror(ret),
2064 io->ac->status->domain_data.minPwdLength);
2065 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
2066 return ret;
2068 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
2069 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2070 ldb_asprintf_errstring(ldb,
2071 "%08X: %s - check_password_restrictions: "
2072 "the password does not meet the complexity criteria!",
2073 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2074 ldb_strerror(ret));
2075 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
2076 return ret;
2078 default:
2079 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2080 ldb_asprintf_errstring(ldb,
2081 "%08X: %s - check_password_restrictions: "
2082 "the password doesn't fit by a certain reason!",
2083 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2084 ldb_strerror(ret));
2085 return ret;
2089 if (io->ac->pwd_reset) {
2090 return LDB_SUCCESS;
2093 if (io->n.nt_hash) {
2094 uint32_t i;
2096 /* checks the NT hash password history */
2097 for (i = 0; i < io->o.nt_history_len; i++) {
2098 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
2099 if (ret == 0) {
2100 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2101 ldb_asprintf_errstring(ldb,
2102 "%08X: %s - check_password_restrictions: "
2103 "the password was already used (in history)!",
2104 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2105 ldb_strerror(ret));
2106 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2107 return ret;
2112 if (io->n.lm_hash) {
2113 uint32_t i;
2115 /* checks the LM hash password history */
2116 for (i = 0; i < io->o.lm_history_len; i++) {
2117 ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
2118 if (ret == 0) {
2119 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2120 ldb_asprintf_errstring(ldb,
2121 "%08X: %s - check_password_restrictions: "
2122 "the password was already used (in history)!",
2123 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2124 ldb_strerror(ret));
2125 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2126 return ret;
2131 /* are all password changes disallowed? */
2132 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
2133 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2134 ldb_asprintf_errstring(ldb,
2135 "%08X: %s - check_password_restrictions: "
2136 "password changes disabled!",
2137 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2138 ldb_strerror(ret));
2139 return ret;
2142 /* can this user change the password? */
2143 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
2144 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2145 ldb_asprintf_errstring(ldb,
2146 "%08X: %s - check_password_restrictions: "
2147 "password can't be changed on this account!",
2148 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2149 ldb_strerror(ret));
2150 return ret;
2153 return LDB_SUCCESS;
2157 * This is intended for use by the "password_hash" module since there
2158 * password changes can be specified through one message element with the
2159 * new password (to set) and another one with the old password (to unset).
2161 * The first which sets a password (new value) can have flags
2162 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2163 * for entries). The latter (old value) has always specified
2164 * LDB_FLAG_MOD_DELETE.
2166 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2167 * matching message elements are malformed in respect to the set/change rules.
2168 * Otherwise it returns LDB_SUCCESS.
2170 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
2171 const char *name,
2172 enum ldb_request_type operation,
2173 const struct ldb_val **new_val,
2174 const struct ldb_val **old_val)
2176 unsigned int i;
2178 *new_val = NULL;
2179 *old_val = NULL;
2181 if (msg == NULL) {
2182 return LDB_SUCCESS;
2185 for (i = 0; i < msg->num_elements; i++) {
2186 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
2187 continue;
2190 if ((operation == LDB_MODIFY) &&
2191 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
2192 /* 0 values are allowed */
2193 if (msg->elements[i].num_values == 1) {
2194 *old_val = &msg->elements[i].values[0];
2195 } else if (msg->elements[i].num_values > 1) {
2196 return LDB_ERR_CONSTRAINT_VIOLATION;
2198 } else if ((operation == LDB_MODIFY) &&
2199 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
2200 if (msg->elements[i].num_values > 0) {
2201 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2202 } else {
2203 return LDB_ERR_UNWILLING_TO_PERFORM;
2205 } else {
2206 /* Add operations and LDB_FLAG_MOD_ADD */
2207 if (msg->elements[i].num_values > 0) {
2208 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2209 } else {
2210 return LDB_ERR_CONSTRAINT_VIOLATION;
2215 return LDB_SUCCESS;
2218 static int setup_io(struct ph_context *ac,
2219 const struct ldb_message *orig_msg,
2220 const struct ldb_message *searched_msg,
2221 struct setup_password_fields_io *io)
2223 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
2224 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2225 struct loadparm_context *lp_ctx =
2226 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2227 struct loadparm_context);
2228 int ret;
2230 ZERO_STRUCTP(io);
2232 /* Some operations below require kerberos contexts */
2234 if (smb_krb5_init_context(ac,
2235 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
2236 &io->smb_krb5_context) != 0) {
2237 return ldb_operr(ldb);
2240 io->ac = ac;
2242 io->u.userAccountControl = ldb_msg_find_attr_as_uint(searched_msg,
2243 "userAccountControl", 0);
2244 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
2245 io->u.sAMAccountName = ldb_msg_find_attr_as_string(searched_msg,
2246 "sAMAccountName", NULL);
2247 io->u.user_principal_name = ldb_msg_find_attr_as_string(searched_msg,
2248 "userPrincipalName", NULL);
2249 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
2251 if (io->u.sAMAccountName == NULL) {
2252 ldb_asprintf_errstring(ldb,
2253 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2254 ldb_dn_get_linearized(searched_msg->dn));
2256 return LDB_ERR_CONSTRAINT_VIOLATION;
2259 /* Only non-trust accounts have restrictions (possibly this test is the
2260 * wrong way around, but we like to be restrictive if possible */
2261 io->u.restrictions = !(io->u.userAccountControl
2262 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
2263 | UF_SERVER_TRUST_ACCOUNT));
2265 if (ac->userPassword) {
2266 ret = msg_find_old_and_new_pwd_val(orig_msg, "userPassword",
2267 ac->req->operation,
2268 &io->n.cleartext_utf8,
2269 &io->og.cleartext_utf8);
2270 if (ret != LDB_SUCCESS) {
2271 ldb_asprintf_errstring(ldb,
2272 "setup_io: "
2273 "it's only allowed to set the old password once!");
2274 return ret;
2278 if (io->n.cleartext_utf8 != NULL) {
2279 struct ldb_val *cleartext_utf8_blob;
2280 char *p;
2282 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
2283 if (!cleartext_utf8_blob) {
2284 return ldb_oom(ldb);
2287 *cleartext_utf8_blob = *io->n.cleartext_utf8;
2289 /* make sure we have a null terminated string */
2290 p = talloc_strndup(cleartext_utf8_blob,
2291 (const char *)io->n.cleartext_utf8->data,
2292 io->n.cleartext_utf8->length);
2293 if ((p == NULL) && (io->n.cleartext_utf8->length > 0)) {
2294 return ldb_oom(ldb);
2296 cleartext_utf8_blob->data = (uint8_t *)p;
2298 io->n.cleartext_utf8 = cleartext_utf8_blob;
2301 ret = msg_find_old_and_new_pwd_val(orig_msg, "clearTextPassword",
2302 ac->req->operation,
2303 &io->n.cleartext_utf16,
2304 &io->og.cleartext_utf16);
2305 if (ret != LDB_SUCCESS) {
2306 ldb_asprintf_errstring(ldb,
2307 "setup_io: "
2308 "it's only allowed to set the old password once!");
2309 return ret;
2312 /* this rather strange looking piece of code is there to
2313 handle a ldap client setting a password remotely using the
2314 unicodePwd ldap field. The syntax is that the password is
2315 in UTF-16LE, with a " at either end. Unfortunately the
2316 unicodePwd field is also used to store the nt hashes
2317 internally in Samba, and is used in the nt hash format on
2318 the wire in DRS replication, so we have a single name for
2319 two distinct values. The code below leaves us with a small
2320 chance (less than 1 in 2^32) of a mixup, if someone manages
2321 to create a MD4 hash which starts and ends in 0x22 0x00, as
2322 that would then be treated as a UTF16 password rather than
2323 a nthash */
2325 ret = msg_find_old_and_new_pwd_val(orig_msg, "unicodePwd",
2326 ac->req->operation,
2327 &quoted_utf16,
2328 &old_quoted_utf16);
2329 if (ret != LDB_SUCCESS) {
2330 ldb_asprintf_errstring(ldb,
2331 "setup_io: "
2332 "it's only allowed to set the old password once!");
2333 return ret;
2336 /* Checks and converts the actual "unicodePwd" attribute */
2337 if (!ac->hash_values &&
2338 quoted_utf16 &&
2339 quoted_utf16->length >= 4 &&
2340 quoted_utf16->data[0] == '"' &&
2341 quoted_utf16->data[1] == 0 &&
2342 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
2343 quoted_utf16->data[quoted_utf16->length-1] == 0) {
2344 struct ldb_val *quoted_utf16_2;
2346 if (io->n.cleartext_utf16) {
2347 /* refuse the change if someone wants to change with
2348 with both UTF16 possibilities at the same time... */
2349 ldb_asprintf_errstring(ldb,
2350 "setup_io: "
2351 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2352 return LDB_ERR_UNWILLING_TO_PERFORM;
2356 * adapt the quoted UTF16 string to be a real
2357 * cleartext one
2359 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2360 if (quoted_utf16_2 == NULL) {
2361 return ldb_oom(ldb);
2364 quoted_utf16_2->data = quoted_utf16->data + 2;
2365 quoted_utf16_2->length = quoted_utf16->length-4;
2366 io->n.cleartext_utf16 = quoted_utf16_2;
2367 io->n.nt_hash = NULL;
2369 } else if (quoted_utf16) {
2370 /* We have only the hash available -> so no plaintext here */
2371 if (!ac->hash_values) {
2372 /* refuse the change if someone wants to change
2373 the hash without control specified... */
2374 ldb_asprintf_errstring(ldb,
2375 "setup_io: "
2376 "it's not allowed to set the NT hash password directly'");
2377 /* this looks odd but this is what Windows does:
2378 returns "UNWILLING_TO_PERFORM" on wrong
2379 password sets and "CONSTRAINT_VIOLATION" on
2380 wrong password changes. */
2381 if (old_quoted_utf16 == NULL) {
2382 return LDB_ERR_UNWILLING_TO_PERFORM;
2385 return LDB_ERR_CONSTRAINT_VIOLATION;
2388 io->n.nt_hash = talloc(io->ac, struct samr_Password);
2389 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
2390 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
2393 /* Checks and converts the previous "unicodePwd" attribute */
2394 if (!ac->hash_values &&
2395 old_quoted_utf16 &&
2396 old_quoted_utf16->length >= 4 &&
2397 old_quoted_utf16->data[0] == '"' &&
2398 old_quoted_utf16->data[1] == 0 &&
2399 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
2400 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
2401 struct ldb_val *old_quoted_utf16_2;
2403 if (io->og.cleartext_utf16) {
2404 /* refuse the change if someone wants to change with
2405 both UTF16 possibilities at the same time... */
2406 ldb_asprintf_errstring(ldb,
2407 "setup_io: "
2408 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2409 return LDB_ERR_UNWILLING_TO_PERFORM;
2413 * adapt the quoted UTF16 string to be a real
2414 * cleartext one
2416 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2417 if (old_quoted_utf16_2 == NULL) {
2418 return ldb_oom(ldb);
2421 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
2422 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
2424 io->og.cleartext_utf16 = old_quoted_utf16_2;
2425 io->og.nt_hash = NULL;
2426 } else if (old_quoted_utf16) {
2427 /* We have only the hash available -> so no plaintext here */
2428 if (!ac->hash_values) {
2429 /* refuse the change if someone wants to change
2430 the hash without control specified... */
2431 ldb_asprintf_errstring(ldb,
2432 "setup_io: "
2433 "it's not allowed to set the NT hash password directly'");
2434 return LDB_ERR_UNWILLING_TO_PERFORM;
2437 io->og.nt_hash = talloc(io->ac, struct samr_Password);
2438 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
2439 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
2442 /* Handles the "dBCSPwd" attribute (LM hash) */
2443 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
2444 ret = msg_find_old_and_new_pwd_val(orig_msg, "dBCSPwd",
2445 ac->req->operation,
2446 &lm_hash, &old_lm_hash);
2447 if (ret != LDB_SUCCESS) {
2448 ldb_asprintf_errstring(ldb,
2449 "setup_io: "
2450 "it's only allowed to set the old password once!");
2451 return ret;
2454 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
2455 /* refuse the change if someone wants to change the hash
2456 without control specified... */
2457 ldb_asprintf_errstring(ldb,
2458 "setup_io: "
2459 "it's not allowed to set the LM hash password directly'");
2460 return LDB_ERR_UNWILLING_TO_PERFORM;
2463 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
2464 io->n.lm_hash = talloc(io->ac, struct samr_Password);
2465 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
2466 sizeof(io->n.lm_hash->hash)));
2468 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
2469 io->og.lm_hash = talloc(io->ac, struct samr_Password);
2470 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
2471 sizeof(io->og.lm_hash->hash)));
2475 * Handles the password change control if it's specified. It has the
2476 * precedance and overrides already specified old password values of
2477 * change requests (but that shouldn't happen since the control is
2478 * fully internal and only used in conjunction with replace requests!).
2480 if (ac->change != NULL) {
2481 io->og.nt_hash = NULL;
2482 if (ac->change->old_nt_pwd_hash != NULL) {
2483 io->og.nt_hash = talloc_memdup(io->ac,
2484 ac->change->old_nt_pwd_hash,
2485 sizeof(struct samr_Password));
2487 io->og.lm_hash = NULL;
2488 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
2489 io->og.lm_hash = talloc_memdup(io->ac,
2490 ac->change->old_lm_pwd_hash,
2491 sizeof(struct samr_Password));
2495 /* refuse the change if someone wants to change the clear-
2496 text and supply his own hashes at the same time... */
2497 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
2498 && (io->n.nt_hash || io->n.lm_hash)) {
2499 ldb_asprintf_errstring(ldb,
2500 "setup_io: "
2501 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2502 return LDB_ERR_UNWILLING_TO_PERFORM;
2505 /* refuse the change if someone wants to change the password
2506 using both plaintext methods (UTF8 and UTF16) at the same time... */
2507 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
2508 ldb_asprintf_errstring(ldb,
2509 "setup_io: "
2510 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2511 return LDB_ERR_UNWILLING_TO_PERFORM;
2514 /* refuse the change if someone tries to set/change the password by
2515 * the lanman hash alone and we've deactivated that mechanism. This
2516 * would end in an account without any password! */
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' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2522 /* on "userPassword" and "clearTextPassword" we've to return
2523 * something different, since these are virtual attributes */
2524 if ((ldb_msg_find_element(orig_msg, "userPassword") != NULL) ||
2525 (ldb_msg_find_element(orig_msg, "clearTextPassword") != NULL)) {
2526 return LDB_ERR_CONSTRAINT_VIOLATION;
2528 return LDB_ERR_UNWILLING_TO_PERFORM;
2531 /* refuse the change if someone wants to compare against a plaintext
2532 or hash at the same time for a "password modify" operation... */
2533 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
2534 && (io->og.nt_hash || io->og.lm_hash)) {
2535 ldb_asprintf_errstring(ldb,
2536 "setup_io: "
2537 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2538 return LDB_ERR_UNWILLING_TO_PERFORM;
2541 /* refuse the change if someone wants to compare against both
2542 * plaintexts at the same time for a "password modify" operation... */
2543 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2544 ldb_asprintf_errstring(ldb,
2545 "setup_io: "
2546 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2547 return LDB_ERR_UNWILLING_TO_PERFORM;
2550 /* Decides if we have a password modify or password reset operation */
2551 if (ac->req->operation == LDB_ADD) {
2552 /* On "add" we have only "password reset" */
2553 ac->pwd_reset = true;
2554 } else if (ac->req->operation == LDB_MODIFY) {
2555 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2556 || io->og.nt_hash || io->og.lm_hash) {
2557 /* If we have an old password specified then for sure it
2558 * is a user "password change" */
2559 ac->pwd_reset = false;
2560 } else {
2561 /* Otherwise we have also here a "password reset" */
2562 ac->pwd_reset = true;
2564 } else {
2565 /* this shouldn't happen */
2566 return ldb_operr(ldb);
2569 return LDB_SUCCESS;
2572 static struct ph_context *ph_init_context(struct ldb_module *module,
2573 struct ldb_request *req,
2574 bool userPassword)
2576 struct ldb_context *ldb;
2577 struct ph_context *ac;
2579 ldb = ldb_module_get_ctx(module);
2581 ac = talloc_zero(req, struct ph_context);
2582 if (ac == NULL) {
2583 ldb_set_errstring(ldb, "Out of Memory");
2584 return NULL;
2587 ac->module = module;
2588 ac->req = req;
2589 ac->userPassword = userPassword;
2591 return ac;
2594 static void ph_apply_controls(struct ph_context *ac)
2596 struct ldb_control *ctrl;
2598 ac->change_status = false;
2599 ctrl = ldb_request_get_control(ac->req,
2600 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2601 if (ctrl != NULL) {
2602 ac->change_status = true;
2604 /* Mark the "change status" control as uncritical (done) */
2605 ctrl->critical = false;
2608 ac->hash_values = false;
2609 ctrl = ldb_request_get_control(ac->req,
2610 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2611 if (ctrl != NULL) {
2612 ac->hash_values = true;
2614 /* Mark the "hash values" control as uncritical (done) */
2615 ctrl->critical = false;
2618 ctrl = ldb_request_get_control(ac->req,
2619 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2620 if (ctrl != NULL) {
2621 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2623 /* Mark the "change" control as uncritical (done) */
2624 ctrl->critical = false;
2627 ac->pwd_last_set_bypass = false;
2628 ctrl = ldb_request_get_control(ac->req,
2629 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID);
2630 if (ctrl != NULL) {
2631 ac->pwd_last_set_bypass = true;
2633 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2634 ctrl->critical = false;
2638 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2640 struct ph_context *ac;
2642 ac = talloc_get_type(req->context, struct ph_context);
2644 if (!ares) {
2645 return ldb_module_done(ac->req, NULL, NULL,
2646 LDB_ERR_OPERATIONS_ERROR);
2649 if (ares->type == LDB_REPLY_REFERRAL) {
2650 return ldb_module_send_referral(ac->req, ares->referral);
2653 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2654 /* On success and trivial errors a status control is being
2655 * added (used for example by the "samdb_set_password" call) */
2656 ldb_reply_add_control(ares,
2657 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2658 false,
2659 ac->status);
2662 if (ares->error != LDB_SUCCESS) {
2663 return ldb_module_done(ac->req, ares->controls,
2664 ares->response, ares->error);
2667 if (ares->type != LDB_REPLY_DONE) {
2668 talloc_free(ares);
2669 return ldb_module_done(ac->req, NULL, NULL,
2670 LDB_ERR_OPERATIONS_ERROR);
2673 return ldb_module_done(ac->req, ares->controls,
2674 ares->response, ares->error);
2677 static int password_hash_add_do_add(struct ph_context *ac);
2678 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2679 static int password_hash_mod_search_self(struct ph_context *ac);
2680 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2681 static int password_hash_mod_do_mod(struct ph_context *ac);
2683 static int get_domain_data_callback(struct ldb_request *req,
2684 struct ldb_reply *ares)
2686 struct ldb_context *ldb;
2687 struct ph_context *ac;
2688 struct loadparm_context *lp_ctx;
2689 int ret = LDB_SUCCESS;
2691 ac = talloc_get_type(req->context, struct ph_context);
2692 ldb = ldb_module_get_ctx(ac->module);
2694 if (!ares) {
2695 ret = LDB_ERR_OPERATIONS_ERROR;
2696 goto done;
2698 if (ares->error != LDB_SUCCESS) {
2699 return ldb_module_done(ac->req, ares->controls,
2700 ares->response, ares->error);
2703 switch (ares->type) {
2704 case LDB_REPLY_ENTRY:
2705 if (ac->status != NULL) {
2706 talloc_free(ares);
2708 ldb_set_errstring(ldb, "Too many results");
2709 ret = LDB_ERR_OPERATIONS_ERROR;
2710 goto done;
2713 /* Setup the "status" structure (used as control later) */
2714 ac->status = talloc_zero(ac->req,
2715 struct dsdb_control_password_change_status);
2716 if (ac->status == NULL) {
2717 talloc_free(ares);
2719 ldb_oom(ldb);
2720 ret = LDB_ERR_OPERATIONS_ERROR;
2721 goto done;
2724 /* Setup the "domain data" structure */
2725 ac->status->domain_data.pwdProperties =
2726 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
2727 ac->status->domain_data.pwdHistoryLength =
2728 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
2729 ac->status->domain_data.maxPwdAge =
2730 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
2731 ac->status->domain_data.minPwdAge =
2732 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
2733 ac->status->domain_data.minPwdLength =
2734 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
2735 ac->status->domain_data.store_cleartext =
2736 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2738 /* For a domain DN, this puts things in dotted notation */
2739 /* For builtin domains, this will give details for the host,
2740 * but that doesn't really matter, as it's just used for salt
2741 * and kerberos principals, which don't exist here */
2743 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2744 struct loadparm_context);
2746 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
2747 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
2748 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
2750 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2752 if (ac->dom_res != NULL) {
2753 talloc_free(ares);
2755 ldb_set_errstring(ldb, "Too many results");
2756 ret = LDB_ERR_OPERATIONS_ERROR;
2757 goto done;
2760 ac->dom_res = talloc_steal(ac, ares);
2761 ret = LDB_SUCCESS;
2762 break;
2764 case LDB_REPLY_REFERRAL:
2765 /* ignore */
2766 talloc_free(ares);
2767 ret = LDB_SUCCESS;
2768 break;
2770 case LDB_REPLY_DONE:
2771 talloc_free(ares);
2772 /* call the next step */
2773 switch (ac->req->operation) {
2774 case LDB_ADD:
2775 ret = password_hash_add_do_add(ac);
2776 break;
2778 case LDB_MODIFY:
2779 ret = password_hash_mod_do_mod(ac);
2780 break;
2782 default:
2783 ret = LDB_ERR_OPERATIONS_ERROR;
2784 break;
2786 break;
2789 done:
2790 if (ret != LDB_SUCCESS) {
2791 struct ldb_reply *new_ares;
2793 new_ares = talloc_zero(ac->req, struct ldb_reply);
2794 if (new_ares == NULL) {
2795 ldb_oom(ldb);
2796 return ldb_module_done(ac->req, NULL, NULL,
2797 LDB_ERR_OPERATIONS_ERROR);
2800 new_ares->error = ret;
2801 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2802 /* On success and trivial errors a status control is being
2803 * added (used for example by the "samdb_set_password" call) */
2804 ldb_reply_add_control(new_ares,
2805 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2806 false,
2807 ac->status);
2810 return ldb_module_done(ac->req, new_ares->controls,
2811 new_ares->response, new_ares->error);
2814 return LDB_SUCCESS;
2817 static int build_domain_data_request(struct ph_context *ac)
2819 /* attrs[] is returned from this function in
2820 ac->dom_req->op.search.attrs, so it must be static, as
2821 otherwise the compiler can put it on the stack */
2822 struct ldb_context *ldb;
2823 static const char * const attrs[] = { "pwdProperties",
2824 "pwdHistoryLength",
2825 "maxPwdAge",
2826 "minPwdAge",
2827 "minPwdLength",
2828 "lockoutThreshold",
2829 "lockOutObservationWindow",
2830 NULL };
2831 int ret;
2833 ldb = ldb_module_get_ctx(ac->module);
2835 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
2836 ldb_get_default_basedn(ldb),
2837 LDB_SCOPE_BASE,
2838 NULL, attrs,
2839 NULL,
2840 ac, get_domain_data_callback,
2841 ac->req);
2842 LDB_REQ_SET_LOCATION(ac->dom_req);
2843 return ret;
2846 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2848 struct ldb_context *ldb;
2849 struct ph_context *ac;
2850 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2851 *ntAttr, *lmAttr;
2852 int ret;
2853 struct ldb_control *bypass = NULL;
2854 bool userPassword = dsdb_user_password_support(module, req, req);
2856 ldb = ldb_module_get_ctx(module);
2858 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2860 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2861 return ldb_next_request(module, req);
2864 bypass = ldb_request_get_control(req,
2865 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2866 if (bypass != NULL) {
2867 /* Mark the "bypass" control as uncritical (done) */
2868 bypass->critical = false;
2869 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
2870 return password_hash_bypass(module, req);
2873 /* nobody must touch password histories and 'supplementalCredentials' */
2874 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2875 return LDB_ERR_UNWILLING_TO_PERFORM;
2877 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2878 return LDB_ERR_UNWILLING_TO_PERFORM;
2880 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2881 return LDB_ERR_UNWILLING_TO_PERFORM;
2884 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2885 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2887 userPasswordAttr = NULL;
2888 if (userPassword) {
2889 userPasswordAttr = ldb_msg_find_element(req->op.add.message,
2890 "userPassword");
2891 /* MS-ADTS 3.1.1.3.1.5.2 */
2892 if ((userPasswordAttr != NULL) &&
2893 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2894 return LDB_ERR_CONSTRAINT_VIOLATION;
2897 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2898 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2899 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2901 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2902 return ldb_next_request(module, req);
2905 /* Make sure we are performing the password set action on a (for us)
2906 * valid object. Those are instances of either "user" and/or
2907 * "inetOrgPerson". Otherwise continue with the submodules. */
2908 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2909 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2911 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2912 ldb_set_errstring(ldb,
2913 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2914 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2917 return ldb_next_request(module, req);
2920 ac = ph_init_context(module, req, userPassword);
2921 if (ac == NULL) {
2922 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2923 return ldb_operr(ldb);
2925 ph_apply_controls(ac);
2927 /* get user domain data */
2928 ret = build_domain_data_request(ac);
2929 if (ret != LDB_SUCCESS) {
2930 return ret;
2933 return ldb_next_request(module, ac->dom_req);
2936 static int password_hash_add_do_add(struct ph_context *ac)
2938 struct ldb_context *ldb;
2939 struct ldb_request *down_req;
2940 struct ldb_message *msg;
2941 struct setup_password_fields_io io;
2942 int ret;
2944 /* Prepare the internal data structure containing the passwords */
2945 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2946 if (ret != LDB_SUCCESS) {
2947 return ret;
2950 ldb = ldb_module_get_ctx(ac->module);
2952 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2953 if (msg == NULL) {
2954 return ldb_operr(ldb);
2957 /* remove attributes that we just read into 'io' */
2958 if (ac->userPassword) {
2959 ldb_msg_remove_attr(msg, "userPassword");
2961 ldb_msg_remove_attr(msg, "clearTextPassword");
2962 ldb_msg_remove_attr(msg, "unicodePwd");
2963 ldb_msg_remove_attr(msg, "dBCSPwd");
2964 ldb_msg_remove_attr(msg, "pwdLastSet");
2966 ret = setup_password_fields(&io);
2967 if (ret != LDB_SUCCESS) {
2968 return ret;
2971 ret = check_password_restrictions(&io);
2972 if (ret != LDB_SUCCESS) {
2973 return ret;
2976 if (io.g.nt_hash) {
2977 ret = samdb_msg_add_hash(ldb, ac, msg,
2978 "unicodePwd", io.g.nt_hash);
2979 if (ret != LDB_SUCCESS) {
2980 return ret;
2983 if (io.g.lm_hash) {
2984 ret = samdb_msg_add_hash(ldb, ac, msg,
2985 "dBCSPwd", io.g.lm_hash);
2986 if (ret != LDB_SUCCESS) {
2987 return ret;
2990 if (io.g.nt_history_len > 0) {
2991 ret = samdb_msg_add_hashes(ldb, ac, msg,
2992 "ntPwdHistory",
2993 io.g.nt_history,
2994 io.g.nt_history_len);
2995 if (ret != LDB_SUCCESS) {
2996 return ret;
2999 if (io.g.lm_history_len > 0) {
3000 ret = samdb_msg_add_hashes(ldb, ac, msg,
3001 "lmPwdHistory",
3002 io.g.lm_history,
3003 io.g.lm_history_len);
3004 if (ret != LDB_SUCCESS) {
3005 return ret;
3008 if (io.g.supplemental.length > 0) {
3009 ret = ldb_msg_add_value(msg, "supplementalCredentials",
3010 &io.g.supplemental, NULL);
3011 if (ret != LDB_SUCCESS) {
3012 return ret;
3015 ret = samdb_msg_add_uint64(ldb, ac, msg,
3016 "pwdLastSet",
3017 io.g.last_set);
3018 if (ret != LDB_SUCCESS) {
3019 return ret;
3022 ret = ldb_build_add_req(&down_req, ldb, ac,
3023 msg,
3024 ac->req->controls,
3025 ac, ph_op_callback,
3026 ac->req);
3027 LDB_REQ_SET_LOCATION(down_req);
3028 if (ret != LDB_SUCCESS) {
3029 return ret;
3032 return ldb_next_request(ac->module, down_req);
3035 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
3037 struct ldb_context *ldb;
3038 struct ph_context *ac;
3039 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
3040 "unicodePwd", "dBCSPwd", NULL }, **l;
3041 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
3042 struct ldb_message_element *passwordAttr;
3043 struct ldb_message *msg;
3044 struct ldb_request *down_req;
3045 int ret;
3046 struct ldb_control *bypass = NULL;
3047 bool userPassword = dsdb_user_password_support(module, req, req);
3049 ldb = ldb_module_get_ctx(module);
3051 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
3053 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
3054 return ldb_next_request(module, req);
3057 bypass = ldb_request_get_control(req,
3058 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
3059 if (bypass != NULL) {
3060 /* Mark the "bypass" control as uncritical (done) */
3061 bypass->critical = false;
3062 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
3063 return password_hash_bypass(module, req);
3066 /* nobody must touch password histories and 'supplementalCredentials' */
3067 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
3068 return LDB_ERR_UNWILLING_TO_PERFORM;
3070 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
3071 return LDB_ERR_UNWILLING_TO_PERFORM;
3073 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
3074 return LDB_ERR_UNWILLING_TO_PERFORM;
3077 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
3078 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
3079 * For password changes/set there should be a 'delete' or a 'modify'
3080 * on these attributes. */
3081 attr_cnt = 0;
3082 for (l = passwordAttrs; *l != NULL; l++) {
3083 if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) {
3084 continue;
3087 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
3088 /* MS-ADTS 3.1.1.3.1.5.2 */
3089 if ((ldb_attr_cmp(*l, "userPassword") == 0) &&
3090 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
3091 return LDB_ERR_CONSTRAINT_VIOLATION;
3094 ++attr_cnt;
3097 if (attr_cnt == 0) {
3098 return ldb_next_request(module, req);
3101 ac = ph_init_context(module, req, userPassword);
3102 if (!ac) {
3103 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3104 return ldb_operr(ldb);
3106 ph_apply_controls(ac);
3108 /* use a new message structure so that we can modify it */
3109 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3110 if (msg == NULL) {
3111 return ldb_oom(ldb);
3114 /* - check for single-valued password attributes
3115 * (if not return "CONSTRAINT_VIOLATION")
3116 * - check that for a password change operation one add and one delete
3117 * operation exists
3118 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3119 * - check that a password change and a password set operation cannot
3120 * be mixed
3121 * (if not return "UNWILLING_TO_PERFORM")
3122 * - remove all password attributes modifications from the first change
3123 * operation (anything without the passwords) - we will make the real
3124 * modification later */
3125 del_attr_cnt = 0;
3126 add_attr_cnt = 0;
3127 rep_attr_cnt = 0;
3128 for (l = passwordAttrs; *l != NULL; l++) {
3129 if ((!ac->userPassword) &&
3130 (ldb_attr_cmp(*l, "userPassword") == 0)) {
3131 continue;
3134 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
3135 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
3136 ++del_attr_cnt;
3138 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
3139 ++add_attr_cnt;
3141 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
3142 ++rep_attr_cnt;
3144 if ((passwordAttr->num_values != 1) &&
3145 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
3146 talloc_free(ac);
3147 ldb_asprintf_errstring(ldb,
3148 "'%s' attribute must have exactly one value on add operations!",
3149 *l);
3150 return LDB_ERR_CONSTRAINT_VIOLATION;
3152 if ((passwordAttr->num_values > 1) &&
3153 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
3154 talloc_free(ac);
3155 ldb_asprintf_errstring(ldb,
3156 "'%s' attribute must have zero or one value(s) on delete operations!",
3157 *l);
3158 return LDB_ERR_CONSTRAINT_VIOLATION;
3160 ldb_msg_remove_element(msg, passwordAttr);
3163 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
3164 talloc_free(ac);
3165 ldb_set_errstring(ldb,
3166 "Only the add action for a password change specified!");
3167 return LDB_ERR_UNWILLING_TO_PERFORM;
3169 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
3170 talloc_free(ac);
3171 ldb_set_errstring(ldb,
3172 "Only one delete and one add action for a password change allowed!");
3173 return LDB_ERR_UNWILLING_TO_PERFORM;
3175 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
3176 talloc_free(ac);
3177 ldb_set_errstring(ldb,
3178 "Either a password change or a password set operation is allowed!");
3179 return LDB_ERR_UNWILLING_TO_PERFORM;
3182 /* if there was nothing else to be modified skip to next step */
3183 if (msg->num_elements == 0) {
3184 return password_hash_mod_search_self(ac);
3187 ret = ldb_build_mod_req(&down_req, ldb, ac,
3188 msg,
3189 req->controls,
3190 ac, ph_modify_callback,
3191 req);
3192 LDB_REQ_SET_LOCATION(down_req);
3193 if (ret != LDB_SUCCESS) {
3194 return ret;
3197 return ldb_next_request(module, down_req);
3200 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3202 struct ph_context *ac;
3204 ac = talloc_get_type(req->context, struct ph_context);
3206 if (!ares) {
3207 return ldb_module_done(ac->req, NULL, NULL,
3208 LDB_ERR_OPERATIONS_ERROR);
3211 if (ares->type == LDB_REPLY_REFERRAL) {
3212 return ldb_module_send_referral(ac->req, ares->referral);
3215 if (ares->error != LDB_SUCCESS) {
3216 return ldb_module_done(ac->req, ares->controls,
3217 ares->response, ares->error);
3220 if (ares->type != LDB_REPLY_DONE) {
3221 talloc_free(ares);
3222 return ldb_module_done(ac->req, NULL, NULL,
3223 LDB_ERR_OPERATIONS_ERROR);
3226 talloc_free(ares);
3228 return password_hash_mod_search_self(ac);
3231 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
3233 struct ldb_context *ldb;
3234 struct ph_context *ac;
3235 int ret = LDB_SUCCESS;
3237 ac = talloc_get_type(req->context, struct ph_context);
3238 ldb = ldb_module_get_ctx(ac->module);
3240 if (!ares) {
3241 ret = LDB_ERR_OPERATIONS_ERROR;
3242 goto done;
3244 if (ares->error != LDB_SUCCESS) {
3245 return ldb_module_done(ac->req, ares->controls,
3246 ares->response, ares->error);
3249 /* we are interested only in the single reply (base search) */
3250 switch (ares->type) {
3251 case LDB_REPLY_ENTRY:
3252 /* Make sure we are performing the password change action on a
3253 * (for us) valid object. Those are instances of either "user"
3254 * and/or "inetOrgPerson". Otherwise continue with the
3255 * submodules. */
3256 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
3257 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
3258 talloc_free(ares);
3260 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
3261 ldb_set_errstring(ldb,
3262 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3263 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
3264 goto done;
3267 ret = ldb_next_request(ac->module, ac->req);
3268 goto done;
3271 if (ac->search_res != NULL) {
3272 talloc_free(ares);
3274 ldb_set_errstring(ldb, "Too many results");
3275 ret = LDB_ERR_OPERATIONS_ERROR;
3276 goto done;
3279 ac->search_res = talloc_steal(ac, ares);
3280 ret = LDB_SUCCESS;
3281 break;
3283 case LDB_REPLY_REFERRAL:
3284 /* ignore anything else for now */
3285 talloc_free(ares);
3286 ret = LDB_SUCCESS;
3287 break;
3289 case LDB_REPLY_DONE:
3290 talloc_free(ares);
3292 /* get user domain data */
3293 ret = build_domain_data_request(ac);
3294 if (ret != LDB_SUCCESS) {
3295 return ldb_module_done(ac->req, NULL, NULL, ret);
3298 ret = ldb_next_request(ac->module, ac->dom_req);
3299 break;
3302 done:
3303 if (ret != LDB_SUCCESS) {
3304 return ldb_module_done(ac->req, NULL, NULL, ret);
3307 return LDB_SUCCESS;
3310 static int password_hash_mod_search_self(struct ph_context *ac)
3312 struct ldb_context *ldb;
3313 static const char * const attrs[] = { "objectClass",
3314 "userAccountControl",
3315 "msDS-User-Account-Control-Computed",
3316 "pwdLastSet",
3317 "sAMAccountName",
3318 "objectSid",
3319 "userPrincipalName",
3320 "supplementalCredentials",
3321 "lmPwdHistory",
3322 "ntPwdHistory",
3323 "dBCSPwd",
3324 "unicodePwd",
3325 "badPasswordTime",
3326 "badPwdCount",
3327 "lockoutTime",
3328 NULL };
3329 struct ldb_request *search_req;
3330 int ret;
3332 ldb = ldb_module_get_ctx(ac->module);
3334 ret = ldb_build_search_req(&search_req, ldb, ac,
3335 ac->req->op.mod.message->dn,
3336 LDB_SCOPE_BASE,
3337 "(objectclass=*)",
3338 attrs,
3339 NULL,
3340 ac, ph_mod_search_callback,
3341 ac->req);
3342 LDB_REQ_SET_LOCATION(search_req);
3343 if (ret != LDB_SUCCESS) {
3344 return ret;
3347 return ldb_next_request(ac->module, search_req);
3350 static int password_hash_mod_do_mod(struct ph_context *ac)
3352 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3353 struct loadparm_context *lp_ctx =
3354 talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3355 struct loadparm_context);
3356 struct ldb_request *mod_req;
3357 struct ldb_message *msg;
3358 const struct ldb_message *orig_msg, *searched_msg;
3359 struct setup_password_fields_io io;
3360 int ret;
3361 NTSTATUS status;
3363 /* use a new message structure so that we can modify it */
3364 msg = ldb_msg_new(ac);
3365 if (msg == NULL) {
3366 return ldb_operr(ldb);
3369 /* modify dn */
3370 msg->dn = ac->req->op.mod.message->dn;
3372 orig_msg = ac->req->op.mod.message;
3373 searched_msg = ac->search_res->message;
3375 /* Prepare the internal data structure containing the passwords */
3376 ret = setup_io(ac, orig_msg, searched_msg, &io);
3377 if (ret != LDB_SUCCESS) {
3378 return ret;
3381 if (io.ac->pwd_reset) {
3382 /* Get the old password from the database */
3383 status = samdb_result_passwords_no_lockout(io.ac,
3384 lp_ctx,
3385 discard_const_p(struct ldb_message, searched_msg),
3386 &io.o.lm_hash,
3387 &io.o.nt_hash);
3388 } else {
3389 /* Get the old password from the database */
3390 status = samdb_result_passwords(io.ac,
3391 lp_ctx,
3392 discard_const_p(struct ldb_message, searched_msg),
3393 &io.o.lm_hash, &io.o.nt_hash);
3396 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
3397 ldb_asprintf_errstring(ldb,
3398 "%08X: check_password: "
3399 "Password change not permitted, account locked out!",
3400 W_ERROR_V(WERR_ACCOUNT_LOCKED_OUT));
3401 return LDB_ERR_CONSTRAINT_VIOLATION;
3404 if (!NT_STATUS_IS_OK(status)) {
3406 * This only happens if the database has gone weird,
3407 * not if we are just missing the passwords
3409 return ldb_operr(ldb);
3412 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
3413 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
3414 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
3416 ret = setup_password_fields(&io);
3417 if (ret != LDB_SUCCESS) {
3418 return ret;
3421 ret = check_password_restrictions(&io);
3422 if (ret != LDB_SUCCESS) {
3423 return ret;
3426 /* make sure we replace all the old attributes */
3427 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
3428 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
3429 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3430 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3431 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
3432 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
3434 if (io.g.nt_hash) {
3435 ret = samdb_msg_add_hash(ldb, ac, msg,
3436 "unicodePwd", io.g.nt_hash);
3437 if (ret != LDB_SUCCESS) {
3438 return ret;
3441 if (io.g.lm_hash) {
3442 ret = samdb_msg_add_hash(ldb, ac, msg,
3443 "dBCSPwd", io.g.lm_hash);
3444 if (ret != LDB_SUCCESS) {
3445 return ret;
3448 if (io.g.nt_history_len > 0) {
3449 ret = samdb_msg_add_hashes(ldb, ac, msg,
3450 "ntPwdHistory",
3451 io.g.nt_history,
3452 io.g.nt_history_len);
3453 if (ret != LDB_SUCCESS) {
3454 return ret;
3457 if (io.g.lm_history_len > 0) {
3458 ret = samdb_msg_add_hashes(ldb, ac, msg,
3459 "lmPwdHistory",
3460 io.g.lm_history,
3461 io.g.lm_history_len);
3462 if (ret != LDB_SUCCESS) {
3463 return ret;
3466 if (io.g.supplemental.length > 0) {
3467 ret = ldb_msg_add_value(msg, "supplementalCredentials",
3468 &io.g.supplemental, NULL);
3469 if (ret != LDB_SUCCESS) {
3470 return ret;
3473 ret = samdb_msg_add_uint64(ldb, ac, msg,
3474 "pwdLastSet",
3475 io.g.last_set);
3476 if (ret != LDB_SUCCESS) {
3477 return ret;
3480 ret = ldb_build_mod_req(&mod_req, ldb, ac,
3481 msg,
3482 ac->req->controls,
3483 ac, ph_op_callback,
3484 ac->req);
3485 LDB_REQ_SET_LOCATION(mod_req);
3486 if (ret != LDB_SUCCESS) {
3487 return ret;
3490 return ldb_next_request(ac->module, mod_req);
3493 static const struct ldb_module_ops ldb_password_hash_module_ops = {
3494 .name = "password_hash",
3495 .add = password_hash_add,
3496 .modify = password_hash_modify
3499 int ldb_password_hash_module_init(const char *version)
3501 LDB_MODULE_CHECK_VERSION(version);
3502 return ldb_register_module(&ldb_password_hash_module_ops);