s4:password_hash LDB module - move "samdb_msg_find_old_and_new_ldb_val" into the...
[Samba.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blob2e89d7f82173381d4f4cf2b7af6fb9c6b3834a39
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;
98 struct dsdb_control_password_change *change;
100 bool pwd_reset;
101 bool change_status;
102 bool hash_values;
103 bool userPassword;
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 return ldb_oom(ldb);
183 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
184 io->o.nt_history_len); i++) {
185 io->g.nt_history[i+1] = io->o.nt_history[i];
187 io->g.nt_history_len = i + 1;
189 if (io->g.nt_hash) {
190 io->g.nt_history[0] = *io->g.nt_hash;
191 } else {
193 * TODO: is this correct?
194 * the simular behavior is correct for the lm history case
196 E_md4hash("", io->g.nt_history[0].hash);
199 return LDB_SUCCESS;
202 /* Get the LANMAN hash, and fill it in as an entry in the password history,
203 and specify it into io->g.lm_hash */
205 static int setup_lm_fields(struct setup_password_fields_io *io)
207 struct ldb_context *ldb;
208 uint32_t i;
210 io->g.lm_hash = io->n.lm_hash;
211 ldb = ldb_module_get_ctx(io->ac->module);
213 if (io->ac->status->domain_data.pwdHistoryLength == 0) {
214 return LDB_SUCCESS;
217 /* We might not have an old LM password */
218 io->g.lm_history = talloc_array(io->ac,
219 struct samr_Password,
220 io->ac->status->domain_data.pwdHistoryLength);
221 if (!io->g.lm_history) {
222 return ldb_oom(ldb);
225 for (i = 0; i < MIN(io->ac->status->domain_data.pwdHistoryLength-1,
226 io->o.lm_history_len); i++) {
227 io->g.lm_history[i+1] = io->o.lm_history[i];
229 io->g.lm_history_len = i + 1;
231 if (io->g.lm_hash) {
232 io->g.lm_history[0] = *io->g.lm_hash;
233 } else {
234 E_deshash("", io->g.lm_history[0].hash);
237 return LDB_SUCCESS;
240 static int setup_kerberos_keys(struct setup_password_fields_io *io)
242 struct ldb_context *ldb;
243 krb5_error_code krb5_ret;
244 Principal *salt_principal;
245 krb5_salt salt;
246 krb5_keyblock key;
247 krb5_data cleartext_data;
249 ldb = ldb_module_get_ctx(io->ac->module);
250 cleartext_data.data = io->n.cleartext_utf8->data;
251 cleartext_data.length = io->n.cleartext_utf8->length;
253 /* Many, many thanks to lukeh@padl.com for this
254 * algorithm, described in his Nov 10 2004 mail to
255 * samba-technical@samba.org */
258 * Determine a salting principal
260 if (io->u.is_computer) {
261 char *name;
262 char *saltbody;
264 name = strlower_talloc(io->ac, io->u.sAMAccountName);
265 if (!name) {
266 return ldb_oom(ldb);
269 if (name[strlen(name)-1] == '$') {
270 name[strlen(name)-1] = '\0';
273 saltbody = talloc_asprintf(io->ac, "%s.%s", name,
274 io->ac->status->domain_data.dns_domain);
275 if (!saltbody) {
276 return ldb_oom(ldb);
279 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
280 &salt_principal,
281 io->ac->status->domain_data.realm,
282 "host", saltbody, NULL);
283 } else if (io->u.user_principal_name) {
284 char *user_principal_name;
285 char *p;
287 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
288 if (!user_principal_name) {
289 return ldb_oom(ldb);
292 p = strchr(user_principal_name, '@');
293 if (p) {
294 p[0] = '\0';
297 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
298 &salt_principal,
299 io->ac->status->domain_data.realm,
300 user_principal_name, NULL);
301 } else {
302 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
303 &salt_principal,
304 io->ac->status->domain_data.realm,
305 io->u.sAMAccountName, NULL);
307 if (krb5_ret) {
308 ldb_asprintf_errstring(ldb,
309 "setup_kerberos_keys: "
310 "generation of a salting principal failed: %s",
311 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
312 krb5_ret, io->ac));
313 return LDB_ERR_OPERATIONS_ERROR;
317 * create salt from salt_principal
319 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
320 salt_principal, &salt);
321 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
322 if (krb5_ret) {
323 ldb_asprintf_errstring(ldb,
324 "setup_kerberos_keys: "
325 "generation of krb5_salt failed: %s",
326 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
327 krb5_ret, io->ac));
328 return LDB_ERR_OPERATIONS_ERROR;
330 /* create a talloc copy */
331 io->g.salt = talloc_strndup(io->ac,
332 (char *)salt.saltvalue.data,
333 salt.saltvalue.length);
334 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
335 if (!io->g.salt) {
336 return ldb_oom(ldb);
338 salt.saltvalue.data = discard_const(io->g.salt);
339 salt.saltvalue.length = strlen(io->g.salt);
342 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
343 * the salt and the cleartext password
345 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
346 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
347 cleartext_data,
348 salt,
349 &key);
350 if (krb5_ret) {
351 ldb_asprintf_errstring(ldb,
352 "setup_kerberos_keys: "
353 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
354 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
355 krb5_ret, io->ac));
356 return LDB_ERR_OPERATIONS_ERROR;
358 io->g.aes_256 = data_blob_talloc(io->ac,
359 key.keyvalue.data,
360 key.keyvalue.length);
361 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
362 if (!io->g.aes_256.data) {
363 return ldb_oom(ldb);
367 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
368 * the salt and the cleartext password
370 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
371 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
372 cleartext_data,
373 salt,
374 &key);
375 if (krb5_ret) {
376 ldb_asprintf_errstring(ldb,
377 "setup_kerberos_keys: "
378 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
379 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
380 krb5_ret, io->ac));
381 return LDB_ERR_OPERATIONS_ERROR;
383 io->g.aes_128 = data_blob_talloc(io->ac,
384 key.keyvalue.data,
385 key.keyvalue.length);
386 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
387 if (!io->g.aes_128.data) {
388 return ldb_oom(ldb);
392 * create ENCTYPE_DES_CBC_MD5 key out of
393 * the salt and the cleartext password
395 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
396 ENCTYPE_DES_CBC_MD5,
397 cleartext_data,
398 salt,
399 &key);
400 if (krb5_ret) {
401 ldb_asprintf_errstring(ldb,
402 "setup_kerberos_keys: "
403 "generation of a des-cbc-md5 key failed: %s",
404 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
405 krb5_ret, io->ac));
406 return LDB_ERR_OPERATIONS_ERROR;
408 io->g.des_md5 = data_blob_talloc(io->ac,
409 key.keyvalue.data,
410 key.keyvalue.length);
411 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
412 if (!io->g.des_md5.data) {
413 return ldb_oom(ldb);
417 * create ENCTYPE_DES_CBC_CRC key out of
418 * the salt and the cleartext password
420 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
421 ENCTYPE_DES_CBC_CRC,
422 cleartext_data,
423 salt,
424 &key);
425 if (krb5_ret) {
426 ldb_asprintf_errstring(ldb,
427 "setup_kerberos_keys: "
428 "generation of a des-cbc-crc key failed: %s",
429 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
430 krb5_ret, io->ac));
431 return LDB_ERR_OPERATIONS_ERROR;
433 io->g.des_crc = data_blob_talloc(io->ac,
434 key.keyvalue.data,
435 key.keyvalue.length);
436 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
437 if (!io->g.des_crc.data) {
438 return ldb_oom(ldb);
441 return LDB_SUCCESS;
444 static int setup_primary_kerberos(struct setup_password_fields_io *io,
445 const struct supplementalCredentialsBlob *old_scb,
446 struct package_PrimaryKerberosBlob *pkb)
448 struct ldb_context *ldb;
449 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
450 struct supplementalCredentialsPackage *old_scp = NULL;
451 struct package_PrimaryKerberosBlob _old_pkb;
452 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
453 uint32_t i;
454 enum ndr_err_code ndr_err;
456 ldb = ldb_module_get_ctx(io->ac->module);
459 * prepare generation of keys
461 * ENCTYPE_DES_CBC_MD5
462 * ENCTYPE_DES_CBC_CRC
464 pkb->version = 3;
465 pkb3->salt.string = io->g.salt;
466 pkb3->num_keys = 2;
467 pkb3->keys = talloc_array(io->ac,
468 struct package_PrimaryKerberosKey3,
469 pkb3->num_keys);
470 if (!pkb3->keys) {
471 return ldb_oom(ldb);
474 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
475 pkb3->keys[0].value = &io->g.des_md5;
476 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
477 pkb3->keys[1].value = &io->g.des_crc;
479 /* initialize the old keys to zero */
480 pkb3->num_old_keys = 0;
481 pkb3->old_keys = NULL;
483 /* if there're no old keys, then we're done */
484 if (!old_scb) {
485 return LDB_SUCCESS;
488 for (i=0; i < old_scb->sub.num_packages; i++) {
489 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
490 continue;
493 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
494 continue;
497 old_scp = &old_scb->sub.packages[i];
498 break;
500 /* Primary:Kerberos element of supplementalCredentials */
501 if (old_scp) {
502 DATA_BLOB blob;
504 blob = strhex_to_data_blob(io->ac, old_scp->data);
505 if (!blob.data) {
506 return ldb_oom(ldb);
509 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
510 ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_old_pkb,
511 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
512 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
513 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
514 ldb_asprintf_errstring(ldb,
515 "setup_primary_kerberos: "
516 "failed to pull old package_PrimaryKerberosBlob: %s",
517 nt_errstr(status));
518 return LDB_ERR_OPERATIONS_ERROR;
521 if (_old_pkb.version != 3) {
522 ldb_asprintf_errstring(ldb,
523 "setup_primary_kerberos: "
524 "package_PrimaryKerberosBlob version[%u] expected[3]",
525 _old_pkb.version);
526 return LDB_ERR_OPERATIONS_ERROR;
529 old_pkb3 = &_old_pkb.ctr.ctr3;
532 /* if we didn't found the old keys we're done */
533 if (!old_pkb3) {
534 return LDB_SUCCESS;
537 /* fill in the old keys */
538 pkb3->num_old_keys = old_pkb3->num_keys;
539 pkb3->old_keys = old_pkb3->keys;
541 return LDB_SUCCESS;
544 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
545 const struct supplementalCredentialsBlob *old_scb,
546 struct package_PrimaryKerberosBlob *pkb)
548 struct ldb_context *ldb;
549 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
550 struct supplementalCredentialsPackage *old_scp = NULL;
551 struct package_PrimaryKerberosBlob _old_pkb;
552 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
553 uint32_t i;
554 enum ndr_err_code ndr_err;
556 ldb = ldb_module_get_ctx(io->ac->module);
559 * prepare generation of keys
561 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
562 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
563 * ENCTYPE_DES_CBC_MD5
564 * ENCTYPE_DES_CBC_CRC
566 pkb->version = 4;
567 pkb4->salt.string = io->g.salt;
568 pkb4->default_iteration_count = 4096;
569 pkb4->num_keys = 4;
571 pkb4->keys = talloc_array(io->ac,
572 struct package_PrimaryKerberosKey4,
573 pkb4->num_keys);
574 if (!pkb4->keys) {
575 return ldb_oom(ldb);
578 pkb4->keys[0].iteration_count = 4096;
579 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
580 pkb4->keys[0].value = &io->g.aes_256;
581 pkb4->keys[1].iteration_count = 4096;
582 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
583 pkb4->keys[1].value = &io->g.aes_128;
584 pkb4->keys[2].iteration_count = 4096;
585 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
586 pkb4->keys[2].value = &io->g.des_md5;
587 pkb4->keys[3].iteration_count = 4096;
588 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
589 pkb4->keys[3].value = &io->g.des_crc;
591 /* initialize the old keys to zero */
592 pkb4->num_old_keys = 0;
593 pkb4->old_keys = NULL;
594 pkb4->num_older_keys = 0;
595 pkb4->older_keys = NULL;
597 /* if there're no old keys, then we're done */
598 if (!old_scb) {
599 return LDB_SUCCESS;
602 for (i=0; i < old_scb->sub.num_packages; i++) {
603 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
604 continue;
607 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
608 continue;
611 old_scp = &old_scb->sub.packages[i];
612 break;
614 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
615 if (old_scp) {
616 DATA_BLOB blob;
618 blob = strhex_to_data_blob(io->ac, old_scp->data);
619 if (!blob.data) {
620 return ldb_oom(ldb);
623 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
624 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
625 &_old_pkb,
626 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
627 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
628 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
629 ldb_asprintf_errstring(ldb,
630 "setup_primary_kerberos_newer: "
631 "failed to pull old package_PrimaryKerberosBlob: %s",
632 nt_errstr(status));
633 return LDB_ERR_OPERATIONS_ERROR;
636 if (_old_pkb.version != 4) {
637 ldb_asprintf_errstring(ldb,
638 "setup_primary_kerberos_newer: "
639 "package_PrimaryKerberosBlob version[%u] expected[4]",
640 _old_pkb.version);
641 return LDB_ERR_OPERATIONS_ERROR;
644 old_pkb4 = &_old_pkb.ctr.ctr4;
647 /* if we didn't found the old keys we're done */
648 if (!old_pkb4) {
649 return LDB_SUCCESS;
652 /* fill in the old keys */
653 pkb4->num_old_keys = old_pkb4->num_keys;
654 pkb4->old_keys = old_pkb4->keys;
655 pkb4->num_older_keys = old_pkb4->num_old_keys;
656 pkb4->older_keys = old_pkb4->old_keys;
658 return LDB_SUCCESS;
661 static int setup_primary_wdigest(struct setup_password_fields_io *io,
662 const struct supplementalCredentialsBlob *old_scb,
663 struct package_PrimaryWDigestBlob *pdb)
665 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
666 DATA_BLOB sAMAccountName;
667 DATA_BLOB sAMAccountName_l;
668 DATA_BLOB sAMAccountName_u;
669 const char *user_principal_name = io->u.user_principal_name;
670 DATA_BLOB userPrincipalName;
671 DATA_BLOB userPrincipalName_l;
672 DATA_BLOB userPrincipalName_u;
673 DATA_BLOB netbios_domain;
674 DATA_BLOB netbios_domain_l;
675 DATA_BLOB netbios_domain_u;
676 DATA_BLOB dns_domain;
677 DATA_BLOB dns_domain_l;
678 DATA_BLOB dns_domain_u;
679 DATA_BLOB digest;
680 DATA_BLOB delim;
681 DATA_BLOB backslash;
682 uint8_t i;
683 struct {
684 DATA_BLOB *user;
685 DATA_BLOB *realm;
686 DATA_BLOB *nt4dom;
687 } wdigest[] = {
689 * See
690 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
691 * for what precalculated hashes are supposed to be stored...
693 * I can't reproduce all values which should contain "Digest" as realm,
694 * am I doing something wrong or is w2k3 just broken...?
696 * W2K3 fills in following for a user:
698 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
699 * sAMAccountName: NewUser2Sam
700 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
702 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
703 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
704 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
705 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
706 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
707 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
708 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
709 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
710 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
711 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
712 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
713 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
714 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
715 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
716 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
717 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
718 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
719 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
720 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
721 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
722 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
723 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
724 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
725 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
726 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
727 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
728 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
729 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
730 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
732 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
733 * sAMAccountName: NewUser2Sam
735 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
736 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
737 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
738 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
739 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
740 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
741 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
742 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
743 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
744 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
745 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
746 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
747 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
748 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
749 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
750 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
751 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
752 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
753 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
754 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
755 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
756 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
757 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
758 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
759 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
760 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
761 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
762 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
763 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
767 * sAMAccountName, netbios_domain
770 .user = &sAMAccountName,
771 .realm = &netbios_domain,
774 .user = &sAMAccountName_l,
775 .realm = &netbios_domain_l,
778 .user = &sAMAccountName_u,
779 .realm = &netbios_domain_u,
782 .user = &sAMAccountName,
783 .realm = &netbios_domain_u,
786 .user = &sAMAccountName,
787 .realm = &netbios_domain_l,
790 .user = &sAMAccountName_u,
791 .realm = &netbios_domain_l,
794 .user = &sAMAccountName_l,
795 .realm = &netbios_domain_u,
798 * sAMAccountName, dns_domain
801 .user = &sAMAccountName,
802 .realm = &dns_domain,
805 .user = &sAMAccountName_l,
806 .realm = &dns_domain_l,
809 .user = &sAMAccountName_u,
810 .realm = &dns_domain_u,
813 .user = &sAMAccountName,
814 .realm = &dns_domain_u,
817 .user = &sAMAccountName,
818 .realm = &dns_domain_l,
821 .user = &sAMAccountName_u,
822 .realm = &dns_domain_l,
825 .user = &sAMAccountName_l,
826 .realm = &dns_domain_u,
829 * userPrincipalName, no realm
832 .user = &userPrincipalName,
836 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
837 * the fallback to the sAMAccountName based userPrincipalName is correct
839 .user = &userPrincipalName_l,
842 .user = &userPrincipalName_u,
845 * nt4dom\sAMAccountName, no realm
848 .user = &sAMAccountName,
849 .nt4dom = &netbios_domain
852 .user = &sAMAccountName_l,
853 .nt4dom = &netbios_domain_l
856 .user = &sAMAccountName_u,
857 .nt4dom = &netbios_domain_u
861 * the following ones are guessed depending on the technet2 article
862 * but not reproducable on a w2k3 server
864 /* sAMAccountName with "Digest" realm */
866 .user = &sAMAccountName,
867 .realm = &digest
870 .user = &sAMAccountName_l,
871 .realm = &digest
874 .user = &sAMAccountName_u,
875 .realm = &digest
877 /* userPrincipalName with "Digest" realm */
879 .user = &userPrincipalName,
880 .realm = &digest
883 .user = &userPrincipalName_l,
884 .realm = &digest
887 .user = &userPrincipalName_u,
888 .realm = &digest
890 /* nt4dom\\sAMAccountName with "Digest" realm */
892 .user = &sAMAccountName,
893 .nt4dom = &netbios_domain,
894 .realm = &digest
897 .user = &sAMAccountName_l,
898 .nt4dom = &netbios_domain_l,
899 .realm = &digest
902 .user = &sAMAccountName_u,
903 .nt4dom = &netbios_domain_u,
904 .realm = &digest
908 /* prepare DATA_BLOB's used in the combinations array */
909 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
910 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
911 if (!sAMAccountName_l.data) {
912 return ldb_oom(ldb);
914 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
915 if (!sAMAccountName_u.data) {
916 return ldb_oom(ldb);
919 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
920 if (!user_principal_name) {
921 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
922 io->u.sAMAccountName,
923 io->ac->status->domain_data.dns_domain);
924 if (!user_principal_name) {
925 return ldb_oom(ldb);
928 userPrincipalName = data_blob_string_const(user_principal_name);
929 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
930 if (!userPrincipalName_l.data) {
931 return ldb_oom(ldb);
933 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
934 if (!userPrincipalName_u.data) {
935 return ldb_oom(ldb);
938 netbios_domain = data_blob_string_const(io->ac->status->domain_data.netbios_domain);
939 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac,
940 io->ac->status->domain_data.netbios_domain));
941 if (!netbios_domain_l.data) {
942 return ldb_oom(ldb);
944 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac,
945 io->ac->status->domain_data.netbios_domain));
946 if (!netbios_domain_u.data) {
947 return ldb_oom(ldb);
950 dns_domain = data_blob_string_const(io->ac->status->domain_data.dns_domain);
951 dns_domain_l = data_blob_string_const(io->ac->status->domain_data.dns_domain);
952 dns_domain_u = data_blob_string_const(io->ac->status->domain_data.realm);
954 digest = data_blob_string_const("Digest");
956 delim = data_blob_string_const(":");
957 backslash = data_blob_string_const("\\");
959 pdb->num_hashes = ARRAY_SIZE(wdigest);
960 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
961 pdb->num_hashes);
962 if (!pdb->hashes) {
963 return ldb_oom(ldb);
966 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
967 struct MD5Context md5;
968 MD5Init(&md5);
969 if (wdigest[i].nt4dom) {
970 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
971 MD5Update(&md5, backslash.data, backslash.length);
973 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
974 MD5Update(&md5, delim.data, delim.length);
975 if (wdigest[i].realm) {
976 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
978 MD5Update(&md5, delim.data, delim.length);
979 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
980 MD5Final(pdb->hashes[i].hash, &md5);
983 return LDB_SUCCESS;
986 static int setup_supplemental_field(struct setup_password_fields_io *io)
988 struct ldb_context *ldb;
989 struct supplementalCredentialsBlob scb;
990 struct supplementalCredentialsBlob _old_scb;
991 struct supplementalCredentialsBlob *old_scb = NULL;
992 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
993 uint32_t num_names = 0;
994 const char *names[1+4];
995 uint32_t num_packages = 0;
996 struct supplementalCredentialsPackage packages[1+4];
997 /* Packages */
998 struct supplementalCredentialsPackage *pp = NULL;
999 struct package_PackagesBlob pb;
1000 DATA_BLOB pb_blob;
1001 char *pb_hexstr;
1002 /* Primary:Kerberos-Newer-Keys */
1003 const char **nkn = NULL;
1004 struct supplementalCredentialsPackage *pkn = NULL;
1005 struct package_PrimaryKerberosBlob pknb;
1006 DATA_BLOB pknb_blob;
1007 char *pknb_hexstr;
1008 /* Primary:Kerberos */
1009 const char **nk = NULL;
1010 struct supplementalCredentialsPackage *pk = NULL;
1011 struct package_PrimaryKerberosBlob pkb;
1012 DATA_BLOB pkb_blob;
1013 char *pkb_hexstr;
1014 /* Primary:WDigest */
1015 const char **nd = NULL;
1016 struct supplementalCredentialsPackage *pd = NULL;
1017 struct package_PrimaryWDigestBlob pdb;
1018 DATA_BLOB pdb_blob;
1019 char *pdb_hexstr;
1020 /* Primary:CLEARTEXT */
1021 const char **nc = NULL;
1022 struct supplementalCredentialsPackage *pc = NULL;
1023 struct package_PrimaryCLEARTEXTBlob pcb;
1024 DATA_BLOB pcb_blob;
1025 char *pcb_hexstr;
1026 int ret;
1027 enum ndr_err_code ndr_err;
1028 uint8_t zero16[16];
1029 bool do_newer_keys = false;
1030 bool do_cleartext = false;
1032 ZERO_STRUCT(zero16);
1033 ZERO_STRUCT(names);
1035 ldb = ldb_module_get_ctx(io->ac->module);
1037 if (!io->n.cleartext_utf8) {
1039 * when we don't have a cleartext password
1040 * we can't setup a supplementalCredential value
1042 return LDB_SUCCESS;
1045 /* if there's an old supplementaCredentials blob then parse it */
1046 if (io->o.supplemental) {
1047 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1048 &_old_scb,
1049 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1050 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1051 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1052 ldb_asprintf_errstring(ldb,
1053 "setup_supplemental_field: "
1054 "failed to pull old supplementalCredentialsBlob: %s",
1055 nt_errstr(status));
1056 return LDB_ERR_OPERATIONS_ERROR;
1059 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1060 old_scb = &_old_scb;
1061 } else {
1062 ldb_debug(ldb, LDB_DEBUG_ERROR,
1063 "setup_supplemental_field: "
1064 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1065 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1068 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1069 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1071 if (io->ac->status->domain_data.store_cleartext &&
1072 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1073 do_cleartext = true;
1077 * The ordering is this
1079 * Primary:Kerberos-Newer-Keys (optional)
1080 * Primary:Kerberos
1081 * Primary:WDigest
1082 * Primary:CLEARTEXT (optional)
1084 * And the 'Packages' package is insert before the last
1085 * other package.
1087 if (do_newer_keys) {
1088 /* Primary:Kerberos-Newer-Keys */
1089 nkn = &names[num_names++];
1090 pkn = &packages[num_packages++];
1093 /* Primary:Kerberos */
1094 nk = &names[num_names++];
1095 pk = &packages[num_packages++];
1097 if (!do_cleartext) {
1098 /* Packages */
1099 pp = &packages[num_packages++];
1102 /* Primary:WDigest */
1103 nd = &names[num_names++];
1104 pd = &packages[num_packages++];
1106 if (do_cleartext) {
1107 /* Packages */
1108 pp = &packages[num_packages++];
1110 /* Primary:CLEARTEXT */
1111 nc = &names[num_names++];
1112 pc = &packages[num_packages++];
1115 if (pkn) {
1117 * setup 'Primary:Kerberos-Newer-Keys' element
1119 *nkn = "Kerberos-Newer-Keys";
1121 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1122 if (ret != LDB_SUCCESS) {
1123 return ret;
1126 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1127 &pknb,
1128 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1129 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1130 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1131 ldb_asprintf_errstring(ldb,
1132 "setup_supplemental_field: "
1133 "failed to push package_PrimaryKerberosNeverBlob: %s",
1134 nt_errstr(status));
1135 return LDB_ERR_OPERATIONS_ERROR;
1137 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1138 if (!pknb_hexstr) {
1139 return ldb_oom(ldb);
1141 pkn->name = "Primary:Kerberos-Newer-Keys";
1142 pkn->reserved = 1;
1143 pkn->data = pknb_hexstr;
1147 * setup 'Primary:Kerberos' element
1149 *nk = "Kerberos";
1151 ret = setup_primary_kerberos(io, old_scb, &pkb);
1152 if (ret != LDB_SUCCESS) {
1153 return ret;
1156 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1157 &pkb,
1158 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1159 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1160 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1161 ldb_asprintf_errstring(ldb,
1162 "setup_supplemental_field: "
1163 "failed to push package_PrimaryKerberosBlob: %s",
1164 nt_errstr(status));
1165 return LDB_ERR_OPERATIONS_ERROR;
1167 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1168 if (!pkb_hexstr) {
1169 return ldb_oom(ldb);
1171 pk->name = "Primary:Kerberos";
1172 pk->reserved = 1;
1173 pk->data = pkb_hexstr;
1176 * setup 'Primary:WDigest' element
1178 *nd = "WDigest";
1180 ret = setup_primary_wdigest(io, old_scb, &pdb);
1181 if (ret != LDB_SUCCESS) {
1182 return ret;
1185 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1186 &pdb,
1187 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1188 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1189 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1190 ldb_asprintf_errstring(ldb,
1191 "setup_supplemental_field: "
1192 "failed to push package_PrimaryWDigestBlob: %s",
1193 nt_errstr(status));
1194 return LDB_ERR_OPERATIONS_ERROR;
1196 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1197 if (!pdb_hexstr) {
1198 return ldb_oom(ldb);
1200 pd->name = "Primary:WDigest";
1201 pd->reserved = 1;
1202 pd->data = pdb_hexstr;
1205 * setup 'Primary:CLEARTEXT' element
1207 if (pc) {
1208 *nc = "CLEARTEXT";
1210 pcb.cleartext = *io->n.cleartext_utf16;
1212 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1213 &pcb,
1214 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1215 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1216 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1217 ldb_asprintf_errstring(ldb,
1218 "setup_supplemental_field: "
1219 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1220 nt_errstr(status));
1221 return LDB_ERR_OPERATIONS_ERROR;
1223 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1224 if (!pcb_hexstr) {
1225 return ldb_oom(ldb);
1227 pc->name = "Primary:CLEARTEXT";
1228 pc->reserved = 1;
1229 pc->data = pcb_hexstr;
1233 * setup 'Packages' element
1235 pb.names = names;
1236 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1237 &pb,
1238 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1239 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1240 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1241 ldb_asprintf_errstring(ldb,
1242 "setup_supplemental_field: "
1243 "failed to push package_PackagesBlob: %s",
1244 nt_errstr(status));
1245 return LDB_ERR_OPERATIONS_ERROR;
1247 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1248 if (!pb_hexstr) {
1249 return ldb_oom(ldb);
1251 pp->name = "Packages";
1252 pp->reserved = 2;
1253 pp->data = pb_hexstr;
1256 * setup 'supplementalCredentials' value
1258 ZERO_STRUCT(scb);
1259 scb.sub.num_packages = num_packages;
1260 scb.sub.packages = packages;
1262 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1263 &scb,
1264 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
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 supplementalCredentialsBlob: %s",
1270 nt_errstr(status));
1271 return LDB_ERR_OPERATIONS_ERROR;
1274 return LDB_SUCCESS;
1277 static int setup_last_set_field(struct setup_password_fields_io *io)
1279 /* set it as now */
1280 unix_to_nt_time(&io->g.last_set, time(NULL));
1282 return LDB_SUCCESS;
1285 static int setup_given_passwords(struct setup_password_fields_io *io,
1286 struct setup_password_fields_given *g)
1288 struct ldb_context *ldb;
1289 bool ok;
1291 ldb = ldb_module_get_ctx(io->ac->module);
1293 if (g->cleartext_utf8) {
1294 char **cleartext_utf16_str;
1295 struct ldb_val *cleartext_utf16_blob;
1296 size_t converted_pw_len;
1298 cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1299 if (!cleartext_utf16_blob) {
1300 return ldb_oom(ldb);
1302 if (!convert_string_talloc(io->ac,
1303 CH_UTF8, CH_UTF16,
1304 g->cleartext_utf8->data,
1305 g->cleartext_utf8->length,
1306 (void *)&cleartext_utf16_str,
1307 &converted_pw_len, false)) {
1308 ldb_asprintf_errstring(ldb,
1309 "setup_password_fields: "
1310 "failed to generate UTF16 password from cleartext UTF8 password");
1311 return LDB_ERR_OPERATIONS_ERROR;
1313 *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str,
1314 converted_pw_len);
1315 g->cleartext_utf16 = cleartext_utf16_blob;
1316 } else if (g->cleartext_utf16) {
1317 char *cleartext_utf8_str;
1318 struct ldb_val *cleartext_utf8_blob;
1319 size_t converted_pw_len;
1321 cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1322 if (!cleartext_utf8_blob) {
1323 return ldb_oom(ldb);
1325 if (!convert_string_talloc(io->ac,
1326 CH_UTF16MUNGED, CH_UTF8,
1327 g->cleartext_utf16->data,
1328 g->cleartext_utf16->length,
1329 (void *)&cleartext_utf8_str,
1330 &converted_pw_len, false)) {
1331 /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
1332 talloc_free(cleartext_utf8_blob);
1333 } else {
1334 *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str,
1335 converted_pw_len);
1336 g->cleartext_utf8 = cleartext_utf8_blob;
1340 if (g->cleartext_utf16) {
1341 struct samr_Password *nt_hash;
1343 nt_hash = talloc(io->ac, struct samr_Password);
1344 if (!nt_hash) {
1345 return ldb_oom(ldb);
1347 g->nt_hash = nt_hash;
1349 /* compute the new nt hash */
1350 mdfour(nt_hash->hash,
1351 g->cleartext_utf16->data,
1352 g->cleartext_utf16->length);
1355 if (g->cleartext_utf8) {
1356 struct samr_Password *lm_hash;
1358 lm_hash = talloc(io->ac, struct samr_Password);
1359 if (!lm_hash) {
1360 return ldb_oom(ldb);
1363 /* compute the new lm hash */
1364 ok = E_deshash((char *)g->cleartext_utf8->data, lm_hash->hash);
1365 if (ok) {
1366 g->lm_hash = lm_hash;
1367 } else {
1368 talloc_free(lm_hash);
1372 return LDB_SUCCESS;
1375 static int setup_password_fields(struct setup_password_fields_io *io)
1377 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
1378 struct loadparm_context *lp_ctx =
1379 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1380 struct loadparm_context);
1381 int ret;
1383 /* transform the old password (for password changes) */
1384 ret = setup_given_passwords(io, &io->og);
1385 if (ret != LDB_SUCCESS) {
1386 return ret;
1389 /* transform the new password */
1390 ret = setup_given_passwords(io, &io->n);
1391 if (ret != LDB_SUCCESS) {
1392 return ret;
1395 if (io->n.cleartext_utf8) {
1396 ret = setup_kerberos_keys(io);
1397 if (ret != LDB_SUCCESS) {
1398 return ret;
1402 ret = setup_nt_fields(io);
1403 if (ret != LDB_SUCCESS) {
1404 return ret;
1407 if (lpcfg_lanman_auth(lp_ctx)) {
1408 ret = setup_lm_fields(io);
1409 if (ret != LDB_SUCCESS) {
1410 return ret;
1412 } else {
1413 io->g.lm_hash = NULL;
1414 io->g.lm_history_len = 0;
1417 ret = setup_supplemental_field(io);
1418 if (ret != LDB_SUCCESS) {
1419 return ret;
1422 ret = setup_last_set_field(io);
1423 if (ret != LDB_SUCCESS) {
1424 return ret;
1427 return LDB_SUCCESS;
1430 static int check_password_restrictions(struct setup_password_fields_io *io)
1432 struct ldb_context *ldb;
1433 int ret;
1434 enum samr_ValidationStatus stat;
1436 ldb = ldb_module_get_ctx(io->ac->module);
1438 /* First check the old password is correct, for password changes */
1439 if (!io->ac->pwd_reset) {
1440 bool nt_hash_checked = false;
1442 /* we need the old nt or lm hash given by the client */
1443 if (!io->og.nt_hash && !io->og.lm_hash) {
1444 ldb_asprintf_errstring(ldb,
1445 "check_password_restrictions: "
1446 "You need to provide the old password in order "
1447 "to change it!");
1448 return LDB_ERR_UNWILLING_TO_PERFORM;
1451 /* The password modify through the NT hash is encouraged and
1452 has no problems at all */
1453 if (io->og.nt_hash) {
1454 if (!io->o.nt_hash) {
1455 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1456 ldb_asprintf_errstring(ldb,
1457 "%08X: %s - check_password_restrictions: "
1458 "There's no old nt_hash, which is needed "
1459 "in order to change your password!",
1460 W_ERROR_V(WERR_INVALID_PASSWORD),
1461 ldb_strerror(ret));
1462 return ret;
1465 if (memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
1466 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1467 ldb_asprintf_errstring(ldb,
1468 "%08X: %s - check_password_restrictions: "
1469 "The old password specified doesn't match!",
1470 W_ERROR_V(WERR_INVALID_PASSWORD),
1471 ldb_strerror(ret));
1472 return ret;
1475 nt_hash_checked = true;
1478 /* But it is also possible to change a password by the LM hash
1479 * alone for compatibility reasons. This check is optional if
1480 * the NT hash was already checked - otherwise it's mandatory.
1481 * (as the SAMR operations request it). */
1482 if (io->og.lm_hash) {
1483 if (!io->o.lm_hash && !nt_hash_checked) {
1484 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1485 ldb_asprintf_errstring(ldb,
1486 "%08X: %s - check_password_restrictions: "
1487 "There's no old lm_hash, which is needed "
1488 "in order to change your password!",
1489 W_ERROR_V(WERR_INVALID_PASSWORD),
1490 ldb_strerror(ret));
1491 return ret;
1494 if (io->o.lm_hash &&
1495 memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0) {
1496 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1497 ldb_asprintf_errstring(ldb,
1498 "%08X: %s - check_password_restrictions: "
1499 "The old password specified doesn't match!",
1500 W_ERROR_V(WERR_INVALID_PASSWORD),
1501 ldb_strerror(ret));
1502 return ret;
1507 if (io->u.restrictions == 0) {
1508 /* FIXME: Is this right? */
1509 return LDB_SUCCESS;
1513 * Fundamental password checks done by the call
1514 * "samdb_check_password".
1515 * It is also in use by "dcesrv_samr_ValidatePassword".
1517 if (io->n.cleartext_utf8 != NULL) {
1518 stat = samdb_check_password(io->n.cleartext_utf8,
1519 io->ac->status->domain_data.pwdProperties,
1520 io->ac->status->domain_data.minPwdLength);
1521 switch (stat) {
1522 case SAMR_VALIDATION_STATUS_SUCCESS:
1523 /* perfect -> proceed! */
1524 break;
1526 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
1527 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1528 ldb_asprintf_errstring(ldb,
1529 "%08X: %s - check_password_restrictions: "
1530 "the password is too short. It should be equal or longer than %u characters!",
1531 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1532 ldb_strerror(ret),
1533 io->ac->status->domain_data.minPwdLength);
1534 io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1535 return ret;
1537 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
1538 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1539 ldb_asprintf_errstring(ldb,
1540 "%08X: %s - check_password_restrictions: "
1541 "the password does not meet the complexity criterias!",
1542 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1543 ldb_strerror(ret));
1544 io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1545 return ret;
1547 default:
1548 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1549 ldb_asprintf_errstring(ldb,
1550 "%08X: %s - check_password_restrictions: "
1551 "the password doesn't fit by a certain reason!",
1552 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1553 ldb_strerror(ret));
1554 return ret;
1558 if (io->ac->pwd_reset) {
1559 return LDB_SUCCESS;
1562 if (io->n.nt_hash) {
1563 uint32_t i;
1565 /* checks the NT hash password history */
1566 for (i = 0; i < io->o.nt_history_len; i++) {
1567 ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
1568 if (ret == 0) {
1569 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1570 ldb_asprintf_errstring(ldb,
1571 "%08X: %s - check_password_restrictions: "
1572 "the password was already used (in history)!",
1573 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1574 ldb_strerror(ret));
1575 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1576 return ret;
1581 if (io->n.lm_hash) {
1582 uint32_t i;
1584 /* checks the LM hash password history */
1585 for (i = 0; i < io->o.lm_history_len; i++) {
1586 ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
1587 if (ret == 0) {
1588 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1589 ldb_asprintf_errstring(ldb,
1590 "%08X: %s - check_password_restrictions: "
1591 "the password was already used (in history)!",
1592 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1593 ldb_strerror(ret));
1594 io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1595 return ret;
1600 /* are all password changes disallowed? */
1601 if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1602 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1603 ldb_asprintf_errstring(ldb,
1604 "%08X: %s - check_password_restrictions: "
1605 "password changes disabled!",
1606 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1607 ldb_strerror(ret));
1608 return ret;
1611 /* can this user change the password? */
1612 if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
1613 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1614 ldb_asprintf_errstring(ldb,
1615 "%08X: %s - check_password_restrictions: "
1616 "password can't be changed on this account!",
1617 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1618 ldb_strerror(ret));
1619 return ret;
1622 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
1623 if (io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) {
1624 ret = LDB_ERR_CONSTRAINT_VIOLATION;
1625 ldb_asprintf_errstring(ldb,
1626 "%08X: %s - check_password_restrictions: "
1627 "password is too young to change!",
1628 W_ERROR_V(WERR_PASSWORD_RESTRICTION),
1629 ldb_strerror(ret));
1630 return ret;
1633 return LDB_SUCCESS;
1637 * This is intended for use by the "password_hash" module since there
1638 * password changes can be specified through one message element with the
1639 * new password (to set) and another one with the old password (to unset).
1641 * The first which sets a password (new value) can have flags
1642 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
1643 * for entries). The latter (old value) has always specified
1644 * LDB_FLAG_MOD_DELETE.
1646 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
1647 * matching message elements are malformed in respect to the set/change rules.
1648 * Otherwise it returns LDB_SUCCESS.
1650 static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg,
1651 const char *name,
1652 enum ldb_request_type operation,
1653 const struct ldb_val **new_val,
1654 const struct ldb_val **old_val)
1656 unsigned int i;
1658 *new_val = NULL;
1659 *old_val = NULL;
1661 if (msg == NULL) {
1662 return LDB_SUCCESS;
1665 for (i = 0; i < msg->num_elements; i++) {
1666 if (ldb_attr_cmp(msg->elements[i].name, name) != 0) {
1667 continue;
1670 if ((operation == LDB_MODIFY) &&
1671 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE)) {
1672 /* 0 values are allowed */
1673 if (msg->elements[i].num_values == 1) {
1674 *old_val = &msg->elements[i].values[0];
1675 } else if (msg->elements[i].num_values > 1) {
1676 return LDB_ERR_CONSTRAINT_VIOLATION;
1678 } else if ((operation == LDB_MODIFY) &&
1679 (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_REPLACE)) {
1680 if (msg->elements[i].num_values > 0) {
1681 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
1682 } else {
1683 return LDB_ERR_UNWILLING_TO_PERFORM;
1685 } else {
1686 /* Add operations and LDB_FLAG_MOD_ADD */
1687 if (msg->elements[i].num_values > 0) {
1688 *new_val = &msg->elements[i].values[msg->elements[i].num_values - 1];
1689 } else {
1690 return LDB_ERR_CONSTRAINT_VIOLATION;
1695 return LDB_SUCCESS;
1698 static int setup_io(struct ph_context *ac,
1699 const struct ldb_message *orig_msg,
1700 const struct ldb_message *searched_msg,
1701 struct setup_password_fields_io *io)
1703 const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
1704 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1705 struct loadparm_context *lp_ctx =
1706 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1707 struct loadparm_context);
1708 int ret;
1710 ZERO_STRUCTP(io);
1712 /* Some operations below require kerberos contexts */
1714 if (smb_krb5_init_context(ac,
1715 ldb_get_event_context(ldb),
1716 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
1717 &io->smb_krb5_context) != 0) {
1718 return ldb_operr(ldb);
1721 io->ac = ac;
1723 io->u.userAccountControl = ldb_msg_find_attr_as_uint(searched_msg,
1724 "userAccountControl", 0);
1725 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
1726 io->u.sAMAccountName = ldb_msg_find_attr_as_string(searched_msg,
1727 "sAMAccountName", NULL);
1728 io->u.user_principal_name = ldb_msg_find_attr_as_string(searched_msg,
1729 "userPrincipalName", NULL);
1730 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
1732 if (io->u.sAMAccountName == NULL) {
1733 ldb_asprintf_errstring(ldb,
1734 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
1735 ldb_dn_get_linearized(searched_msg->dn));
1737 return LDB_ERR_CONSTRAINT_VIOLATION;
1740 /* Only non-trust accounts have restrictions (possibly this test is the
1741 * wrong way around, but we like to be restrictive if possible */
1742 io->u.restrictions = !(io->u.userAccountControl
1743 & (UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT
1744 | UF_SERVER_TRUST_ACCOUNT));
1746 if ((io->u.userAccountControl & UF_PASSWD_NOTREQD) != 0) {
1747 /* see [MS-ADTS] 2.2.15 */
1748 io->u.restrictions = 0;
1751 if (ac->userPassword) {
1752 ret = msg_find_old_and_new_pwd_val(orig_msg, "userPassword",
1753 ac->req->operation,
1754 &io->n.cleartext_utf8,
1755 &io->og.cleartext_utf8);
1756 if (ret != LDB_SUCCESS) {
1757 ldb_asprintf_errstring(ldb,
1758 "setup_io: "
1759 "it's only allowed to set the old password once!");
1760 return ret;
1764 ret = msg_find_old_and_new_pwd_val(orig_msg, "clearTextPassword",
1765 ac->req->operation,
1766 &io->n.cleartext_utf16,
1767 &io->og.cleartext_utf16);
1768 if (ret != LDB_SUCCESS) {
1769 ldb_asprintf_errstring(ldb,
1770 "setup_io: "
1771 "it's only allowed to set the old password once!");
1772 return ret;
1775 /* this rather strange looking piece of code is there to
1776 handle a ldap client setting a password remotely using the
1777 unicodePwd ldap field. The syntax is that the password is
1778 in UTF-16LE, with a " at either end. Unfortunately the
1779 unicodePwd field is also used to store the nt hashes
1780 internally in Samba, and is used in the nt hash format on
1781 the wire in DRS replication, so we have a single name for
1782 two distinct values. The code below leaves us with a small
1783 chance (less than 1 in 2^32) of a mixup, if someone manages
1784 to create a MD4 hash which starts and ends in 0x22 0x00, as
1785 that would then be treated as a UTF16 password rather than
1786 a nthash */
1788 ret = msg_find_old_and_new_pwd_val(orig_msg, "unicodePwd",
1789 ac->req->operation,
1790 &quoted_utf16,
1791 &old_quoted_utf16);
1792 if (ret != LDB_SUCCESS) {
1793 ldb_asprintf_errstring(ldb,
1794 "setup_io: "
1795 "it's only allowed to set the old password once!");
1796 return ret;
1799 /* Checks and converts the actual "unicodePwd" attribute */
1800 if (quoted_utf16 &&
1801 quoted_utf16->length >= 4 &&
1802 quoted_utf16->data[0] == '"' &&
1803 quoted_utf16->data[1] == 0 &&
1804 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
1805 quoted_utf16->data[quoted_utf16->length-1] == 0) {
1806 struct ldb_val *quoted_utf16_2;
1808 if (io->n.cleartext_utf16) {
1809 /* refuse the change if someone wants to change with
1810 with both UTF16 possibilities at the same time... */
1811 ldb_asprintf_errstring(ldb,
1812 "setup_io: "
1813 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1814 return LDB_ERR_UNWILLING_TO_PERFORM;
1818 * adapt the quoted UTF16 string to be a real
1819 * cleartext one
1821 quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1822 if (quoted_utf16_2 == NULL) {
1823 return ldb_oom(ldb);
1826 quoted_utf16_2->data = quoted_utf16->data + 2;
1827 quoted_utf16_2->length = quoted_utf16->length-4;
1828 io->n.cleartext_utf16 = quoted_utf16_2;
1829 io->n.nt_hash = NULL;
1831 } else if (quoted_utf16) {
1832 /* We have only the hash available -> so no plaintext here */
1833 if (!ac->hash_values) {
1834 /* refuse the change if someone wants to change
1835 the hash without control specified... */
1836 ldb_asprintf_errstring(ldb,
1837 "setup_io: "
1838 "it's not allowed to set the NT hash password directly'");
1839 /* this looks odd but this is what Windows does:
1840 returns "UNWILLING_TO_PERFORM" on wrong
1841 password sets and "CONSTRAINT_VIOLATION" on
1842 wrong password changes. */
1843 if (old_quoted_utf16 == NULL) {
1844 return LDB_ERR_UNWILLING_TO_PERFORM;
1847 return LDB_ERR_CONSTRAINT_VIOLATION;
1850 io->n.nt_hash = talloc(io->ac, struct samr_Password);
1851 memcpy(io->n.nt_hash->hash, quoted_utf16->data,
1852 MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
1855 /* Checks and converts the previous "unicodePwd" attribute */
1856 if (old_quoted_utf16 &&
1857 old_quoted_utf16->length >= 4 &&
1858 old_quoted_utf16->data[0] == '"' &&
1859 old_quoted_utf16->data[1] == 0 &&
1860 old_quoted_utf16->data[old_quoted_utf16->length-2] == '"' &&
1861 old_quoted_utf16->data[old_quoted_utf16->length-1] == 0) {
1862 struct ldb_val *old_quoted_utf16_2;
1864 if (io->og.cleartext_utf16) {
1865 /* refuse the change if someone wants to change with
1866 both UTF16 possibilities at the same time... */
1867 ldb_asprintf_errstring(ldb,
1868 "setup_io: "
1869 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
1870 return LDB_ERR_UNWILLING_TO_PERFORM;
1874 * adapt the quoted UTF16 string to be a real
1875 * cleartext one
1877 old_quoted_utf16_2 = talloc(io->ac, struct ldb_val);
1878 if (old_quoted_utf16_2 == NULL) {
1879 return ldb_oom(ldb);
1882 old_quoted_utf16_2->data = old_quoted_utf16->data + 2;
1883 old_quoted_utf16_2->length = old_quoted_utf16->length-4;
1885 io->og.cleartext_utf16 = old_quoted_utf16_2;
1886 io->og.nt_hash = NULL;
1887 } else if (old_quoted_utf16) {
1888 /* We have only the hash available -> so no plaintext here */
1889 if (!ac->hash_values) {
1890 /* refuse the change if someone wants to change
1891 the hash without control specified... */
1892 ldb_asprintf_errstring(ldb,
1893 "setup_io: "
1894 "it's not allowed to set the NT hash password directly'");
1895 return LDB_ERR_UNWILLING_TO_PERFORM;
1898 io->og.nt_hash = talloc(io->ac, struct samr_Password);
1899 memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
1900 MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
1903 /* Handles the "dBCSPwd" attribute (LM hash) */
1904 io->n.lm_hash = NULL; io->og.lm_hash = NULL;
1905 ret = msg_find_old_and_new_pwd_val(orig_msg, "dBCSPwd",
1906 ac->req->operation,
1907 &lm_hash, &old_lm_hash);
1908 if (ret != LDB_SUCCESS) {
1909 ldb_asprintf_errstring(ldb,
1910 "setup_io: "
1911 "it's only allowed to set the old password once!");
1912 return ret;
1915 if (((lm_hash != NULL) || (old_lm_hash != NULL)) && (!ac->hash_values)) {
1916 /* refuse the change if someone wants to change the hash
1917 without control specified... */
1918 ldb_asprintf_errstring(ldb,
1919 "setup_io: "
1920 "it's not allowed to set the LM hash password directly'");
1921 return LDB_ERR_UNWILLING_TO_PERFORM;
1924 if (lpcfg_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
1925 io->n.lm_hash = talloc(io->ac, struct samr_Password);
1926 memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
1927 sizeof(io->n.lm_hash->hash)));
1929 if (lpcfg_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
1930 io->og.lm_hash = talloc(io->ac, struct samr_Password);
1931 memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
1932 sizeof(io->og.lm_hash->hash)));
1936 * Handles the password change control if it's specified. It has the
1937 * precedance and overrides already specified old password values of
1938 * change requests (but that shouldn't happen since the control is
1939 * fully internal and only used in conjunction with replace requests!).
1941 if (ac->change != NULL) {
1942 io->og.nt_hash = NULL;
1943 if (ac->change->old_nt_pwd_hash != NULL) {
1944 io->og.nt_hash = talloc_memdup(io->ac,
1945 ac->change->old_nt_pwd_hash,
1946 sizeof(struct samr_Password));
1948 io->og.lm_hash = NULL;
1949 if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
1950 io->og.lm_hash = talloc_memdup(io->ac,
1951 ac->change->old_lm_pwd_hash,
1952 sizeof(struct samr_Password));
1956 /* refuse the change if someone wants to change the clear-
1957 text and supply his own hashes at the same time... */
1958 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
1959 && (io->n.nt_hash || io->n.lm_hash)) {
1960 ldb_asprintf_errstring(ldb,
1961 "setup_io: "
1962 "it's only allowed to set the password in form of cleartext attributes or as hashes");
1963 return LDB_ERR_UNWILLING_TO_PERFORM;
1966 /* refuse the change if someone wants to change the password
1967 using both plaintext methods (UTF8 and UTF16) at the same time... */
1968 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
1969 ldb_asprintf_errstring(ldb,
1970 "setup_io: "
1971 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
1972 return LDB_ERR_UNWILLING_TO_PERFORM;
1975 /* refuse the change if someone tries to set/change the password by
1976 * the lanman hash alone and we've deactivated that mechanism. This
1977 * would end in an account without any password! */
1978 if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
1979 && (!io->n.nt_hash) && (!io->n.lm_hash)) {
1980 ldb_asprintf_errstring(ldb,
1981 "setup_io: "
1982 "It' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
1983 /* on "userPassword" and "clearTextPassword" we've to return
1984 * something different, since these are virtual attributes */
1985 if ((ldb_msg_find_element(orig_msg, "userPassword") != NULL) ||
1986 (ldb_msg_find_element(orig_msg, "clearTextPassword") != NULL)) {
1987 return LDB_ERR_CONSTRAINT_VIOLATION;
1989 return LDB_ERR_UNWILLING_TO_PERFORM;
1992 /* refuse the change if someone wants to compare against a plaintext
1993 or hash at the same time for a "password modify" operation... */
1994 if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
1995 && (io->og.nt_hash || io->og.lm_hash)) {
1996 ldb_asprintf_errstring(ldb,
1997 "setup_io: "
1998 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
1999 return LDB_ERR_UNWILLING_TO_PERFORM;
2002 /* refuse the change if someone wants to compare against both
2003 * plaintexts at the same time for a "password modify" operation... */
2004 if (io->og.cleartext_utf8 && io->og.cleartext_utf16) {
2005 ldb_asprintf_errstring(ldb,
2006 "setup_io: "
2007 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2008 return LDB_ERR_UNWILLING_TO_PERFORM;
2011 /* Decides if we have a password modify or password reset operation */
2012 if (ac->req->operation == LDB_ADD) {
2013 /* On "add" we have only "password reset" */
2014 ac->pwd_reset = true;
2015 } else if (ac->req->operation == LDB_MODIFY) {
2016 if (io->og.cleartext_utf8 || io->og.cleartext_utf16
2017 || io->og.nt_hash || io->og.lm_hash) {
2018 /* If we have an old password specified then for sure it
2019 * is a user "password change" */
2020 ac->pwd_reset = false;
2021 } else {
2022 /* Otherwise we have also here a "password reset" */
2023 ac->pwd_reset = true;
2025 } else {
2026 /* this shouldn't happen */
2027 return ldb_operr(ldb);
2030 return LDB_SUCCESS;
2033 static struct ph_context *ph_init_context(struct ldb_module *module,
2034 struct ldb_request *req,
2035 bool userPassword)
2037 struct ldb_context *ldb;
2038 struct ph_context *ac;
2040 ldb = ldb_module_get_ctx(module);
2042 ac = talloc_zero(req, struct ph_context);
2043 if (ac == NULL) {
2044 ldb_set_errstring(ldb, "Out of Memory");
2045 return NULL;
2048 ac->module = module;
2049 ac->req = req;
2050 ac->userPassword = userPassword;
2052 return ac;
2055 static void ph_apply_controls(struct ph_context *ac)
2057 struct ldb_control *ctrl;
2059 ac->change_status = false;
2060 ctrl = ldb_request_get_control(ac->req,
2061 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID);
2062 if (ctrl != NULL) {
2063 ac->change_status = true;
2065 /* Mark the "change status" control as uncritical (done) */
2066 ctrl->critical = false;
2069 ac->hash_values = false;
2070 ctrl = ldb_request_get_control(ac->req,
2071 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
2072 if (ctrl != NULL) {
2073 ac->hash_values = true;
2075 /* Mark the "hash values" control as uncritical (done) */
2076 ctrl->critical = false;
2079 ctrl = ldb_request_get_control(ac->req,
2080 DSDB_CONTROL_PASSWORD_CHANGE_OID);
2081 if (ctrl != NULL) {
2082 ac->change = (struct dsdb_control_password_change *) ctrl->data;
2084 /* Mark the "change" control as uncritical (done) */
2085 ctrl->critical = false;
2089 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
2091 struct ph_context *ac;
2093 ac = talloc_get_type(req->context, struct ph_context);
2095 if (!ares) {
2096 return ldb_module_done(ac->req, NULL, NULL,
2097 LDB_ERR_OPERATIONS_ERROR);
2100 if (ares->type == LDB_REPLY_REFERRAL) {
2101 return ldb_module_send_referral(ac->req, ares->referral);
2104 if ((ares->error != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2105 /* On success and trivial errors a status control is being
2106 * added (used for example by the "samdb_set_password" call) */
2107 ldb_reply_add_control(ares,
2108 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2109 false,
2110 ac->status);
2113 if (ares->error != LDB_SUCCESS) {
2114 return ldb_module_done(ac->req, ares->controls,
2115 ares->response, ares->error);
2118 if (ares->type != LDB_REPLY_DONE) {
2119 talloc_free(ares);
2120 return ldb_module_done(ac->req, NULL, NULL,
2121 LDB_ERR_OPERATIONS_ERROR);
2124 return ldb_module_done(ac->req, ares->controls,
2125 ares->response, ares->error);
2128 static int password_hash_add_do_add(struct ph_context *ac);
2129 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
2130 static int password_hash_mod_search_self(struct ph_context *ac);
2131 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
2132 static int password_hash_mod_do_mod(struct ph_context *ac);
2134 static int get_domain_data_callback(struct ldb_request *req,
2135 struct ldb_reply *ares)
2137 struct ldb_context *ldb;
2138 struct ph_context *ac;
2139 struct loadparm_context *lp_ctx;
2140 int ret;
2142 ac = talloc_get_type(req->context, struct ph_context);
2143 ldb = ldb_module_get_ctx(ac->module);
2145 if (!ares) {
2146 ret = LDB_ERR_OPERATIONS_ERROR;
2147 goto done;
2149 if (ares->error != LDB_SUCCESS) {
2150 return ldb_module_done(ac->req, ares->controls,
2151 ares->response, ares->error);
2154 switch (ares->type) {
2155 case LDB_REPLY_ENTRY:
2156 if (ac->status != NULL) {
2157 talloc_free(ares);
2159 ldb_set_errstring(ldb, "Too many results");
2160 ret = LDB_ERR_OPERATIONS_ERROR;
2161 goto done;
2164 /* Setup the "status" structure (used as control later) */
2165 ac->status = talloc_zero(ac->req,
2166 struct dsdb_control_password_change_status);
2167 if (ac->status == NULL) {
2168 talloc_free(ares);
2170 ldb_oom(ldb);
2171 ret = LDB_ERR_OPERATIONS_ERROR;
2172 goto done;
2175 /* Setup the "domain data" structure */
2176 ac->status->domain_data.pwdProperties =
2177 ldb_msg_find_attr_as_uint(ares->message, "pwdProperties", -1);
2178 ac->status->domain_data.pwdHistoryLength =
2179 ldb_msg_find_attr_as_uint(ares->message, "pwdHistoryLength", -1);
2180 ac->status->domain_data.maxPwdAge =
2181 ldb_msg_find_attr_as_int64(ares->message, "maxPwdAge", -1);
2182 ac->status->domain_data.minPwdAge =
2183 ldb_msg_find_attr_as_int64(ares->message, "minPwdAge", -1);
2184 ac->status->domain_data.minPwdLength =
2185 ldb_msg_find_attr_as_uint(ares->message, "minPwdLength", -1);
2186 ac->status->domain_data.store_cleartext =
2187 ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
2189 talloc_free(ares);
2191 /* For a domain DN, this puts things in dotted notation */
2192 /* For builtin domains, this will give details for the host,
2193 * but that doesn't really matter, as it's just used for salt
2194 * and kerberos principals, which don't exist here */
2196 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2197 struct loadparm_context);
2199 ac->status->domain_data.dns_domain = lpcfg_dnsdomain(lp_ctx);
2200 ac->status->domain_data.realm = lpcfg_realm(lp_ctx);
2201 ac->status->domain_data.netbios_domain = lpcfg_sam_name(lp_ctx);
2203 ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
2205 ret = LDB_SUCCESS;
2206 break;
2208 case LDB_REPLY_REFERRAL:
2209 /* ignore */
2210 talloc_free(ares);
2211 ret = LDB_SUCCESS;
2212 break;
2214 case LDB_REPLY_DONE:
2215 talloc_free(ares);
2216 /* call the next step */
2217 switch (ac->req->operation) {
2218 case LDB_ADD:
2219 ret = password_hash_add_do_add(ac);
2220 break;
2222 case LDB_MODIFY:
2223 ret = password_hash_mod_do_mod(ac);
2224 break;
2226 default:
2227 ret = LDB_ERR_OPERATIONS_ERROR;
2228 break;
2230 break;
2233 done:
2234 if (ret != LDB_SUCCESS) {
2235 struct ldb_reply *new_ares;
2237 new_ares = talloc_zero(ac->req, struct ldb_reply);
2238 if (new_ares == NULL) {
2239 ldb_oom(ldb);
2240 return ldb_module_done(ac->req, NULL, NULL,
2241 LDB_ERR_OPERATIONS_ERROR);
2244 new_ares->error = ret;
2245 if ((ret != LDB_ERR_OPERATIONS_ERROR) && (ac->change_status)) {
2246 /* On success and trivial errors a status control is being
2247 * added (used for example by the "samdb_set_password" call) */
2248 ldb_reply_add_control(new_ares,
2249 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
2250 false,
2251 ac->status);
2254 return ldb_module_done(ac->req, new_ares->controls,
2255 new_ares->response, new_ares->error);
2258 return LDB_SUCCESS;
2261 static int build_domain_data_request(struct ph_context *ac)
2263 /* attrs[] is returned from this function in
2264 ac->dom_req->op.search.attrs, so it must be static, as
2265 otherwise the compiler can put it on the stack */
2266 struct ldb_context *ldb;
2267 static const char * const attrs[] = { "pwdProperties",
2268 "pwdHistoryLength",
2269 "maxPwdAge",
2270 "minPwdAge",
2271 "minPwdLength",
2272 NULL };
2273 int ret;
2275 ldb = ldb_module_get_ctx(ac->module);
2277 ret = ldb_build_search_req(&ac->dom_req, ldb, ac,
2278 ldb_get_default_basedn(ldb),
2279 LDB_SCOPE_BASE,
2280 NULL, attrs,
2281 NULL,
2282 ac, get_domain_data_callback,
2283 ac->req);
2284 LDB_REQ_SET_LOCATION(ac->dom_req);
2285 return ret;
2288 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
2290 struct ldb_context *ldb;
2291 struct ph_context *ac;
2292 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
2293 *ntAttr, *lmAttr;
2294 int ret;
2295 struct ldb_control *bypass = NULL;
2296 bool userPassword = true;
2298 ldb = ldb_module_get_ctx(module);
2300 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
2302 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
2303 return ldb_next_request(module, req);
2306 /* If the caller is manipulating the local passwords directly, let them pass */
2307 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2308 req->op.add.message->dn) == 0) {
2309 return ldb_next_request(module, req);
2312 bypass = ldb_request_get_control(req,
2313 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2314 if (bypass != NULL) {
2315 /* Mark the "bypass" control as uncritical (done) */
2316 bypass->critical = false;
2317 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
2318 return ldb_next_request(module, req);
2321 /* nobody must touch password histories and 'supplementalCredentials' */
2322 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
2323 return LDB_ERR_UNWILLING_TO_PERFORM;
2325 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
2326 return LDB_ERR_UNWILLING_TO_PERFORM;
2328 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
2329 return LDB_ERR_UNWILLING_TO_PERFORM;
2332 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2333 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2335 userPasswordAttr = NULL;
2336 if (userPassword) {
2337 userPasswordAttr = ldb_msg_find_element(req->op.add.message,
2338 "userPassword");
2340 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
2341 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
2342 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
2344 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
2345 return ldb_next_request(module, req);
2348 /* Make sure we are performing the password set action on a (for us)
2349 * valid object. Those are instances of either "user" and/or
2350 * "inetOrgPerson". Otherwise continue with the submodules. */
2351 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
2352 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
2354 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
2355 ldb_set_errstring(ldb,
2356 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2357 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2360 return ldb_next_request(module, req);
2363 ac = ph_init_context(module, req, userPassword);
2364 if (ac == NULL) {
2365 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2366 return ldb_operr(ldb);
2368 ph_apply_controls(ac);
2370 /* get user domain data */
2371 ret = build_domain_data_request(ac);
2372 if (ret != LDB_SUCCESS) {
2373 return ret;
2376 return ldb_next_request(module, ac->dom_req);
2379 static int password_hash_add_do_add(struct ph_context *ac)
2381 struct ldb_context *ldb;
2382 struct ldb_request *down_req;
2383 struct ldb_message *msg;
2384 struct setup_password_fields_io io;
2385 int ret;
2387 /* Prepare the internal data structure containing the passwords */
2388 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
2389 if (ret != LDB_SUCCESS) {
2390 return ret;
2393 ldb = ldb_module_get_ctx(ac->module);
2395 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
2396 if (msg == NULL) {
2397 return ldb_operr(ldb);
2400 /* remove attributes that we just read into 'io' */
2401 if (ac->userPassword) {
2402 ldb_msg_remove_attr(msg, "userPassword");
2404 ldb_msg_remove_attr(msg, "clearTextPassword");
2405 ldb_msg_remove_attr(msg, "unicodePwd");
2406 ldb_msg_remove_attr(msg, "dBCSPwd");
2407 ldb_msg_remove_attr(msg, "pwdLastSet");
2409 ret = setup_password_fields(&io);
2410 if (ret != LDB_SUCCESS) {
2411 return ret;
2414 ret = check_password_restrictions(&io);
2415 if (ret != LDB_SUCCESS) {
2416 return ret;
2419 if (io.g.nt_hash) {
2420 ret = samdb_msg_add_hash(ldb, ac, msg,
2421 "unicodePwd", io.g.nt_hash);
2422 if (ret != LDB_SUCCESS) {
2423 return ret;
2426 if (io.g.lm_hash) {
2427 ret = samdb_msg_add_hash(ldb, ac, msg,
2428 "dBCSPwd", io.g.lm_hash);
2429 if (ret != LDB_SUCCESS) {
2430 return ret;
2433 if (io.g.nt_history_len > 0) {
2434 ret = samdb_msg_add_hashes(ldb, ac, msg,
2435 "ntPwdHistory",
2436 io.g.nt_history,
2437 io.g.nt_history_len);
2438 if (ret != LDB_SUCCESS) {
2439 return ret;
2442 if (io.g.lm_history_len > 0) {
2443 ret = samdb_msg_add_hashes(ldb, ac, msg,
2444 "lmPwdHistory",
2445 io.g.lm_history,
2446 io.g.lm_history_len);
2447 if (ret != LDB_SUCCESS) {
2448 return ret;
2451 if (io.g.supplemental.length > 0) {
2452 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2453 &io.g.supplemental, NULL);
2454 if (ret != LDB_SUCCESS) {
2455 return ret;
2458 ret = samdb_msg_add_uint64(ldb, ac, msg,
2459 "pwdLastSet",
2460 io.g.last_set);
2461 if (ret != LDB_SUCCESS) {
2462 return ret;
2465 ret = ldb_build_add_req(&down_req, ldb, ac,
2466 msg,
2467 ac->req->controls,
2468 ac, ph_op_callback,
2469 ac->req);
2470 LDB_REQ_SET_LOCATION(down_req);
2471 if (ret != LDB_SUCCESS) {
2472 return ret;
2475 return ldb_next_request(ac->module, down_req);
2478 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
2480 struct ldb_context *ldb;
2481 struct ph_context *ac;
2482 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
2483 "unicodePwd", "dBCSPwd", NULL }, **l;
2484 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
2485 struct ldb_message_element *passwordAttr;
2486 struct ldb_message *msg;
2487 struct ldb_request *down_req;
2488 int ret;
2489 struct ldb_control *bypass = NULL;
2490 bool userPassword = true;
2492 ldb = ldb_module_get_ctx(module);
2494 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
2496 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
2497 return ldb_next_request(module, req);
2500 /* If the caller is manipulating the local passwords directly, let them pass */
2501 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
2502 req->op.mod.message->dn) == 0) {
2503 return ldb_next_request(module, req);
2506 bypass = ldb_request_get_control(req,
2507 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
2508 if (bypass != NULL) {
2509 /* Mark the "bypass" control as uncritical (done) */
2510 bypass->critical = false;
2511 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
2512 return ldb_next_request(module, req);
2515 /* nobody must touch password histories and 'supplementalCredentials' */
2516 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
2517 return LDB_ERR_UNWILLING_TO_PERFORM;
2519 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
2520 return LDB_ERR_UNWILLING_TO_PERFORM;
2522 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
2523 return LDB_ERR_UNWILLING_TO_PERFORM;
2526 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2527 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
2528 * For password changes/set there should be a 'delete' or a 'modify'
2529 * on these attributes. */
2530 attr_cnt = 0;
2531 for (l = passwordAttrs; *l != NULL; l++) {
2532 if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) {
2533 continue;
2536 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
2537 ++attr_cnt;
2540 if (attr_cnt == 0) {
2541 return ldb_next_request(module, req);
2544 ac = ph_init_context(module, req, userPassword);
2545 if (!ac) {
2546 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2547 return ldb_operr(ldb);
2549 ph_apply_controls(ac);
2551 /* use a new message structure so that we can modify it */
2552 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2553 if (msg == NULL) {
2554 return ldb_oom(ldb);
2557 /* - check for single-valued password attributes
2558 * (if not return "CONSTRAINT_VIOLATION")
2559 * - check that for a password change operation one add and one delete
2560 * operation exists
2561 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
2562 * - check that a password change and a password set operation cannot
2563 * be mixed
2564 * (if not return "UNWILLING_TO_PERFORM")
2565 * - remove all password attributes modifications from the first change
2566 * operation (anything without the passwords) - we will make the real
2567 * modification later */
2568 del_attr_cnt = 0;
2569 add_attr_cnt = 0;
2570 rep_attr_cnt = 0;
2571 for (l = passwordAttrs; *l != NULL; l++) {
2572 if ((!ac->userPassword) &&
2573 (ldb_attr_cmp(*l, "userPassword") == 0)) {
2574 continue;
2577 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
2578 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
2579 ++del_attr_cnt;
2581 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
2582 ++add_attr_cnt;
2584 if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
2585 ++rep_attr_cnt;
2587 if ((passwordAttr->num_values != 1) &&
2588 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
2589 talloc_free(ac);
2590 ldb_asprintf_errstring(ldb,
2591 "'%s' attribute must have exactly one value on add operations!",
2592 *l);
2593 return LDB_ERR_CONSTRAINT_VIOLATION;
2595 if ((passwordAttr->num_values > 1) &&
2596 (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
2597 talloc_free(ac);
2598 ldb_asprintf_errstring(ldb,
2599 "'%s' attribute must have zero or one value(s) on delete operations!",
2600 *l);
2601 return LDB_ERR_CONSTRAINT_VIOLATION;
2603 ldb_msg_remove_element(msg, passwordAttr);
2606 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
2607 talloc_free(ac);
2608 ldb_set_errstring(ldb,
2609 "Only the add action for a password change specified!");
2610 return LDB_ERR_UNWILLING_TO_PERFORM;
2612 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
2613 talloc_free(ac);
2614 ldb_set_errstring(ldb,
2615 "Only one delete and one add action for a password change allowed!");
2616 return LDB_ERR_UNWILLING_TO_PERFORM;
2618 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
2619 talloc_free(ac);
2620 ldb_set_errstring(ldb,
2621 "Either a password change or a password set operation is allowed!");
2622 return LDB_ERR_UNWILLING_TO_PERFORM;
2625 /* if there was nothing else to be modified skip to next step */
2626 if (msg->num_elements == 0) {
2627 return password_hash_mod_search_self(ac);
2630 ret = ldb_build_mod_req(&down_req, ldb, ac,
2631 msg,
2632 req->controls,
2633 ac, ph_modify_callback,
2634 req);
2635 LDB_REQ_SET_LOCATION(down_req);
2636 if (ret != LDB_SUCCESS) {
2637 return ret;
2640 return ldb_next_request(module, down_req);
2643 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
2645 struct ph_context *ac;
2647 ac = talloc_get_type(req->context, struct ph_context);
2649 if (!ares) {
2650 return ldb_module_done(ac->req, NULL, NULL,
2651 LDB_ERR_OPERATIONS_ERROR);
2654 if (ares->type == LDB_REPLY_REFERRAL) {
2655 return ldb_module_send_referral(ac->req, ares->referral);
2658 if (ares->error != LDB_SUCCESS) {
2659 return ldb_module_done(ac->req, ares->controls,
2660 ares->response, ares->error);
2663 if (ares->type != LDB_REPLY_DONE) {
2664 talloc_free(ares);
2665 return ldb_module_done(ac->req, NULL, NULL,
2666 LDB_ERR_OPERATIONS_ERROR);
2669 talloc_free(ares);
2671 return password_hash_mod_search_self(ac);
2674 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
2676 struct ldb_context *ldb;
2677 struct ph_context *ac;
2678 int ret;
2680 ac = talloc_get_type(req->context, struct ph_context);
2681 ldb = ldb_module_get_ctx(ac->module);
2683 if (!ares) {
2684 ret = LDB_ERR_OPERATIONS_ERROR;
2685 goto done;
2687 if (ares->error != LDB_SUCCESS) {
2688 return ldb_module_done(ac->req, ares->controls,
2689 ares->response, ares->error);
2692 /* we are interested only in the single reply (base search) */
2693 switch (ares->type) {
2694 case LDB_REPLY_ENTRY:
2695 /* Make sure we are performing the password change action on a
2696 * (for us) valid object. Those are instances of either "user"
2697 * and/or "inetOrgPerson". Otherwise continue with the
2698 * submodules. */
2699 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
2700 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
2701 talloc_free(ares);
2703 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
2704 ldb_set_errstring(ldb,
2705 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2706 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
2707 goto done;
2710 ret = ldb_next_request(ac->module, ac->req);
2711 goto done;
2714 if (ac->search_res != NULL) {
2715 talloc_free(ares);
2717 ldb_set_errstring(ldb, "Too many results");
2718 ret = LDB_ERR_OPERATIONS_ERROR;
2719 goto done;
2722 ac->search_res = talloc_steal(ac, ares);
2723 ret = LDB_SUCCESS;
2724 break;
2726 case LDB_REPLY_REFERRAL:
2727 /* ignore anything else for now */
2728 talloc_free(ares);
2729 ret = LDB_SUCCESS;
2730 break;
2732 case LDB_REPLY_DONE:
2733 talloc_free(ares);
2735 /* get user domain data */
2736 ret = build_domain_data_request(ac);
2737 if (ret != LDB_SUCCESS) {
2738 return ldb_module_done(ac->req, NULL, NULL, ret);
2741 ret = ldb_next_request(ac->module, ac->dom_req);
2742 break;
2745 done:
2746 if (ret != LDB_SUCCESS) {
2747 return ldb_module_done(ac->req, NULL, NULL, ret);
2750 return LDB_SUCCESS;
2753 static int password_hash_mod_search_self(struct ph_context *ac)
2755 struct ldb_context *ldb;
2756 static const char * const attrs[] = { "objectClass",
2757 "userAccountControl",
2758 "pwdLastSet",
2759 "sAMAccountName",
2760 "objectSid",
2761 "userPrincipalName",
2762 "supplementalCredentials",
2763 "lmPwdHistory",
2764 "ntPwdHistory",
2765 "dBCSPwd",
2766 "unicodePwd",
2767 NULL };
2768 struct ldb_request *search_req;
2769 int ret;
2771 ldb = ldb_module_get_ctx(ac->module);
2773 ret = ldb_build_search_req(&search_req, ldb, ac,
2774 ac->req->op.mod.message->dn,
2775 LDB_SCOPE_BASE,
2776 "(objectclass=*)",
2777 attrs,
2778 NULL,
2779 ac, ph_mod_search_callback,
2780 ac->req);
2781 LDB_REQ_SET_LOCATION(search_req);
2782 if (ret != LDB_SUCCESS) {
2783 return ret;
2786 return ldb_next_request(ac->module, search_req);
2789 static int password_hash_mod_do_mod(struct ph_context *ac)
2791 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2792 struct loadparm_context *lp_ctx =
2793 talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2794 struct loadparm_context);
2795 struct ldb_request *mod_req;
2796 struct ldb_message *msg;
2797 const struct ldb_message *orig_msg, *searched_msg;
2798 struct setup_password_fields_io io;
2799 int ret;
2800 NTSTATUS status;
2802 /* use a new message structure so that we can modify it */
2803 msg = ldb_msg_new(ac);
2804 if (msg == NULL) {
2805 return ldb_operr(ldb);
2808 /* modify dn */
2809 msg->dn = ac->req->op.mod.message->dn;
2811 orig_msg = ac->req->op.mod.message;
2812 searched_msg = ac->search_res->message;
2814 /* Prepare the internal data structure containing the passwords */
2815 ret = setup_io(ac, orig_msg, searched_msg, &io);
2816 if (ret != LDB_SUCCESS) {
2817 return ret;
2820 /* Get the old password from the database */
2821 status = samdb_result_passwords(io.ac,
2822 lp_ctx,
2823 discard_const_p(struct ldb_message, searched_msg),
2824 &io.o.lm_hash, &io.o.nt_hash);
2825 if (!NT_STATUS_IS_OK(status)) {
2826 return ldb_operr(ldb);
2829 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
2830 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
2831 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
2833 ret = setup_password_fields(&io);
2834 if (ret != LDB_SUCCESS) {
2835 return ret;
2838 ret = check_password_restrictions(&io);
2839 if (ret != LDB_SUCCESS) {
2840 return ret;
2843 /* make sure we replace all the old attributes */
2844 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
2845 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
2846 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2847 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2848 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
2849 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
2851 if (io.g.nt_hash) {
2852 ret = samdb_msg_add_hash(ldb, ac, msg,
2853 "unicodePwd", io.g.nt_hash);
2854 if (ret != LDB_SUCCESS) {
2855 return ret;
2858 if (io.g.lm_hash) {
2859 ret = samdb_msg_add_hash(ldb, ac, msg,
2860 "dBCSPwd", io.g.lm_hash);
2861 if (ret != LDB_SUCCESS) {
2862 return ret;
2865 if (io.g.nt_history_len > 0) {
2866 ret = samdb_msg_add_hashes(ldb, ac, msg,
2867 "ntPwdHistory",
2868 io.g.nt_history,
2869 io.g.nt_history_len);
2870 if (ret != LDB_SUCCESS) {
2871 return ret;
2874 if (io.g.lm_history_len > 0) {
2875 ret = samdb_msg_add_hashes(ldb, ac, msg,
2876 "lmPwdHistory",
2877 io.g.lm_history,
2878 io.g.lm_history_len);
2879 if (ret != LDB_SUCCESS) {
2880 return ret;
2883 if (io.g.supplemental.length > 0) {
2884 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2885 &io.g.supplemental, NULL);
2886 if (ret != LDB_SUCCESS) {
2887 return ret;
2890 ret = samdb_msg_add_uint64(ldb, ac, msg,
2891 "pwdLastSet",
2892 io.g.last_set);
2893 if (ret != LDB_SUCCESS) {
2894 return ret;
2897 ret = ldb_build_mod_req(&mod_req, ldb, ac,
2898 msg,
2899 ac->req->controls,
2900 ac, ph_op_callback,
2901 ac->req);
2902 LDB_REQ_SET_LOCATION(mod_req);
2903 if (ret != LDB_SUCCESS) {
2904 return ret;
2907 return ldb_next_request(ac->module, mod_req);
2910 static const struct ldb_module_ops ldb_password_hash_module_ops = {
2911 .name = "password_hash",
2912 .add = password_hash_add,
2913 .modify = password_hash_modify
2916 int ldb_password_hash_module_init(const char *version)
2918 LDB_MODULE_CHECK_VERSION(version);
2919 return ldb_register_module(&ldb_password_hash_module_ops);