dsdb: collapse wrong password and no-password-hash errors into one handler
[Samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
bloba4e663ee36dac9740e34c60c2d4d38ece7aa6e78
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"
47 /* If we have decided there is a reason to work on this request, then
48 * setup all the password hash types correctly.
50 * If we haven't the hashes yet but the password given as plain-text (attributes
51 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
52 * the constraints. Once this is done, we calculate the password hashes.
54 * Notice: unlike the real AD which only supports the UTF16 special based
55 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
56 * understand also a UTF16 based 'clearTextPassword' one.
57 * The latter is also accessible through LDAP so it can also be set by external
58 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
60 * Also when the module receives only the password hashes (possible through
61 * specifying an internal LDB control - for security reasons) some checks are
62 * performed depending on the operation mode (see below) (e.g. if the password
63 * has been in use before if the password memory policy was activated).
65 * Attention: There is a difference between "modify" and "reset" operations
66 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
67 * operation for a password attribute we thread this as a "modify"; if it sends
68 * only a "replace" one we have an (administrative) reset.
70 * Finally, if the administrator has requested that a password history
71 * be maintained, then this should also be written out.
75 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
76 * - Check for right connection encryption
79 /* Notice: Definition of "dsdb_control_password_change_status" moved into
80 * "samdb.h" */
82 struct ph_context {
83 struct ldb_module *module;
84 struct ldb_request *req;
86 struct ldb_request *dom_req;
87 struct ldb_reply *dom_res;
89 struct ldb_reply *search_res;
91 struct dsdb_control_password_change_status *status;
92 struct dsdb_control_password_change *change;
94 bool pwd_reset;
95 bool change_status;
96 bool hash_values;
97 bool userPassword;
98 bool pwd_last_set_bypass;
102 struct setup_password_fields_io {
103 struct ph_context *ac;
105 struct smb_krb5_context *smb_krb5_context;
107 /* infos about the user account */
108 struct {
109 uint32_t userAccountControl;
110 NTTIME pwdLastSet;
111 const char *sAMAccountName;
112 const char *user_principal_name;
113 bool is_computer;
114 uint32_t restrictions;
115 } u;
117 /* new credentials and old given credentials */
118 struct setup_password_fields_given {
119 const struct ldb_val *cleartext_utf8;
120 const struct ldb_val *cleartext_utf16;
121 struct samr_Password *nt_hash;
122 struct samr_Password *lm_hash;
123 } n, og;
125 /* old credentials */
126 struct {
127 struct samr_Password *nt_hash;
128 struct samr_Password *lm_hash;
129 uint32_t nt_history_len;
130 struct samr_Password *nt_history;
131 uint32_t lm_history_len;
132 struct samr_Password *lm_history;
133 const struct ldb_val *supplemental;
134 struct supplementalCredentialsBlob scb;
135 } o;
137 /* generated credentials */
138 struct {
139 struct samr_Password *nt_hash;
140 struct samr_Password *lm_hash;
141 uint32_t nt_history_len;
142 struct samr_Password *nt_history;
143 uint32_t lm_history_len;
144 struct samr_Password *lm_history;
145 const char *salt;
146 DATA_BLOB aes_256;
147 DATA_BLOB aes_128;
148 DATA_BLOB des_md5;
149 DATA_BLOB des_crc;
150 struct ldb_val supplemental;
151 NTTIME last_set;
152 } g;
155 static int password_hash_bypass(struct ldb_module *module, struct ldb_request *request)
157 struct ldb_context *ldb = ldb_module_get_ctx(module);
158 const struct ldb_message *msg;
159 struct ldb_message_element *nte;
160 struct ldb_message_element *lme;
161 struct ldb_message_element *nthe;
162 struct ldb_message_element *lmhe;
163 struct ldb_message_element *sce;
165 switch (request->operation) {
166 case LDB_ADD:
167 msg = request->op.add.message;
168 break;
169 case LDB_MODIFY:
170 msg = request->op.mod.message;
171 break;
172 default:
173 return ldb_next_request(module, request);
176 /* nobody must touch password histories and 'supplementalCredentials' */
177 nte = dsdb_get_single_valued_attr(msg, "unicodePwd",
178 request->operation);
179 lme = dsdb_get_single_valued_attr(msg, "dBCSPwd",
180 request->operation);
181 nthe = dsdb_get_single_valued_attr(msg, "ntPwdHistory",
182 request->operation);
183 lmhe = dsdb_get_single_valued_attr(msg, "lmPwdHistory",
184 request->operation);
185 sce = dsdb_get_single_valued_attr(msg, "supplementalCredentials",
186 request->operation);
188 #define CHECK_HASH_ELEMENT(e, min, max) do {\
189 if (e && e->num_values) { \
190 unsigned int _count; \
191 if (e->num_values != 1) { \
192 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
193 "num_values != 1"); \
195 if ((e->values[0].length % 16) != 0) { \
196 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
197 "length % 16 != 0"); \
199 _count = e->values[0].length / 16; \
200 if (_count < min) { \
201 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
202 "count < min"); \
204 if (_count > max) { \
205 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
206 "count > max"); \
209 } while (0)
211 CHECK_HASH_ELEMENT(nte, 1, 1);
212 CHECK_HASH_ELEMENT(lme, 1, 1);
213 CHECK_HASH_ELEMENT(nthe, 1, INT32_MAX);
214 CHECK_HASH_ELEMENT(lmhe, 1, INT32_MAX);
216 if (sce && sce->num_values) {
217 enum ndr_err_code ndr_err;
218 struct supplementalCredentialsBlob *scb;
219 struct supplementalCredentialsPackage *scpp = NULL;
220 struct supplementalCredentialsPackage *scpk = NULL;
221 struct supplementalCredentialsPackage *scpkn = NULL;
222 struct supplementalCredentialsPackage *scpct = NULL;
223 DATA_BLOB scpbp = data_blob_null;
224 DATA_BLOB scpbk = data_blob_null;
225 DATA_BLOB scpbkn = data_blob_null;
226 DATA_BLOB scpbct = data_blob_null;
227 DATA_BLOB blob;
228 uint32_t i;
230 if (sce->num_values != 1) {
231 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
232 "num_values != 1");
235 scb = talloc_zero(request, struct supplementalCredentialsBlob);
236 if (!scb) {
237 return ldb_module_oom(module);
240 ndr_err = ndr_pull_struct_blob_all(&sce->values[0], scb, scb,
241 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
242 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
243 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
244 "ndr_pull_struct_blob_all");
247 if (scb->sub.num_packages < 2) {
248 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
249 "num_packages < 2");
252 for (i=0; i < scb->sub.num_packages; i++) {
253 DATA_BLOB subblob;
255 subblob = strhex_to_data_blob(scb, scb->sub.packages[i].data);
256 if (subblob.data == NULL) {
257 return ldb_module_oom(module);
260 if (strcmp(scb->sub.packages[i].name, "Packages") == 0) {
261 if (scpp) {
262 return ldb_error(ldb,
263 LDB_ERR_CONSTRAINT_VIOLATION,
264 "Packages twice");
266 scpp = &scb->sub.packages[i];
267 scpbp = subblob;
268 continue;
270 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos") == 0) {
271 if (scpk) {
272 return ldb_error(ldb,
273 LDB_ERR_CONSTRAINT_VIOLATION,
274 "Primary:Kerberos twice");
276 scpk = &scb->sub.packages[i];
277 scpbk = subblob;
278 continue;
280 if (strcmp(scb->sub.packages[i].name, "Primary:Kerberos-Newer-Keys") == 0) {
281 if (scpkn) {
282 return ldb_error(ldb,
283 LDB_ERR_CONSTRAINT_VIOLATION,
284 "Primary:Kerberos-Newer-Keys twice");
286 scpkn = &scb->sub.packages[i];
287 scpbkn = subblob;
288 continue;
290 if (strcmp(scb->sub.packages[i].name, "Primary:CLEARTEXT") == 0) {
291 if (scpct) {
292 return ldb_error(ldb,
293 LDB_ERR_CONSTRAINT_VIOLATION,
294 "Primary:CLEARTEXT twice");
296 scpct = &scb->sub.packages[i];
297 scpbct = subblob;
298 continue;
301 data_blob_free(&subblob);
304 if (scpp == NULL) {
305 return ldb_error(ldb,
306 LDB_ERR_CONSTRAINT_VIOLATION,
307 "Primary:Packages missing");
310 if (scpk == NULL) {
312 * If Primary:Kerberos is missing w2k8r2 reboots
313 * when a password is changed.
315 return ldb_error(ldb,
316 LDB_ERR_CONSTRAINT_VIOLATION,
317 "Primary:Kerberos missing");
320 if (scpp) {
321 struct package_PackagesBlob *p;
322 uint32_t n;
324 p = talloc_zero(scb, struct package_PackagesBlob);
325 if (p == NULL) {
326 return ldb_module_oom(module);
329 ndr_err = ndr_pull_struct_blob(&scpbp, p, p,
330 (ndr_pull_flags_fn_t)ndr_pull_package_PackagesBlob);
331 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
332 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
333 "ndr_pull_struct_blob Packages");
336 if (p->names == NULL) {
337 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
338 "Packages names == NULL");
341 for (n = 0; p->names[n]; n++) {
342 /* noop */
345 if (scb->sub.num_packages != (n + 1)) {
346 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
347 "Packages num_packages != num_names + 1");
350 talloc_free(p);
353 if (scpk) {
354 struct package_PrimaryKerberosBlob *k;
356 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
357 if (k == NULL) {
358 return ldb_module_oom(module);
361 ndr_err = ndr_pull_struct_blob(&scpbk, k, k,
362 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
363 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
364 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
365 "ndr_pull_struct_blob PrimaryKerberos");
368 if (k->version != 3) {
369 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
370 "PrimaryKerberos version != 3");
373 if (k->ctr.ctr3.salt.string == NULL) {
374 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
375 "PrimaryKerberos salt == NULL");
378 if (strlen(k->ctr.ctr3.salt.string) == 0) {
379 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
380 "PrimaryKerberos strlen(salt) == 0");
383 if (k->ctr.ctr3.num_keys != 2) {
384 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
385 "PrimaryKerberos num_keys != 2");
388 if (k->ctr.ctr3.num_old_keys > k->ctr.ctr3.num_keys) {
389 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
390 "PrimaryKerberos num_old_keys > num_keys");
393 if (k->ctr.ctr3.keys[0].keytype != ENCTYPE_DES_CBC_MD5) {
394 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
395 "PrimaryKerberos key[0] != DES_CBC_MD5");
397 if (k->ctr.ctr3.keys[1].keytype != ENCTYPE_DES_CBC_CRC) {
398 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
399 "PrimaryKerberos key[1] != DES_CBC_CRC");
402 if (k->ctr.ctr3.keys[0].value_len != 8) {
403 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
404 "PrimaryKerberos key[0] value_len != 8");
406 if (k->ctr.ctr3.keys[1].value_len != 8) {
407 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
408 "PrimaryKerberos key[1] value_len != 8");
411 for (i = 0; i < k->ctr.ctr3.num_old_keys; i++) {
412 if (k->ctr.ctr3.old_keys[i].keytype ==
413 k->ctr.ctr3.keys[i].keytype &&
414 k->ctr.ctr3.old_keys[i].value_len ==
415 k->ctr.ctr3.keys[i].value_len) {
416 continue;
419 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
420 "PrimaryKerberos old_keys type/value_len doesn't match");
423 talloc_free(k);
426 if (scpkn) {
427 struct package_PrimaryKerberosBlob *k;
429 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
430 if (k == NULL) {
431 return ldb_module_oom(module);
434 ndr_err = ndr_pull_struct_blob(&scpbkn, k, k,
435 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
436 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
437 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
438 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
441 if (k->version != 4) {
442 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
443 "KerberosNerverKeys version != 4");
446 if (k->ctr.ctr4.salt.string == NULL) {
447 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
448 "KerberosNewerKeys salt == NULL");
451 if (strlen(k->ctr.ctr4.salt.string) == 0) {
452 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
453 "KerberosNewerKeys strlen(salt) == 0");
456 if (k->ctr.ctr4.num_keys != 4) {
457 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
458 "KerberosNewerKeys num_keys != 2");
461 if (k->ctr.ctr4.num_old_keys > k->ctr.ctr4.num_keys) {
462 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
463 "KerberosNewerKeys num_old_keys > num_keys");
466 if (k->ctr.ctr4.num_older_keys > k->ctr.ctr4.num_old_keys) {
467 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
468 "KerberosNewerKeys num_older_keys > num_old_keys");
471 if (k->ctr.ctr4.keys[0].keytype != ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
472 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
473 "KerberosNewerKeys key[0] != AES256");
475 if (k->ctr.ctr4.keys[1].keytype != ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
476 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
477 "KerberosNewerKeys key[1] != AES128");
479 if (k->ctr.ctr4.keys[2].keytype != ENCTYPE_DES_CBC_MD5) {
480 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
481 "KerberosNewerKeys key[2] != DES_CBC_MD5");
483 if (k->ctr.ctr4.keys[3].keytype != ENCTYPE_DES_CBC_CRC) {
484 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
485 "KerberosNewerKeys key[3] != DES_CBC_CRC");
488 if (k->ctr.ctr4.keys[0].value_len != 32) {
489 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
490 "KerberosNewerKeys key[0] value_len != 32");
492 if (k->ctr.ctr4.keys[1].value_len != 16) {
493 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
494 "KerberosNewerKeys key[1] value_len != 16");
496 if (k->ctr.ctr4.keys[2].value_len != 8) {
497 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
498 "KerberosNewerKeys key[2] value_len != 8");
500 if (k->ctr.ctr4.keys[3].value_len != 8) {
501 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
502 "KerberosNewerKeys key[3] value_len != 8");
506 * TODO:
507 * Maybe we can check old and older keys here.
508 * But we need to do some tests, if the old keys
509 * can be taken from the PrimaryKerberos blob
510 * (with only des keys), when the domain was upgraded
511 * from w2k3 to w2k8.
514 talloc_free(k);
517 if (scpct) {
518 struct package_PrimaryCLEARTEXTBlob *ct;
520 ct = talloc_zero(scb, struct package_PrimaryCLEARTEXTBlob);
521 if (ct == NULL) {
522 return ldb_module_oom(module);
525 ndr_err = ndr_pull_struct_blob(&scpbct, ct, ct,
526 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryCLEARTEXTBlob);
527 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
528 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
529 "ndr_pull_struct_blob PrimaryCLEARTEXT");
532 if ((ct->cleartext.length % 2) != 0) {
533 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
534 "PrimaryCLEARTEXT length % 2 != 0");
537 talloc_free(ct);
540 ndr_err = ndr_push_struct_blob(&blob, scb, scb,
541 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
542 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
543 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
544 "ndr_pull_struct_blob_all");
547 if (sce->values[0].length != blob.length) {
548 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
549 "supplementalCredentialsBlob length differ");
552 if (memcmp(sce->values[0].data, blob.data, blob.length) != 0) {
553 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
554 "supplementalCredentialsBlob memcmp differ");
557 talloc_free(scb);
560 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_bypass - validated\n");
561 return ldb_next_request(module, request);
564 /* Get the NT hash, and fill it in as an entry in the password history,
565 and specify it into io->g.nt_hash */
567 static int setup_nt_fields(struct setup_password_fields_io *io)
569 struct ldb_context *ldb;
570 uint32_t i;
572 io->g.nt_hash = io->n.nt_hash;
573 ldb = ldb_module_get_ctx(io->ac->module);
575 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
576 return LDB_SUCCESS;
579 /* We might not have an old NT password */
580 io->g.nt_history = talloc_array(io->ac,
581 struct samr_Password,
582 io->ac->status->domain_data.pwdHistoryLength);
583 if (!io->g.nt_history) {
584 return ldb_oom(ldb);
587 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
588 io->o.nt_history_len); i++) {
589 io->g.nt_history[i+1] = io->o.nt_history[i];
591 io->g.nt_history_len = i + 1;
593 if (io->g.nt_hash) {
594 io->g.nt_history[0] = *io->g.nt_hash;
595 } else {
597 * TODO: is this correct?
598 * the simular behavior is correct for the lm history case
600 E_md4hash("", io->g.nt_history[0].hash);
603 return LDB_SUCCESS;
606 /* Get the LANMAN hash, and fill it in as an entry in the password history,
607 and specify it into io->g.lm_hash */
609 static int setup_lm_fields(struct setup_password_fields_io *io)
611 struct ldb_context *ldb;
612 uint32_t i;
614 io->g.lm_hash = io->n.lm_hash;
615 ldb = ldb_module_get_ctx(io->ac->module);
617 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
618 return LDB_SUCCESS;
621 /* We might not have an old LM password */
622 io->g.lm_history = talloc_array(io->ac,
623 struct samr_Password,
624 io->ac->status->domain_data.pwdHistoryLength);
625 if (!io->g.lm_history) {
626 return ldb_oom(ldb);
629 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
630 io->o.lm_history_len); i++) {
631 io->g.lm_history[i+1] = io->o.lm_history[i];
633 io->g.lm_history_len = i + 1;
635 if (io->g.lm_hash) {
636 io->g.lm_history[0] = *io->g.lm_hash;
637 } else {
638 E_deshash("", io->g.lm_history[0].hash);
641 return LDB_SUCCESS;
644 static int setup_kerberos_keys(struct setup_password_fields_io *io)
646 struct ldb_context *ldb;
647 krb5_error_code krb5_ret;
648 Principal *salt_principal;
649 krb5_salt salt;
650 krb5_keyblock key;
651 krb5_data cleartext_data;
653 ldb = ldb_module_get_ctx(io->ac->module);
654 cleartext_data.data = io->n.cleartext_utf8->data;
655 cleartext_data.length = io->n.cleartext_utf8->length;
657 /* Many, many thanks to lukeh@padl.com for this
658 * algorithm, described in his Nov 10 2004 mail to
659 * samba-technical@samba.org */
662 * Determine a salting principal
664 if (io->u.is_computer) {
665 char *name;
666 char *saltbody;
668 name = strlower_talloc(io->ac, io->u.sAMAccountName);
669 if (!name) {
670 return ldb_oom(ldb);
673 if (name[strlen(name)-1] == '$') {
674 name[strlen(name)-1] = '\0';
677 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
678 io->ac->status->domain_data.dns_domain);
679 if (!saltbody) {
680 return ldb_oom(ldb);
683 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
684 &salt_principal,
685 io->ac->status->domain_data.realm,
686 "host", saltbody, NULL);
687 } else if (io->u.user_principal_name) {
688 char *user_principal_name;
689 char *p;
691 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
692 if (!user_principal_name) {
693 return ldb_oom(ldb);
696 p = strchr(user_principal_name, '@');
697 if (p) {
698 p[0] = '\0';
701 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
702 &salt_principal,
703 io->ac->status->domain_data.realm,
704 user_principal_name, NULL);
705 } else {
706 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
707 &salt_principal,
708 io->ac->status->domain_data.realm,
709 io->u.sAMAccountName, NULL);
711 if (krb5_ret) {
712 ldb_asprintf_errstring(ldb,
713 "setup_kerberos_keys: "
714 "generation of a salting principal failed: %s",
715 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
716 krb5_ret, io->ac));
717 return LDB_ERR_OPERATIONS_ERROR;
721 * create salt from salt_principal
723 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
724 salt_principal, &salt);
725 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
726 if (krb5_ret) {
727 ldb_asprintf_errstring(ldb,
728 "setup_kerberos_keys: "
729 "generation of krb5_salt failed: %s",
730 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
731 krb5_ret, io->ac));
732 return LDB_ERR_OPERATIONS_ERROR;
734 /* create a talloc copy */
735 io->g.salt = talloc_strndup(io->ac,
736 (char *)salt.saltvalue.data,
737 salt.saltvalue.length);
738 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
739 if (!io->g.salt) {
740 return ldb_oom(ldb);
742 salt.saltvalue.data = discard_const(io->g.salt);
743 salt.saltvalue.length = strlen(io->g.salt);
746 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
747 * the salt and the cleartext password
749 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
750 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
751 cleartext_data,
752 salt,
753 &key);
754 if (krb5_ret) {
755 ldb_asprintf_errstring(ldb,
756 "setup_kerberos_keys: "
757 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
758 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
759 krb5_ret, io->ac));
760 return LDB_ERR_OPERATIONS_ERROR;
762 io->g.aes_256 = data_blob_talloc(io->ac,
763 KRB5_KEY_DATA(&key),
764 KRB5_KEY_LENGTH(&key));
765 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
766 if (!io->g.aes_256.data) {
767 return ldb_oom(ldb);
771 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
772 * the salt and the cleartext password
774 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
775 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
776 cleartext_data,
777 salt,
778 &key);
779 if (krb5_ret) {
780 ldb_asprintf_errstring(ldb,
781 "setup_kerberos_keys: "
782 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
783 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
784 krb5_ret, io->ac));
785 return LDB_ERR_OPERATIONS_ERROR;
787 io->g.aes_128 = data_blob_talloc(io->ac,
788 KRB5_KEY_DATA(&key),
789 KRB5_KEY_LENGTH(&key));
790 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
791 if (!io->g.aes_128.data) {
792 return ldb_oom(ldb);
796 * create ENCTYPE_DES_CBC_MD5 key out of
797 * the salt and the cleartext password
799 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
800 ENCTYPE_DES_CBC_MD5,
801 cleartext_data,
802 salt,
803 &key);
804 if (krb5_ret) {
805 ldb_asprintf_errstring(ldb,
806 "setup_kerberos_keys: "
807 "generation of a des-cbc-md5 key failed: %s",
808 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
809 krb5_ret, io->ac));
810 return LDB_ERR_OPERATIONS_ERROR;
812 io->g.des_md5 = data_blob_talloc(io->ac,
813 KRB5_KEY_DATA(&key),
814 KRB5_KEY_LENGTH(&key));
815 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
816 if (!io->g.des_md5.data) {
817 return ldb_oom(ldb);
821 * create ENCTYPE_DES_CBC_CRC key out of
822 * the salt and the cleartext password
824 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
825 ENCTYPE_DES_CBC_CRC,
826 cleartext_data,
827 salt,
828 &key);
829 if (krb5_ret) {
830 ldb_asprintf_errstring(ldb,
831 "setup_kerberos_keys: "
832 "generation of a des-cbc-crc key failed: %s",
833 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
834 krb5_ret, io->ac));
835 return LDB_ERR_OPERATIONS_ERROR;
837 io->g.des_crc = data_blob_talloc(io->ac,
838 KRB5_KEY_DATA(&key),
839 KRB5_KEY_LENGTH(&key));
840 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
841 if (!io->g.des_crc.data) {
842 return ldb_oom(ldb);
845 return LDB_SUCCESS;
848 static int setup_primary_kerberos(struct setup_password_fields_io *io,
849 const struct supplementalCredentialsBlob *old_scb,
850 struct package_PrimaryKerberosBlob *pkb)
852 struct ldb_context *ldb;
853 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
854 struct supplementalCredentialsPackage *old_scp = NULL;
855 struct package_PrimaryKerberosBlob _old_pkb;
856 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
857 uint32_t i;
858 enum ndr_err_code ndr_err;
860 ldb = ldb_module_get_ctx(io->ac->module);
863 * prepare generation of keys
865 * ENCTYPE_DES_CBC_MD5
866 * ENCTYPE_DES_CBC_CRC
868 pkb->version = 3;
869 pkb3->salt.string = io->g.salt;
870 pkb3->num_keys = 2;
871 pkb3->keys = talloc_array(io->ac,
872 struct package_PrimaryKerberosKey3,
873 pkb3->num_keys);
874 if (!pkb3->keys) {
875 return ldb_oom(ldb);
878 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
879 pkb3->keys[0].value = &io->g.des_md5;
880 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
881 pkb3->keys[1].value = &io->g.des_crc;
883 /* initialize the old keys to zero */
884 pkb3->num_old_keys = 0;
885 pkb3->old_keys = NULL;
887 /* if there're no old keys, then we're done */
888 if (!old_scb) {
889 return LDB_SUCCESS;
892 for (i=0; i < old_scb->sub.num_packages; i++) {
893 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
894 continue;
897 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
898 continue;
901 old_scp = &old_scb->sub.packages[i];
902 break;
904 /* Primary:Kerberos element of supplementalCredentials */
905 if (old_scp) {
906 DATA_BLOB blob;
908 blob = strhex_to_data_blob(io->ac, old_scp->data);
909 if (!blob.data) {
910 return ldb_oom(ldb);
913 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
914 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
915 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
916 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
917 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
918 ldb_asprintf_errstring(ldb,
919 "setup_primary_kerberos: "
920 "failed to pull old package_PrimaryKerberosBlob: %s",
921 nt_errstr(status));
922 return LDB_ERR_OPERATIONS_ERROR;
925 if (_old_pkb.version != 3) {
926 ldb_asprintf_errstring(ldb,
927 "setup_primary_kerberos: "
928 "package_PrimaryKerberosBlob version[%u] expected[3]",
929 _old_pkb.version);
930 return LDB_ERR_OPERATIONS_ERROR;
933 old_pkb3 = &_old_pkb.ctr.ctr3;
936 /* if we didn't found the old keys we're done */
937 if (!old_pkb3) {
938 return LDB_SUCCESS;
941 /* fill in the old keys */
942 pkb3->num_old_keys = old_pkb3->num_keys;
943 pkb3->old_keys = old_pkb3->keys;
945 return LDB_SUCCESS;
948 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
949 const struct supplementalCredentialsBlob *old_scb,
950 struct package_PrimaryKerberosBlob *pkb)
952 struct ldb_context *ldb;
953 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
954 struct supplementalCredentialsPackage *old_scp = NULL;
955 struct package_PrimaryKerberosBlob _old_pkb;
956 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
957 uint32_t i;
958 enum ndr_err_code ndr_err;
960 ldb = ldb_module_get_ctx(io->ac->module);
963 * prepare generation of keys
965 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
966 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
967 * ENCTYPE_DES_CBC_MD5
968 * ENCTYPE_DES_CBC_CRC
970 pkb->version = 4;
971 pkb4->salt.string = io->g.salt;
972 pkb4->default_iteration_count = 4096;
973 pkb4->num_keys = 4;
975 pkb4->keys = talloc_array(io->ac,
976 struct package_PrimaryKerberosKey4,
977 pkb4->num_keys);
978 if (!pkb4->keys) {
979 return ldb_oom(ldb);
982 pkb4->keys[0].iteration_count = 4096;
983 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
984 pkb4->keys[0].value = &io->g.aes_256;
985 pkb4->keys[1].iteration_count = 4096;
986 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
987 pkb4->keys[1].value = &io->g.aes_128;
988 pkb4->keys[2].iteration_count = 4096;
989 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
990 pkb4->keys[2].value = &io->g.des_md5;
991 pkb4->keys[3].iteration_count = 4096;
992 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
993 pkb4->keys[3].value = &io->g.des_crc;
995 /* initialize the old keys to zero */
996 pkb4->num_old_keys = 0;
997 pkb4->old_keys = NULL;
998 pkb4->num_older_keys = 0;
999 pkb4->older_keys = NULL;
1001 /* if there're no old keys, then we're done */
1002 if (!old_scb) {
1003 return LDB_SUCCESS;
1006 for (i=0; i < old_scb->sub.num_packages; i++) {
1007 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
1008 continue;
1011 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
1012 continue;
1015 old_scp = &old_scb->sub.packages[i];
1016 break;
1018 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1019 if (old_scp) {
1020 DATA_BLOB blob;
1022 blob = strhex_to_data_blob(io->ac, old_scp->data);
1023 if (!blob.data) {
1024 return ldb_oom(ldb);
1027 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1028 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
1029 &_old_pkb,
1030 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
1031 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1032 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1033 ldb_asprintf_errstring(ldb,
1034 "setup_primary_kerberos_newer: "
1035 "failed to pull old package_PrimaryKerberosBlob: %s",
1036 nt_errstr(status));
1037 return LDB_ERR_OPERATIONS_ERROR;
1040 if (_old_pkb.version != 4) {
1041 ldb_asprintf_errstring(ldb,
1042 "setup_primary_kerberos_newer: "
1043 "package_PrimaryKerberosBlob version[%u] expected[4]",
1044 _old_pkb.version);
1045 return LDB_ERR_OPERATIONS_ERROR;
1048 old_pkb4 = &_old_pkb.ctr.ctr4;
1051 /* if we didn't found the old keys we're done */
1052 if (!old_pkb4) {
1053 return LDB_SUCCESS;
1056 /* fill in the old keys */
1057 pkb4->num_old_keys = old_pkb4->num_keys;
1058 pkb4->old_keys = old_pkb4->keys;
1059 pkb4->num_older_keys = old_pkb4->num_old_keys;
1060 pkb4->older_keys = old_pkb4->old_keys;
1062 return LDB_SUCCESS;
1065 static int setup_primary_wdigest(struct setup_password_fields_io *io,
1066 const struct supplementalCredentialsBlob *old_scb,
1067 struct package_PrimaryWDigestBlob *pdb)
1069 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1070 DATA_BLOB sAMAccountName;
1071 DATA_BLOB sAMAccountName_l;
1072 DATA_BLOB sAMAccountName_u;
1073 const char *user_principal_name = io->u.user_principal_name;
1074 DATA_BLOB userPrincipalName;
1075 DATA_BLOB userPrincipalName_l;
1076 DATA_BLOB userPrincipalName_u;
1077 DATA_BLOB netbios_domain;
1078 DATA_BLOB netbios_domain_l;
1079 DATA_BLOB netbios_domain_u;
1080 DATA_BLOB dns_domain;
1081 DATA_BLOB dns_domain_l;
1082 DATA_BLOB dns_domain_u;
1083 DATA_BLOB digest;
1084 DATA_BLOB delim;
1085 DATA_BLOB backslash;
1086 uint8_t i;
1087 struct {
1088 DATA_BLOB *user;
1089 DATA_BLOB *realm;
1090 DATA_BLOB *nt4dom;
1091 } wdigest[] = {
1093 * See
1094 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1095 * for what precalculated hashes are supposed to be stored...
1097 * I can't reproduce all values which should contain "Digest" as realm,
1098 * am I doing something wrong or is w2k3 just broken...?
1100 * W2K3 fills in following for a user:
1102 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1103 * sAMAccountName: NewUser2Sam
1104 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1106 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1107 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1108 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1109 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1110 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1111 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1112 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1113 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1114 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1115 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1116 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1117 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1118 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1119 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1120 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1121 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1122 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1123 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1124 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1125 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1126 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1127 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1128 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1129 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1130 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1131 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1132 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1133 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1134 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1136 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1137 * sAMAccountName: NewUser2Sam
1139 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1140 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1141 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1142 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1143 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1144 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1145 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1146 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1147 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1148 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1149 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1150 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1151 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1152 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1153 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1154 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1155 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1156 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1157 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1158 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1159 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1160 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1161 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1162 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1163 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1164 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1165 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1166 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1167 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1171 * sAMAccountName, netbios_domain
1174 .user = &sAMAccountName,
1175 .realm = &netbios_domain,
1178 .user = &sAMAccountName_l,
1179 .realm = &netbios_domain_l,
1182 .user = &sAMAccountName_u,
1183 .realm = &netbios_domain_u,
1186 .user = &sAMAccountName,
1187 .realm = &netbios_domain_u,
1190 .user = &sAMAccountName,
1191 .realm = &netbios_domain_l,
1194 .user = &sAMAccountName_u,
1195 .realm = &netbios_domain_l,
1198 .user = &sAMAccountName_l,
1199 .realm = &netbios_domain_u,
1202 * sAMAccountName, dns_domain
1205 .user = &sAMAccountName,
1206 .realm = &dns_domain,
1209 .user = &sAMAccountName_l,
1210 .realm = &dns_domain_l,
1213 .user = &sAMAccountName_u,
1214 .realm = &dns_domain_u,
1217 .user = &sAMAccountName,
1218 .realm = &dns_domain_u,
1221 .user = &sAMAccountName,
1222 .realm = &dns_domain_l,
1225 .user = &sAMAccountName_u,
1226 .realm = &dns_domain_l,
1229 .user = &sAMAccountName_l,
1230 .realm = &dns_domain_u,
1233 * userPrincipalName, no realm
1236 .user = &userPrincipalName,
1240 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1241 * the fallback to the sAMAccountName based userPrincipalName is correct
1243 .user = &userPrincipalName_l,
1246 .user = &userPrincipalName_u,
1249 * nt4dom\sAMAccountName, no realm
1252 .user = &sAMAccountName,
1253 .nt4dom = &netbios_domain
1256 .user = &sAMAccountName_l,
1257 .nt4dom = &netbios_domain_l
1260 .user = &sAMAccountName_u,
1261 .nt4dom = &netbios_domain_u
1265 * the following ones are guessed depending on the technet2 article
1266 * but not reproducable on a w2k3 server
1268 /* sAMAccountName with "Digest" realm */
1270 .user = &sAMAccountName,
1271 .realm = &digest
1274 .user = &sAMAccountName_l,
1275 .realm = &digest
1278 .user = &sAMAccountName_u,
1279 .realm = &digest
1281 /* userPrincipalName with "Digest" realm */
1283 .user = &userPrincipalName,
1284 .realm = &digest
1287 .user = &userPrincipalName_l,
1288 .realm = &digest
1291 .user = &userPrincipalName_u,
1292 .realm = &digest
1294 /* nt4dom\\sAMAccountName with "Digest" realm */
1296 .user = &sAMAccountName,
1297 .nt4dom = &netbios_domain,
1298 .realm = &digest
1301 .user = &sAMAccountName_l,
1302 .nt4dom = &netbios_domain_l,
1303 .realm = &digest
1306 .user = &sAMAccountName_u,
1307 .nt4dom = &netbios_domain_u,
1308 .realm = &digest
1312 /* prepare DATA_BLOB's used in the combinations array */
1313 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
1314 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
1315 if (!sAMAccountName_l.data) {
1316 return ldb_oom(ldb);
1318 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
1319 if (!sAMAccountName_u.data) {
1320 return ldb_oom(ldb);
1323 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1324 if (!user_principal_name) {
1325 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
1326 io->u.sAMAccountName,
1327 io->ac->status->domain_data.dns_domain);
1328 if (!user_principal_name) {
1329 return ldb_oom(ldb);
1332 userPrincipalName = data_blob_string_const(user_principal_name);
1333 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
1334 if (!userPrincipalName_l.data) {
1335 return ldb_oom(ldb);
1337 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
1338 if (!userPrincipalName_u.data) {
1339 return ldb_oom(ldb);
1342 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
1343 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
1344 io->ac->status->domain_data.netbios_domain));
1345 if (!netbios_domain_l.data) {
1346 return ldb_oom(ldb);
1348 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
1349 io->ac->status->domain_data.netbios_domain));
1350 if (!netbios_domain_u.data) {
1351 return ldb_oom(ldb);
1354 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1355 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1356 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
1358 digest = data_blob_string_const("Digest");
1360 delim = data_blob_string_const(":");
1361 backslash = data_blob_string_const("\\");
1363 pdb->num_hashes = ARRAY_SIZE(wdigest);
1364 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
1365 pdb->num_hashes);
1366 if (!pdb->hashes) {
1367 return ldb_oom(ldb);
1370 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
1371 MD5_CTX md5;
1372 MD5Init(&md5);
1373 if (wdigest[i].nt4dom) {
1374 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
1375 MD5Update(&md5, backslash.data, backslash.length);
1377 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
1378 MD5Update(&md5, delim.data, delim.length);
1379 if (wdigest[i].realm) {
1380 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1382 MD5Update(&md5, delim.data, delim.length);
1383 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1384 MD5Final(pdb->hashes[i].hash, &md5);
1387 return LDB_SUCCESS;
1390 static int setup_supplemental_field(struct setup_password_fields_io *io)
1392 struct ldb_context *ldb;
1393 struct supplementalCredentialsBlob scb;
1394 struct supplementalCredentialsBlob _old_scb;
1395 struct supplementalCredentialsBlob *old_scb = NULL;
1396 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1397 uint32_t num_names = 0;
1398 const char *names[1+4];
1399 uint32_t num_packages = 0;
1400 struct supplementalCredentialsPackage packages[1+4];
1401 /* Packages */
1402 struct supplementalCredentialsPackage *pp = NULL;
1403 struct package_PackagesBlob pb;
1404 DATA_BLOB pb_blob;
1405 char *pb_hexstr;
1406 /* Primary:Kerberos-Newer-Keys */
1407 const char **nkn = NULL;
1408 struct supplementalCredentialsPackage *pkn = NULL;
1409 struct package_PrimaryKerberosBlob pknb;
1410 DATA_BLOB pknb_blob;
1411 char *pknb_hexstr;
1412 /* Primary:Kerberos */
1413 const char **nk = NULL;
1414 struct supplementalCredentialsPackage *pk = NULL;
1415 struct package_PrimaryKerberosBlob pkb;
1416 DATA_BLOB pkb_blob;
1417 char *pkb_hexstr;
1418 /* Primary:WDigest */
1419 const char **nd = NULL;
1420 struct supplementalCredentialsPackage *pd = NULL;
1421 struct package_PrimaryWDigestBlob pdb;
1422 DATA_BLOB pdb_blob;
1423 char *pdb_hexstr;
1424 /* Primary:CLEARTEXT */
1425 const char **nc = NULL;
1426 struct supplementalCredentialsPackage *pc = NULL;
1427 struct package_PrimaryCLEARTEXTBlob pcb;
1428 DATA_BLOB pcb_blob;
1429 char *pcb_hexstr;
1430 int ret;
1431 enum ndr_err_code ndr_err;
1432 uint8_t zero16[16];
1433 bool do_newer_keys = false;
1434 bool do_cleartext = false;
1436 ZERO_STRUCT(zero16);
1437 ZERO_STRUCT(names);
1439 ldb = ldb_module_get_ctx(io->ac->module);
1441 if (!io->n.cleartext_utf8) {
1443 * when we don't have a cleartext password
1444 * we can't setup a supplementalCredential value
1446 return LDB_SUCCESS;
1449 /* if there's an old supplementaCredentials blob then parse it */
1450 if (io->o.supplemental) {
1451 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1452 &_old_scb,
1453 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1454 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1455 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1456 ldb_asprintf_errstring(ldb,
1457 "setup_supplemental_field: "
1458 "failed to pull old supplementalCredentialsBlob: %s",
1459 nt_errstr(status));
1460 return LDB_ERR_OPERATIONS_ERROR;
1463 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1464 old_scb = &_old_scb;
1465 } else {
1466 ldb_debug(ldb, LDB_DEBUG_ERROR,
1467 "setup_supplemental_field: "
1468 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1469 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1472 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1473 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1475 if (io->ac->status->domain_data.store_cleartext &&
1476 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1477 do_cleartext = true;
1481 * The ordering is this
1483 * Primary:Kerberos-Newer-Keys (optional)
1484 * Primary:Kerberos
1485 * Primary:WDigest
1486 * Primary:CLEARTEXT (optional)
1488 * And the 'Packages' package is insert before the last
1489 * other package.
1491 if (do_newer_keys) {
1492 /* Primary:Kerberos-Newer-Keys */
1493 nkn = &names[num_names++];
1494 pkn = &packages[num_packages++];
1497 /* Primary:Kerberos */
1498 nk = &names[num_names++];
1499 pk = &packages[num_packages++];
1501 if (!do_cleartext) {
1502 /* Packages */
1503 pp = &packages[num_packages++];
1506 /* Primary:WDigest */
1507 nd = &names[num_names++];
1508 pd = &packages[num_packages++];
1510 if (do_cleartext) {
1511 /* Packages */
1512 pp = &packages[num_packages++];
1514 /* Primary:CLEARTEXT */
1515 nc = &names[num_names++];
1516 pc = &packages[num_packages++];
1519 if (pkn) {
1521 * setup 'Primary:Kerberos-Newer-Keys' element
1523 *nkn = "Kerberos-Newer-Keys";
1525 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1526 if (ret != LDB_SUCCESS) {
1527 return ret;
1530 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1531 &pknb,
1532 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1533 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1534 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1535 ldb_asprintf_errstring(ldb,
1536 "setup_supplemental_field: "
1537 "failed to push package_PrimaryKerberosNeverBlob: %s",
1538 nt_errstr(status));
1539 return LDB_ERR_OPERATIONS_ERROR;
1541 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1542 if (!pknb_hexstr) {
1543 return ldb_oom(ldb);
1545 pkn->name = "Primary:Kerberos-Newer-Keys";
1546 pkn->reserved = 1;
1547 pkn->data = pknb_hexstr;
1551 * setup 'Primary:Kerberos' element
1553 *nk = "Kerberos";
1555 ret = setup_primary_kerberos(io, old_scb, &pkb);
1556 if (ret != LDB_SUCCESS) {
1557 return ret;
1560 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1561 &pkb,
1562 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1563 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1564 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1565 ldb_asprintf_errstring(ldb,
1566 "setup_supplemental_field: "
1567 "failed to push package_PrimaryKerberosBlob: %s",
1568 nt_errstr(status));
1569 return LDB_ERR_OPERATIONS_ERROR;
1571 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1572 if (!pkb_hexstr) {
1573 return ldb_oom(ldb);
1575 pk->name = "Primary:Kerberos";
1576 pk->reserved = 1;
1577 pk->data = pkb_hexstr;
1580 * setup 'Primary:WDigest' element
1582 *nd = "WDigest";
1584 ret = setup_primary_wdigest(io, old_scb, &pdb);
1585 if (ret != LDB_SUCCESS) {
1586 return ret;
1589 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1590 &pdb,
1591 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1592 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1593 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1594 ldb_asprintf_errstring(ldb,
1595 "setup_supplemental_field: "
1596 "failed to push package_PrimaryWDigestBlob: %s",
1597 nt_errstr(status));
1598 return LDB_ERR_OPERATIONS_ERROR;
1600 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1601 if (!pdb_hexstr) {
1602 return ldb_oom(ldb);
1604 pd->name = "Primary:WDigest";
1605 pd->reserved = 1;
1606 pd->data = pdb_hexstr;
1609 * setup 'Primary:CLEARTEXT' element
1611 if (pc) {
1612 *nc = "CLEARTEXT";
1614 pcb.cleartext = *io->n.cleartext_utf16;
1616 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1617 &pcb,
1618 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1619 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1620 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1621 ldb_asprintf_errstring(ldb,
1622 "setup_supplemental_field: "
1623 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1624 nt_errstr(status));
1625 return LDB_ERR_OPERATIONS_ERROR;
1627 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1628 if (!pcb_hexstr) {
1629 return ldb_oom(ldb);
1631 pc->name = "Primary:CLEARTEXT";
1632 pc->reserved = 1;
1633 pc->data = pcb_hexstr;
1637 * setup 'Packages' element
1639 pb.names = names;
1640 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1641 &pb,
1642 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1643 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1644 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1645 ldb_asprintf_errstring(ldb,
1646 "setup_supplemental_field: "
1647 "failed to push package_PackagesBlob: %s",
1648 nt_errstr(status));
1649 return LDB_ERR_OPERATIONS_ERROR;
1651 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1652 if (!pb_hexstr) {
1653 return ldb_oom(ldb);
1655 pp->name = "Packages";
1656 pp->reserved = 2;
1657 pp->data = pb_hexstr;
1660 * setup 'supplementalCredentials' value
1662 ZERO_STRUCT(scb);
1663 scb.sub.num_packages = num_packages;
1664 scb.sub.packages = packages;
1666 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1667 &scb,
1668 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1669 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1670 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1671 ldb_asprintf_errstring(ldb,
1672 "setup_supplemental_field: "
1673 "failed to push supplementalCredentialsBlob: %s",
1674 nt_errstr(status));
1675 return LDB_ERR_OPERATIONS_ERROR;
1678 return LDB_SUCCESS;
1681 static int setup_last_set_field(struct setup_password_fields_io *io)
1683 const struct ldb_message *msg = NULL;
1685 switch (io->ac->req->operation) {
1686 case LDB_ADD:
1687 msg = io->ac->req->op.add.message;
1688 break;
1689 case LDB_MODIFY:
1690 msg = io->ac->req->op.mod.message;
1691 break;
1692 default:
1693 return LDB_ERR_OPERATIONS_ERROR;
1694 break;
1697 if (io->ac->pwd_last_set_bypass) {
1698 struct ldb_message_element *el;
1700 if (msg == NULL) {
1701 return LDB_ERR_CONSTRAINT_VIOLATION;
1704 el = ldb_msg_find_element(msg, "pwdLastSet");
1705 if (el == NULL) {
1706 return LDB_ERR_CONSTRAINT_VIOLATION;
1709 io->g.last_set = samdb_result_nttime(msg, "pwdLastSet", 0);
1710 return LDB_SUCCESS;
1713 /* set it as now */
1714 unix_to_nt_time(&io->g.last_set, time(NULL));
1716 return LDB_SUCCESS;
1719 static int setup_given_passwords(struct setup_password_fields_io *io,
1720 struct setup_password_fields_given *g)
1722 struct ldb_context *ldb;
1723 bool ok;
1725 ldb = ldb_module_get_ctx(io->ac->module);
1727 if (g->cleartext_utf8) {
1728 struct ldb_val *cleartext_utf16_blob;
1730 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1731 if (!cleartext_utf16_blob) {
1732 return ldb_oom(ldb);
1734 if (!convert_string_talloc(io->ac,
1735 CH_UTF8, CH_UTF16,
1736 g->cleartext_utf8->data,
1737 g->cleartext_utf8->length,
1738 (void *)&cleartext_utf16_blob->data,
1739 &cleartext_utf16_blob->length)) {
1740 if (g->cleartext_utf8->length != 0) {
1741 talloc_free(cleartext_utf16_blob);
1742 ldb_asprintf_errstring(ldb,
1743 "setup_password_fields: "
1744 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1745 io->u.sAMAccountName);
1746 return LDB_ERR_CONSTRAINT_VIOLATION;
1747 } else {
1748 /* passwords with length "0" are valid! */
1749 cleartext_utf16_blob->data = NULL;
1750 cleartext_utf16_blob->length = 0;
1753 g->cleartext_utf16 = cleartext_utf16_blob;
1754 } else if (g->cleartext_utf16) {
1755 struct ldb_val *cleartext_utf8_blob;
1757 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1758 if (!cleartext_utf8_blob) {
1759 return ldb_oom(ldb);
1761 if (!convert_string_talloc(io->ac,
1762 CH_UTF16MUNGED, CH_UTF8,
1763 g->cleartext_utf16->data,
1764 g->cleartext_utf16->length,
1765 (void *)&cleartext_utf8_blob->data,
1766 &cleartext_utf8_blob->length)) {
1767 if (g->cleartext_utf16->length != 0) {
1768 /* We must bail out here, the input wasn't even
1769 * a multiple of 2 bytes */
1770 talloc_free(cleartext_utf8_blob);
1771 ldb_asprintf_errstring(ldb,
1772 "setup_password_fields: "
1773 "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)!",
1774 io->u.sAMAccountName);
1775 return LDB_ERR_CONSTRAINT_VIOLATION;
1776 } else {
1777 /* passwords with length "0" are valid! */
1778 cleartext_utf8_blob->data = NULL;
1779 cleartext_utf8_blob->length = 0;
1782 g->cleartext_utf8 = cleartext_utf8_blob;
1785 if (g->cleartext_utf16) {
1786 struct samr_Password *nt_hash;
1788 nt_hash = talloc(io->ac, struct samr_Password);
1789 if (!nt_hash) {
1790 return ldb_oom(ldb);
1792 g->nt_hash = nt_hash;
1794 /* compute the new nt hash */
1795 mdfour(nt_hash->hash,
1796 g->cleartext_utf16->data,
1797 g->cleartext_utf16->length);
1800 if (g->cleartext_utf8) {
1801 struct samr_Password *lm_hash;
1803 lm_hash = talloc(io->ac, struct samr_Password);
1804 if (!lm_hash) {
1805 return ldb_oom(ldb);
1808 /* compute the new lm hash */
1809 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1810 if (ok) {
1811 g->lm_hash = lm_hash;
1812 } else {
1813 talloc_free(lm_hash);
1817 return LDB_SUCCESS;
1820 static int setup_password_fields(struct setup_password_fields_io *io)
1822 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1823 struct loadparm_context *lp_ctx =
1824 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1825 struct loadparm_context);
1826 int ret;
1828 /* transform the old password (for password changes) */
1829 ret = setup_given_passwords(io, &io->og);
1830 if (ret != LDB_SUCCESS) {
1831 return ret;
1834 /* transform the new password */
1835 ret = setup_given_passwords(io, &io->n);
1836 if (ret != LDB_SUCCESS) {
1837 return ret;
1840 if (io->n.cleartext_utf8) {
1841 ret = setup_kerberos_keys(io);
1842 if (ret != LDB_SUCCESS) {
1843 return ret;
1847 ret = setup_nt_fields(io);
1848 if (ret != LDB_SUCCESS) {
1849 return ret;
1852 if (lpcfg_lanman_auth(lp_ctx)) {
1853 ret = setup_lm_fields(io);
1854 if (ret != LDB_SUCCESS) {
1855 return ret;
1857 } else {
1858 io->g.lm_hash = NULL;
1859 io->g.lm_history_len = 0;
1862 ret = setup_supplemental_field(io);
1863 if (ret != LDB_SUCCESS) {
1864 return ret;
1867 ret = setup_last_set_field(io);
1868 if (ret != LDB_SUCCESS) {
1869 return ret;
1872 return LDB_SUCCESS;
1875 static int check_password_restrictions(struct setup_password_fields_io *io)
1877 struct ldb_context *ldb;
1878 int ret;
1880 ldb = ldb_module_get_ctx(io->ac->module);
1882 /* First check the old password is correct, for password changes */
1883 if (!io->ac->pwd_reset) {
1884 bool nt_hash_checked = false;
1886 /* we need the old nt or lm hash given by the client */
1887 if (!io->og.nt_hash && !io->og.lm_hash) {
1888 ldb_asprintf_errstring(ldb,
1889 "check_password_restrictions: "
1890 "You need to provide the old password in order "
1891 "to change it!");
1892 return LDB_ERR_UNWILLING_TO_PERFORM;
1895 /* The password modify through the NT hash is encouraged and
1896 has no problems at all */
1897 if (io->og.nt_hash) {
1898 if (!io->o.nt_hash || memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
1899 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1900 ldb_asprintf_errstring(ldb,
1901 "%08X: %s - check_password_restrictions: "
1902 "The old password specified doesn't match!",
1903 W_ERROR_V(WERR_INVALID_PASSWORD),
1904 ldb_strerror(ret));
1905 return ret;
1908 nt_hash_checked = true;
1911 /* But it is also possible to change a password by the LM hash
1912 * alone for compatibility reasons. This check is optional if
1913 * the NT hash was already checked - otherwise it's mandatory.
1914 * (as the SAMR operations request it). */
1915 if (io->og.lm_hash) {
1916 if ((!io->o.lm_hash && !nt_hash_checked)
1917 || (io->o.lm_hash && memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0)) {
1918 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1919 ldb_asprintf_errstring(ldb,
1920 "%08X: %s - check_password_restrictions: "
1921 "The old password specified doesn't match!",
1922 W_ERROR_V(WERR_INVALID_PASSWORD),
1923 ldb_strerror(ret));
1924 return ret;
1929 if (io->u.restrictions == 0) {
1930 /* FIXME: Is this right? */
1931 return LDB_SUCCESS;
1934 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
1935 if ((io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) &&
1936 !io->ac->pwd_reset)
1938 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1939 ldb_asprintf_errstring(ldb,
1940 "%08X: %s - check_password_restrictions: "
1941 "password is too young to change!",
1942 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1943 ldb_strerror(ret));
1944 return ret;
1948 * Fundamental password checks done by the call
1949 * "samdb_check_password".
1950 * It is also in use by "dcesrv_samr_ValidatePassword".
1952 if (io->n.cleartext_utf8 != NULL) {
1953 enum samr_ValidationStatus vstat;
1954 vstat = samdb_check_password(io->n.cleartext_utf8,
1955 io->ac->status->domain_data.pwdProperties,
1956 io->ac->status->domain_data.minPwdLength);
1957 switch (vstat) {
1958 case SAMR_VALIDATION_STATUS_SUCCESS:
1959 /* perfect -> proceed! */
1960 break;
1962 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
1963 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1964 ldb_asprintf_errstring(ldb,
1965 "%08X: %s - check_password_restrictions: "
1966 "the password is too short. It should be equal or longer than %u characters!",
1967 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1968 ldb_strerror(ret),
1969 io->ac->status->domain_data.minPwdLength);
1970 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1971 return ret;
1973 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
1974 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1975 ldb_asprintf_errstring(ldb,
1976 "%08X: %s - check_password_restrictions: "
1977 "the password does not meet the complexity criteria!",
1978 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1979 ldb_strerror(ret));
1980 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1981 return ret;
1983 default:
1984 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1985 ldb_asprintf_errstring(ldb,
1986 "%08X: %s - check_password_restrictions: "
1987 "the password doesn't fit by a certain reason!",
1988 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1989 ldb_strerror(ret));
1990 return ret;
1994 if (io->ac->pwd_reset) {
1995 return LDB_SUCCESS;
1998 if (io->n.nt_hash) {
1999 uint32_t i;
2001 /* checks the NT hash password history */
2002 for (i = 0; i < io->o.nt_history_len; i++) {
2003 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
2004 if (ret == 0) {
2005 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2006 ldb_asprintf_errstring(ldb,
2007 "%08X: %s - check_password_restrictions: "
2008 "the password was already used (in history)!",
2009 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2010 ldb_strerror(ret));
2011 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2012 return ret;
2017 if (io->n.lm_hash) {
2018 uint32_t i;
2020 /* checks the LM hash password history */
2021 for (i = 0; i < io->o.lm_history_len; i++) {
2022 ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
2023 if (ret == 0) {
2024 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2025 ldb_asprintf_errstring(ldb,
2026 "%08X: %s - check_password_restrictions: "
2027 "the password was already used (in history)!",
2028 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2029 ldb_strerror(ret));
2030 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2031 return ret;
2036 /* are all password changes disallowed? */
2037 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
2038 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2039 ldb_asprintf_errstring(ldb,
2040 "%08X: %s - check_password_restrictions: "
2041 "password changes disabled!",
2042 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2043 ldb_strerror(ret));
2044 return ret;
2047 /* can this user change the password? */
2048 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
2049 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2050 ldb_asprintf_errstring(ldb,
2051 "%08X: %s - check_password_restrictions: "
2052 "password can't be changed on this account!",
2053 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2054 ldb_strerror(ret));
2055 return ret;
2058 return LDB_SUCCESS;
2062 * This is intended for use by the "password_hash" module since there
2063 * password changes can be specified through one message element with the
2064 * new password (to set) and another one with the old password (to unset).
2066 * The first which sets a password (new value) can have flags
2067 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2068 * for entries). The latter (old value) has always specified
2069 * LDB_FLAG_MOD_DELETE.
2071 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2072 * matching message elements are malformed in respect to the set/change rules.
2073 * Otherwise it returns LDB_SUCCESS.
2075 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
2076 const char *name,
2077 enum ldb_request_type operation,
2078 const struct ldb_val **new_val,
2079 const struct ldb_val **old_val)
2081 unsigned int i;
2083 *new_val = NULL;
2084 *old_val = NULL;
2086 if (msg == NULL) {
2087 return LDB_SUCCESS;
2090 for (i = 0; i < msg->num_elements; i++) {
2091 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
2092 continue;
2095 if ((operation == LDB_MODIFY) &&
2096 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
2097 /* 0 values are allowed */
2098 if (msg->elements[i].num_values == 1) {
2099 *old_val = &msg->elements[i].values[0];
2100 } else if (msg->elements[i].num_values > 1) {
2101 return LDB_ERR_CONSTRAINT_VIOLATION;
2103 } else if ((operation == LDB_MODIFY) &&
2104 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
2105 if (msg->elements[i].num_values > 0) {
2106 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2107 } else {
2108 return LDB_ERR_UNWILLING_TO_PERFORM;
2110 } else {
2111 /* Add operations and LDB_FLAG_MOD_ADD */
2112 if (msg->elements[i].num_values > 0) {
2113 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2114 } else {
2115 return LDB_ERR_CONSTRAINT_VIOLATION;
2120 return LDB_SUCCESS;
2123 static int setup_io(struct ph_context *ac,
2124 const struct ldb_message *orig_msg,
2125 const struct ldb_message *searched_msg,
2126 struct setup_password_fields_io *io)
2128 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
2129 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2130 struct loadparm_context *lp_ctx =
2131 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2132 struct loadparm_context);
2133 int ret;
2135 ZERO_STRUCTP(io);
2137 /* Some operations below require kerberos contexts */
2139 if (smb_krb5_init_context(ac,
2140 ldb_get_event_context(ldb),
2141 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
2142 &io->smb_krb5_context) != 0) {
2143 return ldb_operr(ldb);
2146 io->ac = ac;
2148 io->u.userAccountControl = ldb_msg_find_attr_as_uint(searched_msg,
2149 "userAccountControl", 0);
2150 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
2151 io->u.sAMAccountName = ldb_msg_find_attr_as_string(searched_msg,
2152 "sAMAccountName", NULL);
2153 io->u.user_principal_name = ldb_msg_find_attr_as_string(searched_msg,
2154 "userPrincipalName", NULL);
2155 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
2157 if (io->u.sAMAccountName == NULL) {
2158 ldb_asprintf_errstring(ldb,
2159 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2160 ldb_dn_get_linearized(searched_msg->dn));
2162 return LDB_ERR_CONSTRAINT_VIOLATION;
2165 /* Only non-trust accounts have restrictions (possibly this test is the
2166 * wrong way around, but we like to be restrictive if possible */
2167 io->u.restrictions = !(io->u.userAccountControl
2168 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
2169 | UF_SERVER_TRUST_ACCOUNT));
2171 if (ac->userPassword) {
2172 ret = msg_find_old_and_new_pwd_val(orig_msg, "userPassword",
2173 ac->req->operation,
2174 &io->n.cleartext_utf8,
2175 &io->og.cleartext_utf8);
2176 if (ret != LDB_SUCCESS) {
2177 ldb_asprintf_errstring(ldb,
2178 "setup_io: "
2179 "it's only allowed to set the old password once!");
2180 return ret;
2184 if (io->n.cleartext_utf8 != NULL) {
2185 struct ldb_val *cleartext_utf8_blob;
2186 char *p;
2188 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
2189 if (!cleartext_utf8_blob) {
2190 return ldb_oom(ldb);
2193 *cleartext_utf8_blob = *io->n.cleartext_utf8;
2195 /* make sure we have a null terminated string */
2196 p = talloc_strndup(cleartext_utf8_blob,
2197 (const char *)io->n.cleartext_utf8->data,
2198 io->n.cleartext_utf8->length);
2199 if ((p == NULL) && (io->n.cleartext_utf8->length > 0)) {
2200 return ldb_oom(ldb);
2202 cleartext_utf8_blob->data = (uint8_t *)p;
2204 io->n.cleartext_utf8 = cleartext_utf8_blob;
2207 ret = msg_find_old_and_new_pwd_val(orig_msg, "clearTextPassword",
2208 ac->req->operation,
2209 &io->n.cleartext_utf16,
2210 &io->og.cleartext_utf16);
2211 if (ret != LDB_SUCCESS) {
2212 ldb_asprintf_errstring(ldb,
2213 "setup_io: "
2214 "it's only allowed to set the old password once!");
2215 return ret;
2218 /* this rather strange looking piece of code is there to
2219 handle a ldap client setting a password remotely using the
2220 unicodePwd ldap field. The syntax is that the password is
2221 in UTF-16LE, with a " at either end. Unfortunately the
2222 unicodePwd field is also used to store the nt hashes
2223 internally in Samba, and is used in the nt hash format on
2224 the wire in DRS replication, so we have a single name for
2225 two distinct values. The code below leaves us with a small
2226 chance (less than 1 in 2^32) of a mixup, if someone manages
2227 to create a MD4 hash which starts and ends in 0x22 0x00, as
2228 that would then be treated as a UTF16 password rather than
2229 a nthash */
2231 ret = msg_find_old_and_new_pwd_val(orig_msg, "unicodePwd",
2232 ac->req->operation,
2233 &quoted_utf16,
2234 &old_quoted_utf16);
2235 if (ret != LDB_SUCCESS) {
2236 ldb_asprintf_errstring(ldb,
2237 "setup_io: "
2238 "it's only allowed to set the old password once!");
2239 return ret;
2242 /* Checks and converts the actual "unicodePwd" attribute */
2243 if (!ac->hash_values &&
2244 quoted_utf16 &&
2245 quoted_utf16->length >= 4 &&
2246 quoted_utf16->data[0] == '"' &&
2247 quoted_utf16->data[1] == 0 &&
2248 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
2249 quoted_utf16->data[quoted_utf16->length-1] == 0) {
2250 struct ldb_val *quoted_utf16_2;
2252 if (io->n.cleartext_utf16) {
2253 /* refuse the change if someone wants to change with
2254 with both UTF16 possibilities at the same time... */
2255 ldb_asprintf_errstring(ldb,
2256 "setup_io: "
2257 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2258 return LDB_ERR_UNWILLING_TO_PERFORM;
2262 * adapt the quoted UTF16 string to be a real
2263 * cleartext one
2265 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2266 if (quoted_utf16_2 == NULL) {
2267 return ldb_oom(ldb);
2270 quoted_utf16_2->data = quoted_utf16->data + 2;
2271 quoted_utf16_2->length = quoted_utf16->length-4;
2272 io->n.cleartext_utf16 = quoted_utf16_2;
2273 io->n.nt_hash = NULL;
2275 } else if (quoted_utf16) {
2276 /* We have only the hash available -> so no plaintext here */
2277 if (!ac->hash_values) {
2278 /* refuse the change if someone wants to change
2279 the hash without control specified... */
2280 ldb_asprintf_errstring(ldb,
2281 "setup_io: "
2282 "it's not allowed to set the NT hash password directly'");
2283 /* this looks odd but this is what Windows does:
2284 returns "UNWILLING_TO_PERFORM" on wrong
2285 password sets and "CONSTRAINT_VIOLATION" on
2286 wrong password changes. */
2287 if (old_quoted_utf16 == NULL) {
2288 return LDB_ERR_UNWILLING_TO_PERFORM;
2291 return LDB_ERR_CONSTRAINT_VIOLATION;
2294 io->n.nt_hash = talloc(io->ac, struct samr_Password);
2295 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
2296 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
2299 /* Checks and converts the previous "unicodePwd" attribute */
2300 if (!ac->hash_values &&
2301 old_quoted_utf16 &&
2302 old_quoted_utf16->length >= 4 &&
2303 old_quoted_utf16->data[0] == '"' &&
2304 old_quoted_utf16->data[1] == 0 &&
2305 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
2306 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
2307 struct ldb_val *old_quoted_utf16_2;
2309 if (io->og.cleartext_utf16) {
2310 /* refuse the change if someone wants to change with
2311 both UTF16 possibilities at the same time... */
2312 ldb_asprintf_errstring(ldb,
2313 "setup_io: "
2314 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2315 return LDB_ERR_UNWILLING_TO_PERFORM;
2319 * adapt the quoted UTF16 string to be a real
2320 * cleartext one
2322 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2323 if (old_quoted_utf16_2 == NULL) {
2324 return ldb_oom(ldb);
2327 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
2328 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
2330 io->og.cleartext_utf16 = old_quoted_utf16_2;
2331 io->og.nt_hash = NULL;
2332 } else if (old_quoted_utf16) {
2333 /* We have only the hash available -> so no plaintext here */
2334 if (!ac->hash_values) {
2335 /* refuse the change if someone wants to change
2336 the hash without control specified... */
2337 ldb_asprintf_errstring(ldb,
2338 "setup_io: "
2339 "it's not allowed to set the NT hash password directly'");
2340 return LDB_ERR_UNWILLING_TO_PERFORM;
2343 io->og.nt_hash = talloc(io->ac, struct samr_Password);
2344 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
2345 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
2348 /* Handles the "dBCSPwd" attribute (LM hash) */
2349 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
2350 ret = msg_find_old_and_new_pwd_val(orig_msg, "dBCSPwd",
2351 ac->req->operation,
2352 &lm_hash, &old_lm_hash);
2353 if (ret != LDB_SUCCESS) {
2354 ldb_asprintf_errstring(ldb,
2355 "setup_io: "
2356 "it's only allowed to set the old password once!");
2357 return ret;
2360 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
2361 /* refuse the change if someone wants to change the hash
2362 without control specified... */
2363 ldb_asprintf_errstring(ldb,
2364 "setup_io: "
2365 "it's not allowed to set the LM hash password directly'");
2366 return LDB_ERR_UNWILLING_TO_PERFORM;
2369 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
2370 io->n.lm_hash = talloc(io->ac, struct samr_Password);
2371 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
2372 sizeof(io->n.lm_hash->hash)));
2374 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
2375 io->og.lm_hash = talloc(io->ac, struct samr_Password);
2376 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
2377 sizeof(io->og.lm_hash->hash)));
2381 * Handles the password change control if it's specified. It has the
2382 * precedance and overrides already specified old password values of
2383 * change requests (but that shouldn't happen since the control is
2384 * fully internal and only used in conjunction with replace requests!).
2386 if (ac->change != NULL) {
2387 io->og.nt_hash = NULL;
2388 if (ac->change->old_nt_pwd_hash != NULL) {
2389 io->og.nt_hash = talloc_memdup(io->ac,
2390 ac->change->old_nt_pwd_hash,
2391 sizeof(struct samr_Password));
2393 io->og.lm_hash = NULL;
2394 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
2395 io->og.lm_hash = talloc_memdup(io->ac,
2396 ac->change->old_lm_pwd_hash,
2397 sizeof(struct samr_Password));
2401 /* refuse the change if someone wants to change the clear-
2402 text and supply his own hashes at the same time... */
2403 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
2404 && (io->n.nt_hash || io->n.lm_hash)) {
2405 ldb_asprintf_errstring(ldb,
2406 "setup_io: "
2407 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2408 return LDB_ERR_UNWILLING_TO_PERFORM;
2411 /* refuse the change if someone wants to change the password
2412 using both plaintext methods (UTF8 and UTF16) at the same time... */
2413 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
2414 ldb_asprintf_errstring(ldb,
2415 "setup_io: "
2416 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2417 return LDB_ERR_UNWILLING_TO_PERFORM;
2420 /* refuse the change if someone tries to set/change the password by
2421 * the lanman hash alone and we've deactivated that mechanism. This
2422 * would end in an account without any password! */
2423 if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
2424 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
2425 ldb_asprintf_errstring(ldb,
2426 "setup_io: "
2427 "It' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2428 /* on "userPassword" and "clearTextPassword" we've to return
2429 * something different, since these are virtual attributes */
2430 if ((ldb_msg_find_element(orig_msg, "userPassword") != NULL) ||
2431 (ldb_msg_find_element(orig_msg, "clearTextPassword") != NULL)) {
2432 return LDB_ERR_CONSTRAINT_VIOLATION;
2434 return LDB_ERR_UNWILLING_TO_PERFORM;
2437 /* refuse the change if someone wants to compare against a plaintext
2438 or hash at the same time for a "password modify" operation... */
2439 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
2440 && (io->og.nt_hash || io->og.lm_hash)) {
2441 ldb_asprintf_errstring(ldb,
2442 "setup_io: "
2443 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2444 return LDB_ERR_UNWILLING_TO_PERFORM;
2447 /* refuse the change if someone wants to compare against both
2448 * plaintexts at the same time for a "password modify" operation... */
2449 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2450 ldb_asprintf_errstring(ldb,
2451 "setup_io: "
2452 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2453 return LDB_ERR_UNWILLING_TO_PERFORM;
2456 /* Decides if we have a password modify or password reset operation */
2457 if (ac->req->operation == LDB_ADD) {
2458 /* On "add" we have only "password reset" */
2459 ac->pwd_reset = true;
2460 } else if (ac->req->operation == LDB_MODIFY) {
2461 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2462 || io->og.nt_hash || io->og.lm_hash) {
2463 /* If we have an old password specified then for sure it
2464 * is a user "password change" */
2465 ac->pwd_reset = false;
2466 } else {
2467 /* Otherwise we have also here a "password reset" */
2468 ac->pwd_reset = true;
2470 } else {
2471 /* this shouldn't happen */
2472 return ldb_operr(ldb);
2475 return LDB_SUCCESS;
2478 static struct ph_context *ph_init_context(struct ldb_module *module,
2479 struct ldb_request *req,
2480 bool userPassword)
2482 struct ldb_context *ldb;
2483 struct ph_context *ac;
2485 ldb = ldb_module_get_ctx(module);
2487 ac = talloc_zero(req, struct ph_context);
2488 if (ac == NULL) {
2489 ldb_set_errstring(ldb, "Out of Memory");
2490 return NULL;
2493 ac->module = module;
2494 ac->req = req;
2495 ac->userPassword = userPassword;
2497 return ac;
2500 static void ph_apply_controls(struct ph_context *ac)
2502 struct ldb_control *ctrl;
2504 ac->change_status = false;
2505 ctrl = ldb_request_get_control(ac->req,
2506 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2507 if (ctrl != NULL) {
2508 ac->change_status = true;
2510 /* Mark the "change status" control as uncritical (done) */
2511 ctrl->critical = false;
2514 ac->hash_values = false;
2515 ctrl = ldb_request_get_control(ac->req,
2516 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2517 if (ctrl != NULL) {
2518 ac->hash_values = true;
2520 /* Mark the "hash values" control as uncritical (done) */
2521 ctrl->critical = false;
2524 ctrl = ldb_request_get_control(ac->req,
2525 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2526 if (ctrl != NULL) {
2527 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2529 /* Mark the "change" control as uncritical (done) */
2530 ctrl->critical = false;
2533 ac->pwd_last_set_bypass = false;
2534 ctrl = ldb_request_get_control(ac->req,
2535 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID);
2536 if (ctrl != NULL) {
2537 ac->pwd_last_set_bypass = true;
2539 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2540 ctrl->critical = false;
2544 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2546 struct ph_context *ac;
2548 ac = talloc_get_type(req->context, struct ph_context);
2550 if (!ares) {
2551 return ldb_module_done(ac->req, NULL, NULL,
2552 LDB_ERR_OPERATIONS_ERROR);
2555 if (ares->type == LDB_REPLY_REFERRAL) {
2556 return ldb_module_send_referral(ac->req, ares->referral);
2559 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2560 /* On success and trivial errors a status control is being
2561 * added (used for example by the "samdb_set_password" call) */
2562 ldb_reply_add_control(ares,
2563 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2564 false,
2565 ac->status);
2568 if (ares->error != LDB_SUCCESS) {
2569 return ldb_module_done(ac->req, ares->controls,
2570 ares->response, ares->error);
2573 if (ares->type != LDB_REPLY_DONE) {
2574 talloc_free(ares);
2575 return ldb_module_done(ac->req, NULL, NULL,
2576 LDB_ERR_OPERATIONS_ERROR);
2579 return ldb_module_done(ac->req, ares->controls,
2580 ares->response, ares->error);
2583 static int password_hash_add_do_add(struct ph_context *ac);
2584 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2585 static int password_hash_mod_search_self(struct ph_context *ac);
2586 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2587 static int password_hash_mod_do_mod(struct ph_context *ac);
2589 static int get_domain_data_callback(struct ldb_request *req,
2590 struct ldb_reply *ares)
2592 struct ldb_context *ldb;
2593 struct ph_context *ac;
2594 struct loadparm_context *lp_ctx;
2595 int ret = LDB_SUCCESS;
2597 ac = talloc_get_type(req->context, struct ph_context);
2598 ldb = ldb_module_get_ctx(ac->module);
2600 if (!ares) {
2601 ret = LDB_ERR_OPERATIONS_ERROR;
2602 goto done;
2604 if (ares->error != LDB_SUCCESS) {
2605 return ldb_module_done(ac->req, ares->controls,
2606 ares->response, ares->error);
2609 switch (ares->type) {
2610 case LDB_REPLY_ENTRY:
2611 if (ac->status != NULL) {
2612 talloc_free(ares);
2614 ldb_set_errstring(ldb, "Too many results");
2615 ret = LDB_ERR_OPERATIONS_ERROR;
2616 goto done;
2619 /* Setup the "status" structure (used as control later) */
2620 ac->status = talloc_zero(ac->req,
2621 struct dsdb_control_password_change_status);
2622 if (ac->status == NULL) {
2623 talloc_free(ares);
2625 ldb_oom(ldb);
2626 ret = LDB_ERR_OPERATIONS_ERROR;
2627 goto done;
2630 /* Setup the "domain data" structure */
2631 ac->status->domain_data.pwdProperties =
2632 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
2633 ac->status->domain_data.pwdHistoryLength =
2634 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
2635 ac->status->domain_data.maxPwdAge =
2636 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
2637 ac->status->domain_data.minPwdAge =
2638 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
2639 ac->status->domain_data.minPwdLength =
2640 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
2641 ac->status->domain_data.store_cleartext =
2642 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2644 talloc_free(ares);
2646 /* For a domain DN, this puts things in dotted notation */
2647 /* For builtin domains, this will give details for the host,
2648 * but that doesn't really matter, as it's just used for salt
2649 * and kerberos principals, which don't exist here */
2651 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2652 struct loadparm_context);
2654 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
2655 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
2656 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
2658 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2660 ret = LDB_SUCCESS;
2661 break;
2663 case LDB_REPLY_REFERRAL:
2664 /* ignore */
2665 talloc_free(ares);
2666 ret = LDB_SUCCESS;
2667 break;
2669 case LDB_REPLY_DONE:
2670 talloc_free(ares);
2671 /* call the next step */
2672 switch (ac->req->operation) {
2673 case LDB_ADD:
2674 ret = password_hash_add_do_add(ac);
2675 break;
2677 case LDB_MODIFY:
2678 ret = password_hash_mod_do_mod(ac);
2679 break;
2681 default:
2682 ret = LDB_ERR_OPERATIONS_ERROR;
2683 break;
2685 break;
2688 done:
2689 if (ret != LDB_SUCCESS) {
2690 struct ldb_reply *new_ares;
2692 new_ares = talloc_zero(ac->req, struct ldb_reply);
2693 if (new_ares == NULL) {
2694 ldb_oom(ldb);
2695 return ldb_module_done(ac->req, NULL, NULL,
2696 LDB_ERR_OPERATIONS_ERROR);
2699 new_ares->error = ret;
2700 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2701 /* On success and trivial errors a status control is being
2702 * added (used for example by the "samdb_set_password" call) */
2703 ldb_reply_add_control(new_ares,
2704 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2705 false,
2706 ac->status);
2709 return ldb_module_done(ac->req, new_ares->controls,
2710 new_ares->response, new_ares->error);
2713 return LDB_SUCCESS;
2716 static int build_domain_data_request(struct ph_context *ac)
2718 /* attrs[] is returned from this function in
2719 ac->dom_req->op.search.attrs, so it must be static, as
2720 otherwise the compiler can put it on the stack */
2721 struct ldb_context *ldb;
2722 static const char * const attrs[] = { "pwdProperties",
2723 "pwdHistoryLength",
2724 "maxPwdAge",
2725 "minPwdAge",
2726 "minPwdLength",
2727 NULL };
2728 int ret;
2730 ldb = ldb_module_get_ctx(ac->module);
2732 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
2733 ldb_get_default_basedn(ldb),
2734 LDB_SCOPE_BASE,
2735 NULL, attrs,
2736 NULL,
2737 ac, get_domain_data_callback,
2738 ac->req);
2739 LDB_REQ_SET_LOCATION(ac->dom_req);
2740 return ret;
2743 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2745 struct ldb_context *ldb;
2746 struct ph_context *ac;
2747 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2748 *ntAttr, *lmAttr;
2749 int ret;
2750 struct ldb_control *bypass = NULL;
2751 bool userPassword = dsdb_user_password_support(module, req, req);
2753 ldb = ldb_module_get_ctx(module);
2755 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2757 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2758 return ldb_next_request(module, req);
2761 bypass = ldb_request_get_control(req,
2762 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2763 if (bypass != NULL) {
2764 /* Mark the "bypass" control as uncritical (done) */
2765 bypass->critical = false;
2766 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
2767 return password_hash_bypass(module, req);
2770 /* nobody must touch password histories and 'supplementalCredentials' */
2771 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2772 return LDB_ERR_UNWILLING_TO_PERFORM;
2774 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2775 return LDB_ERR_UNWILLING_TO_PERFORM;
2777 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2778 return LDB_ERR_UNWILLING_TO_PERFORM;
2781 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2782 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2784 userPasswordAttr = NULL;
2785 if (userPassword) {
2786 userPasswordAttr = ldb_msg_find_element(req->op.add.message,
2787 "userPassword");
2788 /* MS-ADTS 3.1.1.3.1.5.2 */
2789 if ((userPasswordAttr != NULL) &&
2790 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2791 return LDB_ERR_CONSTRAINT_VIOLATION;
2794 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2795 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2796 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2798 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2799 return ldb_next_request(module, req);
2802 /* Make sure we are performing the password set action on a (for us)
2803 * valid object. Those are instances of either "user" and/or
2804 * "inetOrgPerson". Otherwise continue with the submodules. */
2805 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2806 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2808 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2809 ldb_set_errstring(ldb,
2810 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2811 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2814 return ldb_next_request(module, req);
2817 ac = ph_init_context(module, req, userPassword);
2818 if (ac == NULL) {
2819 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2820 return ldb_operr(ldb);
2822 ph_apply_controls(ac);
2824 /* get user domain data */
2825 ret = build_domain_data_request(ac);
2826 if (ret != LDB_SUCCESS) {
2827 return ret;
2830 return ldb_next_request(module, ac->dom_req);
2833 static int password_hash_add_do_add(struct ph_context *ac)
2835 struct ldb_context *ldb;
2836 struct ldb_request *down_req;
2837 struct ldb_message *msg;
2838 struct setup_password_fields_io io;
2839 int ret;
2841 /* Prepare the internal data structure containing the passwords */
2842 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2843 if (ret != LDB_SUCCESS) {
2844 return ret;
2847 ldb = ldb_module_get_ctx(ac->module);
2849 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2850 if (msg == NULL) {
2851 return ldb_operr(ldb);
2854 /* remove attributes that we just read into 'io' */
2855 if (ac->userPassword) {
2856 ldb_msg_remove_attr(msg, "userPassword");
2858 ldb_msg_remove_attr(msg, "clearTextPassword");
2859 ldb_msg_remove_attr(msg, "unicodePwd");
2860 ldb_msg_remove_attr(msg, "dBCSPwd");
2861 ldb_msg_remove_attr(msg, "pwdLastSet");
2863 ret = setup_password_fields(&io);
2864 if (ret != LDB_SUCCESS) {
2865 return ret;
2868 ret = check_password_restrictions(&io);
2869 if (ret != LDB_SUCCESS) {
2870 return ret;
2873 if (io.g.nt_hash) {
2874 ret = samdb_msg_add_hash(ldb, ac, msg,
2875 "unicodePwd", io.g.nt_hash);
2876 if (ret != LDB_SUCCESS) {
2877 return ret;
2880 if (io.g.lm_hash) {
2881 ret = samdb_msg_add_hash(ldb, ac, msg,
2882 "dBCSPwd", io.g.lm_hash);
2883 if (ret != LDB_SUCCESS) {
2884 return ret;
2887 if (io.g.nt_history_len > 0) {
2888 ret = samdb_msg_add_hashes(ldb, ac, msg,
2889 "ntPwdHistory",
2890 io.g.nt_history,
2891 io.g.nt_history_len);
2892 if (ret != LDB_SUCCESS) {
2893 return ret;
2896 if (io.g.lm_history_len > 0) {
2897 ret = samdb_msg_add_hashes(ldb, ac, msg,
2898 "lmPwdHistory",
2899 io.g.lm_history,
2900 io.g.lm_history_len);
2901 if (ret != LDB_SUCCESS) {
2902 return ret;
2905 if (io.g.supplemental.length > 0) {
2906 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2907 &io.g.supplemental, NULL);
2908 if (ret != LDB_SUCCESS) {
2909 return ret;
2912 ret = samdb_msg_add_uint64(ldb, ac, msg,
2913 "pwdLastSet",
2914 io.g.last_set);
2915 if (ret != LDB_SUCCESS) {
2916 return ret;
2919 ret = ldb_build_add_req(&down_req, ldb, ac,
2920 msg,
2921 ac->req->controls,
2922 ac, ph_op_callback,
2923 ac->req);
2924 LDB_REQ_SET_LOCATION(down_req);
2925 if (ret != LDB_SUCCESS) {
2926 return ret;
2929 return ldb_next_request(ac->module, down_req);
2932 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
2934 struct ldb_context *ldb;
2935 struct ph_context *ac;
2936 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
2937 "unicodePwd", "dBCSPwd", NULL }, **l;
2938 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
2939 struct ldb_message_element *passwordAttr;
2940 struct ldb_message *msg;
2941 struct ldb_request *down_req;
2942 int ret;
2943 struct ldb_control *bypass = NULL;
2944 bool userPassword = dsdb_user_password_support(module, req, req);
2946 ldb = ldb_module_get_ctx(module);
2948 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
2950 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
2951 return ldb_next_request(module, req);
2954 bypass = ldb_request_get_control(req,
2955 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2956 if (bypass != NULL) {
2957 /* Mark the "bypass" control as uncritical (done) */
2958 bypass->critical = false;
2959 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
2960 return password_hash_bypass(module, req);
2963 /* nobody must touch password histories and 'supplementalCredentials' */
2964 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
2965 return LDB_ERR_UNWILLING_TO_PERFORM;
2967 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
2968 return LDB_ERR_UNWILLING_TO_PERFORM;
2970 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
2971 return LDB_ERR_UNWILLING_TO_PERFORM;
2974 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2975 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
2976 * For password changes/set there should be a 'delete' or a 'modify'
2977 * on these attributes. */
2978 attr_cnt = 0;
2979 for (l = passwordAttrs; *l != NULL; l++) {
2980 if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) {
2981 continue;
2984 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
2985 /* MS-ADTS 3.1.1.3.1.5.2 */
2986 if ((ldb_attr_cmp(*l, "userPassword") == 0) &&
2987 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2988 return LDB_ERR_CONSTRAINT_VIOLATION;
2991 ++attr_cnt;
2994 if (attr_cnt == 0) {
2995 return ldb_next_request(module, req);
2998 ac = ph_init_context(module, req, userPassword);
2999 if (!ac) {
3000 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3001 return ldb_operr(ldb);
3003 ph_apply_controls(ac);
3005 /* use a new message structure so that we can modify it */
3006 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3007 if (msg == NULL) {
3008 return ldb_oom(ldb);
3011 /* - check for single-valued password attributes
3012 * (if not return "CONSTRAINT_VIOLATION")
3013 * - check that for a password change operation one add and one delete
3014 * operation exists
3015 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3016 * - check that a password change and a password set operation cannot
3017 * be mixed
3018 * (if not return "UNWILLING_TO_PERFORM")
3019 * - remove all password attributes modifications from the first change
3020 * operation (anything without the passwords) - we will make the real
3021 * modification later */
3022 del_attr_cnt = 0;
3023 add_attr_cnt = 0;
3024 rep_attr_cnt = 0;
3025 for (l = passwordAttrs; *l != NULL; l++) {
3026 if ((!ac->userPassword) &&
3027 (ldb_attr_cmp(*l, "userPassword") == 0)) {
3028 continue;
3031 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
3032 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
3033 ++del_attr_cnt;
3035 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
3036 ++add_attr_cnt;
3038 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
3039 ++rep_attr_cnt;
3041 if ((passwordAttr->num_values != 1) &&
3042 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
3043 talloc_free(ac);
3044 ldb_asprintf_errstring(ldb,
3045 "'%s' attribute must have exactly one value on add operations!",
3046 *l);
3047 return LDB_ERR_CONSTRAINT_VIOLATION;
3049 if ((passwordAttr->num_values > 1) &&
3050 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
3051 talloc_free(ac);
3052 ldb_asprintf_errstring(ldb,
3053 "'%s' attribute must have zero or one value(s) on delete operations!",
3054 *l);
3055 return LDB_ERR_CONSTRAINT_VIOLATION;
3057 ldb_msg_remove_element(msg, passwordAttr);
3060 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
3061 talloc_free(ac);
3062 ldb_set_errstring(ldb,
3063 "Only the add action for a password change specified!");
3064 return LDB_ERR_UNWILLING_TO_PERFORM;
3066 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
3067 talloc_free(ac);
3068 ldb_set_errstring(ldb,
3069 "Only one delete and one add action for a password change allowed!");
3070 return LDB_ERR_UNWILLING_TO_PERFORM;
3072 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
3073 talloc_free(ac);
3074 ldb_set_errstring(ldb,
3075 "Either a password change or a password set operation is allowed!");
3076 return LDB_ERR_UNWILLING_TO_PERFORM;
3079 /* if there was nothing else to be modified skip to next step */
3080 if (msg->num_elements == 0) {
3081 return password_hash_mod_search_self(ac);
3084 ret = ldb_build_mod_req(&down_req, ldb, ac,
3085 msg,
3086 req->controls,
3087 ac, ph_modify_callback,
3088 req);
3089 LDB_REQ_SET_LOCATION(down_req);
3090 if (ret != LDB_SUCCESS) {
3091 return ret;
3094 return ldb_next_request(module, down_req);
3097 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3099 struct ph_context *ac;
3101 ac = talloc_get_type(req->context, struct ph_context);
3103 if (!ares) {
3104 return ldb_module_done(ac->req, NULL, NULL,
3105 LDB_ERR_OPERATIONS_ERROR);
3108 if (ares->type == LDB_REPLY_REFERRAL) {
3109 return ldb_module_send_referral(ac->req, ares->referral);
3112 if (ares->error != LDB_SUCCESS) {
3113 return ldb_module_done(ac->req, ares->controls,
3114 ares->response, ares->error);
3117 if (ares->type != LDB_REPLY_DONE) {
3118 talloc_free(ares);
3119 return ldb_module_done(ac->req, NULL, NULL,
3120 LDB_ERR_OPERATIONS_ERROR);
3123 talloc_free(ares);
3125 return password_hash_mod_search_self(ac);
3128 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
3130 struct ldb_context *ldb;
3131 struct ph_context *ac;
3132 int ret = LDB_SUCCESS;
3134 ac = talloc_get_type(req->context, struct ph_context);
3135 ldb = ldb_module_get_ctx(ac->module);
3137 if (!ares) {
3138 ret = LDB_ERR_OPERATIONS_ERROR;
3139 goto done;
3141 if (ares->error != LDB_SUCCESS) {
3142 return ldb_module_done(ac->req, ares->controls,
3143 ares->response, ares->error);
3146 /* we are interested only in the single reply (base search) */
3147 switch (ares->type) {
3148 case LDB_REPLY_ENTRY:
3149 /* Make sure we are performing the password change action on a
3150 * (for us) valid object. Those are instances of either "user"
3151 * and/or "inetOrgPerson". Otherwise continue with the
3152 * submodules. */
3153 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
3154 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
3155 talloc_free(ares);
3157 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
3158 ldb_set_errstring(ldb,
3159 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3160 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
3161 goto done;
3164 ret = ldb_next_request(ac->module, ac->req);
3165 goto done;
3168 if (ac->search_res != NULL) {
3169 talloc_free(ares);
3171 ldb_set_errstring(ldb, "Too many results");
3172 ret = LDB_ERR_OPERATIONS_ERROR;
3173 goto done;
3176 ac->search_res = talloc_steal(ac, ares);
3177 ret = LDB_SUCCESS;
3178 break;
3180 case LDB_REPLY_REFERRAL:
3181 /* ignore anything else for now */
3182 talloc_free(ares);
3183 ret = LDB_SUCCESS;
3184 break;
3186 case LDB_REPLY_DONE:
3187 talloc_free(ares);
3189 /* get user domain data */
3190 ret = build_domain_data_request(ac);
3191 if (ret != LDB_SUCCESS) {
3192 return ldb_module_done(ac->req, NULL, NULL, ret);
3195 ret = ldb_next_request(ac->module, ac->dom_req);
3196 break;
3199 done:
3200 if (ret != LDB_SUCCESS) {
3201 return ldb_module_done(ac->req, NULL, NULL, ret);
3204 return LDB_SUCCESS;
3207 static int password_hash_mod_search_self(struct ph_context *ac)
3209 struct ldb_context *ldb;
3210 static const char * const attrs[] = { "objectClass",
3211 "userAccountControl",
3212 "msDS-User-Account-Control-Computed",
3213 "pwdLastSet",
3214 "sAMAccountName",
3215 "objectSid",
3216 "userPrincipalName",
3217 "supplementalCredentials",
3218 "lmPwdHistory",
3219 "ntPwdHistory",
3220 "dBCSPwd",
3221 "unicodePwd",
3222 NULL };
3223 struct ldb_request *search_req;
3224 int ret;
3226 ldb = ldb_module_get_ctx(ac->module);
3228 ret = ldb_build_search_req(&search_req, ldb, ac,
3229 ac->req->op.mod.message->dn,
3230 LDB_SCOPE_BASE,
3231 "(objectclass=*)",
3232 attrs,
3233 NULL,
3234 ac, ph_mod_search_callback,
3235 ac->req);
3236 LDB_REQ_SET_LOCATION(search_req);
3237 if (ret != LDB_SUCCESS) {
3238 return ret;
3241 return ldb_next_request(ac->module, search_req);
3244 static int password_hash_mod_do_mod(struct ph_context *ac)
3246 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3247 struct loadparm_context *lp_ctx =
3248 talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3249 struct loadparm_context);
3250 struct ldb_request *mod_req;
3251 struct ldb_message *msg;
3252 const struct ldb_message *orig_msg, *searched_msg;
3253 struct setup_password_fields_io io;
3254 int ret;
3255 NTSTATUS status;
3257 /* use a new message structure so that we can modify it */
3258 msg = ldb_msg_new(ac);
3259 if (msg == NULL) {
3260 return ldb_operr(ldb);
3263 /* modify dn */
3264 msg->dn = ac->req->op.mod.message->dn;
3266 orig_msg = ac->req->op.mod.message;
3267 searched_msg = ac->search_res->message;
3269 /* Prepare the internal data structure containing the passwords */
3270 ret = setup_io(ac, orig_msg, searched_msg, &io);
3271 if (ret != LDB_SUCCESS) {
3272 return ret;
3275 if (io.ac->pwd_reset) {
3276 /* Get the old password from the database */
3277 status = samdb_result_passwords_no_lockout(io.ac,
3278 lp_ctx,
3279 discard_const_p(struct ldb_message, searched_msg),
3280 &io.o.lm_hash,
3281 &io.o.nt_hash);
3282 } else {
3283 /* Get the old password from the database */
3284 status = samdb_result_passwords(io.ac,
3285 lp_ctx,
3286 discard_const_p(struct ldb_message, searched_msg),
3287 &io.o.lm_hash, &io.o.nt_hash);
3290 if (!NT_STATUS_IS_OK(status)) {
3291 return ldb_operr(ldb);
3294 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
3295 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
3296 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
3298 ret = setup_password_fields(&io);
3299 if (ret != LDB_SUCCESS) {
3300 return ret;
3303 ret = check_password_restrictions(&io);
3304 if (ret != LDB_SUCCESS) {
3305 return ret;
3308 /* make sure we replace all the old attributes */
3309 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
3310 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
3311 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3312 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3313 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
3314 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
3316 if (io.g.nt_hash) {
3317 ret = samdb_msg_add_hash(ldb, ac, msg,
3318 "unicodePwd", io.g.nt_hash);
3319 if (ret != LDB_SUCCESS) {
3320 return ret;
3323 if (io.g.lm_hash) {
3324 ret = samdb_msg_add_hash(ldb, ac, msg,
3325 "dBCSPwd", io.g.lm_hash);
3326 if (ret != LDB_SUCCESS) {
3327 return ret;
3330 if (io.g.nt_history_len > 0) {
3331 ret = samdb_msg_add_hashes(ldb, ac, msg,
3332 "ntPwdHistory",
3333 io.g.nt_history,
3334 io.g.nt_history_len);
3335 if (ret != LDB_SUCCESS) {
3336 return ret;
3339 if (io.g.lm_history_len > 0) {
3340 ret = samdb_msg_add_hashes(ldb, ac, msg,
3341 "lmPwdHistory",
3342 io.g.lm_history,
3343 io.g.lm_history_len);
3344 if (ret != LDB_SUCCESS) {
3345 return ret;
3348 if (io.g.supplemental.length > 0) {
3349 ret = ldb_msg_add_value(msg, "supplementalCredentials",
3350 &io.g.supplemental, NULL);
3351 if (ret != LDB_SUCCESS) {
3352 return ret;
3355 ret = samdb_msg_add_uint64(ldb, ac, msg,
3356 "pwdLastSet",
3357 io.g.last_set);
3358 if (ret != LDB_SUCCESS) {
3359 return ret;
3362 ret = ldb_build_mod_req(&mod_req, ldb, ac,
3363 msg,
3364 ac->req->controls,
3365 ac, ph_op_callback,
3366 ac->req);
3367 LDB_REQ_SET_LOCATION(mod_req);
3368 if (ret != LDB_SUCCESS) {
3369 return ret;
3372 return ldb_next_request(ac->module, mod_req);
3375 static const struct ldb_module_ops ldb_password_hash_module_ops = {
3376 .name = "password_hash",
3377 .add = password_hash_add,
3378 .modify = password_hash_modify
3381 int ldb_password_hash_module_init(const char *version)
3383 LDB_MODULE_CHECK_VERSION(version);
3384 return ldb_register_module(&ldb_password_hash_module_ops);