s3:registry: do not use regdb functions during db upgrade
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blobbf94ba3dc328744f68443b5e0fac643cfa7c6f39
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) {
305 struct package_PackagesBlob *p;
306 uint32_t n;
308 p = talloc_zero(scb, struct package_PackagesBlob);
309 if (p == NULL) {
310 return ldb_module_oom(module);
313 ndr_err = ndr_pull_struct_blob(&scpbp, p, p,
314 (ndr_pull_flags_fn_t)ndr_pull_package_PackagesBlob);
315 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
316 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
317 "ndr_pull_struct_blob Packages");
320 if (p->names == NULL) {
321 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
322 "Packages names == NULL");
325 for (n = 0; p->names[n]; n++) {
326 /* noop */
329 if (scb->sub.num_packages != (n + 1)) {
330 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
331 "Packages num_packages != num_names + 1");
334 talloc_free(p);
337 if (scpk) {
338 struct package_PrimaryKerberosBlob *k;
340 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
341 if (k == NULL) {
342 return ldb_module_oom(module);
345 ndr_err = ndr_pull_struct_blob(&scpbk, k, k,
346 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
347 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
348 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
349 "ndr_pull_struct_blob PrimaryKerberos");
352 if (k->version != 3) {
353 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
354 "PrimaryKerberos version != 3");
357 if (k->ctr.ctr3.salt.string == NULL) {
358 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
359 "PrimaryKerberos salt == NULL");
362 if (strlen(k->ctr.ctr3.salt.string) == 0) {
363 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
364 "PrimaryKerberos strlen(salt) == 0");
367 if (k->ctr.ctr3.num_keys != 2) {
368 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
369 "PrimaryKerberos num_keys != 2");
372 if (k->ctr.ctr3.num_old_keys > k->ctr.ctr3.num_keys) {
373 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
374 "PrimaryKerberos num_old_keys > num_keys");
377 if (k->ctr.ctr3.keys[0].keytype != ENCTYPE_DES_CBC_MD5) {
378 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
379 "PrimaryKerberos key[0] != DES_CBC_MD5");
381 if (k->ctr.ctr3.keys[1].keytype != ENCTYPE_DES_CBC_CRC) {
382 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
383 "PrimaryKerberos key[1] != DES_CBC_CRC");
386 if (k->ctr.ctr3.keys[0].value_len != 8) {
387 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
388 "PrimaryKerberos key[0] value_len != 8");
390 if (k->ctr.ctr3.keys[1].value_len != 8) {
391 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
392 "PrimaryKerberos key[1] value_len != 8");
395 for (i = 0; i < k->ctr.ctr3.num_old_keys; i++) {
396 if (k->ctr.ctr3.old_keys[i].keytype ==
397 k->ctr.ctr3.keys[i].keytype &&
398 k->ctr.ctr3.old_keys[i].value_len ==
399 k->ctr.ctr3.keys[i].value_len) {
400 continue;
403 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
404 "PrimaryKerberos old_keys type/value_len doesn't match");
407 talloc_free(k);
410 if (scpkn) {
411 struct package_PrimaryKerberosBlob *k;
413 k = talloc_zero(scb, struct package_PrimaryKerberosBlob);
414 if (k == NULL) {
415 return ldb_module_oom(module);
418 ndr_err = ndr_pull_struct_blob(&scpbkn, k, k,
419 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
420 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
421 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
422 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
425 if (k->version != 4) {
426 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
427 "KerberosNerverKeys version != 4");
430 if (k->ctr.ctr4.salt.string == NULL) {
431 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
432 "KerberosNewerKeys salt == NULL");
435 if (strlen(k->ctr.ctr4.salt.string) == 0) {
436 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
437 "KerberosNewerKeys strlen(salt) == 0");
440 if (k->ctr.ctr4.num_keys != 4) {
441 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
442 "KerberosNewerKeys num_keys != 2");
445 if (k->ctr.ctr4.num_old_keys > k->ctr.ctr4.num_keys) {
446 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
447 "KerberosNewerKeys num_old_keys > num_keys");
450 if (k->ctr.ctr4.num_older_keys > k->ctr.ctr4.num_old_keys) {
451 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
452 "KerberosNewerKeys num_older_keys > num_old_keys");
455 if (k->ctr.ctr4.keys[0].keytype != ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
456 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
457 "KerberosNewerKeys key[0] != AES256");
459 if (k->ctr.ctr4.keys[1].keytype != ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
460 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
461 "KerberosNewerKeys key[1] != AES128");
463 if (k->ctr.ctr4.keys[2].keytype != ENCTYPE_DES_CBC_MD5) {
464 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
465 "KerberosNewerKeys key[2] != DES_CBC_MD5");
467 if (k->ctr.ctr4.keys[3].keytype != ENCTYPE_DES_CBC_CRC) {
468 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
469 "KerberosNewerKeys key[3] != DES_CBC_CRC");
472 if (k->ctr.ctr4.keys[0].value_len != 32) {
473 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
474 "KerberosNewerKeys key[0] value_len != 32");
476 if (k->ctr.ctr4.keys[1].value_len != 16) {
477 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
478 "KerberosNewerKeys key[1] value_len != 16");
480 if (k->ctr.ctr4.keys[2].value_len != 8) {
481 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
482 "KerberosNewerKeys key[2] value_len != 8");
484 if (k->ctr.ctr4.keys[3].value_len != 8) {
485 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
486 "KerberosNewerKeys key[3] value_len != 8");
490 * TODO:
491 * Maybe we can check old and older keys here.
492 * But we need to do some tests, if the old keys
493 * can be taken from the PrimaryKerberos blob
494 * (with only des keys), when the domain was upgraded
495 * from w2k3 to w2k8.
498 talloc_free(k);
501 if (scpct) {
502 struct package_PrimaryCLEARTEXTBlob *ct;
504 ct = talloc_zero(scb, struct package_PrimaryCLEARTEXTBlob);
505 if (ct == NULL) {
506 return ldb_module_oom(module);
509 ndr_err = ndr_pull_struct_blob(&scpbct, ct, ct,
510 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryCLEARTEXTBlob);
511 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
512 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
513 "ndr_pull_struct_blob PrimaryCLEARTEXT");
516 if ((ct->cleartext.length % 2) != 0) {
517 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
518 "PrimaryCLEARTEXT length % 2 != 0");
521 talloc_free(ct);
524 ndr_err = ndr_push_struct_blob(&blob, scb, scb,
525 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
526 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
527 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
528 "ndr_pull_struct_blob_all");
531 if (sce->values[0].length != blob.length) {
532 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
533 "supplementalCredentialsBlob length differ");
536 if (memcmp(sce->values[0].data, blob.data, blob.length) != 0) {
537 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION,
538 "supplementalCredentialsBlob memcmp differ");
541 talloc_free(scb);
544 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_bypass - validated\n");
545 return ldb_next_request(module, request);
548 /* Get the NT hash, and fill it in as an entry in the password history,
549 and specify it into io->g.nt_hash */
551 static int setup_nt_fields(struct setup_password_fields_io *io)
553 struct ldb_context *ldb;
554 uint32_t i;
556 io->g.nt_hash = io->n.nt_hash;
557 ldb = ldb_module_get_ctx(io->ac->module);
559 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
560 return LDB_SUCCESS;
563 /* We might not have an old NT password */
564 io->g.nt_history = talloc_array(io->ac,
565 struct samr_Password,
566 io->ac->status->domain_data.pwdHistoryLength);
567 if (!io->g.nt_history) {
568 return ldb_oom(ldb);
571 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
572 io->o.nt_history_len); i++) {
573 io->g.nt_history[i+1] = io->o.nt_history[i];
575 io->g.nt_history_len = i + 1;
577 if (io->g.nt_hash) {
578 io->g.nt_history[0] = *io->g.nt_hash;
579 } else {
581 * TODO: is this correct?
582 * the simular behavior is correct for the lm history case
584 E_md4hash("", io->g.nt_history[0].hash);
587 return LDB_SUCCESS;
590 /* Get the LANMAN hash, and fill it in as an entry in the password history,
591 and specify it into io->g.lm_hash */
593 static int setup_lm_fields(struct setup_password_fields_io *io)
595 struct ldb_context *ldb;
596 uint32_t i;
598 io->g.lm_hash = io->n.lm_hash;
599 ldb = ldb_module_get_ctx(io->ac->module);
601 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
602 return LDB_SUCCESS;
605 /* We might not have an old LM password */
606 io->g.lm_history = talloc_array(io->ac,
607 struct samr_Password,
608 io->ac->status->domain_data.pwdHistoryLength);
609 if (!io->g.lm_history) {
610 return ldb_oom(ldb);
613 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
614 io->o.lm_history_len); i++) {
615 io->g.lm_history[i+1] = io->o.lm_history[i];
617 io->g.lm_history_len = i + 1;
619 if (io->g.lm_hash) {
620 io->g.lm_history[0] = *io->g.lm_hash;
621 } else {
622 E_deshash("", io->g.lm_history[0].hash);
625 return LDB_SUCCESS;
628 static int setup_kerberos_keys(struct setup_password_fields_io *io)
630 struct ldb_context *ldb;
631 krb5_error_code krb5_ret;
632 Principal *salt_principal;
633 krb5_salt salt;
634 krb5_keyblock key;
635 krb5_data cleartext_data;
637 ldb = ldb_module_get_ctx(io->ac->module);
638 cleartext_data.data = io->n.cleartext_utf8->data;
639 cleartext_data.length = io->n.cleartext_utf8->length;
641 /* Many, many thanks to lukeh@padl.com for this
642 * algorithm, described in his Nov 10 2004 mail to
643 * samba-technical@samba.org */
646 * Determine a salting principal
648 if (io->u.is_computer) {
649 char *name;
650 char *saltbody;
652 name = strlower_talloc(io->ac, io->u.sAMAccountName);
653 if (!name) {
654 return ldb_oom(ldb);
657 if (name[strlen(name)-1] == '$') {
658 name[strlen(name)-1] = '\0';
661 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
662 io->ac->status->domain_data.dns_domain);
663 if (!saltbody) {
664 return ldb_oom(ldb);
667 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
668 &salt_principal,
669 io->ac->status->domain_data.realm,
670 "host", saltbody, NULL);
671 } else if (io->u.user_principal_name) {
672 char *user_principal_name;
673 char *p;
675 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
676 if (!user_principal_name) {
677 return ldb_oom(ldb);
680 p = strchr(user_principal_name, '@');
681 if (p) {
682 p[0] = '\0';
685 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
686 &salt_principal,
687 io->ac->status->domain_data.realm,
688 user_principal_name, NULL);
689 } else {
690 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
691 &salt_principal,
692 io->ac->status->domain_data.realm,
693 io->u.sAMAccountName, NULL);
695 if (krb5_ret) {
696 ldb_asprintf_errstring(ldb,
697 "setup_kerberos_keys: "
698 "generation of a salting principal failed: %s",
699 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
700 krb5_ret, io->ac));
701 return LDB_ERR_OPERATIONS_ERROR;
705 * create salt from salt_principal
707 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
708 salt_principal, &salt);
709 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
710 if (krb5_ret) {
711 ldb_asprintf_errstring(ldb,
712 "setup_kerberos_keys: "
713 "generation of krb5_salt failed: %s",
714 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
715 krb5_ret, io->ac));
716 return LDB_ERR_OPERATIONS_ERROR;
718 /* create a talloc copy */
719 io->g.salt = talloc_strndup(io->ac,
720 (char *)salt.saltvalue.data,
721 salt.saltvalue.length);
722 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
723 if (!io->g.salt) {
724 return ldb_oom(ldb);
726 salt.saltvalue.data = discard_const(io->g.salt);
727 salt.saltvalue.length = strlen(io->g.salt);
730 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
731 * the salt and the cleartext password
733 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
734 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
735 cleartext_data,
736 salt,
737 &key);
738 if (krb5_ret) {
739 ldb_asprintf_errstring(ldb,
740 "setup_kerberos_keys: "
741 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
742 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
743 krb5_ret, io->ac));
744 return LDB_ERR_OPERATIONS_ERROR;
746 io->g.aes_256 = data_blob_talloc(io->ac,
747 key.keyvalue.data,
748 key.keyvalue.length);
749 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
750 if (!io->g.aes_256.data) {
751 return ldb_oom(ldb);
755 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
756 * the salt and the cleartext password
758 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
759 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
760 cleartext_data,
761 salt,
762 &key);
763 if (krb5_ret) {
764 ldb_asprintf_errstring(ldb,
765 "setup_kerberos_keys: "
766 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
767 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
768 krb5_ret, io->ac));
769 return LDB_ERR_OPERATIONS_ERROR;
771 io->g.aes_128 = data_blob_talloc(io->ac,
772 key.keyvalue.data,
773 key.keyvalue.length);
774 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
775 if (!io->g.aes_128.data) {
776 return ldb_oom(ldb);
780 * create ENCTYPE_DES_CBC_MD5 key out of
781 * the salt and the cleartext password
783 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
784 ENCTYPE_DES_CBC_MD5,
785 cleartext_data,
786 salt,
787 &key);
788 if (krb5_ret) {
789 ldb_asprintf_errstring(ldb,
790 "setup_kerberos_keys: "
791 "generation of a des-cbc-md5 key failed: %s",
792 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
793 krb5_ret, io->ac));
794 return LDB_ERR_OPERATIONS_ERROR;
796 io->g.des_md5 = data_blob_talloc(io->ac,
797 key.keyvalue.data,
798 key.keyvalue.length);
799 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
800 if (!io->g.des_md5.data) {
801 return ldb_oom(ldb);
805 * create ENCTYPE_DES_CBC_CRC key out of
806 * the salt and the cleartext password
808 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
809 ENCTYPE_DES_CBC_CRC,
810 cleartext_data,
811 salt,
812 &key);
813 if (krb5_ret) {
814 ldb_asprintf_errstring(ldb,
815 "setup_kerberos_keys: "
816 "generation of a des-cbc-crc key failed: %s",
817 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
818 krb5_ret, io->ac));
819 return LDB_ERR_OPERATIONS_ERROR;
821 io->g.des_crc = data_blob_talloc(io->ac,
822 key.keyvalue.data,
823 key.keyvalue.length);
824 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
825 if (!io->g.des_crc.data) {
826 return ldb_oom(ldb);
829 return LDB_SUCCESS;
832 static int setup_primary_kerberos(struct setup_password_fields_io *io,
833 const struct supplementalCredentialsBlob *old_scb,
834 struct package_PrimaryKerberosBlob *pkb)
836 struct ldb_context *ldb;
837 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
838 struct supplementalCredentialsPackage *old_scp = NULL;
839 struct package_PrimaryKerberosBlob _old_pkb;
840 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
841 uint32_t i;
842 enum ndr_err_code ndr_err;
844 ldb = ldb_module_get_ctx(io->ac->module);
847 * prepare generation of keys
849 * ENCTYPE_DES_CBC_MD5
850 * ENCTYPE_DES_CBC_CRC
852 pkb->version = 3;
853 pkb3->salt.string = io->g.salt;
854 pkb3->num_keys = 2;
855 pkb3->keys = talloc_array(io->ac,
856 struct package_PrimaryKerberosKey3,
857 pkb3->num_keys);
858 if (!pkb3->keys) {
859 return ldb_oom(ldb);
862 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
863 pkb3->keys[0].value = &io->g.des_md5;
864 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
865 pkb3->keys[1].value = &io->g.des_crc;
867 /* initialize the old keys to zero */
868 pkb3->num_old_keys = 0;
869 pkb3->old_keys = NULL;
871 /* if there're no old keys, then we're done */
872 if (!old_scb) {
873 return LDB_SUCCESS;
876 for (i=0; i < old_scb->sub.num_packages; i++) {
877 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
878 continue;
881 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
882 continue;
885 old_scp = &old_scb->sub.packages[i];
886 break;
888 /* Primary:Kerberos element of supplementalCredentials */
889 if (old_scp) {
890 DATA_BLOB blob;
892 blob = strhex_to_data_blob(io->ac, old_scp->data);
893 if (!blob.data) {
894 return ldb_oom(ldb);
897 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
898 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
899 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
900 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
901 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
902 ldb_asprintf_errstring(ldb,
903 "setup_primary_kerberos: "
904 "failed to pull old package_PrimaryKerberosBlob: %s",
905 nt_errstr(status));
906 return LDB_ERR_OPERATIONS_ERROR;
909 if (_old_pkb.version != 3) {
910 ldb_asprintf_errstring(ldb,
911 "setup_primary_kerberos: "
912 "package_PrimaryKerberosBlob version[%u] expected[3]",
913 _old_pkb.version);
914 return LDB_ERR_OPERATIONS_ERROR;
917 old_pkb3 = &_old_pkb.ctr.ctr3;
920 /* if we didn't found the old keys we're done */
921 if (!old_pkb3) {
922 return LDB_SUCCESS;
925 /* fill in the old keys */
926 pkb3->num_old_keys = old_pkb3->num_keys;
927 pkb3->old_keys = old_pkb3->keys;
929 return LDB_SUCCESS;
932 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
933 const struct supplementalCredentialsBlob *old_scb,
934 struct package_PrimaryKerberosBlob *pkb)
936 struct ldb_context *ldb;
937 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
938 struct supplementalCredentialsPackage *old_scp = NULL;
939 struct package_PrimaryKerberosBlob _old_pkb;
940 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
941 uint32_t i;
942 enum ndr_err_code ndr_err;
944 ldb = ldb_module_get_ctx(io->ac->module);
947 * prepare generation of keys
949 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
950 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
951 * ENCTYPE_DES_CBC_MD5
952 * ENCTYPE_DES_CBC_CRC
954 pkb->version = 4;
955 pkb4->salt.string = io->g.salt;
956 pkb4->default_iteration_count = 4096;
957 pkb4->num_keys = 4;
959 pkb4->keys = talloc_array(io->ac,
960 struct package_PrimaryKerberosKey4,
961 pkb4->num_keys);
962 if (!pkb4->keys) {
963 return ldb_oom(ldb);
966 pkb4->keys[0].iteration_count = 4096;
967 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
968 pkb4->keys[0].value = &io->g.aes_256;
969 pkb4->keys[1].iteration_count = 4096;
970 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
971 pkb4->keys[1].value = &io->g.aes_128;
972 pkb4->keys[2].iteration_count = 4096;
973 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
974 pkb4->keys[2].value = &io->g.des_md5;
975 pkb4->keys[3].iteration_count = 4096;
976 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
977 pkb4->keys[3].value = &io->g.des_crc;
979 /* initialize the old keys to zero */
980 pkb4->num_old_keys = 0;
981 pkb4->old_keys = NULL;
982 pkb4->num_older_keys = 0;
983 pkb4->older_keys = NULL;
985 /* if there're no old keys, then we're done */
986 if (!old_scb) {
987 return LDB_SUCCESS;
990 for (i=0; i < old_scb->sub.num_packages; i++) {
991 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
992 continue;
995 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
996 continue;
999 old_scp = &old_scb->sub.packages[i];
1000 break;
1002 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1003 if (old_scp) {
1004 DATA_BLOB blob;
1006 blob = strhex_to_data_blob(io->ac, old_scp->data);
1007 if (!blob.data) {
1008 return ldb_oom(ldb);
1011 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1012 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
1013 &_old_pkb,
1014 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
1015 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1016 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1017 ldb_asprintf_errstring(ldb,
1018 "setup_primary_kerberos_newer: "
1019 "failed to pull old package_PrimaryKerberosBlob: %s",
1020 nt_errstr(status));
1021 return LDB_ERR_OPERATIONS_ERROR;
1024 if (_old_pkb.version != 4) {
1025 ldb_asprintf_errstring(ldb,
1026 "setup_primary_kerberos_newer: "
1027 "package_PrimaryKerberosBlob version[%u] expected[4]",
1028 _old_pkb.version);
1029 return LDB_ERR_OPERATIONS_ERROR;
1032 old_pkb4 = &_old_pkb.ctr.ctr4;
1035 /* if we didn't found the old keys we're done */
1036 if (!old_pkb4) {
1037 return LDB_SUCCESS;
1040 /* fill in the old keys */
1041 pkb4->num_old_keys = old_pkb4->num_keys;
1042 pkb4->old_keys = old_pkb4->keys;
1043 pkb4->num_older_keys = old_pkb4->num_old_keys;
1044 pkb4->older_keys = old_pkb4->old_keys;
1046 return LDB_SUCCESS;
1049 static int setup_primary_wdigest(struct setup_password_fields_io *io,
1050 const struct supplementalCredentialsBlob *old_scb,
1051 struct package_PrimaryWDigestBlob *pdb)
1053 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1054 DATA_BLOB sAMAccountName;
1055 DATA_BLOB sAMAccountName_l;
1056 DATA_BLOB sAMAccountName_u;
1057 const char *user_principal_name = io->u.user_principal_name;
1058 DATA_BLOB userPrincipalName;
1059 DATA_BLOB userPrincipalName_l;
1060 DATA_BLOB userPrincipalName_u;
1061 DATA_BLOB netbios_domain;
1062 DATA_BLOB netbios_domain_l;
1063 DATA_BLOB netbios_domain_u;
1064 DATA_BLOB dns_domain;
1065 DATA_BLOB dns_domain_l;
1066 DATA_BLOB dns_domain_u;
1067 DATA_BLOB digest;
1068 DATA_BLOB delim;
1069 DATA_BLOB backslash;
1070 uint8_t i;
1071 struct {
1072 DATA_BLOB *user;
1073 DATA_BLOB *realm;
1074 DATA_BLOB *nt4dom;
1075 } wdigest[] = {
1077 * See
1078 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1079 * for what precalculated hashes are supposed to be stored...
1081 * I can't reproduce all values which should contain "Digest" as realm,
1082 * am I doing something wrong or is w2k3 just broken...?
1084 * W2K3 fills in following for a user:
1086 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1087 * sAMAccountName: NewUser2Sam
1088 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1090 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1091 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1092 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1093 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1094 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1095 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1096 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1097 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1098 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1099 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1100 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1101 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1102 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1103 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1104 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1105 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1106 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1107 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1108 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1109 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1110 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1111 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1112 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1113 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1114 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1115 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1116 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1117 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1118 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1120 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1121 * sAMAccountName: NewUser2Sam
1123 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1124 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1125 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1126 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1127 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1128 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1129 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1130 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1131 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1132 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1133 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1134 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1135 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1136 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1137 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1138 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1139 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1140 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1141 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1142 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1143 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1144 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1145 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1146 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1147 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1148 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1149 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1150 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1151 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1155 * sAMAccountName, netbios_domain
1158 .user = &sAMAccountName,
1159 .realm = &netbios_domain,
1162 .user = &sAMAccountName_l,
1163 .realm = &netbios_domain_l,
1166 .user = &sAMAccountName_u,
1167 .realm = &netbios_domain_u,
1170 .user = &sAMAccountName,
1171 .realm = &netbios_domain_u,
1174 .user = &sAMAccountName,
1175 .realm = &netbios_domain_l,
1178 .user = &sAMAccountName_u,
1179 .realm = &netbios_domain_l,
1182 .user = &sAMAccountName_l,
1183 .realm = &netbios_domain_u,
1186 * sAMAccountName, dns_domain
1189 .user = &sAMAccountName,
1190 .realm = &dns_domain,
1193 .user = &sAMAccountName_l,
1194 .realm = &dns_domain_l,
1197 .user = &sAMAccountName_u,
1198 .realm = &dns_domain_u,
1201 .user = &sAMAccountName,
1202 .realm = &dns_domain_u,
1205 .user = &sAMAccountName,
1206 .realm = &dns_domain_l,
1209 .user = &sAMAccountName_u,
1210 .realm = &dns_domain_l,
1213 .user = &sAMAccountName_l,
1214 .realm = &dns_domain_u,
1217 * userPrincipalName, no realm
1220 .user = &userPrincipalName,
1224 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1225 * the fallback to the sAMAccountName based userPrincipalName is correct
1227 .user = &userPrincipalName_l,
1230 .user = &userPrincipalName_u,
1233 * nt4dom\sAMAccountName, no realm
1236 .user = &sAMAccountName,
1237 .nt4dom = &netbios_domain
1240 .user = &sAMAccountName_l,
1241 .nt4dom = &netbios_domain_l
1244 .user = &sAMAccountName_u,
1245 .nt4dom = &netbios_domain_u
1249 * the following ones are guessed depending on the technet2 article
1250 * but not reproducable on a w2k3 server
1252 /* sAMAccountName with "Digest" realm */
1254 .user = &sAMAccountName,
1255 .realm = &digest
1258 .user = &sAMAccountName_l,
1259 .realm = &digest
1262 .user = &sAMAccountName_u,
1263 .realm = &digest
1265 /* userPrincipalName with "Digest" realm */
1267 .user = &userPrincipalName,
1268 .realm = &digest
1271 .user = &userPrincipalName_l,
1272 .realm = &digest
1275 .user = &userPrincipalName_u,
1276 .realm = &digest
1278 /* nt4dom\\sAMAccountName with "Digest" realm */
1280 .user = &sAMAccountName,
1281 .nt4dom = &netbios_domain,
1282 .realm = &digest
1285 .user = &sAMAccountName_l,
1286 .nt4dom = &netbios_domain_l,
1287 .realm = &digest
1290 .user = &sAMAccountName_u,
1291 .nt4dom = &netbios_domain_u,
1292 .realm = &digest
1296 /* prepare DATA_BLOB's used in the combinations array */
1297 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
1298 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
1299 if (!sAMAccountName_l.data) {
1300 return ldb_oom(ldb);
1302 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
1303 if (!sAMAccountName_u.data) {
1304 return ldb_oom(ldb);
1307 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1308 if (!user_principal_name) {
1309 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
1310 io->u.sAMAccountName,
1311 io->ac->status->domain_data.dns_domain);
1312 if (!user_principal_name) {
1313 return ldb_oom(ldb);
1316 userPrincipalName = data_blob_string_const(user_principal_name);
1317 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
1318 if (!userPrincipalName_l.data) {
1319 return ldb_oom(ldb);
1321 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
1322 if (!userPrincipalName_u.data) {
1323 return ldb_oom(ldb);
1326 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
1327 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
1328 io->ac->status->domain_data.netbios_domain));
1329 if (!netbios_domain_l.data) {
1330 return ldb_oom(ldb);
1332 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
1333 io->ac->status->domain_data.netbios_domain));
1334 if (!netbios_domain_u.data) {
1335 return ldb_oom(ldb);
1338 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1339 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
1340 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
1342 digest = data_blob_string_const("Digest");
1344 delim = data_blob_string_const(":");
1345 backslash = data_blob_string_const("\\");
1347 pdb->num_hashes = ARRAY_SIZE(wdigest);
1348 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
1349 pdb->num_hashes);
1350 if (!pdb->hashes) {
1351 return ldb_oom(ldb);
1354 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
1355 struct MD5Context md5;
1356 MD5Init(&md5);
1357 if (wdigest[i].nt4dom) {
1358 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
1359 MD5Update(&md5, backslash.data, backslash.length);
1361 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
1362 MD5Update(&md5, delim.data, delim.length);
1363 if (wdigest[i].realm) {
1364 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1366 MD5Update(&md5, delim.data, delim.length);
1367 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1368 MD5Final(pdb->hashes[i].hash, &md5);
1371 return LDB_SUCCESS;
1374 static int setup_supplemental_field(struct setup_password_fields_io *io)
1376 struct ldb_context *ldb;
1377 struct supplementalCredentialsBlob scb;
1378 struct supplementalCredentialsBlob _old_scb;
1379 struct supplementalCredentialsBlob *old_scb = NULL;
1380 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1381 uint32_t num_names = 0;
1382 const char *names[1+4];
1383 uint32_t num_packages = 0;
1384 struct supplementalCredentialsPackage packages[1+4];
1385 /* Packages */
1386 struct supplementalCredentialsPackage *pp = NULL;
1387 struct package_PackagesBlob pb;
1388 DATA_BLOB pb_blob;
1389 char *pb_hexstr;
1390 /* Primary:Kerberos-Newer-Keys */
1391 const char **nkn = NULL;
1392 struct supplementalCredentialsPackage *pkn = NULL;
1393 struct package_PrimaryKerberosBlob pknb;
1394 DATA_BLOB pknb_blob;
1395 char *pknb_hexstr;
1396 /* Primary:Kerberos */
1397 const char **nk = NULL;
1398 struct supplementalCredentialsPackage *pk = NULL;
1399 struct package_PrimaryKerberosBlob pkb;
1400 DATA_BLOB pkb_blob;
1401 char *pkb_hexstr;
1402 /* Primary:WDigest */
1403 const char **nd = NULL;
1404 struct supplementalCredentialsPackage *pd = NULL;
1405 struct package_PrimaryWDigestBlob pdb;
1406 DATA_BLOB pdb_blob;
1407 char *pdb_hexstr;
1408 /* Primary:CLEARTEXT */
1409 const char **nc = NULL;
1410 struct supplementalCredentialsPackage *pc = NULL;
1411 struct package_PrimaryCLEARTEXTBlob pcb;
1412 DATA_BLOB pcb_blob;
1413 char *pcb_hexstr;
1414 int ret;
1415 enum ndr_err_code ndr_err;
1416 uint8_t zero16[16];
1417 bool do_newer_keys = false;
1418 bool do_cleartext = false;
1420 ZERO_STRUCT(zero16);
1421 ZERO_STRUCT(names);
1423 ldb = ldb_module_get_ctx(io->ac->module);
1425 if (!io->n.cleartext_utf8) {
1427 * when we don't have a cleartext password
1428 * we can't setup a supplementalCredential value
1430 return LDB_SUCCESS;
1433 /* if there's an old supplementaCredentials blob then parse it */
1434 if (io->o.supplemental) {
1435 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1436 &_old_scb,
1437 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1438 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1439 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1440 ldb_asprintf_errstring(ldb,
1441 "setup_supplemental_field: "
1442 "failed to pull old supplementalCredentialsBlob: %s",
1443 nt_errstr(status));
1444 return LDB_ERR_OPERATIONS_ERROR;
1447 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1448 old_scb = &_old_scb;
1449 } else {
1450 ldb_debug(ldb, LDB_DEBUG_ERROR,
1451 "setup_supplemental_field: "
1452 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1453 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1456 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1457 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1459 if (io->ac->status->domain_data.store_cleartext &&
1460 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1461 do_cleartext = true;
1465 * The ordering is this
1467 * Primary:Kerberos-Newer-Keys (optional)
1468 * Primary:Kerberos
1469 * Primary:WDigest
1470 * Primary:CLEARTEXT (optional)
1472 * And the 'Packages' package is insert before the last
1473 * other package.
1475 if (do_newer_keys) {
1476 /* Primary:Kerberos-Newer-Keys */
1477 nkn = &names[num_names++];
1478 pkn = &packages[num_packages++];
1481 /* Primary:Kerberos */
1482 nk = &names[num_names++];
1483 pk = &packages[num_packages++];
1485 if (!do_cleartext) {
1486 /* Packages */
1487 pp = &packages[num_packages++];
1490 /* Primary:WDigest */
1491 nd = &names[num_names++];
1492 pd = &packages[num_packages++];
1494 if (do_cleartext) {
1495 /* Packages */
1496 pp = &packages[num_packages++];
1498 /* Primary:CLEARTEXT */
1499 nc = &names[num_names++];
1500 pc = &packages[num_packages++];
1503 if (pkn) {
1505 * setup 'Primary:Kerberos-Newer-Keys' element
1507 *nkn = "Kerberos-Newer-Keys";
1509 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1510 if (ret != LDB_SUCCESS) {
1511 return ret;
1514 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1515 &pknb,
1516 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1517 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1518 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1519 ldb_asprintf_errstring(ldb,
1520 "setup_supplemental_field: "
1521 "failed to push package_PrimaryKerberosNeverBlob: %s",
1522 nt_errstr(status));
1523 return LDB_ERR_OPERATIONS_ERROR;
1525 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1526 if (!pknb_hexstr) {
1527 return ldb_oom(ldb);
1529 pkn->name = "Primary:Kerberos-Newer-Keys";
1530 pkn->reserved = 1;
1531 pkn->data = pknb_hexstr;
1535 * setup 'Primary:Kerberos' element
1537 *nk = "Kerberos";
1539 ret = setup_primary_kerberos(io, old_scb, &pkb);
1540 if (ret != LDB_SUCCESS) {
1541 return ret;
1544 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1545 &pkb,
1546 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1547 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1548 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1549 ldb_asprintf_errstring(ldb,
1550 "setup_supplemental_field: "
1551 "failed to push package_PrimaryKerberosBlob: %s",
1552 nt_errstr(status));
1553 return LDB_ERR_OPERATIONS_ERROR;
1555 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1556 if (!pkb_hexstr) {
1557 return ldb_oom(ldb);
1559 pk->name = "Primary:Kerberos";
1560 pk->reserved = 1;
1561 pk->data = pkb_hexstr;
1564 * setup 'Primary:WDigest' element
1566 *nd = "WDigest";
1568 ret = setup_primary_wdigest(io, old_scb, &pdb);
1569 if (ret != LDB_SUCCESS) {
1570 return ret;
1573 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1574 &pdb,
1575 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1576 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1577 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1578 ldb_asprintf_errstring(ldb,
1579 "setup_supplemental_field: "
1580 "failed to push package_PrimaryWDigestBlob: %s",
1581 nt_errstr(status));
1582 return LDB_ERR_OPERATIONS_ERROR;
1584 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1585 if (!pdb_hexstr) {
1586 return ldb_oom(ldb);
1588 pd->name = "Primary:WDigest";
1589 pd->reserved = 1;
1590 pd->data = pdb_hexstr;
1593 * setup 'Primary:CLEARTEXT' element
1595 if (pc) {
1596 *nc = "CLEARTEXT";
1598 pcb.cleartext = *io->n.cleartext_utf16;
1600 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1601 &pcb,
1602 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1603 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1604 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1605 ldb_asprintf_errstring(ldb,
1606 "setup_supplemental_field: "
1607 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1608 nt_errstr(status));
1609 return LDB_ERR_OPERATIONS_ERROR;
1611 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1612 if (!pcb_hexstr) {
1613 return ldb_oom(ldb);
1615 pc->name = "Primary:CLEARTEXT";
1616 pc->reserved = 1;
1617 pc->data = pcb_hexstr;
1621 * setup 'Packages' element
1623 pb.names = names;
1624 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1625 &pb,
1626 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1627 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1628 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1629 ldb_asprintf_errstring(ldb,
1630 "setup_supplemental_field: "
1631 "failed to push package_PackagesBlob: %s",
1632 nt_errstr(status));
1633 return LDB_ERR_OPERATIONS_ERROR;
1635 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1636 if (!pb_hexstr) {
1637 return ldb_oom(ldb);
1639 pp->name = "Packages";
1640 pp->reserved = 2;
1641 pp->data = pb_hexstr;
1644 * setup 'supplementalCredentials' value
1646 ZERO_STRUCT(scb);
1647 scb.sub.num_packages = num_packages;
1648 scb.sub.packages = packages;
1650 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1651 &scb,
1652 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1653 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1654 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1655 ldb_asprintf_errstring(ldb,
1656 "setup_supplemental_field: "
1657 "failed to push supplementalCredentialsBlob: %s",
1658 nt_errstr(status));
1659 return LDB_ERR_OPERATIONS_ERROR;
1662 return LDB_SUCCESS;
1665 static int setup_last_set_field(struct setup_password_fields_io *io)
1667 const struct ldb_message *msg = NULL;
1669 switch (io->ac->req->operation) {
1670 case LDB_ADD:
1671 msg = io->ac->req->op.add.message;
1672 break;
1673 case LDB_MODIFY:
1674 msg = io->ac->req->op.mod.message;
1675 break;
1676 default:
1677 return LDB_ERR_OPERATIONS_ERROR;
1678 break;
1681 if (io->ac->pwd_last_set_bypass) {
1682 struct ldb_message_element *el;
1684 if (msg == NULL) {
1685 return LDB_ERR_CONSTRAINT_VIOLATION;
1688 el = ldb_msg_find_element(msg, "pwdLastSet");
1689 if (el == NULL) {
1690 return LDB_ERR_CONSTRAINT_VIOLATION;
1693 io->g.last_set = samdb_result_nttime(msg, "pwdLastSet", 0);
1694 return LDB_SUCCESS;
1697 /* set it as now */
1698 unix_to_nt_time(&io->g.last_set, time(NULL));
1700 return LDB_SUCCESS;
1703 static int setup_given_passwords(struct setup_password_fields_io *io,
1704 struct setup_password_fields_given *g)
1706 struct ldb_context *ldb;
1707 bool ok;
1709 ldb = ldb_module_get_ctx(io->ac->module);
1711 if (g->cleartext_utf8) {
1712 struct ldb_val *cleartext_utf16_blob;
1714 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1715 if (!cleartext_utf16_blob) {
1716 return ldb_oom(ldb);
1718 if (!convert_string_talloc(io->ac,
1719 CH_UTF8, CH_UTF16,
1720 g->cleartext_utf8->data,
1721 g->cleartext_utf8->length,
1722 (void *)&cleartext_utf16_blob->data,
1723 &cleartext_utf16_blob->length)) {
1724 if (g->cleartext_utf8->length != 0) {
1725 talloc_free(cleartext_utf16_blob);
1726 ldb_asprintf_errstring(ldb,
1727 "setup_password_fields: "
1728 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1729 io->u.sAMAccountName);
1730 return LDB_ERR_CONSTRAINT_VIOLATION;
1731 } else {
1732 /* passwords with length "0" are valid! */
1733 cleartext_utf16_blob->data = NULL;
1734 cleartext_utf16_blob->length = 0;
1737 g->cleartext_utf16 = cleartext_utf16_blob;
1738 } else if (g->cleartext_utf16) {
1739 struct ldb_val *cleartext_utf8_blob;
1741 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1742 if (!cleartext_utf8_blob) {
1743 return ldb_oom(ldb);
1745 if (!convert_string_talloc(io->ac,
1746 CH_UTF16MUNGED, CH_UTF8,
1747 g->cleartext_utf16->data,
1748 g->cleartext_utf16->length,
1749 (void *)&cleartext_utf8_blob->data,
1750 &cleartext_utf8_blob->length)) {
1751 if (g->cleartext_utf16->length != 0) {
1752 /* We must bail out here, the input wasn't even
1753 * a multiple of 2 bytes */
1754 talloc_free(cleartext_utf8_blob);
1755 ldb_asprintf_errstring(ldb,
1756 "setup_password_fields: "
1757 "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)!",
1758 io->u.sAMAccountName);
1759 return LDB_ERR_CONSTRAINT_VIOLATION;
1760 } else {
1761 /* passwords with length "0" are valid! */
1762 cleartext_utf8_blob->data = NULL;
1763 cleartext_utf8_blob->length = 0;
1766 g->cleartext_utf8 = cleartext_utf8_blob;
1769 if (g->cleartext_utf16) {
1770 struct samr_Password *nt_hash;
1772 nt_hash = talloc(io->ac, struct samr_Password);
1773 if (!nt_hash) {
1774 return ldb_oom(ldb);
1776 g->nt_hash = nt_hash;
1778 /* compute the new nt hash */
1779 mdfour(nt_hash->hash,
1780 g->cleartext_utf16->data,
1781 g->cleartext_utf16->length);
1784 if (g->cleartext_utf8) {
1785 struct samr_Password *lm_hash;
1787 lm_hash = talloc(io->ac, struct samr_Password);
1788 if (!lm_hash) {
1789 return ldb_oom(ldb);
1792 /* compute the new lm hash */
1793 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1794 if (ok) {
1795 g->lm_hash = lm_hash;
1796 } else {
1797 talloc_free(lm_hash);
1801 return LDB_SUCCESS;
1804 static int setup_password_fields(struct setup_password_fields_io *io)
1806 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1807 struct loadparm_context *lp_ctx =
1808 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1809 struct loadparm_context);
1810 int ret;
1812 /* transform the old password (for password changes) */
1813 ret = setup_given_passwords(io, &io->og);
1814 if (ret != LDB_SUCCESS) {
1815 return ret;
1818 /* transform the new password */
1819 ret = setup_given_passwords(io, &io->n);
1820 if (ret != LDB_SUCCESS) {
1821 return ret;
1824 if (io->n.cleartext_utf8) {
1825 ret = setup_kerberos_keys(io);
1826 if (ret != LDB_SUCCESS) {
1827 return ret;
1831 ret = setup_nt_fields(io);
1832 if (ret != LDB_SUCCESS) {
1833 return ret;
1836 if (lpcfg_lanman_auth(lp_ctx)) {
1837 ret = setup_lm_fields(io);
1838 if (ret != LDB_SUCCESS) {
1839 return ret;
1841 } else {
1842 io->g.lm_hash = NULL;
1843 io->g.lm_history_len = 0;
1846 ret = setup_supplemental_field(io);
1847 if (ret != LDB_SUCCESS) {
1848 return ret;
1851 ret = setup_last_set_field(io);
1852 if (ret != LDB_SUCCESS) {
1853 return ret;
1856 return LDB_SUCCESS;
1859 static int check_password_restrictions(struct setup_password_fields_io *io)
1861 struct ldb_context *ldb;
1862 int ret;
1863 enum samr_ValidationStatus stat;
1865 ldb = ldb_module_get_ctx(io->ac->module);
1867 /* First check the old password is correct, for password changes */
1868 if (!io->ac->pwd_reset) {
1869 bool nt_hash_checked = false;
1871 /* we need the old nt or lm hash given by the client */
1872 if (!io->og.nt_hash && !io->og.lm_hash) {
1873 ldb_asprintf_errstring(ldb,
1874 "check_password_restrictions: "
1875 "You need to provide the old password in order "
1876 "to change it!");
1877 return LDB_ERR_UNWILLING_TO_PERFORM;
1880 /* The password modify through the NT hash is encouraged and
1881 has no problems at all */
1882 if (io->og.nt_hash) {
1883 if (!io->o.nt_hash) {
1884 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1885 ldb_asprintf_errstring(ldb,
1886 "%08X: %s - check_password_restrictions: "
1887 "There's no old nt_hash, which is needed "
1888 "in order to change your password!",
1889 W_ERROR_V(WERR_INVALID_PASSWORD),
1890 ldb_strerror(ret));
1891 return ret;
1894 if (memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
1895 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1896 ldb_asprintf_errstring(ldb,
1897 "%08X: %s - check_password_restrictions: "
1898 "The old password specified doesn't match!",
1899 W_ERROR_V(WERR_INVALID_PASSWORD),
1900 ldb_strerror(ret));
1901 return ret;
1904 nt_hash_checked = true;
1907 /* But it is also possible to change a password by the LM hash
1908 * alone for compatibility reasons. This check is optional if
1909 * the NT hash was already checked - otherwise it's mandatory.
1910 * (as the SAMR operations request it). */
1911 if (io->og.lm_hash) {
1912 if (!io->o.lm_hash && !nt_hash_checked) {
1913 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1914 ldb_asprintf_errstring(ldb,
1915 "%08X: %s - check_password_restrictions: "
1916 "There's no old lm_hash, which is needed "
1917 "in order to change your password!",
1918 W_ERROR_V(WERR_INVALID_PASSWORD),
1919 ldb_strerror(ret));
1920 return ret;
1923 if (io->o.lm_hash &&
1924 memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0) {
1925 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1926 ldb_asprintf_errstring(ldb,
1927 "%08X: %s - check_password_restrictions: "
1928 "The old password specified doesn't match!",
1929 W_ERROR_V(WERR_INVALID_PASSWORD),
1930 ldb_strerror(ret));
1931 return ret;
1936 if (io->u.restrictions == 0) {
1937 /* FIXME: Is this right? */
1938 return LDB_SUCCESS;
1942 * Fundamental password checks done by the call
1943 * "samdb_check_password".
1944 * It is also in use by "dcesrv_samr_ValidatePassword".
1946 if (io->n.cleartext_utf8 != NULL) {
1947 stat = samdb_check_password(io->n.cleartext_utf8,
1948 io->ac->status->domain_data.pwdProperties,
1949 io->ac->status->domain_data.minPwdLength);
1950 switch (stat) {
1951 case SAMR_VALIDATION_STATUS_SUCCESS:
1952 /* perfect -> proceed! */
1953 break;
1955 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
1956 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1957 ldb_asprintf_errstring(ldb,
1958 "%08X: %s - check_password_restrictions: "
1959 "the password is too short. It should be equal or longer than %u characters!",
1960 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1961 ldb_strerror(ret),
1962 io->ac->status->domain_data.minPwdLength);
1963 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1964 return ret;
1966 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
1967 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1968 ldb_asprintf_errstring(ldb,
1969 "%08X: %s - check_password_restrictions: "
1970 "the password does not meet the complexity criteria!",
1971 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1972 ldb_strerror(ret));
1973 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1974 return ret;
1976 default:
1977 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1978 ldb_asprintf_errstring(ldb,
1979 "%08X: %s - check_password_restrictions: "
1980 "the password doesn't fit by a certain reason!",
1981 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1982 ldb_strerror(ret));
1983 return ret;
1987 if (io->ac->pwd_reset) {
1988 return LDB_SUCCESS;
1991 if (io->n.nt_hash) {
1992 uint32_t i;
1994 /* checks the NT hash password history */
1995 for (i = 0; i < io->o.nt_history_len; i++) {
1996 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
1997 if (ret == 0) {
1998 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1999 ldb_asprintf_errstring(ldb,
2000 "%08X: %s - check_password_restrictions: "
2001 "the password was already used (in history)!",
2002 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2003 ldb_strerror(ret));
2004 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2005 return ret;
2010 if (io->n.lm_hash) {
2011 uint32_t i;
2013 /* checks the LM hash password history */
2014 for (i = 0; i < io->o.lm_history_len; i++) {
2015 ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
2016 if (ret == 0) {
2017 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2018 ldb_asprintf_errstring(ldb,
2019 "%08X: %s - check_password_restrictions: "
2020 "the password was already used (in history)!",
2021 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2022 ldb_strerror(ret));
2023 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
2024 return ret;
2029 /* are all password changes disallowed? */
2030 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
2031 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2032 ldb_asprintf_errstring(ldb,
2033 "%08X: %s - check_password_restrictions: "
2034 "password changes disabled!",
2035 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2036 ldb_strerror(ret));
2037 return ret;
2040 /* can this user change the password? */
2041 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
2042 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2043 ldb_asprintf_errstring(ldb,
2044 "%08X: %s - check_password_restrictions: "
2045 "password can't be changed on this account!",
2046 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2047 ldb_strerror(ret));
2048 return ret;
2051 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2052 if (io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) {
2053 ret = LDB_ERR_CONSTRAINT_VIOLATION;
2054 ldb_asprintf_errstring(ldb,
2055 "%08X: %s - check_password_restrictions: "
2056 "password is too young to change!",
2057 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
2058 ldb_strerror(ret));
2059 return ret;
2062 return LDB_SUCCESS;
2066 * This is intended for use by the "password_hash" module since there
2067 * password changes can be specified through one message element with the
2068 * new password (to set) and another one with the old password (to unset).
2070 * The first which sets a password (new value) can have flags
2071 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2072 * for entries). The latter (old value) has always specified
2073 * LDB_FLAG_MOD_DELETE.
2075 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2076 * matching message elements are malformed in respect to the set/change rules.
2077 * Otherwise it returns LDB_SUCCESS.
2079 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
2080 const char *name,
2081 enum ldb_request_type operation,
2082 const struct ldb_val **new_val,
2083 const struct ldb_val **old_val)
2085 unsigned int i;
2087 *new_val = NULL;
2088 *old_val = NULL;
2090 if (msg == NULL) {
2091 return LDB_SUCCESS;
2094 for (i = 0; i < msg->num_elements; i++) {
2095 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
2096 continue;
2099 if ((operation == LDB_MODIFY) &&
2100 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
2101 /* 0 values are allowed */
2102 if (msg->elements[i].num_values == 1) {
2103 *old_val = &msg->elements[i].values[0];
2104 } else if (msg->elements[i].num_values > 1) {
2105 return LDB_ERR_CONSTRAINT_VIOLATION;
2107 } else if ((operation == LDB_MODIFY) &&
2108 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
2109 if (msg->elements[i].num_values > 0) {
2110 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2111 } else {
2112 return LDB_ERR_UNWILLING_TO_PERFORM;
2114 } else {
2115 /* Add operations and LDB_FLAG_MOD_ADD */
2116 if (msg->elements[i].num_values > 0) {
2117 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
2118 } else {
2119 return LDB_ERR_CONSTRAINT_VIOLATION;
2124 return LDB_SUCCESS;
2127 static int setup_io(struct ph_context *ac,
2128 const struct ldb_message *orig_msg,
2129 const struct ldb_message *searched_msg,
2130 struct setup_password_fields_io *io)
2132 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
2133 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2134 struct loadparm_context *lp_ctx =
2135 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2136 struct loadparm_context);
2137 int ret;
2139 ZERO_STRUCTP(io);
2141 /* Some operations below require kerberos contexts */
2143 if (smb_krb5_init_context(ac,
2144 ldb_get_event_context(ldb),
2145 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
2146 &io->smb_krb5_context) != 0) {
2147 return ldb_operr(ldb);
2150 io->ac = ac;
2152 io->u.userAccountControl = ldb_msg_find_attr_as_uint(searched_msg,
2153 "userAccountControl", 0);
2154 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
2155 io->u.sAMAccountName = ldb_msg_find_attr_as_string(searched_msg,
2156 "sAMAccountName", NULL);
2157 io->u.user_principal_name = ldb_msg_find_attr_as_string(searched_msg,
2158 "userPrincipalName", NULL);
2159 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
2161 if (io->u.sAMAccountName == NULL) {
2162 ldb_asprintf_errstring(ldb,
2163 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2164 ldb_dn_get_linearized(searched_msg->dn));
2166 return LDB_ERR_CONSTRAINT_VIOLATION;
2169 /* Only non-trust accounts have restrictions (possibly this test is the
2170 * wrong way around, but we like to be restrictive if possible */
2171 io->u.restrictions = !(io->u.userAccountControl
2172 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
2173 | UF_SERVER_TRUST_ACCOUNT));
2175 if ((io->u.userAccountControl & UF_PASSWD_NOTREQD) != 0) {
2176 /* see [MS-ADTS] 2.2.15 */
2177 io->u.restrictions = 0;
2180 if (ac->userPassword) {
2181 ret = msg_find_old_and_new_pwd_val(orig_msg, "userPassword",
2182 ac->req->operation,
2183 &io->n.cleartext_utf8,
2184 &io->og.cleartext_utf8);
2185 if (ret != LDB_SUCCESS) {
2186 ldb_asprintf_errstring(ldb,
2187 "setup_io: "
2188 "it's only allowed to set the old password once!");
2189 return ret;
2193 ret = msg_find_old_and_new_pwd_val(orig_msg, "clearTextPassword",
2194 ac->req->operation,
2195 &io->n.cleartext_utf16,
2196 &io->og.cleartext_utf16);
2197 if (ret != LDB_SUCCESS) {
2198 ldb_asprintf_errstring(ldb,
2199 "setup_io: "
2200 "it's only allowed to set the old password once!");
2201 return ret;
2204 /* this rather strange looking piece of code is there to
2205 handle a ldap client setting a password remotely using the
2206 unicodePwd ldap field. The syntax is that the password is
2207 in UTF-16LE, with a " at either end. Unfortunately the
2208 unicodePwd field is also used to store the nt hashes
2209 internally in Samba, and is used in the nt hash format on
2210 the wire in DRS replication, so we have a single name for
2211 two distinct values. The code below leaves us with a small
2212 chance (less than 1 in 2^32) of a mixup, if someone manages
2213 to create a MD4 hash which starts and ends in 0x22 0x00, as
2214 that would then be treated as a UTF16 password rather than
2215 a nthash */
2217 ret = msg_find_old_and_new_pwd_val(orig_msg, "unicodePwd",
2218 ac->req->operation,
2219 &quoted_utf16,
2220 &old_quoted_utf16);
2221 if (ret != LDB_SUCCESS) {
2222 ldb_asprintf_errstring(ldb,
2223 "setup_io: "
2224 "it's only allowed to set the old password once!");
2225 return ret;
2228 /* Checks and converts the actual "unicodePwd" attribute */
2229 if (!ac->hash_values &&
2230 quoted_utf16 &&
2231 quoted_utf16->length >= 4 &&
2232 quoted_utf16->data[0] == '"' &&
2233 quoted_utf16->data[1] == 0 &&
2234 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
2235 quoted_utf16->data[quoted_utf16->length-1] == 0) {
2236 struct ldb_val *quoted_utf16_2;
2238 if (io->n.cleartext_utf16) {
2239 /* refuse the change if someone wants to change with
2240 with both UTF16 possibilities at the same time... */
2241 ldb_asprintf_errstring(ldb,
2242 "setup_io: "
2243 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2244 return LDB_ERR_UNWILLING_TO_PERFORM;
2248 * adapt the quoted UTF16 string to be a real
2249 * cleartext one
2251 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2252 if (quoted_utf16_2 == NULL) {
2253 return ldb_oom(ldb);
2256 quoted_utf16_2->data = quoted_utf16->data + 2;
2257 quoted_utf16_2->length = quoted_utf16->length-4;
2258 io->n.cleartext_utf16 = quoted_utf16_2;
2259 io->n.nt_hash = NULL;
2261 } else if (quoted_utf16) {
2262 /* We have only the hash available -> so no plaintext here */
2263 if (!ac->hash_values) {
2264 /* refuse the change if someone wants to change
2265 the hash without control specified... */
2266 ldb_asprintf_errstring(ldb,
2267 "setup_io: "
2268 "it's not allowed to set the NT hash password directly'");
2269 /* this looks odd but this is what Windows does:
2270 returns "UNWILLING_TO_PERFORM" on wrong
2271 password sets and "CONSTRAINT_VIOLATION" on
2272 wrong password changes. */
2273 if (old_quoted_utf16 == NULL) {
2274 return LDB_ERR_UNWILLING_TO_PERFORM;
2277 return LDB_ERR_CONSTRAINT_VIOLATION;
2280 io->n.nt_hash = talloc(io->ac, struct samr_Password);
2281 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
2282 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
2285 /* Checks and converts the previous "unicodePwd" attribute */
2286 if (!ac->hash_values &&
2287 old_quoted_utf16 &&
2288 old_quoted_utf16->length >= 4 &&
2289 old_quoted_utf16->data[0] == '"' &&
2290 old_quoted_utf16->data[1] == 0 &&
2291 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
2292 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
2293 struct ldb_val *old_quoted_utf16_2;
2295 if (io->og.cleartext_utf16) {
2296 /* refuse the change if someone wants to change with
2297 both UTF16 possibilities at the same time... */
2298 ldb_asprintf_errstring(ldb,
2299 "setup_io: "
2300 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2301 return LDB_ERR_UNWILLING_TO_PERFORM;
2305 * adapt the quoted UTF16 string to be a real
2306 * cleartext one
2308 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
2309 if (old_quoted_utf16_2 == NULL) {
2310 return ldb_oom(ldb);
2313 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
2314 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
2316 io->og.cleartext_utf16 = old_quoted_utf16_2;
2317 io->og.nt_hash = NULL;
2318 } else if (old_quoted_utf16) {
2319 /* We have only the hash available -> so no plaintext here */
2320 if (!ac->hash_values) {
2321 /* refuse the change if someone wants to change
2322 the hash without control specified... */
2323 ldb_asprintf_errstring(ldb,
2324 "setup_io: "
2325 "it's not allowed to set the NT hash password directly'");
2326 return LDB_ERR_UNWILLING_TO_PERFORM;
2329 io->og.nt_hash = talloc(io->ac, struct samr_Password);
2330 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
2331 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
2334 /* Handles the "dBCSPwd" attribute (LM hash) */
2335 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
2336 ret = msg_find_old_and_new_pwd_val(orig_msg, "dBCSPwd",
2337 ac->req->operation,
2338 &lm_hash, &old_lm_hash);
2339 if (ret != LDB_SUCCESS) {
2340 ldb_asprintf_errstring(ldb,
2341 "setup_io: "
2342 "it's only allowed to set the old password once!");
2343 return ret;
2346 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
2347 /* refuse the change if someone wants to change the hash
2348 without control specified... */
2349 ldb_asprintf_errstring(ldb,
2350 "setup_io: "
2351 "it's not allowed to set the LM hash password directly'");
2352 return LDB_ERR_UNWILLING_TO_PERFORM;
2355 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
2356 io->n.lm_hash = talloc(io->ac, struct samr_Password);
2357 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
2358 sizeof(io->n.lm_hash->hash)));
2360 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
2361 io->og.lm_hash = talloc(io->ac, struct samr_Password);
2362 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
2363 sizeof(io->og.lm_hash->hash)));
2367 * Handles the password change control if it's specified. It has the
2368 * precedance and overrides already specified old password values of
2369 * change requests (but that shouldn't happen since the control is
2370 * fully internal and only used in conjunction with replace requests!).
2372 if (ac->change != NULL) {
2373 io->og.nt_hash = NULL;
2374 if (ac->change->old_nt_pwd_hash != NULL) {
2375 io->og.nt_hash = talloc_memdup(io->ac,
2376 ac->change->old_nt_pwd_hash,
2377 sizeof(struct samr_Password));
2379 io->og.lm_hash = NULL;
2380 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
2381 io->og.lm_hash = talloc_memdup(io->ac,
2382 ac->change->old_lm_pwd_hash,
2383 sizeof(struct samr_Password));
2387 /* refuse the change if someone wants to change the clear-
2388 text and supply his own hashes at the same time... */
2389 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
2390 && (io->n.nt_hash || io->n.lm_hash)) {
2391 ldb_asprintf_errstring(ldb,
2392 "setup_io: "
2393 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2394 return LDB_ERR_UNWILLING_TO_PERFORM;
2397 /* refuse the change if someone wants to change the password
2398 using both plaintext methods (UTF8 and UTF16) at the same time... */
2399 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
2400 ldb_asprintf_errstring(ldb,
2401 "setup_io: "
2402 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2403 return LDB_ERR_UNWILLING_TO_PERFORM;
2406 /* refuse the change if someone tries to set/change the password by
2407 * the lanman hash alone and we've deactivated that mechanism. This
2408 * would end in an account without any password! */
2409 if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
2410 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
2411 ldb_asprintf_errstring(ldb,
2412 "setup_io: "
2413 "It' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2414 /* on "userPassword" and "clearTextPassword" we've to return
2415 * something different, since these are virtual attributes */
2416 if ((ldb_msg_find_element(orig_msg, "userPassword") != NULL) ||
2417 (ldb_msg_find_element(orig_msg, "clearTextPassword") != NULL)) {
2418 return LDB_ERR_CONSTRAINT_VIOLATION;
2420 return LDB_ERR_UNWILLING_TO_PERFORM;
2423 /* refuse the change if someone wants to compare against a plaintext
2424 or hash at the same time for a "password modify" operation... */
2425 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
2426 && (io->og.nt_hash || io->og.lm_hash)) {
2427 ldb_asprintf_errstring(ldb,
2428 "setup_io: "
2429 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2430 return LDB_ERR_UNWILLING_TO_PERFORM;
2433 /* refuse the change if someone wants to compare against both
2434 * plaintexts at the same time for a "password modify" operation... */
2435 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2436 ldb_asprintf_errstring(ldb,
2437 "setup_io: "
2438 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2439 return LDB_ERR_UNWILLING_TO_PERFORM;
2442 /* Decides if we have a password modify or password reset operation */
2443 if (ac->req->operation == LDB_ADD) {
2444 /* On "add" we have only "password reset" */
2445 ac->pwd_reset = true;
2446 } else if (ac->req->operation == LDB_MODIFY) {
2447 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2448 || io->og.nt_hash || io->og.lm_hash) {
2449 /* If we have an old password specified then for sure it
2450 * is a user "password change" */
2451 ac->pwd_reset = false;
2452 } else {
2453 /* Otherwise we have also here a "password reset" */
2454 ac->pwd_reset = true;
2456 } else {
2457 /* this shouldn't happen */
2458 return ldb_operr(ldb);
2461 return LDB_SUCCESS;
2464 static struct ph_context *ph_init_context(struct ldb_module *module,
2465 struct ldb_request *req,
2466 bool userPassword)
2468 struct ldb_context *ldb;
2469 struct ph_context *ac;
2471 ldb = ldb_module_get_ctx(module);
2473 ac = talloc_zero(req, struct ph_context);
2474 if (ac == NULL) {
2475 ldb_set_errstring(ldb, "Out of Memory");
2476 return NULL;
2479 ac->module = module;
2480 ac->req = req;
2481 ac->userPassword = userPassword;
2483 return ac;
2486 static void ph_apply_controls(struct ph_context *ac)
2488 struct ldb_control *ctrl;
2490 ac->change_status = false;
2491 ctrl = ldb_request_get_control(ac->req,
2492 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2493 if (ctrl != NULL) {
2494 ac->change_status = true;
2496 /* Mark the "change status" control as uncritical (done) */
2497 ctrl->critical = false;
2500 ac->hash_values = false;
2501 ctrl = ldb_request_get_control(ac->req,
2502 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2503 if (ctrl != NULL) {
2504 ac->hash_values = true;
2506 /* Mark the "hash values" control as uncritical (done) */
2507 ctrl->critical = false;
2510 ctrl = ldb_request_get_control(ac->req,
2511 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2512 if (ctrl != NULL) {
2513 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2515 /* Mark the "change" control as uncritical (done) */
2516 ctrl->critical = false;
2519 ac->pwd_last_set_bypass = false;
2520 ctrl = ldb_request_get_control(ac->req,
2521 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID);
2522 if (ctrl != NULL) {
2523 ac->pwd_last_set_bypass = true;
2525 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2526 ctrl->critical = false;
2530 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2532 struct ph_context *ac;
2534 ac = talloc_get_type(req->context, struct ph_context);
2536 if (!ares) {
2537 return ldb_module_done(ac->req, NULL, NULL,
2538 LDB_ERR_OPERATIONS_ERROR);
2541 if (ares->type == LDB_REPLY_REFERRAL) {
2542 return ldb_module_send_referral(ac->req, ares->referral);
2545 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2546 /* On success and trivial errors a status control is being
2547 * added (used for example by the "samdb_set_password" call) */
2548 ldb_reply_add_control(ares,
2549 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2550 false,
2551 ac->status);
2554 if (ares->error != LDB_SUCCESS) {
2555 return ldb_module_done(ac->req, ares->controls,
2556 ares->response, ares->error);
2559 if (ares->type != LDB_REPLY_DONE) {
2560 talloc_free(ares);
2561 return ldb_module_done(ac->req, NULL, NULL,
2562 LDB_ERR_OPERATIONS_ERROR);
2565 return ldb_module_done(ac->req, ares->controls,
2566 ares->response, ares->error);
2569 static int password_hash_add_do_add(struct ph_context *ac);
2570 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2571 static int password_hash_mod_search_self(struct ph_context *ac);
2572 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2573 static int password_hash_mod_do_mod(struct ph_context *ac);
2575 static int get_domain_data_callback(struct ldb_request *req,
2576 struct ldb_reply *ares)
2578 struct ldb_context *ldb;
2579 struct ph_context *ac;
2580 struct loadparm_context *lp_ctx;
2581 int ret;
2583 ac = talloc_get_type(req->context, struct ph_context);
2584 ldb = ldb_module_get_ctx(ac->module);
2586 if (!ares) {
2587 ret = LDB_ERR_OPERATIONS_ERROR;
2588 goto done;
2590 if (ares->error != LDB_SUCCESS) {
2591 return ldb_module_done(ac->req, ares->controls,
2592 ares->response, ares->error);
2595 switch (ares->type) {
2596 case LDB_REPLY_ENTRY:
2597 if (ac->status != NULL) {
2598 talloc_free(ares);
2600 ldb_set_errstring(ldb, "Too many results");
2601 ret = LDB_ERR_OPERATIONS_ERROR;
2602 goto done;
2605 /* Setup the "status" structure (used as control later) */
2606 ac->status = talloc_zero(ac->req,
2607 struct dsdb_control_password_change_status);
2608 if (ac->status == NULL) {
2609 talloc_free(ares);
2611 ldb_oom(ldb);
2612 ret = LDB_ERR_OPERATIONS_ERROR;
2613 goto done;
2616 /* Setup the "domain data" structure */
2617 ac->status->domain_data.pwdProperties =
2618 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
2619 ac->status->domain_data.pwdHistoryLength =
2620 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
2621 ac->status->domain_data.maxPwdAge =
2622 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
2623 ac->status->domain_data.minPwdAge =
2624 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
2625 ac->status->domain_data.minPwdLength =
2626 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
2627 ac->status->domain_data.store_cleartext =
2628 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2630 talloc_free(ares);
2632 /* For a domain DN, this puts things in dotted notation */
2633 /* For builtin domains, this will give details for the host,
2634 * but that doesn't really matter, as it's just used for salt
2635 * and kerberos principals, which don't exist here */
2637 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2638 struct loadparm_context);
2640 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
2641 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
2642 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
2644 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2646 ret = LDB_SUCCESS;
2647 break;
2649 case LDB_REPLY_REFERRAL:
2650 /* ignore */
2651 talloc_free(ares);
2652 ret = LDB_SUCCESS;
2653 break;
2655 case LDB_REPLY_DONE:
2656 talloc_free(ares);
2657 /* call the next step */
2658 switch (ac->req->operation) {
2659 case LDB_ADD:
2660 ret = password_hash_add_do_add(ac);
2661 break;
2663 case LDB_MODIFY:
2664 ret = password_hash_mod_do_mod(ac);
2665 break;
2667 default:
2668 ret = LDB_ERR_OPERATIONS_ERROR;
2669 break;
2671 break;
2674 done:
2675 if (ret != LDB_SUCCESS) {
2676 struct ldb_reply *new_ares;
2678 new_ares = talloc_zero(ac->req, struct ldb_reply);
2679 if (new_ares == NULL) {
2680 ldb_oom(ldb);
2681 return ldb_module_done(ac->req, NULL, NULL,
2682 LDB_ERR_OPERATIONS_ERROR);
2685 new_ares->error = ret;
2686 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2687 /* On success and trivial errors a status control is being
2688 * added (used for example by the "samdb_set_password" call) */
2689 ldb_reply_add_control(new_ares,
2690 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2691 false,
2692 ac->status);
2695 return ldb_module_done(ac->req, new_ares->controls,
2696 new_ares->response, new_ares->error);
2699 return LDB_SUCCESS;
2702 static int build_domain_data_request(struct ph_context *ac)
2704 /* attrs[] is returned from this function in
2705 ac->dom_req->op.search.attrs, so it must be static, as
2706 otherwise the compiler can put it on the stack */
2707 struct ldb_context *ldb;
2708 static const char * const attrs[] = { "pwdProperties",
2709 "pwdHistoryLength",
2710 "maxPwdAge",
2711 "minPwdAge",
2712 "minPwdLength",
2713 NULL };
2714 int ret;
2716 ldb = ldb_module_get_ctx(ac->module);
2718 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
2719 ldb_get_default_basedn(ldb),
2720 LDB_SCOPE_BASE,
2721 NULL, attrs,
2722 NULL,
2723 ac, get_domain_data_callback,
2724 ac->req);
2725 LDB_REQ_SET_LOCATION(ac->dom_req);
2726 return ret;
2729 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2731 struct ldb_context *ldb;
2732 struct ph_context *ac;
2733 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2734 *ntAttr, *lmAttr;
2735 int ret;
2736 struct ldb_control *bypass = NULL;
2737 bool userPassword = dsdb_user_password_support(module, req, req);
2739 ldb = ldb_module_get_ctx(module);
2741 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2743 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2744 return ldb_next_request(module, req);
2747 /* If the caller is manipulating the local passwords directly, let them pass */
2748 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2749 req->op.add.message->dn) == 0) {
2750 return ldb_next_request(module, req);
2753 bypass = ldb_request_get_control(req,
2754 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2755 if (bypass != NULL) {
2756 /* Mark the "bypass" control as uncritical (done) */
2757 bypass->critical = false;
2758 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
2759 return password_hash_bypass(module, req);
2762 /* nobody must touch password histories and 'supplementalCredentials' */
2763 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2764 return LDB_ERR_UNWILLING_TO_PERFORM;
2766 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2767 return LDB_ERR_UNWILLING_TO_PERFORM;
2769 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2770 return LDB_ERR_UNWILLING_TO_PERFORM;
2773 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2774 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2776 userPasswordAttr = NULL;
2777 if (userPassword) {
2778 userPasswordAttr = ldb_msg_find_element(req->op.add.message,
2779 "userPassword");
2780 /* MS-ADTS 3.1.1.3.1.5.2 */
2781 if ((userPasswordAttr != NULL) &&
2782 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2783 return LDB_ERR_CONSTRAINT_VIOLATION;
2786 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2787 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2788 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2790 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2791 return ldb_next_request(module, req);
2794 /* Make sure we are performing the password set action on a (for us)
2795 * valid object. Those are instances of either "user" and/or
2796 * "inetOrgPerson". Otherwise continue with the submodules. */
2797 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2798 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2800 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2801 ldb_set_errstring(ldb,
2802 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2803 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2806 return ldb_next_request(module, req);
2809 ac = ph_init_context(module, req, userPassword);
2810 if (ac == NULL) {
2811 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2812 return ldb_operr(ldb);
2814 ph_apply_controls(ac);
2816 /* get user domain data */
2817 ret = build_domain_data_request(ac);
2818 if (ret != LDB_SUCCESS) {
2819 return ret;
2822 return ldb_next_request(module, ac->dom_req);
2825 static int password_hash_add_do_add(struct ph_context *ac)
2827 struct ldb_context *ldb;
2828 struct ldb_request *down_req;
2829 struct ldb_message *msg;
2830 struct setup_password_fields_io io;
2831 int ret;
2833 /* Prepare the internal data structure containing the passwords */
2834 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2835 if (ret != LDB_SUCCESS) {
2836 return ret;
2839 ldb = ldb_module_get_ctx(ac->module);
2841 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2842 if (msg == NULL) {
2843 return ldb_operr(ldb);
2846 /* remove attributes that we just read into 'io' */
2847 if (ac->userPassword) {
2848 ldb_msg_remove_attr(msg, "userPassword");
2850 ldb_msg_remove_attr(msg, "clearTextPassword");
2851 ldb_msg_remove_attr(msg, "unicodePwd");
2852 ldb_msg_remove_attr(msg, "dBCSPwd");
2853 ldb_msg_remove_attr(msg, "pwdLastSet");
2855 ret = setup_password_fields(&io);
2856 if (ret != LDB_SUCCESS) {
2857 return ret;
2860 ret = check_password_restrictions(&io);
2861 if (ret != LDB_SUCCESS) {
2862 return ret;
2865 if (io.g.nt_hash) {
2866 ret = samdb_msg_add_hash(ldb, ac, msg,
2867 "unicodePwd", io.g.nt_hash);
2868 if (ret != LDB_SUCCESS) {
2869 return ret;
2872 if (io.g.lm_hash) {
2873 ret = samdb_msg_add_hash(ldb, ac, msg,
2874 "dBCSPwd", io.g.lm_hash);
2875 if (ret != LDB_SUCCESS) {
2876 return ret;
2879 if (io.g.nt_history_len > 0) {
2880 ret = samdb_msg_add_hashes(ldb, ac, msg,
2881 "ntPwdHistory",
2882 io.g.nt_history,
2883 io.g.nt_history_len);
2884 if (ret != LDB_SUCCESS) {
2885 return ret;
2888 if (io.g.lm_history_len > 0) {
2889 ret = samdb_msg_add_hashes(ldb, ac, msg,
2890 "lmPwdHistory",
2891 io.g.lm_history,
2892 io.g.lm_history_len);
2893 if (ret != LDB_SUCCESS) {
2894 return ret;
2897 if (io.g.supplemental.length > 0) {
2898 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2899 &io.g.supplemental, NULL);
2900 if (ret != LDB_SUCCESS) {
2901 return ret;
2904 ret = samdb_msg_add_uint64(ldb, ac, msg,
2905 "pwdLastSet",
2906 io.g.last_set);
2907 if (ret != LDB_SUCCESS) {
2908 return ret;
2911 ret = ldb_build_add_req(&down_req, ldb, ac,
2912 msg,
2913 ac->req->controls,
2914 ac, ph_op_callback,
2915 ac->req);
2916 LDB_REQ_SET_LOCATION(down_req);
2917 if (ret != LDB_SUCCESS) {
2918 return ret;
2921 return ldb_next_request(ac->module, down_req);
2924 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
2926 struct ldb_context *ldb;
2927 struct ph_context *ac;
2928 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
2929 "unicodePwd", "dBCSPwd", NULL }, **l;
2930 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
2931 struct ldb_message_element *passwordAttr;
2932 struct ldb_message *msg;
2933 struct ldb_request *down_req;
2934 int ret;
2935 struct ldb_control *bypass = NULL;
2936 bool userPassword = dsdb_user_password_support(module, req, req);
2938 ldb = ldb_module_get_ctx(module);
2940 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
2942 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
2943 return ldb_next_request(module, req);
2946 /* If the caller is manipulating the local passwords directly, let them pass */
2947 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2948 req->op.mod.message->dn) == 0) {
2949 return ldb_next_request(module, req);
2952 bypass = ldb_request_get_control(req,
2953 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2954 if (bypass != NULL) {
2955 /* Mark the "bypass" control as uncritical (done) */
2956 bypass->critical = false;
2957 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
2958 return password_hash_bypass(module, req);
2961 /* nobody must touch password histories and 'supplementalCredentials' */
2962 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
2963 return LDB_ERR_UNWILLING_TO_PERFORM;
2965 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
2966 return LDB_ERR_UNWILLING_TO_PERFORM;
2968 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
2969 return LDB_ERR_UNWILLING_TO_PERFORM;
2972 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2973 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
2974 * For password changes/set there should be a 'delete' or a 'modify'
2975 * on these attributes. */
2976 attr_cnt = 0;
2977 for (l = passwordAttrs; *l != NULL; l++) {
2978 if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) {
2979 continue;
2982 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
2983 /* MS-ADTS 3.1.1.3.1.5.2 */
2984 if ((ldb_attr_cmp(*l, "userPassword") == 0) &&
2985 (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
2986 return LDB_ERR_CONSTRAINT_VIOLATION;
2989 ++attr_cnt;
2992 if (attr_cnt == 0) {
2993 return ldb_next_request(module, req);
2996 ac = ph_init_context(module, req, userPassword);
2997 if (!ac) {
2998 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2999 return ldb_operr(ldb);
3001 ph_apply_controls(ac);
3003 /* use a new message structure so that we can modify it */
3004 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3005 if (msg == NULL) {
3006 return ldb_oom(ldb);
3009 /* - check for single-valued password attributes
3010 * (if not return "CONSTRAINT_VIOLATION")
3011 * - check that for a password change operation one add and one delete
3012 * operation exists
3013 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3014 * - check that a password change and a password set operation cannot
3015 * be mixed
3016 * (if not return "UNWILLING_TO_PERFORM")
3017 * - remove all password attributes modifications from the first change
3018 * operation (anything without the passwords) - we will make the real
3019 * modification later */
3020 del_attr_cnt = 0;
3021 add_attr_cnt = 0;
3022 rep_attr_cnt = 0;
3023 for (l = passwordAttrs; *l != NULL; l++) {
3024 if ((!ac->userPassword) &&
3025 (ldb_attr_cmp(*l, "userPassword") == 0)) {
3026 continue;
3029 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
3030 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
3031 ++del_attr_cnt;
3033 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
3034 ++add_attr_cnt;
3036 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
3037 ++rep_attr_cnt;
3039 if ((passwordAttr->num_values != 1) &&
3040 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
3041 talloc_free(ac);
3042 ldb_asprintf_errstring(ldb,
3043 "'%s' attribute must have exactly one value on add operations!",
3044 *l);
3045 return LDB_ERR_CONSTRAINT_VIOLATION;
3047 if ((passwordAttr->num_values > 1) &&
3048 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
3049 talloc_free(ac);
3050 ldb_asprintf_errstring(ldb,
3051 "'%s' attribute must have zero or one value(s) on delete operations!",
3052 *l);
3053 return LDB_ERR_CONSTRAINT_VIOLATION;
3055 ldb_msg_remove_element(msg, passwordAttr);
3058 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
3059 talloc_free(ac);
3060 ldb_set_errstring(ldb,
3061 "Only the add action for a password change specified!");
3062 return LDB_ERR_UNWILLING_TO_PERFORM;
3064 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
3065 talloc_free(ac);
3066 ldb_set_errstring(ldb,
3067 "Only one delete and one add action for a password change allowed!");
3068 return LDB_ERR_UNWILLING_TO_PERFORM;
3070 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
3071 talloc_free(ac);
3072 ldb_set_errstring(ldb,
3073 "Either a password change or a password set operation is allowed!");
3074 return LDB_ERR_UNWILLING_TO_PERFORM;
3077 /* if there was nothing else to be modified skip to next step */
3078 if (msg->num_elements == 0) {
3079 return password_hash_mod_search_self(ac);
3082 ret = ldb_build_mod_req(&down_req, ldb, ac,
3083 msg,
3084 req->controls,
3085 ac, ph_modify_callback,
3086 req);
3087 LDB_REQ_SET_LOCATION(down_req);
3088 if (ret != LDB_SUCCESS) {
3089 return ret;
3092 return ldb_next_request(module, down_req);
3095 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3097 struct ph_context *ac;
3099 ac = talloc_get_type(req->context, struct ph_context);
3101 if (!ares) {
3102 return ldb_module_done(ac->req, NULL, NULL,
3103 LDB_ERR_OPERATIONS_ERROR);
3106 if (ares->type == LDB_REPLY_REFERRAL) {
3107 return ldb_module_send_referral(ac->req, ares->referral);
3110 if (ares->error != LDB_SUCCESS) {
3111 return ldb_module_done(ac->req, ares->controls,
3112 ares->response, ares->error);
3115 if (ares->type != LDB_REPLY_DONE) {
3116 talloc_free(ares);
3117 return ldb_module_done(ac->req, NULL, NULL,
3118 LDB_ERR_OPERATIONS_ERROR);
3121 talloc_free(ares);
3123 return password_hash_mod_search_self(ac);
3126 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
3128 struct ldb_context *ldb;
3129 struct ph_context *ac;
3130 int ret;
3132 ac = talloc_get_type(req->context, struct ph_context);
3133 ldb = ldb_module_get_ctx(ac->module);
3135 if (!ares) {
3136 ret = LDB_ERR_OPERATIONS_ERROR;
3137 goto done;
3139 if (ares->error != LDB_SUCCESS) {
3140 return ldb_module_done(ac->req, ares->controls,
3141 ares->response, ares->error);
3144 /* we are interested only in the single reply (base search) */
3145 switch (ares->type) {
3146 case LDB_REPLY_ENTRY:
3147 /* Make sure we are performing the password change action on a
3148 * (for us) valid object. Those are instances of either "user"
3149 * and/or "inetOrgPerson". Otherwise continue with the
3150 * submodules. */
3151 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
3152 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
3153 talloc_free(ares);
3155 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
3156 ldb_set_errstring(ldb,
3157 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3158 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
3159 goto done;
3162 ret = ldb_next_request(ac->module, ac->req);
3163 goto done;
3166 if (ac->search_res != NULL) {
3167 talloc_free(ares);
3169 ldb_set_errstring(ldb, "Too many results");
3170 ret = LDB_ERR_OPERATIONS_ERROR;
3171 goto done;
3174 ac->search_res = talloc_steal(ac, ares);
3175 ret = LDB_SUCCESS;
3176 break;
3178 case LDB_REPLY_REFERRAL:
3179 /* ignore anything else for now */
3180 talloc_free(ares);
3181 ret = LDB_SUCCESS;
3182 break;
3184 case LDB_REPLY_DONE:
3185 talloc_free(ares);
3187 /* get user domain data */
3188 ret = build_domain_data_request(ac);
3189 if (ret != LDB_SUCCESS) {
3190 return ldb_module_done(ac->req, NULL, NULL, ret);
3193 ret = ldb_next_request(ac->module, ac->dom_req);
3194 break;
3197 done:
3198 if (ret != LDB_SUCCESS) {
3199 return ldb_module_done(ac->req, NULL, NULL, ret);
3202 return LDB_SUCCESS;
3205 static int password_hash_mod_search_self(struct ph_context *ac)
3207 struct ldb_context *ldb;
3208 static const char * const attrs[] = { "objectClass",
3209 "userAccountControl",
3210 "pwdLastSet",
3211 "sAMAccountName",
3212 "objectSid",
3213 "userPrincipalName",
3214 "supplementalCredentials",
3215 "lmPwdHistory",
3216 "ntPwdHistory",
3217 "dBCSPwd",
3218 "unicodePwd",
3219 NULL };
3220 struct ldb_request *search_req;
3221 int ret;
3223 ldb = ldb_module_get_ctx(ac->module);
3225 ret = ldb_build_search_req(&search_req, ldb, ac,
3226 ac->req->op.mod.message->dn,
3227 LDB_SCOPE_BASE,
3228 "(objectclass=*)",
3229 attrs,
3230 NULL,
3231 ac, ph_mod_search_callback,
3232 ac->req);
3233 LDB_REQ_SET_LOCATION(search_req);
3234 if (ret != LDB_SUCCESS) {
3235 return ret;
3238 return ldb_next_request(ac->module, search_req);
3241 static int password_hash_mod_do_mod(struct ph_context *ac)
3243 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3244 struct loadparm_context *lp_ctx =
3245 talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3246 struct loadparm_context);
3247 struct ldb_request *mod_req;
3248 struct ldb_message *msg;
3249 const struct ldb_message *orig_msg, *searched_msg;
3250 struct setup_password_fields_io io;
3251 int ret;
3252 NTSTATUS status;
3254 /* use a new message structure so that we can modify it */
3255 msg = ldb_msg_new(ac);
3256 if (msg == NULL) {
3257 return ldb_operr(ldb);
3260 /* modify dn */
3261 msg->dn = ac->req->op.mod.message->dn;
3263 orig_msg = ac->req->op.mod.message;
3264 searched_msg = ac->search_res->message;
3266 /* Prepare the internal data structure containing the passwords */
3267 ret = setup_io(ac, orig_msg, searched_msg, &io);
3268 if (ret != LDB_SUCCESS) {
3269 return ret;
3272 /* Get the old password from the database */
3273 status = samdb_result_passwords(io.ac,
3274 lp_ctx,
3275 discard_const_p(struct ldb_message, searched_msg),
3276 &io.o.lm_hash, &io.o.nt_hash);
3277 if (!NT_STATUS_IS_OK(status)) {
3278 return ldb_operr(ldb);
3281 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
3282 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
3283 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
3285 ret = setup_password_fields(&io);
3286 if (ret != LDB_SUCCESS) {
3287 return ret;
3290 ret = check_password_restrictions(&io);
3291 if (ret != LDB_SUCCESS) {
3292 return ret;
3295 /* make sure we replace all the old attributes */
3296 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
3297 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
3298 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3299 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
3300 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
3301 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
3303 if (io.g.nt_hash) {
3304 ret = samdb_msg_add_hash(ldb, ac, msg,
3305 "unicodePwd", io.g.nt_hash);
3306 if (ret != LDB_SUCCESS) {
3307 return ret;
3310 if (io.g.lm_hash) {
3311 ret = samdb_msg_add_hash(ldb, ac, msg,
3312 "dBCSPwd", io.g.lm_hash);
3313 if (ret != LDB_SUCCESS) {
3314 return ret;
3317 if (io.g.nt_history_len > 0) {
3318 ret = samdb_msg_add_hashes(ldb, ac, msg,
3319 "ntPwdHistory",
3320 io.g.nt_history,
3321 io.g.nt_history_len);
3322 if (ret != LDB_SUCCESS) {
3323 return ret;
3326 if (io.g.lm_history_len > 0) {
3327 ret = samdb_msg_add_hashes(ldb, ac, msg,
3328 "lmPwdHistory",
3329 io.g.lm_history,
3330 io.g.lm_history_len);
3331 if (ret != LDB_SUCCESS) {
3332 return ret;
3335 if (io.g.supplemental.length > 0) {
3336 ret = ldb_msg_add_value(msg, "supplementalCredentials",
3337 &io.g.supplemental, NULL);
3338 if (ret != LDB_SUCCESS) {
3339 return ret;
3342 ret = samdb_msg_add_uint64(ldb, ac, msg,
3343 "pwdLastSet",
3344 io.g.last_set);
3345 if (ret != LDB_SUCCESS) {
3346 return ret;
3349 ret = ldb_build_mod_req(&mod_req, ldb, ac,
3350 msg,
3351 ac->req->controls,
3352 ac, ph_op_callback,
3353 ac->req);
3354 LDB_REQ_SET_LOCATION(mod_req);
3355 if (ret != LDB_SUCCESS) {
3356 return ret;
3359 return ldb_next_request(ac->module, mod_req);
3362 static const struct ldb_module_ops ldb_password_hash_module_ops = {
3363 .name = "password_hash",
3364 .add = password_hash_add,
3365 .modify = password_hash_modify
3368 int ldb_password_hash_module_init(const char *version)
3370 LDB_MODULE_CHECK_VERSION(version);
3371 return ldb_register_module(&ldb_password_hash_module_ops);