s4:dsdb/password_hash: implement DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID
[Samba/ita.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blob505f2c26f74bc8663b1c74bfe51f23dbecf2e538
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 "libcli/ldap/ldap_ndr.h"
37 #include "ldb_module.h"
38 #include "librpc/gen_ndr/misc.h"
39 #include "librpc/gen_ndr/samr.h"
40 #include "libcli/auth/libcli_auth.h"
41 #include "libcli/security/security.h"
42 #include "system/kerberos.h"
43 #include "auth/kerberos/kerberos.h"
44 #include "system/time.h"
45 #include "dsdb/samdb/samdb.h"
46 #include "../libds/common/flags.h"
47 #include "dsdb/samdb/ldb_modules/password_modules.h"
48 #include "librpc/ndr/libndr.h"
49 #include "librpc/gen_ndr/ndr_drsblobs.h"
50 #include "../lib/crypto/crypto.h"
51 #include "param/param.h"
53 /* If we have decided there is a reason to work on this request, then
54 * setup all the password hash types correctly.
56 * If we haven't the hashes yet but the password given as plain-text (attributes
57 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
58 * the constraints. Once this is done, we calculate the password hashes.
60 * Notice: unlike the real AD which only supports the UTF16 special based
61 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
62 * understand also a UTF16 based 'clearTextPassword' one.
63 * The latter is also accessible through LDAP so it can also be set by external
64 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
66 * Also when the module receives only the password hashes (possible through
67 * specifying an internal LDB control - for security reasons) some checks are
68 * performed depending on the operation mode (see below) (e.g. if the password
69 * has been in use before if the password memory policy was activated).
71 * Attention: There is a difference between "modify" and "reset" operations
72 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
73 * operation for a password attribute we thread this as a "modify"; if it sends
74 * only a "replace" one we have an (administrative) reset.
76 * Finally, if the administrator has requested that a password history
77 * be maintained, then this should also be written out.
81 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
82 * - Check for right connection encryption
85 /* Notice: Definition of "dsdb_control_password_change_status" moved into
86 * "samdb.h" */
88 struct ph_context {
89 struct ldb_module *module;
90 struct ldb_request *req;
92 struct ldb_request *dom_req;
93 struct ldb_reply *dom_res;
95 struct ldb_reply *search_res;
97 struct dsdb_control_password_change_status *status;
99 bool pwd_reset;
101 bool change_status;
102 bool hash_values;
103 bool change_old_pw_checked;
107 struct setup_password_fields_io {
108 struct ph_context *ac;
110 struct smb_krb5_context *smb_krb5_context;
112 /* infos about the user account */
113 struct {
114 uint32_t userAccountControl;
115 NTTIME pwdLastSet;
116 const char *sAMAccountName;
117 const char *user_principal_name;
118 bool is_computer;
119 uint32_t restrictions;
120 } u;
122 /* new credentials and old given credentials */
123 struct setup_password_fields_given {
124 const struct ldb_val *cleartext_utf8;
125 const struct ldb_val *cleartext_utf16;
126 struct samr_Password *nt_hash;
127 struct samr_Password *lm_hash;
128 } n, og;
130 /* old credentials */
131 struct {
132 struct samr_Password *nt_hash;
133 struct samr_Password *lm_hash;
134 uint32_t nt_history_len;
135 struct samr_Password *nt_history;
136 uint32_t lm_history_len;
137 struct samr_Password *lm_history;
138 const struct ldb_val *supplemental;
139 struct supplementalCredentialsBlob scb;
140 } o;
142 /* generated credentials */
143 struct {
144 struct samr_Password *nt_hash;
145 struct samr_Password *lm_hash;
146 uint32_t nt_history_len;
147 struct samr_Password *nt_history;
148 uint32_t lm_history_len;
149 struct samr_Password *lm_history;
150 const char *salt;
151 DATA_BLOB aes_256;
152 DATA_BLOB aes_128;
153 DATA_BLOB des_md5;
154 DATA_BLOB des_crc;
155 struct ldb_val supplemental;
156 NTTIME last_set;
157 } g;
160 /* Get the NT hash, and fill it in as an entry in the password history,
161 and specify it into io->g.nt_hash */
163 static int setup_nt_fields(struct setup_password_fields_io *io)
165 struct ldb_context *ldb;
166 uint32_t i;
168 io->g.nt_hash = io->n.nt_hash;
169 ldb = ldb_module_get_ctx(io->ac->module);
171 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
172 return LDB_SUCCESS;
175 /* We might not have an old NT password */
176 io->g.nt_history = talloc_array(io->ac,
177 struct samr_Password,
178 io->ac->status->domain_data.pwdHistoryLength);
179 if (!io->g.nt_history) {
180 ldb_oom(ldb);
181 return LDB_ERR_OPERATIONS_ERROR;
184 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
185 io->o.nt_history_len); i++) {
186 io->g.nt_history[i+1] = io->o.nt_history[i];
188 io->g.nt_history_len = i + 1;
190 if (io->g.nt_hash) {
191 io->g.nt_history[0] = *io->g.nt_hash;
192 } else {
194 * TODO: is this correct?
195 * the simular behavior is correct for the lm history case
197 E_md4hash("", io->g.nt_history[0].hash);
200 return LDB_SUCCESS;
203 /* Get the LANMAN hash, and fill it in as an entry in the password history,
204 and specify it into io->g.lm_hash */
206 static int setup_lm_fields(struct setup_password_fields_io *io)
208 struct ldb_context *ldb;
209 uint32_t i;
211 io->g.lm_hash = io->n.lm_hash;
212 ldb = ldb_module_get_ctx(io->ac->module);
214 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
215 return LDB_SUCCESS;
218 /* We might not have an old LM password */
219 io->g.lm_history = talloc_array(io->ac,
220 struct samr_Password,
221 io->ac->status->domain_data.pwdHistoryLength);
222 if (!io->g.lm_history) {
223 ldb_oom(ldb);
224 return LDB_ERR_OPERATIONS_ERROR;
227 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
228 io->o.lm_history_len); i++) {
229 io->g.lm_history[i+1] = io->o.lm_history[i];
231 io->g.lm_history_len = i + 1;
233 if (io->g.lm_hash) {
234 io->g.lm_history[0] = *io->g.lm_hash;
235 } else {
236 E_deshash("", io->g.lm_history[0].hash);
239 return LDB_SUCCESS;
242 static int setup_kerberos_keys(struct setup_password_fields_io *io)
244 struct ldb_context *ldb;
245 krb5_error_code krb5_ret;
246 Principal *salt_principal;
247 krb5_salt salt;
248 krb5_keyblock key;
249 krb5_data cleartext_data;
251 ldb = ldb_module_get_ctx(io->ac->module);
252 cleartext_data.data = io->n.cleartext_utf8->data;
253 cleartext_data.length = io->n.cleartext_utf8->length;
255 /* Many, many thanks to lukeh@padl.com for this
256 * algorithm, described in his Nov 10 2004 mail to
257 * samba-technical@samba.org */
260 * Determine a salting principal
262 if (io->u.is_computer) {
263 char *name;
264 char *saltbody;
266 name = strlower_talloc(io->ac, io->u.sAMAccountName);
267 if (!name) {
268 ldb_oom(ldb);
269 return LDB_ERR_OPERATIONS_ERROR;
272 if (name[strlen(name)-1] == '$') {
273 name[strlen(name)-1] = '\0';
276 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
277 io->ac->status->domain_data.dns_domain);
278 if (!saltbody) {
279 ldb_oom(ldb);
280 return LDB_ERR_OPERATIONS_ERROR;
283 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
284 &salt_principal,
285 io->ac->status->domain_data.realm,
286 "host", saltbody, NULL);
287 } else if (io->u.user_principal_name) {
288 char *user_principal_name;
289 char *p;
291 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
292 if (!user_principal_name) {
293 ldb_oom(ldb);
294 return LDB_ERR_OPERATIONS_ERROR;
297 p = strchr(user_principal_name, '@');
298 if (p) {
299 p[0] = '\0';
302 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
303 &salt_principal,
304 io->ac->status->domain_data.realm,
305 user_principal_name, NULL);
306 } else {
307 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
308 &salt_principal,
309 io->ac->status->domain_data.realm,
310 io->u.sAMAccountName, NULL);
312 if (krb5_ret) {
313 ldb_asprintf_errstring(ldb,
314 "setup_kerberos_keys: "
315 "generation of a salting principal failed: %s",
316 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
317 krb5_ret, io->ac));
318 return LDB_ERR_OPERATIONS_ERROR;
322 * create salt from salt_principal
324 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
325 salt_principal, &salt);
326 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
327 if (krb5_ret) {
328 ldb_asprintf_errstring(ldb,
329 "setup_kerberos_keys: "
330 "generation of krb5_salt failed: %s",
331 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
332 krb5_ret, io->ac));
333 return LDB_ERR_OPERATIONS_ERROR;
335 /* create a talloc copy */
336 io->g.salt = talloc_strndup(io->ac,
337 (char *)salt.saltvalue.data,
338 salt.saltvalue.length);
339 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
340 if (!io->g.salt) {
341 ldb_oom(ldb);
342 return LDB_ERR_OPERATIONS_ERROR;
344 salt.saltvalue.data = discard_const(io->g.salt);
345 salt.saltvalue.length = strlen(io->g.salt);
348 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
349 * the salt and the cleartext password
351 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
352 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
353 cleartext_data,
354 salt,
355 &key);
356 if (krb5_ret) {
357 ldb_asprintf_errstring(ldb,
358 "setup_kerberos_keys: "
359 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
360 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
361 krb5_ret, io->ac));
362 return LDB_ERR_OPERATIONS_ERROR;
364 io->g.aes_256 = data_blob_talloc(io->ac,
365 key.keyvalue.data,
366 key.keyvalue.length);
367 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
368 if (!io->g.aes_256.data) {
369 ldb_oom(ldb);
370 return LDB_ERR_OPERATIONS_ERROR;
374 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
375 * the salt and the cleartext password
377 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
378 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
379 cleartext_data,
380 salt,
381 &key);
382 if (krb5_ret) {
383 ldb_asprintf_errstring(ldb,
384 "setup_kerberos_keys: "
385 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
386 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
387 krb5_ret, io->ac));
388 return LDB_ERR_OPERATIONS_ERROR;
390 io->g.aes_128 = data_blob_talloc(io->ac,
391 key.keyvalue.data,
392 key.keyvalue.length);
393 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
394 if (!io->g.aes_128.data) {
395 ldb_oom(ldb);
396 return LDB_ERR_OPERATIONS_ERROR;
400 * create ENCTYPE_DES_CBC_MD5 key out of
401 * the salt and the cleartext password
403 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
404 ENCTYPE_DES_CBC_MD5,
405 cleartext_data,
406 salt,
407 &key);
408 if (krb5_ret) {
409 ldb_asprintf_errstring(ldb,
410 "setup_kerberos_keys: "
411 "generation of a des-cbc-md5 key failed: %s",
412 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
413 krb5_ret, io->ac));
414 return LDB_ERR_OPERATIONS_ERROR;
416 io->g.des_md5 = data_blob_talloc(io->ac,
417 key.keyvalue.data,
418 key.keyvalue.length);
419 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
420 if (!io->g.des_md5.data) {
421 ldb_oom(ldb);
422 return LDB_ERR_OPERATIONS_ERROR;
426 * create ENCTYPE_DES_CBC_CRC key out of
427 * the salt and the cleartext password
429 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
430 ENCTYPE_DES_CBC_CRC,
431 cleartext_data,
432 salt,
433 &key);
434 if (krb5_ret) {
435 ldb_asprintf_errstring(ldb,
436 "setup_kerberos_keys: "
437 "generation of a des-cbc-crc key failed: %s",
438 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
439 krb5_ret, io->ac));
440 return LDB_ERR_OPERATIONS_ERROR;
442 io->g.des_crc = data_blob_talloc(io->ac,
443 key.keyvalue.data,
444 key.keyvalue.length);
445 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
446 if (!io->g.des_crc.data) {
447 ldb_oom(ldb);
448 return LDB_ERR_OPERATIONS_ERROR;
451 return LDB_SUCCESS;
454 static int setup_primary_kerberos(struct setup_password_fields_io *io,
455 const struct supplementalCredentialsBlob *old_scb,
456 struct package_PrimaryKerberosBlob *pkb)
458 struct ldb_context *ldb;
459 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
460 struct supplementalCredentialsPackage *old_scp = NULL;
461 struct package_PrimaryKerberosBlob _old_pkb;
462 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
463 uint32_t i;
464 enum ndr_err_code ndr_err;
466 ldb = ldb_module_get_ctx(io->ac->module);
469 * prepare generation of keys
471 * ENCTYPE_DES_CBC_MD5
472 * ENCTYPE_DES_CBC_CRC
474 pkb->version = 3;
475 pkb3->salt.string = io->g.salt;
476 pkb3->num_keys = 2;
477 pkb3->keys = talloc_array(io->ac,
478 struct package_PrimaryKerberosKey3,
479 pkb3->num_keys);
480 if (!pkb3->keys) {
481 ldb_oom(ldb);
482 return LDB_ERR_OPERATIONS_ERROR;
485 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
486 pkb3->keys[0].value = &io->g.des_md5;
487 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
488 pkb3->keys[1].value = &io->g.des_crc;
490 /* initialize the old keys to zero */
491 pkb3->num_old_keys = 0;
492 pkb3->old_keys = NULL;
494 /* if there're no old keys, then we're done */
495 if (!old_scb) {
496 return LDB_SUCCESS;
499 for (i=0; i < old_scb->sub.num_packages; i++) {
500 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
501 continue;
504 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
505 continue;
508 old_scp = &old_scb->sub.packages[i];
509 break;
511 /* Primary:Kerberos element of supplementalCredentials */
512 if (old_scp) {
513 DATA_BLOB blob;
515 blob = strhex_to_data_blob(io->ac, old_scp->data);
516 if (!blob.data) {
517 ldb_oom(ldb);
518 return LDB_ERR_OPERATIONS_ERROR;
521 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
522 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
523 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
524 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
525 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
526 ldb_asprintf_errstring(ldb,
527 "setup_primary_kerberos: "
528 "failed to pull old package_PrimaryKerberosBlob: %s",
529 nt_errstr(status));
530 return LDB_ERR_OPERATIONS_ERROR;
533 if (_old_pkb.version != 3) {
534 ldb_asprintf_errstring(ldb,
535 "setup_primary_kerberos: "
536 "package_PrimaryKerberosBlob version[%u] expected[3]",
537 _old_pkb.version);
538 return LDB_ERR_OPERATIONS_ERROR;
541 old_pkb3 = &_old_pkb.ctr.ctr3;
544 /* if we didn't found the old keys we're done */
545 if (!old_pkb3) {
546 return LDB_SUCCESS;
549 /* fill in the old keys */
550 pkb3->num_old_keys = old_pkb3->num_keys;
551 pkb3->old_keys = old_pkb3->keys;
553 return LDB_SUCCESS;
556 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
557 const struct supplementalCredentialsBlob *old_scb,
558 struct package_PrimaryKerberosBlob *pkb)
560 struct ldb_context *ldb;
561 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
562 struct supplementalCredentialsPackage *old_scp = NULL;
563 struct package_PrimaryKerberosBlob _old_pkb;
564 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
565 uint32_t i;
566 enum ndr_err_code ndr_err;
568 ldb = ldb_module_get_ctx(io->ac->module);
571 * prepare generation of keys
573 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
574 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
575 * ENCTYPE_DES_CBC_MD5
576 * ENCTYPE_DES_CBC_CRC
578 pkb->version = 4;
579 pkb4->salt.string = io->g.salt;
580 pkb4->default_iteration_count = 4096;
581 pkb4->num_keys = 4;
583 pkb4->keys = talloc_array(io->ac,
584 struct package_PrimaryKerberosKey4,
585 pkb4->num_keys);
586 if (!pkb4->keys) {
587 ldb_oom(ldb);
588 return LDB_ERR_OPERATIONS_ERROR;
591 pkb4->keys[0].iteration_count = 4096;
592 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
593 pkb4->keys[0].value = &io->g.aes_256;
594 pkb4->keys[1].iteration_count = 4096;
595 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
596 pkb4->keys[1].value = &io->g.aes_128;
597 pkb4->keys[2].iteration_count = 4096;
598 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
599 pkb4->keys[2].value = &io->g.des_md5;
600 pkb4->keys[3].iteration_count = 4096;
601 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
602 pkb4->keys[3].value = &io->g.des_crc;
604 /* initialize the old keys to zero */
605 pkb4->num_old_keys = 0;
606 pkb4->old_keys = NULL;
607 pkb4->num_older_keys = 0;
608 pkb4->older_keys = NULL;
610 /* if there're no old keys, then we're done */
611 if (!old_scb) {
612 return LDB_SUCCESS;
615 for (i=0; i < old_scb->sub.num_packages; i++) {
616 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
617 continue;
620 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
621 continue;
624 old_scp = &old_scb->sub.packages[i];
625 break;
627 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
628 if (old_scp) {
629 DATA_BLOB blob;
631 blob = strhex_to_data_blob(io->ac, old_scp->data);
632 if (!blob.data) {
633 ldb_oom(ldb);
634 return LDB_ERR_OPERATIONS_ERROR;
637 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
638 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
639 &_old_pkb,
640 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
641 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
642 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
643 ldb_asprintf_errstring(ldb,
644 "setup_primary_kerberos_newer: "
645 "failed to pull old package_PrimaryKerberosBlob: %s",
646 nt_errstr(status));
647 return LDB_ERR_OPERATIONS_ERROR;
650 if (_old_pkb.version != 4) {
651 ldb_asprintf_errstring(ldb,
652 "setup_primary_kerberos_newer: "
653 "package_PrimaryKerberosBlob version[%u] expected[4]",
654 _old_pkb.version);
655 return LDB_ERR_OPERATIONS_ERROR;
658 old_pkb4 = &_old_pkb.ctr.ctr4;
661 /* if we didn't found the old keys we're done */
662 if (!old_pkb4) {
663 return LDB_SUCCESS;
666 /* fill in the old keys */
667 pkb4->num_old_keys = old_pkb4->num_keys;
668 pkb4->old_keys = old_pkb4->keys;
669 pkb4->num_older_keys = old_pkb4->num_old_keys;
670 pkb4->older_keys = old_pkb4->old_keys;
672 return LDB_SUCCESS;
675 static int setup_primary_wdigest(struct setup_password_fields_io *io,
676 const struct supplementalCredentialsBlob *old_scb,
677 struct package_PrimaryWDigestBlob *pdb)
679 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
680 DATA_BLOB sAMAccountName;
681 DATA_BLOB sAMAccountName_l;
682 DATA_BLOB sAMAccountName_u;
683 const char *user_principal_name = io->u.user_principal_name;
684 DATA_BLOB userPrincipalName;
685 DATA_BLOB userPrincipalName_l;
686 DATA_BLOB userPrincipalName_u;
687 DATA_BLOB netbios_domain;
688 DATA_BLOB netbios_domain_l;
689 DATA_BLOB netbios_domain_u;
690 DATA_BLOB dns_domain;
691 DATA_BLOB dns_domain_l;
692 DATA_BLOB dns_domain_u;
693 DATA_BLOB digest;
694 DATA_BLOB delim;
695 DATA_BLOB backslash;
696 uint8_t i;
697 struct {
698 DATA_BLOB *user;
699 DATA_BLOB *realm;
700 DATA_BLOB *nt4dom;
701 } wdigest[] = {
703 * See
704 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
705 * for what precalculated hashes are supposed to be stored...
707 * I can't reproduce all values which should contain "Digest" as realm,
708 * am I doing something wrong or is w2k3 just broken...?
710 * W2K3 fills in following for a user:
712 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
713 * sAMAccountName: NewUser2Sam
714 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
716 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
717 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
718 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
719 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
720 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
721 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
722 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
723 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
724 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
725 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
726 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
727 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
728 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
729 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
730 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
731 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
732 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
733 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
734 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
735 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
736 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
737 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
738 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
739 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
740 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
741 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
742 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
743 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
744 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
746 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
747 * sAMAccountName: NewUser2Sam
749 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
750 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
751 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
752 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
753 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
754 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
755 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
756 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
757 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
758 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
759 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
760 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
761 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
762 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
763 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
764 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
765 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
766 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
767 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
768 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
769 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
770 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
771 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
772 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
773 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
774 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
775 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
776 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
777 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
781 * sAMAccountName, netbios_domain
784 .user = &sAMAccountName,
785 .realm = &netbios_domain,
788 .user = &sAMAccountName_l,
789 .realm = &netbios_domain_l,
792 .user = &sAMAccountName_u,
793 .realm = &netbios_domain_u,
796 .user = &sAMAccountName,
797 .realm = &netbios_domain_u,
800 .user = &sAMAccountName,
801 .realm = &netbios_domain_l,
804 .user = &sAMAccountName_u,
805 .realm = &netbios_domain_l,
808 .user = &sAMAccountName_l,
809 .realm = &netbios_domain_u,
812 * sAMAccountName, dns_domain
815 .user = &sAMAccountName,
816 .realm = &dns_domain,
819 .user = &sAMAccountName_l,
820 .realm = &dns_domain_l,
823 .user = &sAMAccountName_u,
824 .realm = &dns_domain_u,
827 .user = &sAMAccountName,
828 .realm = &dns_domain_u,
831 .user = &sAMAccountName,
832 .realm = &dns_domain_l,
835 .user = &sAMAccountName_u,
836 .realm = &dns_domain_l,
839 .user = &sAMAccountName_l,
840 .realm = &dns_domain_u,
843 * userPrincipalName, no realm
846 .user = &userPrincipalName,
850 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
851 * the fallback to the sAMAccountName based userPrincipalName is correct
853 .user = &userPrincipalName_l,
856 .user = &userPrincipalName_u,
859 * nt4dom\sAMAccountName, no realm
862 .user = &sAMAccountName,
863 .nt4dom = &netbios_domain
866 .user = &sAMAccountName_l,
867 .nt4dom = &netbios_domain_l
870 .user = &sAMAccountName_u,
871 .nt4dom = &netbios_domain_u
875 * the following ones are guessed depending on the technet2 article
876 * but not reproducable on a w2k3 server
878 /* sAMAccountName with "Digest" realm */
880 .user = &sAMAccountName,
881 .realm = &digest
884 .user = &sAMAccountName_l,
885 .realm = &digest
888 .user = &sAMAccountName_u,
889 .realm = &digest
891 /* userPrincipalName with "Digest" realm */
893 .user = &userPrincipalName,
894 .realm = &digest
897 .user = &userPrincipalName_l,
898 .realm = &digest
901 .user = &userPrincipalName_u,
902 .realm = &digest
904 /* nt4dom\\sAMAccountName with "Digest" realm */
906 .user = &sAMAccountName,
907 .nt4dom = &netbios_domain,
908 .realm = &digest
911 .user = &sAMAccountName_l,
912 .nt4dom = &netbios_domain_l,
913 .realm = &digest
916 .user = &sAMAccountName_u,
917 .nt4dom = &netbios_domain_u,
918 .realm = &digest
922 /* prepare DATA_BLOB's used in the combinations array */
923 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
924 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
925 if (!sAMAccountName_l.data) {
926 ldb_oom(ldb);
927 return LDB_ERR_OPERATIONS_ERROR;
929 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
930 if (!sAMAccountName_u.data) {
931 ldb_oom(ldb);
932 return LDB_ERR_OPERATIONS_ERROR;
935 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
936 if (!user_principal_name) {
937 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
938 io->u.sAMAccountName,
939 io->ac->status->domain_data.dns_domain);
940 if (!user_principal_name) {
941 ldb_oom(ldb);
942 return LDB_ERR_OPERATIONS_ERROR;
945 userPrincipalName = data_blob_string_const(user_principal_name);
946 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
947 if (!userPrincipalName_l.data) {
948 ldb_oom(ldb);
949 return LDB_ERR_OPERATIONS_ERROR;
951 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
952 if (!userPrincipalName_u.data) {
953 ldb_oom(ldb);
954 return LDB_ERR_OPERATIONS_ERROR;
957 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
958 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
959 io->ac->status->domain_data.netbios_domain));
960 if (!netbios_domain_l.data) {
961 ldb_oom(ldb);
962 return LDB_ERR_OPERATIONS_ERROR;
964 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
965 io->ac->status->domain_data.netbios_domain));
966 if (!netbios_domain_u.data) {
967 ldb_oom(ldb);
968 return LDB_ERR_OPERATIONS_ERROR;
971 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
972 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
973 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
975 digest = data_blob_string_const("Digest");
977 delim = data_blob_string_const(":");
978 backslash = data_blob_string_const("\\");
980 pdb->num_hashes = ARRAY_SIZE(wdigest);
981 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
982 pdb->num_hashes);
983 if (!pdb->hashes) {
984 ldb_oom(ldb);
985 return LDB_ERR_OPERATIONS_ERROR;
988 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
989 struct MD5Context md5;
990 MD5Init(&md5);
991 if (wdigest[i].nt4dom) {
992 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
993 MD5Update(&md5, backslash.data, backslash.length);
995 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
996 MD5Update(&md5, delim.data, delim.length);
997 if (wdigest[i].realm) {
998 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1000 MD5Update(&md5, delim.data, delim.length);
1001 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1002 MD5Final(pdb->hashes[i].hash, &md5);
1005 return LDB_SUCCESS;
1008 static int setup_supplemental_field(struct setup_password_fields_io *io)
1010 struct ldb_context *ldb;
1011 struct supplementalCredentialsBlob scb;
1012 struct supplementalCredentialsBlob _old_scb;
1013 struct supplementalCredentialsBlob *old_scb = NULL;
1014 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1015 uint32_t num_names = 0;
1016 const char *names[1+4];
1017 uint32_t num_packages = 0;
1018 struct supplementalCredentialsPackage packages[1+4];
1019 /* Packages */
1020 struct supplementalCredentialsPackage *pp = NULL;
1021 struct package_PackagesBlob pb;
1022 DATA_BLOB pb_blob;
1023 char *pb_hexstr;
1024 /* Primary:Kerberos-Newer-Keys */
1025 const char **nkn = NULL;
1026 struct supplementalCredentialsPackage *pkn = NULL;
1027 struct package_PrimaryKerberosBlob pknb;
1028 DATA_BLOB pknb_blob;
1029 char *pknb_hexstr;
1030 /* Primary:Kerberos */
1031 const char **nk = NULL;
1032 struct supplementalCredentialsPackage *pk = NULL;
1033 struct package_PrimaryKerberosBlob pkb;
1034 DATA_BLOB pkb_blob;
1035 char *pkb_hexstr;
1036 /* Primary:WDigest */
1037 const char **nd = NULL;
1038 struct supplementalCredentialsPackage *pd = NULL;
1039 struct package_PrimaryWDigestBlob pdb;
1040 DATA_BLOB pdb_blob;
1041 char *pdb_hexstr;
1042 /* Primary:CLEARTEXT */
1043 const char **nc = NULL;
1044 struct supplementalCredentialsPackage *pc = NULL;
1045 struct package_PrimaryCLEARTEXTBlob pcb;
1046 DATA_BLOB pcb_blob;
1047 char *pcb_hexstr;
1048 int ret;
1049 enum ndr_err_code ndr_err;
1050 uint8_t zero16[16];
1051 bool do_newer_keys = false;
1052 bool do_cleartext = false;
1054 ZERO_STRUCT(zero16);
1055 ZERO_STRUCT(names);
1057 ldb = ldb_module_get_ctx(io->ac->module);
1059 if (!io->n.cleartext_utf8) {
1061 * when we don't have a cleartext password
1062 * we can't setup a supplementalCredential value
1064 return LDB_SUCCESS;
1067 /* if there's an old supplementaCredentials blob then parse it */
1068 if (io->o.supplemental) {
1069 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1070 &_old_scb,
1071 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1072 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1073 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1074 ldb_asprintf_errstring(ldb,
1075 "setup_supplemental_field: "
1076 "failed to pull old supplementalCredentialsBlob: %s",
1077 nt_errstr(status));
1078 return LDB_ERR_OPERATIONS_ERROR;
1081 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1082 old_scb = &_old_scb;
1083 } else {
1084 ldb_debug(ldb, LDB_DEBUG_ERROR,
1085 "setup_supplemental_field: "
1086 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1087 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1090 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1091 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1093 if (io->ac->status->domain_data.store_cleartext &&
1094 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1095 do_cleartext = true;
1099 * The ordering is this
1101 * Primary:Kerberos-Newer-Keys (optional)
1102 * Primary:Kerberos
1103 * Primary:WDigest
1104 * Primary:CLEARTEXT (optional)
1106 * And the 'Packages' package is insert before the last
1107 * other package.
1109 if (do_newer_keys) {
1110 /* Primary:Kerberos-Newer-Keys */
1111 nkn = &names[num_names++];
1112 pkn = &packages[num_packages++];
1115 /* Primary:Kerberos */
1116 nk = &names[num_names++];
1117 pk = &packages[num_packages++];
1119 if (!do_cleartext) {
1120 /* Packages */
1121 pp = &packages[num_packages++];
1124 /* Primary:WDigest */
1125 nd = &names[num_names++];
1126 pd = &packages[num_packages++];
1128 if (do_cleartext) {
1129 /* Packages */
1130 pp = &packages[num_packages++];
1132 /* Primary:CLEARTEXT */
1133 nc = &names[num_names++];
1134 pc = &packages[num_packages++];
1137 if (pkn) {
1139 * setup 'Primary:Kerberos-Newer-Keys' element
1141 *nkn = "Kerberos-Newer-Keys";
1143 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1144 if (ret != LDB_SUCCESS) {
1145 return ret;
1148 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1149 &pknb,
1150 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1151 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1152 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1153 ldb_asprintf_errstring(ldb,
1154 "setup_supplemental_field: "
1155 "failed to push package_PrimaryKerberosNeverBlob: %s",
1156 nt_errstr(status));
1157 return LDB_ERR_OPERATIONS_ERROR;
1159 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1160 if (!pknb_hexstr) {
1161 ldb_oom(ldb);
1162 return LDB_ERR_OPERATIONS_ERROR;
1164 pkn->name = "Primary:Kerberos-Newer-Keys";
1165 pkn->reserved = 1;
1166 pkn->data = pknb_hexstr;
1170 * setup 'Primary:Kerberos' element
1172 *nk = "Kerberos";
1174 ret = setup_primary_kerberos(io, old_scb, &pkb);
1175 if (ret != LDB_SUCCESS) {
1176 return ret;
1179 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1180 &pkb,
1181 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1182 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1183 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1184 ldb_asprintf_errstring(ldb,
1185 "setup_supplemental_field: "
1186 "failed to push package_PrimaryKerberosBlob: %s",
1187 nt_errstr(status));
1188 return LDB_ERR_OPERATIONS_ERROR;
1190 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1191 if (!pkb_hexstr) {
1192 ldb_oom(ldb);
1193 return LDB_ERR_OPERATIONS_ERROR;
1195 pk->name = "Primary:Kerberos";
1196 pk->reserved = 1;
1197 pk->data = pkb_hexstr;
1200 * setup 'Primary:WDigest' element
1202 *nd = "WDigest";
1204 ret = setup_primary_wdigest(io, old_scb, &pdb);
1205 if (ret != LDB_SUCCESS) {
1206 return ret;
1209 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1210 &pdb,
1211 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1212 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1213 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1214 ldb_asprintf_errstring(ldb,
1215 "setup_supplemental_field: "
1216 "failed to push package_PrimaryWDigestBlob: %s",
1217 nt_errstr(status));
1218 return LDB_ERR_OPERATIONS_ERROR;
1220 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1221 if (!pdb_hexstr) {
1222 ldb_oom(ldb);
1223 return LDB_ERR_OPERATIONS_ERROR;
1225 pd->name = "Primary:WDigest";
1226 pd->reserved = 1;
1227 pd->data = pdb_hexstr;
1230 * setup 'Primary:CLEARTEXT' element
1232 if (pc) {
1233 *nc = "CLEARTEXT";
1235 pcb.cleartext = *io->n.cleartext_utf16;
1237 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1238 &pcb,
1239 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1240 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1241 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1242 ldb_asprintf_errstring(ldb,
1243 "setup_supplemental_field: "
1244 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1245 nt_errstr(status));
1246 return LDB_ERR_OPERATIONS_ERROR;
1248 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1249 if (!pcb_hexstr) {
1250 ldb_oom(ldb);
1251 return LDB_ERR_OPERATIONS_ERROR;
1253 pc->name = "Primary:CLEARTEXT";
1254 pc->reserved = 1;
1255 pc->data = pcb_hexstr;
1259 * setup 'Packages' element
1261 pb.names = names;
1262 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1263 &pb,
1264 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1265 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1266 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1267 ldb_asprintf_errstring(ldb,
1268 "setup_supplemental_field: "
1269 "failed to push package_PackagesBlob: %s",
1270 nt_errstr(status));
1271 return LDB_ERR_OPERATIONS_ERROR;
1273 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1274 if (!pb_hexstr) {
1275 ldb_oom(ldb);
1276 return LDB_ERR_OPERATIONS_ERROR;
1278 pp->name = "Packages";
1279 pp->reserved = 2;
1280 pp->data = pb_hexstr;
1283 * setup 'supplementalCredentials' value
1285 ZERO_STRUCT(scb);
1286 scb.sub.num_packages = num_packages;
1287 scb.sub.packages = packages;
1289 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1290 &scb,
1291 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1292 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1293 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1294 ldb_asprintf_errstring(ldb,
1295 "setup_supplemental_field: "
1296 "failed to push supplementalCredentialsBlob: %s",
1297 nt_errstr(status));
1298 return LDB_ERR_OPERATIONS_ERROR;
1301 return LDB_SUCCESS;
1304 static int setup_last_set_field(struct setup_password_fields_io *io)
1306 /* set it as now */
1307 unix_to_nt_time(&io->g.last_set, time(NULL));
1309 return LDB_SUCCESS;
1312 static int setup_given_passwords(struct setup_password_fields_io *io,
1313 struct setup_password_fields_given *g)
1315 struct ldb_context *ldb;
1316 bool ok;
1318 ldb = ldb_module_get_ctx(io->ac->module);
1320 if (g->cleartext_utf8) {
1321 char **cleartext_utf16_str;
1322 struct ldb_val *cleartext_utf16_blob;
1323 size_t converted_pw_len;
1325 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1326 if (!cleartext_utf16_blob) {
1327 ldb_oom(ldb);
1328 return LDB_ERR_OPERATIONS_ERROR;
1330 if (!convert_string_talloc(io->ac,
1331 CH_UTF8, CH_UTF16,
1332 g->cleartext_utf8->data,
1333 g->cleartext_utf8->length,
1334 (void *)&cleartext_utf16_str,
1335 &converted_pw_len, false)) {
1336 ldb_asprintf_errstring(ldb,
1337 "setup_password_fields: "
1338 "failed to generate UTF16 password from cleartext UTF8 password");
1339 return LDB_ERR_OPERATIONS_ERROR;
1341 *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str,
1342 converted_pw_len);
1343 g->cleartext_utf16 = cleartext_utf16_blob;
1344 } else if (g->cleartext_utf16) {
1345 char *cleartext_utf8_str;
1346 struct ldb_val *cleartext_utf8_blob;
1347 size_t converted_pw_len;
1349 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1350 if (!cleartext_utf8_blob) {
1351 ldb_oom(ldb);
1352 return LDB_ERR_OPERATIONS_ERROR;
1354 if (!convert_string_talloc(io->ac,
1355 CH_UTF16MUNGED, CH_UTF8,
1356 g->cleartext_utf16->data,
1357 g->cleartext_utf16->length,
1358 (void *)&cleartext_utf8_str,
1359 &converted_pw_len, false)) {
1360 /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
1361 talloc_free(cleartext_utf8_blob);
1362 } else {
1363 *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str,
1364 converted_pw_len);
1365 g->cleartext_utf8 = cleartext_utf8_blob;
1369 if (g->cleartext_utf16) {
1370 struct samr_Password *nt_hash;
1372 nt_hash = talloc(io->ac, struct samr_Password);
1373 if (!nt_hash) {
1374 ldb_oom(ldb);
1375 return LDB_ERR_OPERATIONS_ERROR;
1377 g->nt_hash = nt_hash;
1379 /* compute the new nt hash */
1380 mdfour(nt_hash->hash,
1381 g->cleartext_utf16->data,
1382 g->cleartext_utf16->length);
1385 if (g->cleartext_utf8) {
1386 struct samr_Password *lm_hash;
1388 lm_hash = talloc(io->ac, struct samr_Password);
1389 if (!lm_hash) {
1390 ldb_oom(ldb);
1391 return LDB_ERR_OPERATIONS_ERROR;
1394 /* compute the new lm hash */
1395 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1396 if (ok) {
1397 g->lm_hash = lm_hash;
1398 } else {
1399 talloc_free(lm_hash);
1403 return LDB_SUCCESS;
1406 static int setup_password_fields(struct setup_password_fields_io *io)
1408 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1409 struct loadparm_context *lp_ctx =
1410 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1411 struct loadparm_context);
1412 int ret;
1414 /* transform the old password (for password changes) */
1415 ret = setup_given_passwords(io, &io->og);
1416 if (ret != LDB_SUCCESS) {
1417 return ret;
1420 /* transform the new password */
1421 ret = setup_given_passwords(io, &io->n);
1422 if (ret != LDB_SUCCESS) {
1423 return ret;
1426 if (io->n.cleartext_utf8) {
1427 ret = setup_kerberos_keys(io);
1428 if (ret != LDB_SUCCESS) {
1429 return ret;
1433 ret = setup_nt_fields(io);
1434 if (ret != LDB_SUCCESS) {
1435 return ret;
1438 if (lp_lanman_auth(lp_ctx)) {
1439 ret = setup_lm_fields(io);
1440 if (ret != LDB_SUCCESS) {
1441 return ret;
1443 } else {
1444 io->g.lm_hash = NULL;
1445 io->g.lm_history_len = 0;
1448 ret = setup_supplemental_field(io);
1449 if (ret != LDB_SUCCESS) {
1450 return ret;
1453 ret = setup_last_set_field(io);
1454 if (ret != LDB_SUCCESS) {
1455 return ret;
1458 return LDB_SUCCESS;
1461 static int check_password_restrictions(struct setup_password_fields_io *io)
1463 struct ldb_context *ldb;
1464 int ret;
1465 enum samr_ValidationStatus stat;
1467 ldb = ldb_module_get_ctx(io->ac->module);
1469 /* First check the old password is correct, for password changes */
1470 if (!io->ac->pwd_reset && !io->ac->change_old_pw_checked) {
1471 /* we need to old nt or lm hash given by the client */
1472 if (!io->og.nt_hash && !io->og.lm_hash) {
1473 ldb_asprintf_errstring(ldb,
1474 "check_password_restrictions: "
1475 "You need to provide the old password "
1476 "in order to change your password!");
1477 return LDB_ERR_UNWILLING_TO_PERFORM;
1480 if (io->og.nt_hash) {
1481 if (!io->o.nt_hash) {
1482 ldb_asprintf_errstring(ldb,
1483 "check_password_restrictions: "
1484 "There's no old nt_hash, which is needed "
1485 "in order to change your password!");
1486 return LDB_ERR_UNWILLING_TO_PERFORM;
1489 /* The password modify through the NT hash is encouraged
1490 and has no problems at all */
1491 if (memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
1492 ldb_asprintf_errstring(ldb,
1493 "check_password_restrictions: "
1494 "The old password specified doesn't match!");
1495 return LDB_ERR_UNWILLING_TO_PERFORM;
1497 } else if (io->og.lm_hash) {
1498 if (!io->o.lm_hash) {
1499 ldb_asprintf_errstring(ldb,
1500 "check_password_restrictions: "
1501 "There's no old lm_hash, which is needed "
1502 "in order to change your password!");
1503 return LDB_ERR_UNWILLING_TO_PERFORM;
1506 if (memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0) {
1507 ldb_asprintf_errstring(ldb,
1508 "check_password_restrictions: "
1509 "The old password specified doesn't match!");
1510 return LDB_ERR_UNWILLING_TO_PERFORM;
1515 if (io->u.restrictions == 0) {
1516 /* FIXME: Is this right? */
1517 return LDB_SUCCESS;
1521 * Fundamental password checks done by the call
1522 * "samdb_check_password".
1523 * It is also in use by "dcesrv_samr_ValidatePassword".
1525 if (io->n.cleartext_utf8 != NULL) {
1526 stat = samdb_check_password(io->n.cleartext_utf8,
1527 io->ac->status->domain_data.pwdProperties,
1528 io->ac->status->domain_data.minPwdLength);
1529 switch (stat) {
1530 case SAMR_VALIDATION_STATUS_SUCCESS:
1531 /* perfect -> proceed! */
1532 break;
1534 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
1535 ldb_asprintf_errstring(ldb,
1536 "check_password_restrictions: "
1537 "the password is too short. It should be equal or longer than %i characters!",
1538 io->ac->status->domain_data.minPwdLength);
1540 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1541 return LDB_ERR_CONSTRAINT_VIOLATION;
1543 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
1544 ldb_asprintf_errstring(ldb,
1545 "check_password_restrictions: "
1546 "the password does not meet the complexity criterias!");
1547 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1549 return LDB_ERR_CONSTRAINT_VIOLATION;
1551 default:
1552 ldb_asprintf_errstring(ldb,
1553 "check_password_restrictions: "
1554 "the password doesn't fit by a certain reason!");
1556 return LDB_ERR_CONSTRAINT_VIOLATION;
1560 if (io->ac->pwd_reset) {
1561 return LDB_SUCCESS;
1564 if (io->n.nt_hash) {
1565 uint32_t i;
1567 /* checks the NT hash password history */
1568 for (i = 0; i < io->o.nt_history_len; i++) {
1569 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
1570 if (ret == 0) {
1571 ldb_asprintf_errstring(ldb,
1572 "check_password_restrictions: "
1573 "the password was already used (in history)!");
1575 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1577 return LDB_ERR_CONSTRAINT_VIOLATION;
1582 if (io->n.lm_hash) {
1583 uint32_t i;
1585 /* checks the LM hash password history */
1586 for (i = 0; i < io->o.lm_history_len; i++) {
1587 ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
1588 if (ret == 0) {
1589 ldb_asprintf_errstring(ldb,
1590 "check_password_restrictions: "
1591 "the password was already used (in history)!");
1593 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1595 return LDB_ERR_CONSTRAINT_VIOLATION;
1600 /* are all password changes disallowed? */
1601 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1602 ldb_asprintf_errstring(ldb,
1603 "check_password_restrictions: "
1604 "password changes disabled!");
1605 return LDB_ERR_CONSTRAINT_VIOLATION;
1608 /* can this user change the password? */
1609 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
1610 ldb_asprintf_errstring(ldb,
1611 "check_password_restrictions: "
1612 "password can't be changed on this account!");
1613 return LDB_ERR_CONSTRAINT_VIOLATION;
1616 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
1617 if (io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) {
1618 ldb_asprintf_errstring(ldb,
1619 "check_password_restrictions: "
1620 "password is too young to change!");
1621 return LDB_ERR_CONSTRAINT_VIOLATION;
1624 return LDB_SUCCESS;
1627 static int setup_io(struct ph_context *ac,
1628 const struct ldb_message *orig_msg,
1629 const struct ldb_message *searched_msg,
1630 struct setup_password_fields_io *io)
1632 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
1633 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1634 struct loadparm_context *lp_ctx =
1635 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1636 struct loadparm_context);
1637 int ret;
1639 ZERO_STRUCTP(io);
1641 /* Some operations below require kerberos contexts */
1643 if (smb_krb5_init_context(ac,
1644 ldb_get_event_context(ldb),
1645 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
1646 &io->smb_krb5_context) != 0) {
1647 return LDB_ERR_OPERATIONS_ERROR;
1650 io->ac = ac;
1652 io->u.userAccountControl = samdb_result_uint(searched_msg, "userAccountControl", 0);
1653 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
1654 io->u.sAMAccountName = samdb_result_string(searched_msg, "sAMAccountName", NULL);
1655 io->u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
1656 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
1658 if (io->u.sAMAccountName == NULL) {
1659 ldb_asprintf_errstring(ldb,
1660 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
1661 ldb_dn_get_linearized(searched_msg->dn));
1663 return LDB_ERR_CONSTRAINT_VIOLATION;
1666 /* Only non-trust accounts have restrictions (possibly this test is the
1667 * wrong way around, but we like to be restrictive if possible */
1668 io->u.restrictions = !(io->u.userAccountControl
1669 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
1670 | UF_SERVER_TRUST_ACCOUNT));
1672 if ((io->u.userAccountControl & UF_PASSWD_NOTREQD) != 0) {
1673 /* see [MS-ADTS] 2.2.15 */
1674 io->u.restrictions = 0;
1677 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "userPassword",
1678 &io->n.cleartext_utf8, &io->og.cleartext_utf8);
1679 if (ret != LDB_SUCCESS) {
1680 ldb_asprintf_errstring(ldb,
1681 "setup_io: "
1682 "it's only allowed to set the old password once!");
1683 return ret;
1686 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "clearTextPassword",
1687 &io->n.cleartext_utf16, &io->og.cleartext_utf16);
1688 if (ret != LDB_SUCCESS) {
1689 ldb_asprintf_errstring(ldb,
1690 "setup_io: "
1691 "it's only allowed to set the old password once!");
1692 return ret;
1695 /* this rather strange looking piece of code is there to
1696 handle a ldap client setting a password remotely using the
1697 unicodePwd ldap field. The syntax is that the password is
1698 in UTF-16LE, with a " at either end. Unfortunately the
1699 unicodePwd field is also used to store the nt hashes
1700 internally in Samba, and is used in the nt hash format on
1701 the wire in DRS replication, so we have a single name for
1702 two distinct values. The code below leaves us with a small
1703 chance (less than 1 in 2^32) of a mixup, if someone manages
1704 to create a MD4 hash which starts and ends in 0x22 0x00, as
1705 that would then be treated as a UTF16 password rather than
1706 a nthash */
1708 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "unicodePwd",
1709 &quoted_utf16, &old_quoted_utf16);
1710 if (ret != LDB_SUCCESS) {
1711 ldb_asprintf_errstring(ldb,
1712 "setup_io: "
1713 "it's only allowed to set the old password once!");
1714 return ret;
1717 /* Checks and converts the actual "unicodePwd" attribute */
1718 if (quoted_utf16 &&
1719 quoted_utf16->length >= 4 &&
1720 quoted_utf16->data[0] == '"' &&
1721 quoted_utf16->data[1] == 0 &&
1722 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
1723 quoted_utf16->data[quoted_utf16->length-1] == 0) {
1724 struct ldb_val *quoted_utf16_2;
1726 if (io->n.cleartext_utf16) {
1727 /* refuse the change if someone wants to change with
1728 with both UTF16 possibilities at the same time... */
1729 ldb_asprintf_errstring(ldb,
1730 "setup_io: "
1731 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1732 return LDB_ERR_UNWILLING_TO_PERFORM;
1736 * adapt the quoted UTF16 string to be a real
1737 * cleartext one
1739 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1740 if (quoted_utf16_2 == NULL) {
1741 ldb_oom(ldb);
1742 return LDB_ERR_OPERATIONS_ERROR;
1745 quoted_utf16_2->data = quoted_utf16->data + 2;
1746 quoted_utf16_2->length = quoted_utf16->length-4;
1747 io->n.cleartext_utf16 = quoted_utf16_2;
1748 io->n.nt_hash = NULL;
1750 } else if (quoted_utf16) {
1751 /* We have only the hash available -> so no plaintext here */
1752 if (!ac->hash_values) {
1753 /* refuse the change if someone wants to change
1754 the hash without control specified... */
1755 ldb_asprintf_errstring(ldb,
1756 "setup_io: "
1757 "it's not allowed to set the NT hash password directly'");
1758 /* this looks odd but this is what Windows does:
1759 returns "UNWILLING_TO_PERFORM" on wrong
1760 password sets and "CONSTRAINT_VIOLATION" on
1761 wrong password changes. */
1762 if (old_quoted_utf16 == NULL) {
1763 return LDB_ERR_UNWILLING_TO_PERFORM;
1766 return LDB_ERR_CONSTRAINT_VIOLATION;
1769 io->n.nt_hash = talloc(io->ac, struct samr_Password);
1770 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
1771 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
1774 /* Checks and converts the previous "unicodePwd" attribute */
1775 if (old_quoted_utf16 &&
1776 old_quoted_utf16->length >= 4 &&
1777 old_quoted_utf16->data[0] == '"' &&
1778 old_quoted_utf16->data[1] == 0 &&
1779 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
1780 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
1781 struct ldb_val *old_quoted_utf16_2;
1783 if (io->og.cleartext_utf16) {
1784 /* refuse the change if someone wants to change with
1785 both UTF16 possibilities at the same time... */
1786 ldb_asprintf_errstring(ldb,
1787 "setup_io: "
1788 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1789 return LDB_ERR_UNWILLING_TO_PERFORM;
1793 * adapt the quoted UTF16 string to be a real
1794 * cleartext one
1796 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1797 if (old_quoted_utf16_2 == NULL) {
1798 ldb_oom(ldb);
1799 return LDB_ERR_OPERATIONS_ERROR;
1802 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
1803 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
1805 io->og.cleartext_utf16 = old_quoted_utf16_2;
1806 io->og.nt_hash = NULL;
1807 } else if (old_quoted_utf16) {
1808 /* We have only the hash available -> so no plaintext here */
1809 if (!ac->hash_values) {
1810 /* refuse the change if someone wants to change
1811 the hash without control specified... */
1812 ldb_asprintf_errstring(ldb,
1813 "setup_io: "
1814 "it's not allowed to set the NT hash password directly'");
1815 return LDB_ERR_UNWILLING_TO_PERFORM;
1818 io->og.nt_hash = talloc(io->ac, struct samr_Password);
1819 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
1820 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
1823 /* Handles the "dBCSPwd" attribute (LM hash) */
1824 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
1825 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "dBCSPwd",
1826 &lm_hash, &old_lm_hash);
1827 if (ret != LDB_SUCCESS) {
1828 ldb_asprintf_errstring(ldb,
1829 "setup_io: "
1830 "it's only allowed to set the old password once!");
1831 return ret;
1834 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
1835 /* refuse the change if someone wants to change the hash
1836 without control specified... */
1837 ldb_asprintf_errstring(ldb,
1838 "setup_io: "
1839 "it's not allowed to set the LM hash password directly'");
1840 return LDB_ERR_UNWILLING_TO_PERFORM;
1843 if (lp_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
1844 io->n.lm_hash = talloc(io->ac, struct samr_Password);
1845 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
1846 sizeof(io->n.lm_hash->hash)));
1848 if (lp_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
1849 io->og.lm_hash = talloc(io->ac, struct samr_Password);
1850 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
1851 sizeof(io->og.lm_hash->hash)));
1854 /* refuse the change if someone wants to change the clear-
1855 text and supply his own hashes at the same time... */
1856 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
1857 && (io->n.nt_hash || io->n.lm_hash)) {
1858 ldb_asprintf_errstring(ldb,
1859 "setup_io: "
1860 "it's only allowed to set the password in form of cleartext attributes or as hashes");
1861 return LDB_ERR_UNWILLING_TO_PERFORM;
1864 /* refuse the change if someone wants to change the password
1865 using both plaintext methods (UTF8 and UTF16) at the same time... */
1866 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
1867 ldb_asprintf_errstring(ldb,
1868 "setup_io: "
1869 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
1870 return LDB_ERR_UNWILLING_TO_PERFORM;
1873 /* refuse the change if someone tries to set/change the password by
1874 * the lanman hash alone and we've deactivated that mechanism. This
1875 * would end in an account without any password! */
1876 if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
1877 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
1878 ldb_asprintf_errstring(ldb,
1879 "setup_io: "
1880 "The password change/set operations performed using the LAN Manager hash alone are deactivated!");
1881 return LDB_ERR_UNWILLING_TO_PERFORM;
1884 /* refuse the change if someone wants to compare against a plaintext
1885 or hash at the same time for a "password modify" operation... */
1886 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
1887 && (io->og.nt_hash || io->og.lm_hash)) {
1888 ldb_asprintf_errstring(ldb,
1889 "setup_io: "
1890 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
1891 return LDB_ERR_UNWILLING_TO_PERFORM;
1894 /* refuse the change if someone wants to compare against both
1895 * plaintexts at the same time for a "password modify" operation... */
1896 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
1897 ldb_asprintf_errstring(ldb,
1898 "setup_io: "
1899 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
1900 return LDB_ERR_UNWILLING_TO_PERFORM;
1903 /* refuse the change if someone wants to compare against both
1904 * hashes at the same time for a "password modify" operation... */
1905 if (io->og.nt_hash && io->og.lm_hash) {
1906 ldb_asprintf_errstring(ldb,
1907 "setup_io: "
1908 "it's only allowed to provide the old password in hash format as 'unicodePwd' or as 'dBCSPwd'");
1909 return LDB_ERR_UNWILLING_TO_PERFORM;
1912 /* Decides if we have a password modify or password reset operation */
1913 if (ac->req->operation == LDB_ADD) {
1914 /* On "add" we have only "password reset" */
1915 ac->pwd_reset = true;
1916 } else if (ac->req->operation == LDB_MODIFY) {
1917 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
1918 || io->og.nt_hash || io->og.lm_hash
1919 || ac->change_old_pw_checked) {
1920 /* If we have an old password or the "change old
1921 * password checked" control specified then for sure it
1922 * is a user "password change" */
1923 ac->pwd_reset = false;
1924 } else {
1925 /* Otherwise we have also here a "password reset" */
1926 ac->pwd_reset = true;
1928 } else {
1929 /* this shouldn't happen */
1930 return LDB_ERR_OPERATIONS_ERROR;
1933 return LDB_SUCCESS;
1936 static struct ph_context *ph_init_context(struct ldb_module *module,
1937 struct ldb_request *req)
1939 struct ldb_context *ldb;
1940 struct ph_context *ac;
1942 ldb = ldb_module_get_ctx(module);
1944 ac = talloc_zero(req, struct ph_context);
1945 if (ac == NULL) {
1946 ldb_set_errstring(ldb, "Out of Memory");
1947 return NULL;
1950 ac->module = module;
1951 ac->req = req;
1953 return ac;
1956 static void ph_apply_controls(struct ph_context *ac)
1958 struct ldb_control *ctrl;
1960 ac->change_status = false;
1961 ctrl = ldb_request_get_control(ac->req,
1962 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
1963 if (ctrl != NULL) {
1964 ac->change_status = true;
1966 /* Mark the "change status" control as uncritical (done) */
1967 ctrl->critical = false;
1970 ac->hash_values = false;
1971 ctrl = ldb_request_get_control(ac->req,
1972 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
1973 if (ctrl != NULL) {
1974 ac->hash_values = true;
1976 /* Mark the "hash values" control as uncritical (done) */
1977 ctrl->critical = false;
1980 ac->change_old_pw_checked = false;
1981 ctrl = ldb_request_get_control(ac->req,
1982 DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID);
1983 if (ctrl != NULL) {
1984 ac->change_old_pw_checked = true;
1986 /* Mark the "change old password checked" control as uncritical
1987 * (done) */
1988 ctrl->critical = false;
1992 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
1994 struct ph_context *ac;
1996 ac = talloc_get_type(req->context, struct ph_context);
1998 if (!ares) {
1999 return ldb_module_done(ac->req, NULL, NULL,
2000 LDB_ERR_OPERATIONS_ERROR);
2003 if (ares->type == LDB_REPLY_REFERRAL) {
2004 return ldb_module_send_referral(ac->req, ares->referral);
2007 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2008 /* On success and trivial errors a status control is being
2009 * added (used for example by the "samdb_set_password" call) */
2010 ldb_reply_add_control(ares,
2011 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2012 false,
2013 ac->status);
2016 if (ares->error != LDB_SUCCESS) {
2017 return ldb_module_done(ac->req, ares->controls,
2018 ares->response, ares->error);
2021 if (ares->type != LDB_REPLY_DONE) {
2022 talloc_free(ares);
2023 return ldb_module_done(ac->req, NULL, NULL,
2024 LDB_ERR_OPERATIONS_ERROR);
2027 return ldb_module_done(ac->req, ares->controls,
2028 ares->response, ares->error);
2031 static int password_hash_add_do_add(struct ph_context *ac);
2032 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2033 static int password_hash_mod_search_self(struct ph_context *ac);
2034 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2035 static int password_hash_mod_do_mod(struct ph_context *ac);
2037 static int get_domain_data_callback(struct ldb_request *req,
2038 struct ldb_reply *ares)
2040 struct ldb_context *ldb;
2041 struct ph_context *ac;
2042 struct loadparm_context *lp_ctx;
2043 int ret;
2045 ac = talloc_get_type(req->context, struct ph_context);
2046 ldb = ldb_module_get_ctx(ac->module);
2048 if (!ares) {
2049 ret = LDB_ERR_OPERATIONS_ERROR;
2050 goto done;
2052 if (ares->error != LDB_SUCCESS) {
2053 return ldb_module_done(ac->req, ares->controls,
2054 ares->response, ares->error);
2057 switch (ares->type) {
2058 case LDB_REPLY_ENTRY:
2059 if (ac->status != NULL) {
2060 talloc_free(ares);
2062 ldb_set_errstring(ldb, "Too many results");
2063 ret = LDB_ERR_OPERATIONS_ERROR;
2064 goto done;
2067 /* Setup the "status" structure (used as control later) */
2068 ac->status = talloc_zero(ac->req,
2069 struct dsdb_control_password_change_status);
2070 if (ac->status == NULL) {
2071 talloc_free(ares);
2073 ldb_oom(ldb);
2074 ret = LDB_ERR_OPERATIONS_ERROR;
2075 goto done;
2078 /* Setup the "domain data" structure */
2079 ac->status->domain_data.pwdProperties = samdb_result_uint(ares->message, "pwdProperties", -1);
2080 ac->status->domain_data.pwdHistoryLength = samdb_result_uint(ares->message, "pwdHistoryLength", -1);
2081 ac->status->domain_data.maxPwdAge = samdb_result_int64(ares->message, "maxPwdAge", -1);
2082 ac->status->domain_data.minPwdAge = samdb_result_int64(ares->message, "minPwdAge", -1);
2083 ac->status->domain_data.minPwdLength = samdb_result_uint(ares->message, "minPwdLength", -1);
2084 ac->status->domain_data.store_cleartext =
2085 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2087 talloc_free(ares);
2089 /* For a domain DN, this puts things in dotted notation */
2090 /* For builtin domains, this will give details for the host,
2091 * but that doesn't really matter, as it's just used for salt
2092 * and kerberos principals, which don't exist here */
2094 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2095 struct loadparm_context);
2097 ac->status->domain_data.dns_domain = lp_dnsdomain(lp_ctx);
2098 ac->status->domain_data.realm = lp_realm(lp_ctx);
2099 ac->status->domain_data.netbios_domain = lp_sam_name(lp_ctx);
2101 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2103 ret = LDB_SUCCESS;
2104 break;
2106 case LDB_REPLY_REFERRAL:
2107 /* ignore */
2108 talloc_free(ares);
2109 ret = LDB_SUCCESS;
2110 break;
2112 case LDB_REPLY_DONE:
2113 talloc_free(ares);
2114 /* call the next step */
2115 switch (ac->req->operation) {
2116 case LDB_ADD:
2117 ret = password_hash_add_do_add(ac);
2118 break;
2120 case LDB_MODIFY:
2121 ret = password_hash_mod_do_mod(ac);
2122 break;
2124 default:
2125 ret = LDB_ERR_OPERATIONS_ERROR;
2126 break;
2128 break;
2131 done:
2132 if (ret != LDB_SUCCESS) {
2133 struct ldb_reply *new_ares;
2135 new_ares = talloc_zero(ac->req, struct ldb_reply);
2136 if (new_ares == NULL) {
2137 ldb_oom(ldb);
2138 return ldb_module_done(ac->req, NULL, NULL,
2139 LDB_ERR_OPERATIONS_ERROR);
2142 new_ares->error = ret;
2143 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2144 /* On success and trivial errors a status control is being
2145 * added (used for example by the "samdb_set_password" call) */
2146 ldb_reply_add_control(new_ares,
2147 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2148 false,
2149 ac->status);
2152 return ldb_module_done(ac->req, new_ares->controls,
2153 new_ares->response, new_ares->error);
2156 return LDB_SUCCESS;
2159 static int build_domain_data_request(struct ph_context *ac)
2161 /* attrs[] is returned from this function in
2162 ac->dom_req->op.search.attrs, so it must be static, as
2163 otherwise the compiler can put it on the stack */
2164 struct ldb_context *ldb;
2165 static const char * const attrs[] = { "pwdProperties",
2166 "pwdHistoryLength",
2167 "maxPwdAge",
2168 "minPwdAge",
2169 "minPwdLength",
2170 NULL };
2172 ldb = ldb_module_get_ctx(ac->module);
2174 return ldb_build_search_req(&ac->dom_req, ldb, ac,
2175 ldb_get_default_basedn(ldb),
2176 LDB_SCOPE_BASE,
2177 NULL, attrs,
2178 NULL,
2179 ac, get_domain_data_callback,
2180 ac->req);
2183 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2185 struct ldb_context *ldb;
2186 struct ph_context *ac;
2187 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2188 *ntAttr, *lmAttr;
2189 int ret;
2190 struct ldb_control *bypass = NULL;
2192 ldb = ldb_module_get_ctx(module);
2194 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2196 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2197 return ldb_next_request(module, req);
2200 /* If the caller is manipulating the local passwords directly, let them pass */
2201 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2202 req->op.add.message->dn) == 0) {
2203 return ldb_next_request(module, req);
2206 bypass = ldb_request_get_control(req,
2207 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2208 if (bypass != NULL) {
2209 /* Mark the "bypass" control as uncritical (done) */
2210 bypass->critical = false;
2211 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
2212 return ldb_next_request(module, req);
2215 /* nobody must touch password histories and 'supplementalCredentials' */
2216 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2217 return LDB_ERR_UNWILLING_TO_PERFORM;
2219 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2220 return LDB_ERR_UNWILLING_TO_PERFORM;
2222 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2223 return LDB_ERR_UNWILLING_TO_PERFORM;
2226 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2227 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2229 userPasswordAttr = ldb_msg_find_element(req->op.add.message, "userPassword");
2230 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2231 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2232 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2234 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2235 return ldb_next_request(module, req);
2238 /* Make sure we are performing the password set action on a (for us)
2239 * valid object. Those are instances of either "user" and/or
2240 * "inetOrgPerson". Otherwise continue with the submodules. */
2241 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2242 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2244 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2245 ldb_set_errstring(ldb,
2246 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2247 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2250 return ldb_next_request(module, req);
2253 ac = ph_init_context(module, req);
2254 if (ac == NULL) {
2255 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2256 return LDB_ERR_OPERATIONS_ERROR;
2258 ph_apply_controls(ac);
2260 /* get user domain data */
2261 ret = build_domain_data_request(ac);
2262 if (ret != LDB_SUCCESS) {
2263 return ret;
2266 return ldb_next_request(module, ac->dom_req);
2269 static int password_hash_add_do_add(struct ph_context *ac)
2271 struct ldb_context *ldb;
2272 struct ldb_request *down_req;
2273 struct ldb_message *msg;
2274 struct setup_password_fields_io io;
2275 int ret;
2277 /* Prepare the internal data structure containing the passwords */
2278 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2279 if (ret != LDB_SUCCESS) {
2280 return ret;
2283 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2284 if (msg == NULL) {
2285 return LDB_ERR_OPERATIONS_ERROR;
2288 /* remove attributes that we just read into 'io' */
2289 ldb_msg_remove_attr(msg, "userPassword");
2290 ldb_msg_remove_attr(msg, "clearTextPassword");
2291 ldb_msg_remove_attr(msg, "unicodePwd");
2292 ldb_msg_remove_attr(msg, "dBCSPwd");
2293 ldb_msg_remove_attr(msg, "pwdLastSet");
2295 ldb = ldb_module_get_ctx(ac->module);
2297 ret = setup_password_fields(&io);
2298 if (ret != LDB_SUCCESS) {
2299 return ret;
2302 ret = check_password_restrictions(&io);
2303 if (ret != LDB_SUCCESS) {
2304 return ret;
2307 if (io.g.nt_hash) {
2308 ret = samdb_msg_add_hash(ldb, ac, msg,
2309 "unicodePwd", io.g.nt_hash);
2310 if (ret != LDB_SUCCESS) {
2311 return ret;
2314 if (io.g.lm_hash) {
2315 ret = samdb_msg_add_hash(ldb, ac, msg,
2316 "dBCSPwd", io.g.lm_hash);
2317 if (ret != LDB_SUCCESS) {
2318 return ret;
2321 if (io.g.nt_history_len > 0) {
2322 ret = samdb_msg_add_hashes(ac, msg,
2323 "ntPwdHistory",
2324 io.g.nt_history,
2325 io.g.nt_history_len);
2326 if (ret != LDB_SUCCESS) {
2327 return ret;
2330 if (io.g.lm_history_len > 0) {
2331 ret = samdb_msg_add_hashes(ac, msg,
2332 "lmPwdHistory",
2333 io.g.lm_history,
2334 io.g.lm_history_len);
2335 if (ret != LDB_SUCCESS) {
2336 return ret;
2339 if (io.g.supplemental.length > 0) {
2340 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2341 &io.g.supplemental, NULL);
2342 if (ret != LDB_SUCCESS) {
2343 return ret;
2346 ret = samdb_msg_add_uint64(ldb, ac, msg,
2347 "pwdLastSet",
2348 io.g.last_set);
2349 if (ret != LDB_SUCCESS) {
2350 return ret;
2353 ret = ldb_build_add_req(&down_req, ldb, ac,
2354 msg,
2355 ac->req->controls,
2356 ac, ph_op_callback,
2357 ac->req);
2358 if (ret != LDB_SUCCESS) {
2359 return ret;
2362 return ldb_next_request(ac->module, down_req);
2365 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
2367 struct ldb_context *ldb;
2368 struct ph_context *ac;
2369 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
2370 "unicodePwd", "dBCSPwd", NULL }, **l;
2371 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
2372 struct ldb_message_element *passwordAttr;
2373 struct ldb_message *msg;
2374 struct ldb_request *down_req;
2375 int ret;
2376 struct ldb_control *bypass = NULL;
2378 ldb = ldb_module_get_ctx(module);
2380 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
2382 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
2383 return ldb_next_request(module, req);
2386 /* If the caller is manipulating the local passwords directly, let them pass */
2387 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2388 req->op.mod.message->dn) == 0) {
2389 return ldb_next_request(module, req);
2392 bypass = ldb_request_get_control(req,
2393 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2394 if (bypass != NULL) {
2395 /* Mark the "bypass" control as uncritical (done) */
2396 bypass->critical = false;
2397 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
2398 return ldb_next_request(module, req);
2401 /* nobody must touch password histories and 'supplementalCredentials' */
2402 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
2403 return LDB_ERR_UNWILLING_TO_PERFORM;
2405 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
2406 return LDB_ERR_UNWILLING_TO_PERFORM;
2408 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
2409 return LDB_ERR_UNWILLING_TO_PERFORM;
2412 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2413 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
2414 * For password changes/set there should be a 'delete' or a 'modify'
2415 * on these attributes. */
2416 attr_cnt = 0;
2417 for (l = passwordAttrs; *l != NULL; l++) {
2418 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
2419 ++attr_cnt;
2422 if (attr_cnt == 0) {
2423 return ldb_next_request(module, req);
2426 ac = ph_init_context(module, req);
2427 if (!ac) {
2428 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2429 return LDB_ERR_OPERATIONS_ERROR;
2431 ph_apply_controls(ac);
2433 /* use a new message structure so that we can modify it */
2434 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2435 if (msg == NULL) {
2436 ldb_oom(ldb);
2437 return LDB_ERR_OPERATIONS_ERROR;
2440 /* - check for single-valued password attributes
2441 * (if not return "CONSTRAINT_VIOLATION")
2442 * - check that for a password change operation one add and one delete
2443 * operation exists
2444 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
2445 * - check that a password change and a password set operation cannot
2446 * be mixed
2447 * (if not return "UNWILLING_TO_PERFORM")
2448 * - remove all password attributes modifications from the first change
2449 * operation (anything without the passwords) - we will make the real
2450 * modification later */
2451 del_attr_cnt = 0;
2452 add_attr_cnt = 0;
2453 rep_attr_cnt = 0;
2454 for (l = passwordAttrs; *l != NULL; l++) {
2455 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
2456 if (passwordAttr->flags == LDB_FLAG_MOD_DELETE) {
2457 ++del_attr_cnt;
2459 if (passwordAttr->flags == LDB_FLAG_MOD_ADD) {
2460 ++add_attr_cnt;
2462 if (passwordAttr->flags == LDB_FLAG_MOD_REPLACE) {
2463 ++rep_attr_cnt;
2465 if ((passwordAttr->num_values != 1) &&
2466 (passwordAttr->flags != LDB_FLAG_MOD_REPLACE)) {
2467 talloc_free(ac);
2468 ldb_asprintf_errstring(ldb,
2469 "'%s' attributes must have exactly one value!",
2470 *l);
2471 return LDB_ERR_CONSTRAINT_VIOLATION;
2473 ldb_msg_remove_element(msg, passwordAttr);
2476 if ((del_attr_cnt > 0) && (add_attr_cnt == 0)) {
2477 talloc_free(ac);
2478 ldb_set_errstring(ldb,
2479 "Only the delete action for a password change specified!");
2480 return LDB_ERR_CONSTRAINT_VIOLATION;
2482 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
2483 talloc_free(ac);
2484 ldb_set_errstring(ldb,
2485 "Only the add action for a password change specified!");
2486 return LDB_ERR_UNWILLING_TO_PERFORM;
2488 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
2489 talloc_free(ac);
2490 ldb_set_errstring(ldb,
2491 "Only one delete and one add action for a password change allowed!");
2492 return LDB_ERR_UNWILLING_TO_PERFORM;
2494 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
2495 talloc_free(ac);
2496 ldb_set_errstring(ldb,
2497 "Either a password change or a password set operation is allowed!");
2498 return LDB_ERR_UNWILLING_TO_PERFORM;
2501 /* if there was nothing else to be modified skip to next step */
2502 if (msg->num_elements == 0) {
2503 return password_hash_mod_search_self(ac);
2506 ret = ldb_build_mod_req(&down_req, ldb, ac,
2507 msg,
2508 req->controls,
2509 ac, ph_modify_callback,
2510 req);
2511 if (ret != LDB_SUCCESS) {
2512 return ret;
2515 return ldb_next_request(module, down_req);
2518 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
2520 struct ph_context *ac;
2522 ac = talloc_get_type(req->context, struct ph_context);
2524 if (!ares) {
2525 return ldb_module_done(ac->req, NULL, NULL,
2526 LDB_ERR_OPERATIONS_ERROR);
2529 if (ares->type == LDB_REPLY_REFERRAL) {
2530 return ldb_module_send_referral(ac->req, ares->referral);
2533 if (ares->error != LDB_SUCCESS) {
2534 return ldb_module_done(ac->req, ares->controls,
2535 ares->response, ares->error);
2538 if (ares->type != LDB_REPLY_DONE) {
2539 talloc_free(ares);
2540 return ldb_module_done(ac->req, NULL, NULL,
2541 LDB_ERR_OPERATIONS_ERROR);
2544 talloc_free(ares);
2546 return password_hash_mod_search_self(ac);
2549 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
2551 struct ldb_context *ldb;
2552 struct ph_context *ac;
2553 int ret;
2555 ac = talloc_get_type(req->context, struct ph_context);
2556 ldb = ldb_module_get_ctx(ac->module);
2558 if (!ares) {
2559 ret = LDB_ERR_OPERATIONS_ERROR;
2560 goto done;
2562 if (ares->error != LDB_SUCCESS) {
2563 return ldb_module_done(ac->req, ares->controls,
2564 ares->response, ares->error);
2567 /* we are interested only in the single reply (base search) */
2568 switch (ares->type) {
2569 case LDB_REPLY_ENTRY:
2570 /* Make sure we are performing the password change action on a
2571 * (for us) valid object. Those are instances of either "user"
2572 * and/or "inetOrgPerson". Otherwise continue with the
2573 * submodules. */
2574 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
2575 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
2576 talloc_free(ares);
2578 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
2579 ldb_set_errstring(ldb,
2580 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2581 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
2582 goto done;
2585 ret = ldb_next_request(ac->module, ac->req);
2586 goto done;
2589 if (ac->search_res != NULL) {
2590 talloc_free(ares);
2592 ldb_set_errstring(ldb, "Too many results");
2593 ret = LDB_ERR_OPERATIONS_ERROR;
2594 goto done;
2597 ac->search_res = talloc_steal(ac, ares);
2598 ret = LDB_SUCCESS;
2599 break;
2601 case LDB_REPLY_REFERRAL:
2602 /* ignore anything else for now */
2603 talloc_free(ares);
2604 ret = LDB_SUCCESS;
2605 break;
2607 case LDB_REPLY_DONE:
2608 talloc_free(ares);
2610 /* get user domain data */
2611 ret = build_domain_data_request(ac);
2612 if (ret != LDB_SUCCESS) {
2613 return ldb_module_done(ac->req, NULL, NULL, ret);
2616 ret = ldb_next_request(ac->module, ac->dom_req);
2617 break;
2620 done:
2621 if (ret != LDB_SUCCESS) {
2622 return ldb_module_done(ac->req, NULL, NULL, ret);
2625 return LDB_SUCCESS;
2628 static int password_hash_mod_search_self(struct ph_context *ac)
2630 struct ldb_context *ldb;
2631 static const char * const attrs[] = { "objectClass",
2632 "userAccountControl",
2633 "pwdLastSet",
2634 "sAMAccountName",
2635 "objectSid",
2636 "userPrincipalName",
2637 "supplementalCredentials",
2638 "lmPwdHistory",
2639 "ntPwdHistory",
2640 "dBCSPwd",
2641 "unicodePwd",
2642 NULL };
2643 struct ldb_request *search_req;
2644 int ret;
2646 ldb = ldb_module_get_ctx(ac->module);
2648 ret = ldb_build_search_req(&search_req, ldb, ac,
2649 ac->req->op.mod.message->dn,
2650 LDB_SCOPE_BASE,
2651 "(objectclass=*)",
2652 attrs,
2653 NULL,
2654 ac, ph_mod_search_callback,
2655 ac->req);
2657 if (ret != LDB_SUCCESS) {
2658 return ret;
2661 return ldb_next_request(ac->module, search_req);
2664 static int password_hash_mod_do_mod(struct ph_context *ac)
2666 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2667 struct loadparm_context *lp_ctx =
2668 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2669 struct loadparm_context);
2670 struct ldb_request *mod_req;
2671 struct ldb_message *msg;
2672 const struct ldb_message *orig_msg, *searched_msg;
2673 struct setup_password_fields_io io;
2674 int ret;
2675 NTSTATUS status;
2677 /* use a new message structure so that we can modify it */
2678 msg = ldb_msg_new(ac);
2679 if (msg == NULL) {
2680 return LDB_ERR_OPERATIONS_ERROR;
2683 /* modify dn */
2684 msg->dn = ac->req->op.mod.message->dn;
2686 orig_msg = ac->req->op.mod.message;
2687 searched_msg = ac->search_res->message;
2689 /* Prepare the internal data structure containing the passwords */
2690 ret = setup_io(ac, orig_msg, searched_msg, &io);
2691 if (ret != LDB_SUCCESS) {
2692 return ret;
2695 /* Get the old password from the database */
2696 status = samdb_result_passwords(io.ac,
2697 lp_ctx,
2698 discard_const_p(struct ldb_message, searched_msg),
2699 &io.o.lm_hash, &io.o.nt_hash);
2700 if (!NT_STATUS_IS_OK(status)) {
2701 return LDB_ERR_OPERATIONS_ERROR;
2704 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
2705 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
2706 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
2708 ret = setup_password_fields(&io);
2709 if (ret != LDB_SUCCESS) {
2710 return ret;
2713 ret = check_password_restrictions(&io);
2714 if (ret != LDB_SUCCESS) {
2715 return ret;
2718 /* make sure we replace all the old attributes */
2719 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
2720 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
2721 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2722 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2723 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
2724 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
2726 if (io.g.nt_hash) {
2727 ret = samdb_msg_add_hash(ldb, ac, msg,
2728 "unicodePwd", io.g.nt_hash);
2729 if (ret != LDB_SUCCESS) {
2730 return ret;
2733 if (io.g.lm_hash) {
2734 ret = samdb_msg_add_hash(ldb, ac, msg,
2735 "dBCSPwd", io.g.lm_hash);
2736 if (ret != LDB_SUCCESS) {
2737 return ret;
2740 if (io.g.nt_history_len > 0) {
2741 ret = samdb_msg_add_hashes(ac, msg,
2742 "ntPwdHistory",
2743 io.g.nt_history,
2744 io.g.nt_history_len);
2745 if (ret != LDB_SUCCESS) {
2746 return ret;
2749 if (io.g.lm_history_len > 0) {
2750 ret = samdb_msg_add_hashes(ac, msg,
2751 "lmPwdHistory",
2752 io.g.lm_history,
2753 io.g.lm_history_len);
2754 if (ret != LDB_SUCCESS) {
2755 return ret;
2758 if (io.g.supplemental.length > 0) {
2759 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2760 &io.g.supplemental, NULL);
2761 if (ret != LDB_SUCCESS) {
2762 return ret;
2765 ret = samdb_msg_add_uint64(ldb, ac, msg,
2766 "pwdLastSet",
2767 io.g.last_set);
2768 if (ret != LDB_SUCCESS) {
2769 return ret;
2772 ret = ldb_build_mod_req(&mod_req, ldb, ac,
2773 msg,
2774 ac->req->controls,
2775 ac, ph_op_callback,
2776 ac->req);
2777 if (ret != LDB_SUCCESS) {
2778 return ret;
2781 return ldb_next_request(ac->module, mod_req);
2784 _PUBLIC_ const struct ldb_module_ops ldb_password_hash_module_ops = {
2785 .name = "password_hash",
2786 .add = password_hash_add,
2787 .modify = password_hash_modify