s4:password_hash LDB module - we might not have a cleartext password at all
[Samba/ekacnet.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blob0334c6d95e220a89954c4a0d5d31cef21ee43c7c
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 NT 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, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &_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 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
640 &_old_pkb,
641 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
642 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
643 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
644 ldb_asprintf_errstring(ldb,
645 "setup_primary_kerberos_newer: "
646 "failed to pull old package_PrimaryKerberosBlob: %s",
647 nt_errstr(status));
648 return LDB_ERR_OPERATIONS_ERROR;
651 if (_old_pkb.version != 4) {
652 ldb_asprintf_errstring(ldb,
653 "setup_primary_kerberos_newer: "
654 "package_PrimaryKerberosBlob version[%u] expected[4]",
655 _old_pkb.version);
656 return LDB_ERR_OPERATIONS_ERROR;
659 old_pkb4 = &_old_pkb.ctr.ctr4;
662 /* if we didn't found the old keys we're done */
663 if (!old_pkb4) {
664 return LDB_SUCCESS;
667 /* fill in the old keys */
668 pkb4->num_old_keys = old_pkb4->num_keys;
669 pkb4->old_keys = old_pkb4->keys;
670 pkb4->num_older_keys = old_pkb4->num_old_keys;
671 pkb4->older_keys = old_pkb4->old_keys;
673 return LDB_SUCCESS;
676 static int setup_primary_wdigest(struct setup_password_fields_io *io,
677 const struct supplementalCredentialsBlob *old_scb,
678 struct package_PrimaryWDigestBlob *pdb)
680 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
681 DATA_BLOB sAMAccountName;
682 DATA_BLOB sAMAccountName_l;
683 DATA_BLOB sAMAccountName_u;
684 const char *user_principal_name = io->u.user_principal_name;
685 DATA_BLOB userPrincipalName;
686 DATA_BLOB userPrincipalName_l;
687 DATA_BLOB userPrincipalName_u;
688 DATA_BLOB netbios_domain;
689 DATA_BLOB netbios_domain_l;
690 DATA_BLOB netbios_domain_u;
691 DATA_BLOB dns_domain;
692 DATA_BLOB dns_domain_l;
693 DATA_BLOB dns_domain_u;
694 DATA_BLOB digest;
695 DATA_BLOB delim;
696 DATA_BLOB backslash;
697 uint8_t i;
698 struct {
699 DATA_BLOB *user;
700 DATA_BLOB *realm;
701 DATA_BLOB *nt4dom;
702 } wdigest[] = {
704 * See
705 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
706 * for what precalculated hashes are supposed to be stored...
708 * I can't reproduce all values which should contain "Digest" as realm,
709 * am I doing something wrong or is w2k3 just broken...?
711 * W2K3 fills in following for a user:
713 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
714 * sAMAccountName: NewUser2Sam
715 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
717 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
718 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
719 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
720 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
721 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
722 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
723 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
724 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
725 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
726 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
727 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
728 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
729 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
730 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
731 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
732 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
733 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
734 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
735 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
736 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
737 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
738 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
739 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
740 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
741 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
742 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
743 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
744 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
745 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
747 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
748 * sAMAccountName: NewUser2Sam
750 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
751 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
752 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
753 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
754 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
755 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
756 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
757 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
758 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
759 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
760 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
761 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
762 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
763 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
764 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
765 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
766 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
767 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
768 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
769 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
770 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
771 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
772 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
773 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
774 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
775 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
776 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
777 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
778 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
782 * sAMAccountName, netbios_domain
785 .user = &sAMAccountName,
786 .realm = &netbios_domain,
789 .user = &sAMAccountName_l,
790 .realm = &netbios_domain_l,
793 .user = &sAMAccountName_u,
794 .realm = &netbios_domain_u,
797 .user = &sAMAccountName,
798 .realm = &netbios_domain_u,
801 .user = &sAMAccountName,
802 .realm = &netbios_domain_l,
805 .user = &sAMAccountName_u,
806 .realm = &netbios_domain_l,
809 .user = &sAMAccountName_l,
810 .realm = &netbios_domain_u,
813 * sAMAccountName, dns_domain
816 .user = &sAMAccountName,
817 .realm = &dns_domain,
820 .user = &sAMAccountName_l,
821 .realm = &dns_domain_l,
824 .user = &sAMAccountName_u,
825 .realm = &dns_domain_u,
828 .user = &sAMAccountName,
829 .realm = &dns_domain_u,
832 .user = &sAMAccountName,
833 .realm = &dns_domain_l,
836 .user = &sAMAccountName_u,
837 .realm = &dns_domain_l,
840 .user = &sAMAccountName_l,
841 .realm = &dns_domain_u,
844 * userPrincipalName, no realm
847 .user = &userPrincipalName,
851 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
852 * the fallback to the sAMAccountName based userPrincipalName is correct
854 .user = &userPrincipalName_l,
857 .user = &userPrincipalName_u,
860 * nt4dom\sAMAccountName, no realm
863 .user = &sAMAccountName,
864 .nt4dom = &netbios_domain
867 .user = &sAMAccountName_l,
868 .nt4dom = &netbios_domain_l
871 .user = &sAMAccountName_u,
872 .nt4dom = &netbios_domain_u
876 * the following ones are guessed depending on the technet2 article
877 * but not reproducable on a w2k3 server
879 /* sAMAccountName with "Digest" realm */
881 .user = &sAMAccountName,
882 .realm = &digest
885 .user = &sAMAccountName_l,
886 .realm = &digest
889 .user = &sAMAccountName_u,
890 .realm = &digest
892 /* userPrincipalName with "Digest" realm */
894 .user = &userPrincipalName,
895 .realm = &digest
898 .user = &userPrincipalName_l,
899 .realm = &digest
902 .user = &userPrincipalName_u,
903 .realm = &digest
905 /* nt4dom\\sAMAccountName with "Digest" realm */
907 .user = &sAMAccountName,
908 .nt4dom = &netbios_domain,
909 .realm = &digest
912 .user = &sAMAccountName_l,
913 .nt4dom = &netbios_domain_l,
914 .realm = &digest
917 .user = &sAMAccountName_u,
918 .nt4dom = &netbios_domain_u,
919 .realm = &digest
923 /* prepare DATA_BLOB's used in the combinations array */
924 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
925 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
926 if (!sAMAccountName_l.data) {
927 ldb_oom(ldb);
928 return LDB_ERR_OPERATIONS_ERROR;
930 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
931 if (!sAMAccountName_u.data) {
932 ldb_oom(ldb);
933 return LDB_ERR_OPERATIONS_ERROR;
936 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
937 if (!user_principal_name) {
938 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
939 io->u.sAMAccountName,
940 io->ac->status->domain_data.dns_domain);
941 if (!user_principal_name) {
942 ldb_oom(ldb);
943 return LDB_ERR_OPERATIONS_ERROR;
946 userPrincipalName = data_blob_string_const(user_principal_name);
947 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
948 if (!userPrincipalName_l.data) {
949 ldb_oom(ldb);
950 return LDB_ERR_OPERATIONS_ERROR;
952 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
953 if (!userPrincipalName_u.data) {
954 ldb_oom(ldb);
955 return LDB_ERR_OPERATIONS_ERROR;
958 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
959 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
960 io->ac->status->domain_data.netbios_domain));
961 if (!netbios_domain_l.data) {
962 ldb_oom(ldb);
963 return LDB_ERR_OPERATIONS_ERROR;
965 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
966 io->ac->status->domain_data.netbios_domain));
967 if (!netbios_domain_u.data) {
968 ldb_oom(ldb);
969 return LDB_ERR_OPERATIONS_ERROR;
972 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
973 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
974 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
976 digest = data_blob_string_const("Digest");
978 delim = data_blob_string_const(":");
979 backslash = data_blob_string_const("\\");
981 pdb->num_hashes = ARRAY_SIZE(wdigest);
982 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
983 pdb->num_hashes);
984 if (!pdb->hashes) {
985 ldb_oom(ldb);
986 return LDB_ERR_OPERATIONS_ERROR;
989 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
990 struct MD5Context md5;
991 MD5Init(&md5);
992 if (wdigest[i].nt4dom) {
993 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
994 MD5Update(&md5, backslash.data, backslash.length);
996 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
997 MD5Update(&md5, delim.data, delim.length);
998 if (wdigest[i].realm) {
999 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
1001 MD5Update(&md5, delim.data, delim.length);
1002 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
1003 MD5Final(pdb->hashes[i].hash, &md5);
1006 return LDB_SUCCESS;
1009 static int setup_supplemental_field(struct setup_password_fields_io *io)
1011 struct ldb_context *ldb;
1012 struct supplementalCredentialsBlob scb;
1013 struct supplementalCredentialsBlob _old_scb;
1014 struct supplementalCredentialsBlob *old_scb = NULL;
1015 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1016 uint32_t num_names = 0;
1017 const char *names[1+4];
1018 uint32_t num_packages = 0;
1019 struct supplementalCredentialsPackage packages[1+4];
1020 /* Packages */
1021 struct supplementalCredentialsPackage *pp = NULL;
1022 struct package_PackagesBlob pb;
1023 DATA_BLOB pb_blob;
1024 char *pb_hexstr;
1025 /* Primary:Kerberos-Newer-Keys */
1026 const char **nkn = NULL;
1027 struct supplementalCredentialsPackage *pkn = NULL;
1028 struct package_PrimaryKerberosBlob pknb;
1029 DATA_BLOB pknb_blob;
1030 char *pknb_hexstr;
1031 /* Primary:Kerberos */
1032 const char **nk = NULL;
1033 struct supplementalCredentialsPackage *pk = NULL;
1034 struct package_PrimaryKerberosBlob pkb;
1035 DATA_BLOB pkb_blob;
1036 char *pkb_hexstr;
1037 /* Primary:WDigest */
1038 const char **nd = NULL;
1039 struct supplementalCredentialsPackage *pd = NULL;
1040 struct package_PrimaryWDigestBlob pdb;
1041 DATA_BLOB pdb_blob;
1042 char *pdb_hexstr;
1043 /* Primary:CLEARTEXT */
1044 const char **nc = NULL;
1045 struct supplementalCredentialsPackage *pc = NULL;
1046 struct package_PrimaryCLEARTEXTBlob pcb;
1047 DATA_BLOB pcb_blob;
1048 char *pcb_hexstr;
1049 int ret;
1050 enum ndr_err_code ndr_err;
1051 uint8_t zero16[16];
1052 bool do_newer_keys = false;
1053 bool do_cleartext = false;
1055 ZERO_STRUCT(zero16);
1056 ZERO_STRUCT(names);
1058 ldb = ldb_module_get_ctx(io->ac->module);
1060 if (!io->n.cleartext_utf8) {
1062 * when we don't have a cleartext password
1063 * we can't setup a supplementalCredential value
1065 return LDB_SUCCESS;
1068 /* if there's an old supplementaCredentials blob then parse it */
1069 if (io->o.supplemental) {
1070 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1071 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1072 &_old_scb,
1073 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1074 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1075 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1076 ldb_asprintf_errstring(ldb,
1077 "setup_supplemental_field: "
1078 "failed to pull old supplementalCredentialsBlob: %s",
1079 nt_errstr(status));
1080 return LDB_ERR_OPERATIONS_ERROR;
1083 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1084 old_scb = &_old_scb;
1085 } else {
1086 ldb_debug(ldb, LDB_DEBUG_ERROR,
1087 "setup_supplemental_field: "
1088 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1089 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1092 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1093 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1095 if (io->ac->status->domain_data.store_cleartext &&
1096 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1097 do_cleartext = true;
1101 * The ordering is this
1103 * Primary:Kerberos-Newer-Keys (optional)
1104 * Primary:Kerberos
1105 * Primary:WDigest
1106 * Primary:CLEARTEXT (optional)
1108 * And the 'Packages' package is insert before the last
1109 * other package.
1111 if (do_newer_keys) {
1112 /* Primary:Kerberos-Newer-Keys */
1113 nkn = &names[num_names++];
1114 pkn = &packages[num_packages++];
1117 /* Primary:Kerberos */
1118 nk = &names[num_names++];
1119 pk = &packages[num_packages++];
1121 if (!do_cleartext) {
1122 /* Packages */
1123 pp = &packages[num_packages++];
1126 /* Primary:WDigest */
1127 nd = &names[num_names++];
1128 pd = &packages[num_packages++];
1130 if (do_cleartext) {
1131 /* Packages */
1132 pp = &packages[num_packages++];
1134 /* Primary:CLEARTEXT */
1135 nc = &names[num_names++];
1136 pc = &packages[num_packages++];
1139 if (pkn) {
1141 * setup 'Primary:Kerberos-Newer-Keys' element
1143 *nkn = "Kerberos-Newer-Keys";
1145 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1146 if (ret != LDB_SUCCESS) {
1147 return ret;
1150 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1151 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1152 &pknb,
1153 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1154 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1155 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1156 ldb_asprintf_errstring(ldb,
1157 "setup_supplemental_field: "
1158 "failed to push package_PrimaryKerberosNeverBlob: %s",
1159 nt_errstr(status));
1160 return LDB_ERR_OPERATIONS_ERROR;
1162 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1163 if (!pknb_hexstr) {
1164 ldb_oom(ldb);
1165 return LDB_ERR_OPERATIONS_ERROR;
1167 pkn->name = "Primary:Kerberos-Newer-Keys";
1168 pkn->reserved = 1;
1169 pkn->data = pknb_hexstr;
1173 * setup 'Primary:Kerberos' element
1175 *nk = "Kerberos";
1177 ret = setup_primary_kerberos(io, old_scb, &pkb);
1178 if (ret != LDB_SUCCESS) {
1179 return ret;
1182 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1183 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1184 &pkb,
1185 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1186 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1187 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1188 ldb_asprintf_errstring(ldb,
1189 "setup_supplemental_field: "
1190 "failed to push package_PrimaryKerberosBlob: %s",
1191 nt_errstr(status));
1192 return LDB_ERR_OPERATIONS_ERROR;
1194 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1195 if (!pkb_hexstr) {
1196 ldb_oom(ldb);
1197 return LDB_ERR_OPERATIONS_ERROR;
1199 pk->name = "Primary:Kerberos";
1200 pk->reserved = 1;
1201 pk->data = pkb_hexstr;
1204 * setup 'Primary:WDigest' element
1206 *nd = "WDigest";
1208 ret = setup_primary_wdigest(io, old_scb, &pdb);
1209 if (ret != LDB_SUCCESS) {
1210 return ret;
1213 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1214 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1215 &pdb,
1216 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1217 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1218 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1219 ldb_asprintf_errstring(ldb,
1220 "setup_supplemental_field: "
1221 "failed to push package_PrimaryWDigestBlob: %s",
1222 nt_errstr(status));
1223 return LDB_ERR_OPERATIONS_ERROR;
1225 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1226 if (!pdb_hexstr) {
1227 ldb_oom(ldb);
1228 return LDB_ERR_OPERATIONS_ERROR;
1230 pd->name = "Primary:WDigest";
1231 pd->reserved = 1;
1232 pd->data = pdb_hexstr;
1235 * setup 'Primary:CLEARTEXT' element
1237 if (pc) {
1238 *nc = "CLEARTEXT";
1240 pcb.cleartext = *io->n.cleartext_utf16;
1242 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1243 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1244 &pcb,
1245 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1246 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1247 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1248 ldb_asprintf_errstring(ldb,
1249 "setup_supplemental_field: "
1250 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1251 nt_errstr(status));
1252 return LDB_ERR_OPERATIONS_ERROR;
1254 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1255 if (!pcb_hexstr) {
1256 ldb_oom(ldb);
1257 return LDB_ERR_OPERATIONS_ERROR;
1259 pc->name = "Primary:CLEARTEXT";
1260 pc->reserved = 1;
1261 pc->data = pcb_hexstr;
1265 * setup 'Packages' element
1267 pb.names = names;
1268 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1269 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1270 &pb,
1271 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1272 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1273 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1274 ldb_asprintf_errstring(ldb,
1275 "setup_supplemental_field: "
1276 "failed to push package_PackagesBlob: %s",
1277 nt_errstr(status));
1278 return LDB_ERR_OPERATIONS_ERROR;
1280 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1281 if (!pb_hexstr) {
1282 ldb_oom(ldb);
1283 return LDB_ERR_OPERATIONS_ERROR;
1285 pp->name = "Packages";
1286 pp->reserved = 2;
1287 pp->data = pb_hexstr;
1290 * setup 'supplementalCredentials' value
1292 ZERO_STRUCT(scb);
1293 scb.sub.num_packages = num_packages;
1294 scb.sub.packages = packages;
1296 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1297 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1298 &scb,
1299 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1300 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1301 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1302 ldb_asprintf_errstring(ldb,
1303 "setup_supplemental_field: "
1304 "failed to push supplementalCredentialsBlob: %s",
1305 nt_errstr(status));
1306 return LDB_ERR_OPERATIONS_ERROR;
1309 return LDB_SUCCESS;
1312 static int setup_last_set_field(struct setup_password_fields_io *io)
1314 /* set it as now */
1315 unix_to_nt_time(&io->g.last_set, time(NULL));
1317 return LDB_SUCCESS;
1320 static int setup_given_passwords(struct setup_password_fields_io *io,
1321 struct setup_password_fields_given *g)
1323 struct ldb_context *ldb;
1324 bool ok;
1326 ldb = ldb_module_get_ctx(io->ac->module);
1328 if (g->cleartext_utf8) {
1329 char **cleartext_utf16_str;
1330 struct ldb_val *cleartext_utf16_blob;
1331 size_t converted_pw_len;
1333 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1334 if (!cleartext_utf16_blob) {
1335 ldb_oom(ldb);
1336 return LDB_ERR_OPERATIONS_ERROR;
1338 if (!convert_string_talloc_convenience(io->ac,
1339 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1340 CH_UTF8, CH_UTF16,
1341 g->cleartext_utf8->data,
1342 g->cleartext_utf8->length,
1343 (void *)&cleartext_utf16_str,
1344 &converted_pw_len, false)) {
1345 ldb_asprintf_errstring(ldb,
1346 "setup_password_fields: "
1347 "failed to generate UTF16 password from cleartext UTF8 password");
1348 return LDB_ERR_OPERATIONS_ERROR;
1350 *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str,
1351 converted_pw_len);
1352 g->cleartext_utf16 = cleartext_utf16_blob;
1353 } else if (g->cleartext_utf16) {
1354 char *cleartext_utf8_str;
1355 struct ldb_val *cleartext_utf8_blob;
1356 size_t converted_pw_len;
1358 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1359 if (!cleartext_utf8_blob) {
1360 ldb_oom(ldb);
1361 return LDB_ERR_OPERATIONS_ERROR;
1363 if (!convert_string_talloc_convenience(io->ac,
1364 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1365 CH_UTF16MUNGED, CH_UTF8,
1366 g->cleartext_utf16->data,
1367 g->cleartext_utf16->length,
1368 (void *)&cleartext_utf8_str,
1369 &converted_pw_len, false)) {
1370 /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
1371 talloc_free(cleartext_utf8_blob);
1372 } else {
1373 *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str,
1374 converted_pw_len);
1375 g->cleartext_utf8 = cleartext_utf8_blob;
1379 if (g->cleartext_utf16) {
1380 struct samr_Password *nt_hash;
1382 nt_hash = talloc(io->ac, struct samr_Password);
1383 if (!nt_hash) {
1384 ldb_oom(ldb);
1385 return LDB_ERR_OPERATIONS_ERROR;
1387 g->nt_hash = nt_hash;
1389 /* compute the new nt hash */
1390 mdfour(nt_hash->hash,
1391 g->cleartext_utf16->data,
1392 g->cleartext_utf16->length);
1395 if (g->cleartext_utf8 &&
1396 lp_lanman_auth(ldb_get_opaque(ldb, "loadparm"))) {
1397 struct samr_Password *lm_hash;
1399 lm_hash = talloc(io->ac, struct samr_Password);
1400 if (!lm_hash) {
1401 ldb_oom(ldb);
1402 return LDB_ERR_OPERATIONS_ERROR;
1405 /* compute the new lm hash */
1406 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1407 if (ok) {
1408 g->lm_hash = lm_hash;
1409 } else {
1410 talloc_free(lm_hash);
1414 return LDB_SUCCESS;
1417 static int setup_password_fields(struct setup_password_fields_io *io)
1419 struct ldb_context *ldb;
1420 int ret;
1422 ldb = ldb_module_get_ctx(io->ac->module);
1424 /* transform the old password (for password changes) */
1425 ret = setup_given_passwords(io, &io->og);
1426 if (ret != LDB_SUCCESS) {
1427 return ret;
1430 /* transform the new password */
1431 ret = setup_given_passwords(io, &io->n);
1432 if (ret != LDB_SUCCESS) {
1433 return ret;
1436 if (io->n.cleartext_utf8) {
1437 ret = setup_kerberos_keys(io);
1438 if (ret != LDB_SUCCESS) {
1439 return ret;
1443 ret = setup_nt_fields(io);
1444 if (ret != LDB_SUCCESS) {
1445 return ret;
1448 ret = setup_lm_fields(io);
1449 if (ret != LDB_SUCCESS) {
1450 return ret;
1453 ret = setup_supplemental_field(io);
1454 if (ret != LDB_SUCCESS) {
1455 return ret;
1458 ret = setup_last_set_field(io);
1459 if (ret != LDB_SUCCESS) {
1460 return ret;
1463 return LDB_SUCCESS;
1466 static int check_password_restrictions(struct setup_password_fields_io *io)
1468 struct ldb_context *ldb;
1469 int ret;
1470 enum samr_ValidationStatus stat;
1472 ldb = ldb_module_get_ctx(io->ac->module);
1474 /* First check the old password is correct, for password changes */
1475 if (!io->ac->pwd_reset && !io->ac->change_old_pw_checked) {
1476 /* we need to old nt or lm hash given by the client */
1477 if (!io->og.nt_hash && !io->og.lm_hash) {
1478 ldb_asprintf_errstring(ldb,
1479 "check_password_restrictions: "
1480 "You need to provide the old password "
1481 "in order to change your password!");
1482 return LDB_ERR_UNWILLING_TO_PERFORM;
1485 if (io->og.nt_hash) {
1486 if (!io->o.nt_hash) {
1487 ldb_asprintf_errstring(ldb,
1488 "check_password_restrictions: "
1489 "There's no old nt_hash, which is needed "
1490 "in order to change your password!");
1491 return LDB_ERR_UNWILLING_TO_PERFORM;
1494 /* The password modify through the NT hash is encouraged
1495 and has no problems at all */
1496 if (memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
1497 ldb_asprintf_errstring(ldb,
1498 "check_password_restrictions: "
1499 "The old password specified doesn't match!");
1500 return LDB_ERR_UNWILLING_TO_PERFORM;
1502 } else if (io->og.lm_hash) {
1503 struct loadparm_context *lp_ctx =
1504 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
1506 if (!lp_lanman_auth(lp_ctx)) {
1507 ldb_asprintf_errstring(ldb,
1508 "check_password_restrictions: "
1509 "The password change through the LM hash is deactivated!");
1510 return LDB_ERR_UNWILLING_TO_PERFORM;
1513 if (!io->o.lm_hash) {
1514 ldb_asprintf_errstring(ldb,
1515 "check_password_restrictions: "
1516 "There's no old lm_hash, which is needed "
1517 "in order to change your password!");
1518 return LDB_ERR_UNWILLING_TO_PERFORM;
1521 if (memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0) {
1522 ldb_asprintf_errstring(ldb,
1523 "check_password_restrictions: "
1524 "The old password specified doesn't match!");
1525 return LDB_ERR_UNWILLING_TO_PERFORM;
1530 if (io->u.restrictions == 0) {
1531 /* FIXME: Is this right? */
1532 return LDB_SUCCESS;
1536 * Fundamental password checks done by the call
1537 * "samdb_check_password".
1538 * It is also in use by "dcesrv_samr_ValidatePassword".
1540 if (io->n.cleartext_utf8 != NULL) {
1541 stat = samdb_check_password(io->n.cleartext_utf8,
1542 io->ac->status->domain_data.pwdProperties,
1543 io->ac->status->domain_data.minPwdLength);
1544 switch (stat) {
1545 case SAMR_VALIDATION_STATUS_SUCCESS:
1546 /* perfect -> proceed! */
1547 break;
1549 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
1550 ldb_asprintf_errstring(ldb,
1551 "check_password_restrictions: "
1552 "the password is too short. It should be equal or longer than %i characters!",
1553 io->ac->status->domain_data.minPwdLength);
1555 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1556 return LDB_ERR_CONSTRAINT_VIOLATION;
1558 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
1559 ldb_asprintf_errstring(ldb,
1560 "check_password_restrictions: "
1561 "the password does not meet the complexity criterias!");
1562 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1564 return LDB_ERR_CONSTRAINT_VIOLATION;
1566 default:
1567 ldb_asprintf_errstring(ldb,
1568 "check_password_restrictions: "
1569 "the password doesn't fit by a certain reason!");
1571 return LDB_ERR_CONSTRAINT_VIOLATION;
1575 if (io->ac->pwd_reset) {
1576 return LDB_SUCCESS;
1579 if (io->n.nt_hash) {
1580 uint32_t i;
1582 /* checks the NT hash password history */
1583 for (i = 0; i < io->o.nt_history_len; i++) {
1584 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
1585 if (ret == 0) {
1586 ldb_asprintf_errstring(ldb,
1587 "check_password_restrictions: "
1588 "the password was already used (in history)!");
1590 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1592 return LDB_ERR_CONSTRAINT_VIOLATION;
1597 if (io->n.lm_hash) {
1598 uint32_t i;
1600 /* checks the LM hash password history */
1601 for (i = 0; i < io->o.lm_history_len; i++) {
1602 ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
1603 if (ret == 0) {
1604 ldb_asprintf_errstring(ldb,
1605 "check_password_restrictions: "
1606 "the password was already used (in history)!");
1608 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1610 return LDB_ERR_CONSTRAINT_VIOLATION;
1615 /* are all password changes disallowed? */
1616 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1617 ldb_asprintf_errstring(ldb,
1618 "check_password_restrictions: "
1619 "password changes disabled!");
1620 return LDB_ERR_CONSTRAINT_VIOLATION;
1623 /* can this user change the password? */
1624 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
1625 ldb_asprintf_errstring(ldb,
1626 "check_password_restrictions: "
1627 "password can't be changed on this account!");
1628 return LDB_ERR_CONSTRAINT_VIOLATION;
1631 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
1632 if (io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) {
1633 ldb_asprintf_errstring(ldb,
1634 "check_password_restrictions: "
1635 "password is too young to change!");
1636 return LDB_ERR_CONSTRAINT_VIOLATION;
1639 return LDB_SUCCESS;
1642 static int setup_io(struct ph_context *ac,
1643 const struct ldb_message *orig_msg,
1644 const struct ldb_message *searched_msg,
1645 struct setup_password_fields_io *io)
1647 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
1648 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1649 int ret;
1651 ZERO_STRUCTP(io);
1653 /* Some operations below require kerberos contexts */
1655 if (smb_krb5_init_context(ac,
1656 ldb_get_event_context(ldb),
1657 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
1658 &io->smb_krb5_context) != 0) {
1659 return LDB_ERR_OPERATIONS_ERROR;
1662 io->ac = ac;
1664 io->u.userAccountControl = samdb_result_uint(searched_msg, "userAccountControl", 0);
1665 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
1666 io->u.sAMAccountName = samdb_result_string(searched_msg, "sAMAccountName", NULL);
1667 io->u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
1668 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
1670 if (io->u.sAMAccountName == NULL) {
1671 ldb_asprintf_errstring(ldb,
1672 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
1673 ldb_dn_get_linearized(searched_msg->dn));
1675 return LDB_ERR_CONSTRAINT_VIOLATION;
1678 /* FIXME: fix to don't break provision */
1679 io->ac->hash_values = true;
1681 /* Only non-trust accounts have restrictions (possibly this test is the
1682 * wrong way around, but we like to be restrictive if possible */
1683 io->u.restrictions = !(io->u.userAccountControl
1684 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
1685 | UF_SERVER_TRUST_ACCOUNT));
1687 if ((io->u.userAccountControl & UF_PASSWD_NOTREQD) != 0) {
1688 /* see [MS-ADTS] 2.2.15 */
1689 io->u.restrictions = 0;
1692 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "userPassword",
1693 &io->n.cleartext_utf8, &io->og.cleartext_utf8);
1694 if (ret != LDB_SUCCESS) {
1695 ldb_asprintf_errstring(ldb,
1696 "setup_io: "
1697 "it's only allowed to set the old password once!");
1698 return ret;
1701 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "clearTextPassword",
1702 &io->n.cleartext_utf16, &io->og.cleartext_utf16);
1703 if (ret != LDB_SUCCESS) {
1704 ldb_asprintf_errstring(ldb,
1705 "setup_io: "
1706 "it's only allowed to set the old password once!");
1707 return ret;
1710 /* this rather strange looking piece of code is there to
1711 handle a ldap client setting a password remotely using the
1712 unicodePwd ldap field. The syntax is that the password is
1713 in UTF-16LE, with a " at either end. Unfortunately the
1714 unicodePwd field is also used to store the nt hashes
1715 internally in Samba, and is used in the nt hash format on
1716 the wire in DRS replication, so we have a single name for
1717 two distinct values. The code below leaves us with a small
1718 chance (less than 1 in 2^32) of a mixup, if someone manages
1719 to create a MD4 hash which starts and ends in 0x22 0x00, as
1720 that would then be treated as a UTF16 password rather than
1721 a nthash */
1723 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "unicodePwd",
1724 &quoted_utf16, &old_quoted_utf16);
1725 if (ret != LDB_SUCCESS) {
1726 ldb_asprintf_errstring(ldb,
1727 "setup_io: "
1728 "it's only allowed to set the old password once!");
1729 return ret;
1732 /* Checks and converts the actual "unicodePwd" attribute */
1733 if (quoted_utf16 &&
1734 quoted_utf16->length >= 4 &&
1735 quoted_utf16->data[0] == '"' &&
1736 quoted_utf16->data[1] == 0 &&
1737 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
1738 quoted_utf16->data[quoted_utf16->length-1] == 0) {
1739 struct ldb_val *quoted_utf16_2;
1741 if (io->n.cleartext_utf16) {
1742 /* refuse the change if someone wants to change with
1743 with both UTF16 possibilities at the same time... */
1744 ldb_asprintf_errstring(ldb,
1745 "setup_io: "
1746 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1747 return LDB_ERR_UNWILLING_TO_PERFORM;
1751 * adapt the quoted UTF16 string to be a real
1752 * cleartext one
1754 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1755 if (quoted_utf16_2 == NULL) {
1756 ldb_oom(ldb);
1757 return LDB_ERR_OPERATIONS_ERROR;
1760 quoted_utf16_2->data = quoted_utf16->data + 2;
1761 quoted_utf16_2->length = quoted_utf16->length-4;
1762 io->n.cleartext_utf16 = quoted_utf16_2;
1763 io->n.nt_hash = NULL;
1765 } else {
1766 /* We have only the hash available -> so no plaintext here */
1767 if (!ac->hash_values) {
1768 /* refuse the change if someone wants to change
1769 the hash without control specified... */
1770 ldb_asprintf_errstring(ldb,
1771 "setup_io: "
1772 "it's not allowed to set the NT hash password directly'");
1773 /* this looks odd but this is what Windows does:
1774 returns "UNWILLING_TO_PERFORM" on wrong
1775 password sets and "CONSTRAINT_VIOLATION" on
1776 wrong password changes. */
1777 if (old_quoted_utf16 == NULL) {
1778 return LDB_ERR_UNWILLING_TO_PERFORM;
1781 return LDB_ERR_CONSTRAINT_VIOLATION;
1784 if (quoted_utf16 != NULL) {
1785 io->n.nt_hash = talloc(io->ac, struct samr_Password);
1786 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
1787 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
1791 /* Checks and converts the previous "unicodePwd" attribute */
1792 if (old_quoted_utf16 &&
1793 old_quoted_utf16->length >= 4 &&
1794 old_quoted_utf16->data[0] == '"' &&
1795 old_quoted_utf16->data[1] == 0 &&
1796 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
1797 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
1798 struct ldb_val *old_quoted_utf16_2;
1800 if (io->og.cleartext_utf16) {
1801 /* refuse the change if someone wants to change with
1802 both UTF16 possibilities at the same time... */
1803 ldb_asprintf_errstring(ldb,
1804 "setup_io: "
1805 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1806 return LDB_ERR_UNWILLING_TO_PERFORM;
1810 * adapt the quoted UTF16 string to be a real
1811 * cleartext one
1813 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1814 if (old_quoted_utf16_2 == NULL) {
1815 ldb_oom(ldb);
1816 return LDB_ERR_OPERATIONS_ERROR;
1819 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
1820 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
1822 io->og.cleartext_utf16 = old_quoted_utf16_2;
1823 io->og.nt_hash = NULL;
1824 } else {
1825 /* We have only the hash available -> so no plaintext here */
1826 if (!ac->hash_values) {
1827 /* refuse the change if someone wants to change
1828 the hash without control specified... */
1829 ldb_asprintf_errstring(ldb,
1830 "setup_io: "
1831 "it's not allowed to set the NT hash password directly'");
1832 return LDB_ERR_UNWILLING_TO_PERFORM;
1835 if (old_quoted_utf16 != NULL) {
1836 io->og.nt_hash = talloc(io->ac, struct samr_Password);
1837 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
1838 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
1842 /* Handles the "dBCSPwd" attribute (LM hash) */
1843 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
1844 ret = samdb_msg_find_old_and_new_ldb_val(orig_msg, "dBCSPwd",
1845 &lm_hash, &old_lm_hash);
1846 if (ret != LDB_SUCCESS) {
1847 ldb_asprintf_errstring(ldb,
1848 "setup_io: "
1849 "it's only allowed to set the old password once!");
1850 return ret;
1853 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
1854 /* refuse the change if someone wants to change the hash
1855 without control specified... */
1856 ldb_asprintf_errstring(ldb,
1857 "setup_io: "
1858 "it's not allowed to set the LM hash password directly'");
1859 return LDB_ERR_UNWILLING_TO_PERFORM;
1861 if (lm_hash != NULL) {
1862 io->n.lm_hash = talloc(io->ac, struct samr_Password);
1863 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
1864 sizeof(io->n.lm_hash->hash)));
1867 if (old_lm_hash != NULL) {
1868 io->og.lm_hash = talloc(io->ac, struct samr_Password);
1869 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
1870 sizeof(io->og.lm_hash->hash)));
1873 /* refuse the change if someone wants to change the clear-
1874 text and supply his own hashes at the same time... */
1875 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
1876 && (io->n.nt_hash || io->n.lm_hash)) {
1877 ldb_asprintf_errstring(ldb,
1878 "setup_io: "
1879 "it's only allowed to set the password in form of cleartext attributes or as hashes");
1880 return LDB_ERR_UNWILLING_TO_PERFORM;
1883 /* refuse the change if someone wants to change the password
1884 using both plaintext methods (UTF8 and UTF16) at the same time... */
1885 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
1886 ldb_asprintf_errstring(ldb,
1887 "setup_io: "
1888 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
1889 return LDB_ERR_UNWILLING_TO_PERFORM;
1892 /* refuse the change if someone wants to compare against a plaintext
1893 or hash at the same time for a "password modify" operation... */
1894 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
1895 && (io->og.nt_hash || io->og.lm_hash)) {
1896 ldb_asprintf_errstring(ldb,
1897 "setup_io: "
1898 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
1899 return LDB_ERR_UNWILLING_TO_PERFORM;
1902 /* refuse the change if someone wants to compare against both
1903 * plaintexts at the same time for a "password modify" operation... */
1904 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
1905 ldb_asprintf_errstring(ldb,
1906 "setup_io: "
1907 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
1908 return LDB_ERR_UNWILLING_TO_PERFORM;
1911 /* refuse the change if someone wants to compare against both
1912 * hashes at the same time for a "password modify" operation... */
1913 if (io->og.nt_hash && io->og.lm_hash) {
1914 ldb_asprintf_errstring(ldb,
1915 "setup_io: "
1916 "it's only allowed to provide the old password in hash format as 'unicodePwd' or as 'dBCSPwd'");
1917 return LDB_ERR_UNWILLING_TO_PERFORM;
1920 /* Decides if we have a password modify or password reset operation */
1921 if (ac->req->operation == LDB_ADD) {
1922 /* On "add" we have only "password reset" */
1923 ac->pwd_reset = true;
1924 } else if (ac->req->operation == LDB_MODIFY) {
1925 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
1926 || io->og.nt_hash || io->og.lm_hash
1927 || ac->change_old_pw_checked) {
1928 /* If we have an old password or the "change old
1929 * password checked" control specified then for sure it
1930 * is a user "password change" */
1931 ac->pwd_reset = false;
1932 } else {
1933 /* Otherwise we have also here a "password reset" */
1934 ac->pwd_reset = true;
1936 } else {
1937 /* this shouldn't happen */
1938 return LDB_ERR_OPERATIONS_ERROR;
1941 return LDB_SUCCESS;
1944 static struct ph_context *ph_init_context(struct ldb_module *module,
1945 struct ldb_request *req)
1947 struct ldb_context *ldb;
1948 struct ph_context *ac;
1950 ldb = ldb_module_get_ctx(module);
1952 ac = talloc_zero(req, struct ph_context);
1953 if (ac == NULL) {
1954 ldb_set_errstring(ldb, "Out of Memory");
1955 return NULL;
1958 ac->module = module;
1959 ac->req = req;
1961 return ac;
1964 static void ph_apply_controls(struct ph_context *ac)
1966 struct ldb_control *ctrl;
1968 ac->change_status = false;
1969 ctrl = ldb_request_get_control(ac->req,
1970 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
1971 if (ctrl != NULL) {
1972 ac->change_status = true;
1974 /* Mark the "change status" control as uncritical (done) */
1975 ctrl->critical = false;
1978 ac->hash_values = false;
1979 ctrl = ldb_request_get_control(ac->req,
1980 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
1981 if (ctrl != NULL) {
1982 ac->hash_values = true;
1984 /* Mark the "hash values" control as uncritical (done) */
1985 ctrl->critical = false;
1988 ac->change_old_pw_checked = false;
1989 ctrl = ldb_request_get_control(ac->req,
1990 DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID);
1991 if (ctrl != NULL) {
1992 ac->change_old_pw_checked = true;
1994 /* Mark the "change old password checked" control as uncritical
1995 * (done) */
1996 ctrl->critical = false;
2000 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2002 struct ph_context *ac;
2004 ac = talloc_get_type(req->context, struct ph_context);
2006 if (!ares) {
2007 return ldb_module_done(ac->req, NULL, NULL,
2008 LDB_ERR_OPERATIONS_ERROR);
2011 if (ares->type == LDB_REPLY_REFERRAL) {
2012 return ldb_module_send_referral(ac->req, ares->referral);
2015 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2016 /* On success and trivial errors a status control is being
2017 * added (used for example by the "samdb_set_password" call) */
2018 ldb_reply_add_control(ares,
2019 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2020 false,
2021 ac->status);
2024 if (ares->error != LDB_SUCCESS) {
2025 return ldb_module_done(ac->req, ares->controls,
2026 ares->response, ares->error);
2029 if (ares->type != LDB_REPLY_DONE) {
2030 talloc_free(ares);
2031 return ldb_module_done(ac->req, NULL, NULL,
2032 LDB_ERR_OPERATIONS_ERROR);
2035 return ldb_module_done(ac->req, ares->controls,
2036 ares->response, ares->error);
2039 static int password_hash_add_do_add(struct ph_context *ac);
2040 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2041 static int password_hash_mod_search_self(struct ph_context *ac);
2042 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2043 static int password_hash_mod_do_mod(struct ph_context *ac);
2045 static int get_domain_data_callback(struct ldb_request *req,
2046 struct ldb_reply *ares)
2048 struct ldb_context *ldb;
2049 struct ph_context *ac;
2050 struct loadparm_context *lp_ctx;
2051 int ret;
2053 ac = talloc_get_type(req->context, struct ph_context);
2054 ldb = ldb_module_get_ctx(ac->module);
2056 if (!ares) {
2057 ret = LDB_ERR_OPERATIONS_ERROR;
2058 goto done;
2060 if (ares->error != LDB_SUCCESS) {
2061 return ldb_module_done(ac->req, ares->controls,
2062 ares->response, ares->error);
2065 switch (ares->type) {
2066 case LDB_REPLY_ENTRY:
2067 if (ac->status != NULL) {
2068 talloc_free(ares);
2070 ldb_set_errstring(ldb, "Too many results");
2071 ret = LDB_ERR_OPERATIONS_ERROR;
2072 goto done;
2075 /* Setup the "status" structure (used as control later) */
2076 ac->status = talloc_zero(ac->req,
2077 struct dsdb_control_password_change_status);
2078 if (ac->status == NULL) {
2079 talloc_free(ares);
2081 ldb_oom(ldb);
2082 ret = LDB_ERR_OPERATIONS_ERROR;
2083 goto done;
2086 /* Setup the "domain data" structure */
2087 ac->status->domain_data.pwdProperties = samdb_result_uint(ares->message, "pwdProperties", -1);
2088 ac->status->domain_data.pwdHistoryLength = samdb_result_uint(ares->message, "pwdHistoryLength", -1);
2089 ac->status->domain_data.maxPwdAge = samdb_result_int64(ares->message, "maxPwdAge", -1);
2090 ac->status->domain_data.minPwdAge = samdb_result_int64(ares->message, "minPwdAge", -1);
2091 ac->status->domain_data.minPwdLength = samdb_result_uint(ares->message, "minPwdLength", -1);
2092 ac->status->domain_data.store_cleartext =
2093 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2095 talloc_free(ares);
2097 /* For a domain DN, this puts things in dotted notation */
2098 /* For builtin domains, this will give details for the host,
2099 * but that doesn't really matter, as it's just used for salt
2100 * and kerberos principals, which don't exist here */
2102 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2103 struct loadparm_context);
2105 ac->status->domain_data.dns_domain = lp_dnsdomain(lp_ctx);
2106 ac->status->domain_data.realm = lp_realm(lp_ctx);
2107 ac->status->domain_data.netbios_domain = lp_sam_name(lp_ctx);
2109 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2111 ret = LDB_SUCCESS;
2112 break;
2114 case LDB_REPLY_REFERRAL:
2115 /* ignore */
2116 talloc_free(ares);
2117 ret = LDB_SUCCESS;
2118 break;
2120 case LDB_REPLY_DONE:
2121 talloc_free(ares);
2122 /* call the next step */
2123 switch (ac->req->operation) {
2124 case LDB_ADD:
2125 ret = password_hash_add_do_add(ac);
2126 break;
2128 case LDB_MODIFY:
2129 ret = password_hash_mod_do_mod(ac);
2130 break;
2132 default:
2133 ret = LDB_ERR_OPERATIONS_ERROR;
2134 break;
2136 break;
2139 done:
2140 if (ret != LDB_SUCCESS) {
2141 struct ldb_reply *new_ares;
2143 new_ares = talloc_zero(ac->req, struct ldb_reply);
2144 if (new_ares == NULL) {
2145 ldb_oom(ldb);
2146 return ldb_module_done(ac->req, NULL, NULL,
2147 LDB_ERR_OPERATIONS_ERROR);
2150 new_ares->error = ret;
2151 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2152 /* On success and trivial errors a status control is being
2153 * added (used for example by the "samdb_set_password" call) */
2154 ldb_reply_add_control(new_ares,
2155 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2156 false,
2157 ac->status);
2160 return ldb_module_done(ac->req, new_ares->controls,
2161 new_ares->response, new_ares->error);
2164 return LDB_SUCCESS;
2167 static int build_domain_data_request(struct ph_context *ac)
2169 /* attrs[] is returned from this function in
2170 ac->dom_req->op.search.attrs, so it must be static, as
2171 otherwise the compiler can put it on the stack */
2172 struct ldb_context *ldb;
2173 static const char * const attrs[] = { "pwdProperties",
2174 "pwdHistoryLength",
2175 "maxPwdAge",
2176 "minPwdAge",
2177 "minPwdLength",
2178 NULL };
2180 ldb = ldb_module_get_ctx(ac->module);
2182 return ldb_build_search_req(&ac->dom_req, ldb, ac,
2183 ldb_get_default_basedn(ldb),
2184 LDB_SCOPE_BASE,
2185 NULL, attrs,
2186 NULL,
2187 ac, get_domain_data_callback,
2188 ac->req);
2191 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2193 struct ldb_context *ldb;
2194 struct ph_context *ac;
2195 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2196 *ntAttr, *lmAttr;
2197 int ret;
2199 ldb = ldb_module_get_ctx(module);
2201 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2203 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2204 return ldb_next_request(module, req);
2207 /* If the caller is manipulating the local passwords directly, let them pass */
2208 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2209 req->op.add.message->dn) == 0) {
2210 return ldb_next_request(module, req);
2213 /* nobody must touch password histories and 'supplementalCredentials' */
2214 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2215 return LDB_ERR_UNWILLING_TO_PERFORM;
2217 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2218 return LDB_ERR_UNWILLING_TO_PERFORM;
2220 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2221 return LDB_ERR_UNWILLING_TO_PERFORM;
2224 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2225 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2227 userPasswordAttr = ldb_msg_find_element(req->op.add.message, "userPassword");
2228 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2229 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2230 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2232 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2233 return ldb_next_request(module, req);
2236 /* Make sure we are performing the password set action on a (for us)
2237 * valid object. Those are instances of either "user" and/or
2238 * "inetOrgPerson". Otherwise continue with the submodules. */
2239 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2240 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2242 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2243 ldb_set_errstring(ldb,
2244 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2245 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2248 return ldb_next_request(module, req);
2251 ac = ph_init_context(module, req);
2252 if (ac == NULL) {
2253 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2254 return LDB_ERR_OPERATIONS_ERROR;
2256 ph_apply_controls(ac);
2258 /* get user domain data */
2259 ret = build_domain_data_request(ac);
2260 if (ret != LDB_SUCCESS) {
2261 return ret;
2264 return ldb_next_request(module, ac->dom_req);
2267 static int password_hash_add_do_add(struct ph_context *ac)
2269 struct ldb_context *ldb;
2270 struct ldb_request *down_req;
2271 struct ldb_message *msg;
2272 struct setup_password_fields_io io;
2273 int ret;
2275 /* Prepare the internal data structure containing the passwords */
2276 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2277 if (ret != LDB_SUCCESS) {
2278 return ret;
2281 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2282 if (msg == NULL) {
2283 return LDB_ERR_OPERATIONS_ERROR;
2286 /* remove attributes that we just read into 'io' (handle also superfluous
2287 * "password modify" trials - multiple attributes with the same name -
2288 * on add operations) */
2289 while (ldb_msg_find_element(msg, "userPassword") != NULL) {
2290 ldb_msg_remove_attr(msg, "userPassword");
2292 while (ldb_msg_find_element(msg, "clearTextPassword") != NULL) {
2293 ldb_msg_remove_attr(msg, "clearTextPassword");
2295 while (ldb_msg_find_element(msg, "unicodePwd") != NULL) {
2296 ldb_msg_remove_attr(msg, "unicodePwd");
2298 while (ldb_msg_find_element(msg, "dBCSPwd") != NULL) {
2299 ldb_msg_remove_attr(msg, "dBCSPwd");
2302 ldb_msg_remove_attr(msg, "pwdLastSet");
2304 ldb = ldb_module_get_ctx(ac->module);
2306 ret = setup_password_fields(&io);
2307 if (ret != LDB_SUCCESS) {
2308 return ret;
2311 ret = check_password_restrictions(&io);
2312 if (ret != LDB_SUCCESS) {
2313 return ret;
2316 if (io.g.nt_hash) {
2317 ret = samdb_msg_add_hash(ldb, ac, msg,
2318 "unicodePwd", io.g.nt_hash);
2319 if (ret != LDB_SUCCESS) {
2320 return ret;
2323 if (io.g.lm_hash) {
2324 ret = samdb_msg_add_hash(ldb, ac, msg,
2325 "dBCSPwd", io.g.lm_hash);
2326 if (ret != LDB_SUCCESS) {
2327 return ret;
2330 if (io.g.nt_history_len > 0) {
2331 ret = samdb_msg_add_hashes(ac, msg,
2332 "ntPwdHistory",
2333 io.g.nt_history,
2334 io.g.nt_history_len);
2335 if (ret != LDB_SUCCESS) {
2336 return ret;
2339 if (io.g.lm_history_len > 0) {
2340 ret = samdb_msg_add_hashes(ac, msg,
2341 "lmPwdHistory",
2342 io.g.lm_history,
2343 io.g.lm_history_len);
2344 if (ret != LDB_SUCCESS) {
2345 return ret;
2348 if (io.g.supplemental.length > 0) {
2349 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2350 &io.g.supplemental, NULL);
2351 if (ret != LDB_SUCCESS) {
2352 return ret;
2355 ret = samdb_msg_add_uint64(ldb, ac, msg,
2356 "pwdLastSet",
2357 io.g.last_set);
2358 if (ret != LDB_SUCCESS) {
2359 return ret;
2362 ret = ldb_build_add_req(&down_req, ldb, ac,
2363 msg,
2364 ac->req->controls,
2365 ac, ph_op_callback,
2366 ac->req);
2367 if (ret != LDB_SUCCESS) {
2368 return ret;
2371 return ldb_next_request(ac->module, down_req);
2374 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
2376 struct ldb_context *ldb;
2377 struct ph_context *ac;
2378 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
2379 "unicodePwd", "dBCSPwd", NULL }, **l;
2380 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
2381 struct ldb_message_element *passwordAttr;
2382 struct ldb_message *msg;
2383 struct ldb_request *down_req;
2384 int ret;
2386 ldb = ldb_module_get_ctx(module);
2388 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
2390 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
2391 return ldb_next_request(module, req);
2394 /* If the caller is manipulating the local passwords directly, let them pass */
2395 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2396 req->op.mod.message->dn) == 0) {
2397 return ldb_next_request(module, req);
2400 /* nobody must touch password histories and 'supplementalCredentials' */
2401 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
2402 return LDB_ERR_UNWILLING_TO_PERFORM;
2404 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
2405 return LDB_ERR_UNWILLING_TO_PERFORM;
2407 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
2408 return LDB_ERR_UNWILLING_TO_PERFORM;
2411 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2412 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
2413 * For password changes/set there should be a 'delete' or a 'modify'
2414 * on these attributes. */
2415 attr_cnt = 0;
2416 for (l = passwordAttrs; *l != NULL; l++) {
2417 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
2418 ++attr_cnt;
2421 if (attr_cnt == 0) {
2422 return ldb_next_request(module, req);
2425 ac = ph_init_context(module, req);
2426 if (!ac) {
2427 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2428 return LDB_ERR_OPERATIONS_ERROR;
2430 ph_apply_controls(ac);
2432 /* use a new message structure so that we can modify it */
2433 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2434 if (msg == NULL) {
2435 ldb_oom(ldb);
2436 return LDB_ERR_OPERATIONS_ERROR;
2439 /* - check for single-valued password attributes
2440 * (if not return "CONSTRAINT_VIOLATION")
2441 * - check that for a password change operation one add and one delete
2442 * operation exists
2443 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
2444 * - check that a password change and a password set operation cannot
2445 * be mixed
2446 * (if not return "UNWILLING_TO_PERFORM")
2447 * - remove all password attributes modifications from the first change
2448 * operation (anything without the passwords) - we will make the real
2449 * modification later */
2450 del_attr_cnt = 0;
2451 add_attr_cnt = 0;
2452 rep_attr_cnt = 0;
2453 for (l = passwordAttrs; *l != NULL; l++) {
2454 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
2455 if (passwordAttr->flags == LDB_FLAG_MOD_DELETE) {
2456 ++del_attr_cnt;
2458 if (passwordAttr->flags == LDB_FLAG_MOD_ADD) {
2459 ++add_attr_cnt;
2461 if (passwordAttr->flags == LDB_FLAG_MOD_REPLACE) {
2462 ++rep_attr_cnt;
2464 if ((passwordAttr->num_values != 1) &&
2465 (passwordAttr->flags != LDB_FLAG_MOD_REPLACE)) {
2466 talloc_free(ac);
2467 ldb_asprintf_errstring(ldb,
2468 "'%s' attributes must have exactly one value!",
2469 *l);
2470 return LDB_ERR_CONSTRAINT_VIOLATION;
2472 ldb_msg_remove_attr(msg, *l);
2475 if ((del_attr_cnt > 0) && (add_attr_cnt == 0)) {
2476 talloc_free(ac);
2477 ldb_set_errstring(ldb,
2478 "Only the delete action for a password change specified!");
2479 return LDB_ERR_CONSTRAINT_VIOLATION;
2481 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
2482 talloc_free(ac);
2483 ldb_set_errstring(ldb,
2484 "Only the add action for a password change specified!");
2485 return LDB_ERR_UNWILLING_TO_PERFORM;
2487 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
2488 talloc_free(ac);
2489 ldb_set_errstring(ldb,
2490 "Only one delete and one add action for a password change allowed!");
2491 return LDB_ERR_UNWILLING_TO_PERFORM;
2493 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
2494 talloc_free(ac);
2495 ldb_set_errstring(ldb,
2496 "Either a password change or a password set operation is allowed!");
2497 return LDB_ERR_UNWILLING_TO_PERFORM;
2500 /* if there was nothing else to be modified skip to next step */
2501 if (msg->num_elements == 0) {
2502 return password_hash_mod_search_self(ac);
2505 ret = ldb_build_mod_req(&down_req, ldb, ac,
2506 msg,
2507 req->controls,
2508 ac, ph_modify_callback,
2509 req);
2510 if (ret != LDB_SUCCESS) {
2511 return ret;
2514 return ldb_next_request(module, down_req);
2517 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
2519 struct ph_context *ac;
2521 ac = talloc_get_type(req->context, struct ph_context);
2523 if (!ares) {
2524 return ldb_module_done(ac->req, NULL, NULL,
2525 LDB_ERR_OPERATIONS_ERROR);
2528 if (ares->type == LDB_REPLY_REFERRAL) {
2529 return ldb_module_send_referral(ac->req, ares->referral);
2532 if (ares->error != LDB_SUCCESS) {
2533 return ldb_module_done(ac->req, ares->controls,
2534 ares->response, ares->error);
2537 if (ares->type != LDB_REPLY_DONE) {
2538 talloc_free(ares);
2539 return ldb_module_done(ac->req, NULL, NULL,
2540 LDB_ERR_OPERATIONS_ERROR);
2543 talloc_free(ares);
2545 return password_hash_mod_search_self(ac);
2548 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
2550 struct ldb_context *ldb;
2551 struct ph_context *ac;
2552 int ret;
2554 ac = talloc_get_type(req->context, struct ph_context);
2555 ldb = ldb_module_get_ctx(ac->module);
2557 if (!ares) {
2558 ret = LDB_ERR_OPERATIONS_ERROR;
2559 goto done;
2561 if (ares->error != LDB_SUCCESS) {
2562 return ldb_module_done(ac->req, ares->controls,
2563 ares->response, ares->error);
2566 /* we are interested only in the single reply (base search) */
2567 switch (ares->type) {
2568 case LDB_REPLY_ENTRY:
2569 /* Make sure we are performing the password change action on a
2570 * (for us) valid object. Those are instances of either "user"
2571 * and/or "inetOrgPerson". Otherwise continue with the
2572 * submodules. */
2573 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
2574 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
2575 talloc_free(ares);
2577 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
2578 ldb_set_errstring(ldb,
2579 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2580 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
2581 goto done;
2584 ret = ldb_next_request(ac->module, ac->req);
2585 goto done;
2588 if (ac->search_res != NULL) {
2589 talloc_free(ares);
2591 ldb_set_errstring(ldb, "Too many results");
2592 ret = LDB_ERR_OPERATIONS_ERROR;
2593 goto done;
2596 ac->search_res = talloc_steal(ac, ares);
2597 ret = LDB_SUCCESS;
2598 break;
2600 case LDB_REPLY_REFERRAL:
2601 /* ignore anything else for now */
2602 talloc_free(ares);
2603 ret = LDB_SUCCESS;
2604 break;
2606 case LDB_REPLY_DONE:
2607 talloc_free(ares);
2609 /* get user domain data */
2610 ret = build_domain_data_request(ac);
2611 if (ret != LDB_SUCCESS) {
2612 return ldb_module_done(ac->req, NULL, NULL, ret);
2615 ret = ldb_next_request(ac->module, ac->dom_req);
2616 break;
2619 done:
2620 if (ret != LDB_SUCCESS) {
2621 return ldb_module_done(ac->req, NULL, NULL, ret);
2624 return LDB_SUCCESS;
2627 static int password_hash_mod_search_self(struct ph_context *ac)
2629 struct ldb_context *ldb;
2630 static const char * const attrs[] = { "objectClass",
2631 "userAccountControl",
2632 "pwdLastSet",
2633 "sAMAccountName",
2634 "objectSid",
2635 "userPrincipalName",
2636 "supplementalCredentials",
2637 "lmPwdHistory",
2638 "ntPwdHistory",
2639 "dBCSPwd",
2640 "unicodePwd",
2641 NULL };
2642 struct ldb_request *search_req;
2643 int ret;
2645 ldb = ldb_module_get_ctx(ac->module);
2647 ret = ldb_build_search_req(&search_req, ldb, ac,
2648 ac->req->op.mod.message->dn,
2649 LDB_SCOPE_BASE,
2650 "(objectclass=*)",
2651 attrs,
2652 NULL,
2653 ac, ph_mod_search_callback,
2654 ac->req);
2656 if (ret != LDB_SUCCESS) {
2657 return ret;
2660 return ldb_next_request(ac->module, search_req);
2663 static int password_hash_mod_do_mod(struct ph_context *ac)
2665 struct ldb_context *ldb;
2666 struct ldb_request *mod_req;
2667 struct ldb_message *msg;
2668 const struct ldb_message *orig_msg, *searched_msg;
2669 struct setup_password_fields_io io;
2670 int ret;
2671 NTSTATUS status;
2673 ldb = ldb_module_get_ctx(ac->module);
2675 /* use a new message structure so that we can modify it */
2676 msg = ldb_msg_new(ac);
2677 if (msg == NULL) {
2678 return LDB_ERR_OPERATIONS_ERROR;
2681 /* modify dn */
2682 msg->dn = ac->req->op.mod.message->dn;
2684 orig_msg = ac->req->op.mod.message;
2685 searched_msg = ac->search_res->message;
2687 /* Prepare the internal data structure containing the passwords */
2688 ret = setup_io(ac, orig_msg, searched_msg, &io);
2689 if (ret != LDB_SUCCESS) {
2690 return ret;
2693 /* Get the old password from the database */
2694 status = samdb_result_passwords(io.ac,
2695 ldb_get_opaque(ldb, "loadparm"),
2696 discard_const_p(struct ldb_message, searched_msg),
2697 &io.o.lm_hash, &io.o.nt_hash);
2698 if (!NT_STATUS_IS_OK(status)) {
2699 return LDB_ERR_OPERATIONS_ERROR;
2702 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
2703 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
2704 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
2706 ret = setup_password_fields(&io);
2707 if (ret != LDB_SUCCESS) {
2708 return ret;
2711 ret = check_password_restrictions(&io);
2712 if (ret != LDB_SUCCESS) {
2713 return ret;
2716 /* make sure we replace all the old attributes */
2717 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
2718 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
2719 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2720 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2721 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
2722 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
2724 if (io.g.nt_hash) {
2725 ret = samdb_msg_add_hash(ldb, ac, msg,
2726 "unicodePwd", io.g.nt_hash);
2727 if (ret != LDB_SUCCESS) {
2728 return ret;
2731 if (io.g.lm_hash) {
2732 ret = samdb_msg_add_hash(ldb, ac, msg,
2733 "dBCSPwd", io.g.lm_hash);
2734 if (ret != LDB_SUCCESS) {
2735 return ret;
2738 if (io.g.nt_history_len > 0) {
2739 ret = samdb_msg_add_hashes(ac, msg,
2740 "ntPwdHistory",
2741 io.g.nt_history,
2742 io.g.nt_history_len);
2743 if (ret != LDB_SUCCESS) {
2744 return ret;
2747 if (io.g.lm_history_len > 0) {
2748 ret = samdb_msg_add_hashes(ac, msg,
2749 "lmPwdHistory",
2750 io.g.lm_history,
2751 io.g.lm_history_len);
2752 if (ret != LDB_SUCCESS) {
2753 return ret;
2756 if (io.g.supplemental.length > 0) {
2757 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2758 &io.g.supplemental, NULL);
2759 if (ret != LDB_SUCCESS) {
2760 return ret;
2763 ret = samdb_msg_add_uint64(ldb, ac, msg,
2764 "pwdLastSet",
2765 io.g.last_set);
2766 if (ret != LDB_SUCCESS) {
2767 return ret;
2770 ret = ldb_build_mod_req(&mod_req, ldb, ac,
2771 msg,
2772 ac->req->controls,
2773 ac, ph_op_callback,
2774 ac->req);
2775 if (ret != LDB_SUCCESS) {
2776 return ret;
2779 return ldb_next_request(ac->module, mod_req);
2782 _PUBLIC_ const struct ldb_module_ops ldb_password_hash_module_ops = {
2783 .name = "password_hash",
2784 .add = password_hash_add,
2785 .modify = password_hash_modify