s4:password_hash - Rework unique value checks
[Samba/ekacnet.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
blobc7f6465e97d5d00d84c9574ced6d4bf282fcf0d2
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
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 struct ph_context {
86 struct ldb_module *module;
87 struct ldb_request *req;
89 struct ldb_request *dom_req;
90 struct ldb_reply *dom_res;
92 struct ldb_reply *search_res;
94 struct domain_data *domain;
97 struct domain_data {
98 bool store_cleartext;
99 uint32_t pwdProperties;
100 uint32_t pwdHistoryLength;
101 int64_t maxPwdAge;
102 int64_t minPwdAge;
103 uint32_t minPwdLength;
104 const char *netbios_domain;
105 const char *dns_domain;
106 const char *realm;
109 struct setup_password_fields_io {
110 struct ph_context *ac;
111 struct domain_data *domain;
112 struct smb_krb5_context *smb_krb5_context;
114 /* infos about the user account */
115 struct {
116 uint32_t userAccountControl;
117 NTTIME pwdLastSet;
118 const char *sAMAccountName;
119 const char *user_principal_name;
120 bool is_computer;
121 } u;
123 /* new credentials */
124 struct {
125 const struct ldb_val *cleartext_utf8;
126 const struct ldb_val *cleartext_utf16;
127 struct ldb_val quoted_utf16;
128 struct samr_Password *nt_hash;
129 struct samr_Password *lm_hash;
130 } n;
132 /* old credentials */
133 struct {
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->domain->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->domain->pwdHistoryLength);
179 if (!io->g.nt_history) {
180 ldb_oom(ldb);
181 return LDB_ERR_OPERATIONS_ERROR;
184 for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, 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->domain->pwdHistoryLength == 0) {
214 return LDB_SUCCESS;
217 /* We might not have an old NT password */
218 io->g.lm_history = talloc_array(io->ac,
219 struct samr_Password,
220 io->domain->pwdHistoryLength);
221 if (!io->g.lm_history) {
222 ldb_oom(ldb);
223 return LDB_ERR_OPERATIONS_ERROR;
226 for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, 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 ldb_oom(ldb);
267 return LDB_ERR_OPERATIONS_ERROR;
270 if (name[strlen(name)-1] == '$') {
271 name[strlen(name)-1] = '\0';
274 saltbody = talloc_asprintf(io->ac, "%s.%s", name, io->domain->dns_domain);
275 if (!saltbody) {
276 ldb_oom(ldb);
277 return LDB_ERR_OPERATIONS_ERROR;
280 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
281 &salt_principal,
282 io->domain->realm, "host",
283 saltbody, NULL);
284 } else if (io->u.user_principal_name) {
285 char *user_principal_name;
286 char *p;
288 user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
289 if (!user_principal_name) {
290 ldb_oom(ldb);
291 return LDB_ERR_OPERATIONS_ERROR;
294 p = strchr(user_principal_name, '@');
295 if (p) {
296 p[0] = '\0';
299 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
300 &salt_principal,
301 io->domain->realm, user_principal_name,
302 NULL);
303 } else {
304 krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
305 &salt_principal,
306 io->domain->realm, io->u.sAMAccountName,
307 NULL);
309 if (krb5_ret) {
310 ldb_asprintf_errstring(ldb,
311 "setup_kerberos_keys: "
312 "generation of a salting principal failed: %s",
313 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
314 krb5_ret, io->ac));
315 return LDB_ERR_OPERATIONS_ERROR;
319 * create salt from salt_principal
321 krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
322 salt_principal, &salt);
323 krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
324 if (krb5_ret) {
325 ldb_asprintf_errstring(ldb,
326 "setup_kerberos_keys: "
327 "generation of krb5_salt failed: %s",
328 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
329 krb5_ret, io->ac));
330 return LDB_ERR_OPERATIONS_ERROR;
332 /* create a talloc copy */
333 io->g.salt = talloc_strndup(io->ac,
334 salt.saltvalue.data,
335 salt.saltvalue.length);
336 krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
337 if (!io->g.salt) {
338 ldb_oom(ldb);
339 return LDB_ERR_OPERATIONS_ERROR;
341 salt.saltvalue.data = discard_const(io->g.salt);
342 salt.saltvalue.length = strlen(io->g.salt);
345 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
346 * the salt and the cleartext password
348 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
349 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
350 cleartext_data,
351 salt,
352 &key);
353 if (krb5_ret) {
354 ldb_asprintf_errstring(ldb,
355 "setup_kerberos_keys: "
356 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
357 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
358 krb5_ret, io->ac));
359 return LDB_ERR_OPERATIONS_ERROR;
361 io->g.aes_256 = data_blob_talloc(io->ac,
362 key.keyvalue.data,
363 key.keyvalue.length);
364 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
365 if (!io->g.aes_256.data) {
366 ldb_oom(ldb);
367 return LDB_ERR_OPERATIONS_ERROR;
371 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
372 * the salt and the cleartext password
374 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
375 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
376 cleartext_data,
377 salt,
378 &key);
379 if (krb5_ret) {
380 ldb_asprintf_errstring(ldb,
381 "setup_kerberos_keys: "
382 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
383 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
384 krb5_ret, io->ac));
385 return LDB_ERR_OPERATIONS_ERROR;
387 io->g.aes_128 = data_blob_talloc(io->ac,
388 key.keyvalue.data,
389 key.keyvalue.length);
390 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
391 if (!io->g.aes_128.data) {
392 ldb_oom(ldb);
393 return LDB_ERR_OPERATIONS_ERROR;
397 * create ENCTYPE_DES_CBC_MD5 key out of
398 * the salt and the cleartext password
400 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
401 ENCTYPE_DES_CBC_MD5,
402 cleartext_data,
403 salt,
404 &key);
405 if (krb5_ret) {
406 ldb_asprintf_errstring(ldb,
407 "setup_kerberos_keys: "
408 "generation of a des-cbc-md5 key failed: %s",
409 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
410 krb5_ret, io->ac));
411 return LDB_ERR_OPERATIONS_ERROR;
413 io->g.des_md5 = data_blob_talloc(io->ac,
414 key.keyvalue.data,
415 key.keyvalue.length);
416 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
417 if (!io->g.des_md5.data) {
418 ldb_oom(ldb);
419 return LDB_ERR_OPERATIONS_ERROR;
423 * create ENCTYPE_DES_CBC_CRC key out of
424 * the salt and the cleartext password
426 krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
427 ENCTYPE_DES_CBC_CRC,
428 cleartext_data,
429 salt,
430 &key);
431 if (krb5_ret) {
432 ldb_asprintf_errstring(ldb,
433 "setup_kerberos_keys: "
434 "generation of a des-cbc-crc key failed: %s",
435 smb_get_krb5_error_message(io->smb_krb5_context->krb5_context,
436 krb5_ret, io->ac));
437 return LDB_ERR_OPERATIONS_ERROR;
439 io->g.des_crc = data_blob_talloc(io->ac,
440 key.keyvalue.data,
441 key.keyvalue.length);
442 krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
443 if (!io->g.des_crc.data) {
444 ldb_oom(ldb);
445 return LDB_ERR_OPERATIONS_ERROR;
448 return LDB_SUCCESS;
451 static int setup_primary_kerberos(struct setup_password_fields_io *io,
452 const struct supplementalCredentialsBlob *old_scb,
453 struct package_PrimaryKerberosBlob *pkb)
455 struct ldb_context *ldb;
456 struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
457 struct supplementalCredentialsPackage *old_scp = NULL;
458 struct package_PrimaryKerberosBlob _old_pkb;
459 struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
460 uint32_t i;
461 enum ndr_err_code ndr_err;
463 ldb = ldb_module_get_ctx(io->ac->module);
466 * prepare generation of keys
468 * ENCTYPE_DES_CBC_MD5
469 * ENCTYPE_DES_CBC_CRC
471 pkb->version = 3;
472 pkb3->salt.string = io->g.salt;
473 pkb3->num_keys = 2;
474 pkb3->keys = talloc_array(io->ac,
475 struct package_PrimaryKerberosKey3,
476 pkb3->num_keys);
477 if (!pkb3->keys) {
478 ldb_oom(ldb);
479 return LDB_ERR_OPERATIONS_ERROR;
482 pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
483 pkb3->keys[0].value = &io->g.des_md5;
484 pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
485 pkb3->keys[1].value = &io->g.des_crc;
487 /* initialize the old keys to zero */
488 pkb3->num_old_keys = 0;
489 pkb3->old_keys = NULL;
491 /* if there're no old keys, then we're done */
492 if (!old_scb) {
493 return LDB_SUCCESS;
496 for (i=0; i < old_scb->sub.num_packages; i++) {
497 if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
498 continue;
501 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
502 continue;
505 old_scp = &old_scb->sub.packages[i];
506 break;
508 /* Primary:Kerberos element of supplementalCredentials */
509 if (old_scp) {
510 DATA_BLOB blob;
512 blob = strhex_to_data_blob(io->ac, old_scp->data);
513 if (!blob.data) {
514 ldb_oom(ldb);
515 return LDB_ERR_OPERATIONS_ERROR;
518 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
519 ndr_err = ndr_pull_struct_blob(&blob, io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &_old_pkb,
520 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
521 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
522 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
523 ldb_asprintf_errstring(ldb,
524 "setup_primary_kerberos: "
525 "failed to pull old package_PrimaryKerberosBlob: %s",
526 nt_errstr(status));
527 return LDB_ERR_OPERATIONS_ERROR;
530 if (_old_pkb.version != 3) {
531 ldb_asprintf_errstring(ldb,
532 "setup_primary_kerberos: "
533 "package_PrimaryKerberosBlob version[%u] expected[3]",
534 _old_pkb.version);
535 return LDB_ERR_OPERATIONS_ERROR;
538 old_pkb3 = &_old_pkb.ctr.ctr3;
541 /* if we didn't found the old keys we're done */
542 if (!old_pkb3) {
543 return LDB_SUCCESS;
546 /* fill in the old keys */
547 pkb3->num_old_keys = old_pkb3->num_keys;
548 pkb3->old_keys = old_pkb3->keys;
550 return LDB_SUCCESS;
553 static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
554 const struct supplementalCredentialsBlob *old_scb,
555 struct package_PrimaryKerberosBlob *pkb)
557 struct ldb_context *ldb;
558 struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
559 struct supplementalCredentialsPackage *old_scp = NULL;
560 struct package_PrimaryKerberosBlob _old_pkb;
561 struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
562 uint32_t i;
563 enum ndr_err_code ndr_err;
565 ldb = ldb_module_get_ctx(io->ac->module);
568 * prepare generation of keys
570 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
571 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
572 * ENCTYPE_DES_CBC_MD5
573 * ENCTYPE_DES_CBC_CRC
575 pkb->version = 4;
576 pkb4->salt.string = io->g.salt;
577 pkb4->default_iteration_count = 4096;
578 pkb4->num_keys = 4;
580 pkb4->keys = talloc_array(io->ac,
581 struct package_PrimaryKerberosKey4,
582 pkb4->num_keys);
583 if (!pkb4->keys) {
584 ldb_oom(ldb);
585 return LDB_ERR_OPERATIONS_ERROR;
588 pkb4->keys[0].iteration_count = 4096;
589 pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
590 pkb4->keys[0].value = &io->g.aes_256;
591 pkb4->keys[1].iteration_count = 4096;
592 pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
593 pkb4->keys[1].value = &io->g.aes_128;
594 pkb4->keys[2].iteration_count = 4096;
595 pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
596 pkb4->keys[2].value = &io->g.des_md5;
597 pkb4->keys[3].iteration_count = 4096;
598 pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
599 pkb4->keys[3].value = &io->g.des_crc;
601 /* initialize the old keys to zero */
602 pkb4->num_old_keys = 0;
603 pkb4->old_keys = NULL;
604 pkb4->num_older_keys = 0;
605 pkb4->older_keys = NULL;
607 /* if there're no old keys, then we're done */
608 if (!old_scb) {
609 return LDB_SUCCESS;
612 for (i=0; i < old_scb->sub.num_packages; i++) {
613 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
614 continue;
617 if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
618 continue;
621 old_scp = &old_scb->sub.packages[i];
622 break;
624 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
625 if (old_scp) {
626 DATA_BLOB blob;
628 blob = strhex_to_data_blob(io->ac, old_scp->data);
629 if (!blob.data) {
630 ldb_oom(ldb);
631 return LDB_ERR_OPERATIONS_ERROR;
634 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
635 ndr_err = ndr_pull_struct_blob(&blob, io->ac,
636 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
637 &_old_pkb,
638 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
639 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
640 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
641 ldb_asprintf_errstring(ldb,
642 "setup_primary_kerberos_newer: "
643 "failed to pull old package_PrimaryKerberosBlob: %s",
644 nt_errstr(status));
645 return LDB_ERR_OPERATIONS_ERROR;
648 if (_old_pkb.version != 4) {
649 ldb_asprintf_errstring(ldb,
650 "setup_primary_kerberos_newer: "
651 "package_PrimaryKerberosBlob version[%u] expected[4]",
652 _old_pkb.version);
653 return LDB_ERR_OPERATIONS_ERROR;
656 old_pkb4 = &_old_pkb.ctr.ctr4;
659 /* if we didn't found the old keys we're done */
660 if (!old_pkb4) {
661 return LDB_SUCCESS;
664 /* fill in the old keys */
665 pkb4->num_old_keys = old_pkb4->num_keys;
666 pkb4->old_keys = old_pkb4->keys;
667 pkb4->num_older_keys = old_pkb4->num_old_keys;
668 pkb4->older_keys = old_pkb4->old_keys;
670 return LDB_SUCCESS;
673 static int setup_primary_wdigest(struct setup_password_fields_io *io,
674 const struct supplementalCredentialsBlob *old_scb,
675 struct package_PrimaryWDigestBlob *pdb)
677 struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
678 DATA_BLOB sAMAccountName;
679 DATA_BLOB sAMAccountName_l;
680 DATA_BLOB sAMAccountName_u;
681 const char *user_principal_name = io->u.user_principal_name;
682 DATA_BLOB userPrincipalName;
683 DATA_BLOB userPrincipalName_l;
684 DATA_BLOB userPrincipalName_u;
685 DATA_BLOB netbios_domain;
686 DATA_BLOB netbios_domain_l;
687 DATA_BLOB netbios_domain_u;
688 DATA_BLOB dns_domain;
689 DATA_BLOB dns_domain_l;
690 DATA_BLOB dns_domain_u;
691 DATA_BLOB digest;
692 DATA_BLOB delim;
693 DATA_BLOB backslash;
694 uint8_t i;
695 struct {
696 DATA_BLOB *user;
697 DATA_BLOB *realm;
698 DATA_BLOB *nt4dom;
699 } wdigest[] = {
701 * See
702 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
703 * for what precalculated hashes are supposed to be stored...
705 * I can't reproduce all values which should contain "Digest" as realm,
706 * am I doing something wrong or is w2k3 just broken...?
708 * W2K3 fills in following for a user:
710 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
711 * sAMAccountName: NewUser2Sam
712 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
714 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
715 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
716 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
717 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
718 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
719 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
720 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
721 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
722 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
723 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
724 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
725 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
726 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
727 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
728 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
729 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
730 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
731 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
732 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
733 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
734 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
735 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
736 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
737 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
738 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
739 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
740 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
741 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
742 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
744 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
745 * sAMAccountName: NewUser2Sam
747 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
748 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
749 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
750 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
751 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
752 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
753 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
754 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
755 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
756 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
757 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
758 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
759 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
760 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
761 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
762 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
763 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
764 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
765 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
766 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
767 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
768 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
769 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
770 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
771 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
772 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
773 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
774 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
775 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
779 * sAMAccountName, netbios_domain
782 .user = &sAMAccountName,
783 .realm = &netbios_domain,
786 .user = &sAMAccountName_l,
787 .realm = &netbios_domain_l,
790 .user = &sAMAccountName_u,
791 .realm = &netbios_domain_u,
794 .user = &sAMAccountName,
795 .realm = &netbios_domain_u,
798 .user = &sAMAccountName,
799 .realm = &netbios_domain_l,
802 .user = &sAMAccountName_u,
803 .realm = &netbios_domain_l,
806 .user = &sAMAccountName_l,
807 .realm = &netbios_domain_u,
810 * sAMAccountName, dns_domain
813 .user = &sAMAccountName,
814 .realm = &dns_domain,
817 .user = &sAMAccountName_l,
818 .realm = &dns_domain_l,
821 .user = &sAMAccountName_u,
822 .realm = &dns_domain_u,
825 .user = &sAMAccountName,
826 .realm = &dns_domain_u,
829 .user = &sAMAccountName,
830 .realm = &dns_domain_l,
833 .user = &sAMAccountName_u,
834 .realm = &dns_domain_l,
837 .user = &sAMAccountName_l,
838 .realm = &dns_domain_u,
841 * userPrincipalName, no realm
844 .user = &userPrincipalName,
848 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
849 * the fallback to the sAMAccountName based userPrincipalName is correct
851 .user = &userPrincipalName_l,
854 .user = &userPrincipalName_u,
857 * nt4dom\sAMAccountName, no realm
860 .user = &sAMAccountName,
861 .nt4dom = &netbios_domain
864 .user = &sAMAccountName_l,
865 .nt4dom = &netbios_domain_l
868 .user = &sAMAccountName_u,
869 .nt4dom = &netbios_domain_u
873 * the following ones are guessed depending on the technet2 article
874 * but not reproducable on a w2k3 server
876 /* sAMAccountName with "Digest" realm */
878 .user = &sAMAccountName,
879 .realm = &digest
882 .user = &sAMAccountName_l,
883 .realm = &digest
886 .user = &sAMAccountName_u,
887 .realm = &digest
889 /* userPrincipalName with "Digest" realm */
891 .user = &userPrincipalName,
892 .realm = &digest
895 .user = &userPrincipalName_l,
896 .realm = &digest
899 .user = &userPrincipalName_u,
900 .realm = &digest
902 /* nt4dom\\sAMAccountName with "Digest" realm */
904 .user = &sAMAccountName,
905 .nt4dom = &netbios_domain,
906 .realm = &digest
909 .user = &sAMAccountName_l,
910 .nt4dom = &netbios_domain_l,
911 .realm = &digest
914 .user = &sAMAccountName_u,
915 .nt4dom = &netbios_domain_u,
916 .realm = &digest
920 /* prepare DATA_BLOB's used in the combinations array */
921 sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
922 sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
923 if (!sAMAccountName_l.data) {
924 ldb_oom(ldb);
925 return LDB_ERR_OPERATIONS_ERROR;
927 sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
928 if (!sAMAccountName_u.data) {
929 ldb_oom(ldb);
930 return LDB_ERR_OPERATIONS_ERROR;
933 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
934 if (!user_principal_name) {
935 user_principal_name = talloc_asprintf(io->ac, "%s@%s",
936 io->u.sAMAccountName,
937 io->domain->dns_domain);
938 if (!user_principal_name) {
939 ldb_oom(ldb);
940 return LDB_ERR_OPERATIONS_ERROR;
943 userPrincipalName = data_blob_string_const(user_principal_name);
944 userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
945 if (!userPrincipalName_l.data) {
946 ldb_oom(ldb);
947 return LDB_ERR_OPERATIONS_ERROR;
949 userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
950 if (!userPrincipalName_u.data) {
951 ldb_oom(ldb);
952 return LDB_ERR_OPERATIONS_ERROR;
955 netbios_domain = data_blob_string_const(io->domain->netbios_domain);
956 netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac, io->domain->netbios_domain));
957 if (!netbios_domain_l.data) {
958 ldb_oom(ldb);
959 return LDB_ERR_OPERATIONS_ERROR;
961 netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac, io->domain->netbios_domain));
962 if (!netbios_domain_u.data) {
963 ldb_oom(ldb);
964 return LDB_ERR_OPERATIONS_ERROR;
967 dns_domain = data_blob_string_const(io->domain->dns_domain);
968 dns_domain_l = data_blob_string_const(io->domain->dns_domain);
969 dns_domain_u = data_blob_string_const(io->domain->realm);
971 digest = data_blob_string_const("Digest");
973 delim = data_blob_string_const(":");
974 backslash = data_blob_string_const("\\");
976 pdb->num_hashes = ARRAY_SIZE(wdigest);
977 pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash,
978 pdb->num_hashes);
979 if (!pdb->hashes) {
980 ldb_oom(ldb);
981 return LDB_ERR_OPERATIONS_ERROR;
984 for (i=0; i < ARRAY_SIZE(wdigest); i++) {
985 struct MD5Context md5;
986 MD5Init(&md5);
987 if (wdigest[i].nt4dom) {
988 MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
989 MD5Update(&md5, backslash.data, backslash.length);
991 MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
992 MD5Update(&md5, delim.data, delim.length);
993 if (wdigest[i].realm) {
994 MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
996 MD5Update(&md5, delim.data, delim.length);
997 MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
998 MD5Final(pdb->hashes[i].hash, &md5);
1001 return LDB_SUCCESS;
1004 static int setup_supplemental_field(struct setup_password_fields_io *io)
1006 struct ldb_context *ldb;
1007 struct supplementalCredentialsBlob scb;
1008 struct supplementalCredentialsBlob _old_scb;
1009 struct supplementalCredentialsBlob *old_scb = NULL;
1010 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1011 uint32_t num_names = 0;
1012 const char *names[1+4];
1013 uint32_t num_packages = 0;
1014 struct supplementalCredentialsPackage packages[1+4];
1015 /* Packages */
1016 struct supplementalCredentialsPackage *pp = NULL;
1017 struct package_PackagesBlob pb;
1018 DATA_BLOB pb_blob;
1019 char *pb_hexstr;
1020 /* Primary:Kerberos-Newer-Keys */
1021 const char **nkn = NULL;
1022 struct supplementalCredentialsPackage *pkn = NULL;
1023 struct package_PrimaryKerberosBlob pknb;
1024 DATA_BLOB pknb_blob;
1025 char *pknb_hexstr;
1026 /* Primary:Kerberos */
1027 const char **nk = NULL;
1028 struct supplementalCredentialsPackage *pk = NULL;
1029 struct package_PrimaryKerberosBlob pkb;
1030 DATA_BLOB pkb_blob;
1031 char *pkb_hexstr;
1032 /* Primary:WDigest */
1033 const char **nd = NULL;
1034 struct supplementalCredentialsPackage *pd = NULL;
1035 struct package_PrimaryWDigestBlob pdb;
1036 DATA_BLOB pdb_blob;
1037 char *pdb_hexstr;
1038 /* Primary:CLEARTEXT */
1039 const char **nc = NULL;
1040 struct supplementalCredentialsPackage *pc = NULL;
1041 struct package_PrimaryCLEARTEXTBlob pcb;
1042 DATA_BLOB pcb_blob;
1043 char *pcb_hexstr;
1044 int ret;
1045 enum ndr_err_code ndr_err;
1046 uint8_t zero16[16];
1047 bool do_newer_keys = false;
1048 bool do_cleartext = false;
1050 ZERO_STRUCT(zero16);
1051 ZERO_STRUCT(names);
1053 ldb = ldb_module_get_ctx(io->ac->module);
1055 if (!io->n.cleartext_utf8) {
1057 * when we don't have a cleartext password
1058 * we can't setup a supplementalCredential value
1060 return LDB_SUCCESS;
1063 /* if there's an old supplementaCredentials blob then parse it */
1064 if (io->o.supplemental) {
1065 ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
1066 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1067 &_old_scb,
1068 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
1069 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1070 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1071 ldb_asprintf_errstring(ldb,
1072 "setup_supplemental_field: "
1073 "failed to pull old supplementalCredentialsBlob: %s",
1074 nt_errstr(status));
1075 return LDB_ERR_OPERATIONS_ERROR;
1078 if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
1079 old_scb = &_old_scb;
1080 } else {
1081 ldb_debug(ldb, LDB_DEBUG_ERROR,
1082 "setup_supplemental_field: "
1083 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1084 _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
1087 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1088 do_newer_keys = (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008);
1090 if (io->domain->store_cleartext &&
1091 (io->u.userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
1092 do_cleartext = true;
1096 * The ordering is this
1098 * Primary:Kerberos-Newer-Keys (optional)
1099 * Primary:Kerberos
1100 * Primary:WDigest
1101 * Primary:CLEARTEXT (optional)
1103 * And the 'Packages' package is insert before the last
1104 * other package.
1106 if (do_newer_keys) {
1107 /* Primary:Kerberos-Newer-Keys */
1108 nkn = &names[num_names++];
1109 pkn = &packages[num_packages++];
1112 /* Primary:Kerberos */
1113 nk = &names[num_names++];
1114 pk = &packages[num_packages++];
1116 if (!do_cleartext) {
1117 /* Packages */
1118 pp = &packages[num_packages++];
1121 /* Primary:WDigest */
1122 nd = &names[num_names++];
1123 pd = &packages[num_packages++];
1125 if (do_cleartext) {
1126 /* Packages */
1127 pp = &packages[num_packages++];
1129 /* Primary:CLEARTEXT */
1130 nc = &names[num_names++];
1131 pc = &packages[num_packages++];
1134 if (pkn) {
1136 * setup 'Primary:Kerberos-Newer-Keys' element
1138 *nkn = "Kerberos-Newer-Keys";
1140 ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
1141 if (ret != LDB_SUCCESS) {
1142 return ret;
1145 ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
1146 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1147 &pknb,
1148 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1149 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1150 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1151 ldb_asprintf_errstring(ldb,
1152 "setup_supplemental_field: "
1153 "failed to push package_PrimaryKerberosNeverBlob: %s",
1154 nt_errstr(status));
1155 return LDB_ERR_OPERATIONS_ERROR;
1157 pknb_hexstr = data_blob_hex_string_upper(io->ac, &pknb_blob);
1158 if (!pknb_hexstr) {
1159 ldb_oom(ldb);
1160 return LDB_ERR_OPERATIONS_ERROR;
1162 pkn->name = "Primary:Kerberos-Newer-Keys";
1163 pkn->reserved = 1;
1164 pkn->data = pknb_hexstr;
1168 * setup 'Primary:Kerberos' element
1170 *nk = "Kerberos";
1172 ret = setup_primary_kerberos(io, old_scb, &pkb);
1173 if (ret != LDB_SUCCESS) {
1174 return ret;
1177 ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
1178 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1179 &pkb,
1180 (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
1181 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1182 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1183 ldb_asprintf_errstring(ldb,
1184 "setup_supplemental_field: "
1185 "failed to push package_PrimaryKerberosBlob: %s",
1186 nt_errstr(status));
1187 return LDB_ERR_OPERATIONS_ERROR;
1189 pkb_hexstr = data_blob_hex_string_upper(io->ac, &pkb_blob);
1190 if (!pkb_hexstr) {
1191 ldb_oom(ldb);
1192 return LDB_ERR_OPERATIONS_ERROR;
1194 pk->name = "Primary:Kerberos";
1195 pk->reserved = 1;
1196 pk->data = pkb_hexstr;
1199 * setup 'Primary:WDigest' element
1201 *nd = "WDigest";
1203 ret = setup_primary_wdigest(io, old_scb, &pdb);
1204 if (ret != LDB_SUCCESS) {
1205 return ret;
1208 ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
1209 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1210 &pdb,
1211 (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
1212 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1213 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1214 ldb_asprintf_errstring(ldb,
1215 "setup_supplemental_field: "
1216 "failed to push package_PrimaryWDigestBlob: %s",
1217 nt_errstr(status));
1218 return LDB_ERR_OPERATIONS_ERROR;
1220 pdb_hexstr = data_blob_hex_string_upper(io->ac, &pdb_blob);
1221 if (!pdb_hexstr) {
1222 ldb_oom(ldb);
1223 return LDB_ERR_OPERATIONS_ERROR;
1225 pd->name = "Primary:WDigest";
1226 pd->reserved = 1;
1227 pd->data = pdb_hexstr;
1230 * setup 'Primary:CLEARTEXT' element
1232 if (pc) {
1233 *nc = "CLEARTEXT";
1235 pcb.cleartext = *io->n.cleartext_utf16;
1237 ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
1238 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1239 &pcb,
1240 (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
1241 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1242 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1243 ldb_asprintf_errstring(ldb,
1244 "setup_supplemental_field: "
1245 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1246 nt_errstr(status));
1247 return LDB_ERR_OPERATIONS_ERROR;
1249 pcb_hexstr = data_blob_hex_string_upper(io->ac, &pcb_blob);
1250 if (!pcb_hexstr) {
1251 ldb_oom(ldb);
1252 return LDB_ERR_OPERATIONS_ERROR;
1254 pc->name = "Primary:CLEARTEXT";
1255 pc->reserved = 1;
1256 pc->data = pcb_hexstr;
1260 * setup 'Packages' element
1262 pb.names = names;
1263 ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
1264 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1265 &pb,
1266 (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
1267 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1268 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1269 ldb_asprintf_errstring(ldb,
1270 "setup_supplemental_field: "
1271 "failed to push package_PackagesBlob: %s",
1272 nt_errstr(status));
1273 return LDB_ERR_OPERATIONS_ERROR;
1275 pb_hexstr = data_blob_hex_string_upper(io->ac, &pb_blob);
1276 if (!pb_hexstr) {
1277 ldb_oom(ldb);
1278 return LDB_ERR_OPERATIONS_ERROR;
1280 pp->name = "Packages";
1281 pp->reserved = 2;
1282 pp->data = pb_hexstr;
1285 * setup 'supplementalCredentials' value
1287 ZERO_STRUCT(scb);
1288 scb.sub.num_packages = num_packages;
1289 scb.sub.packages = packages;
1291 ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
1292 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1293 &scb,
1294 (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
1295 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1296 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
1297 ldb_asprintf_errstring(ldb,
1298 "setup_supplemental_field: "
1299 "failed to push supplementalCredentialsBlob: %s",
1300 nt_errstr(status));
1301 return LDB_ERR_OPERATIONS_ERROR;
1304 return LDB_SUCCESS;
1307 static int setup_last_set_field(struct setup_password_fields_io *io)
1309 /* set it as now */
1310 unix_to_nt_time(&io->g.last_set, time(NULL));
1312 return LDB_SUCCESS;
1315 static int setup_password_fields(struct setup_password_fields_io *io)
1317 struct ldb_context *ldb;
1318 bool ok;
1319 int ret;
1320 size_t converted_pw_len;
1322 ldb = ldb_module_get_ctx(io->ac->module);
1325 * refuse the change if someone want to change the cleartext
1326 * and supply his own hashes at the same time...
1328 if ((io->n.cleartext_utf8 || io->n.cleartext_utf16) && (io->n.nt_hash || io->n.lm_hash)) {
1329 ldb_asprintf_errstring(ldb,
1330 "setup_password_fields: "
1331 "it's only allowed to set the cleartext password or the password hashes");
1332 return LDB_ERR_UNWILLING_TO_PERFORM;
1335 if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
1336 ldb_asprintf_errstring(ldb,
1337 "setup_password_fields: "
1338 "it's only allowed to set the cleartext password as userPassword or clearTextPasssword, not both at once");
1339 return LDB_ERR_UNWILLING_TO_PERFORM;
1342 if (io->n.cleartext_utf8) {
1343 char **cleartext_utf16_str;
1344 struct ldb_val *cleartext_utf16_blob;
1345 io->n.cleartext_utf16 = cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
1346 if (!io->n.cleartext_utf16) {
1347 ldb_oom(ldb);
1348 return LDB_ERR_OPERATIONS_ERROR;
1350 if (!convert_string_talloc_convenience(io->ac,
1351 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1352 CH_UTF8, CH_UTF16,
1353 io->n.cleartext_utf8->data,
1354 io->n.cleartext_utf8->length,
1355 (void **)&cleartext_utf16_str,
1356 &converted_pw_len, false)) {
1357 ldb_asprintf_errstring(ldb,
1358 "setup_password_fields: "
1359 "failed to generate UTF16 password from cleartext UTF8 password");
1360 return LDB_ERR_OPERATIONS_ERROR;
1362 *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str,
1363 converted_pw_len);
1364 } else if (io->n.cleartext_utf16) {
1365 char *cleartext_utf8_str;
1366 struct ldb_val *cleartext_utf8_blob;
1367 io->n.cleartext_utf8 = cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
1368 if (!io->n.cleartext_utf8) {
1369 ldb_oom(ldb);
1370 return LDB_ERR_OPERATIONS_ERROR;
1372 if (!convert_string_talloc_convenience(io->ac,
1373 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1374 CH_UTF16MUNGED, CH_UTF8,
1375 io->n.cleartext_utf16->data,
1376 io->n.cleartext_utf16->length,
1377 (void **)&cleartext_utf8_str,
1378 &converted_pw_len, false)) {
1379 /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
1380 io->n.cleartext_utf8 = NULL;
1381 talloc_free(cleartext_utf8_blob);
1383 *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str,
1384 converted_pw_len);
1386 if (io->n.cleartext_utf16) {
1387 struct samr_Password *nt_hash;
1388 nt_hash = talloc(io->ac, struct samr_Password);
1389 if (!nt_hash) {
1390 ldb_oom(ldb);
1391 return LDB_ERR_OPERATIONS_ERROR;
1393 io->n.nt_hash = nt_hash;
1395 /* compute the new nt hash */
1396 mdfour(nt_hash->hash, io->n.cleartext_utf16->data,
1397 io->n.cleartext_utf16->length);
1400 if (io->n.cleartext_utf8) {
1401 struct samr_Password *lm_hash;
1402 char *cleartext_unix;
1403 if (lp_lanman_auth(ldb_get_opaque(ldb, "loadparm")) &&
1404 convert_string_talloc_convenience(io->ac,
1405 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1406 CH_UTF8, CH_UNIX,
1407 io->n.cleartext_utf8->data,
1408 io->n.cleartext_utf8->length,
1409 (void **)&cleartext_unix,
1410 &converted_pw_len, false)) {
1411 lm_hash = talloc(io->ac, struct samr_Password);
1412 if (!lm_hash) {
1413 ldb_oom(ldb);
1414 return LDB_ERR_OPERATIONS_ERROR;
1417 /* compute the new lm hash */
1418 ok = E_deshash((char *)cleartext_unix, lm_hash->hash);
1419 if (ok) {
1420 io->n.lm_hash = lm_hash;
1421 } else {
1422 talloc_free(lm_hash->hash);
1426 ret = setup_kerberos_keys(io);
1427 if (ret != LDB_SUCCESS) {
1428 return ret;
1432 ret = setup_nt_fields(io);
1433 if (ret != LDB_SUCCESS) {
1434 return ret;
1437 ret = setup_lm_fields(io);
1438 if (ret != LDB_SUCCESS) {
1439 return ret;
1442 ret = setup_supplemental_field(io);
1443 if (ret != LDB_SUCCESS) {
1444 return ret;
1447 ret = setup_last_set_field(io);
1448 if (ret != LDB_SUCCESS) {
1449 return ret;
1452 return LDB_SUCCESS;
1455 static int setup_io(struct ph_context *ac,
1456 const struct ldb_message *orig_msg,
1457 const struct ldb_message *searched_msg,
1458 struct setup_password_fields_io *io)
1460 const struct ldb_val *quoted_utf16;
1461 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1463 ZERO_STRUCTP(io);
1465 /* Some operations below require kerberos contexts */
1466 if (smb_krb5_init_context(ac,
1467 ldb_get_event_context(ldb),
1468 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
1469 &io->smb_krb5_context) != 0) {
1470 return LDB_ERR_OPERATIONS_ERROR;
1473 io->ac = ac;
1474 io->domain = ac->domain;
1476 io->u.userAccountControl = samdb_result_uint(searched_msg, "userAccountControl", 0);
1477 io->u.pwdLastSet = samdb_result_nttime(searched_msg, "pwdLastSet", 0);
1478 io->u.sAMAccountName = samdb_result_string(searched_msg, "sAMAccountName", NULL);
1479 io->u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
1480 io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
1482 if (io->u.sAMAccountName == NULL) {
1483 ldb_asprintf_errstring(ldb,
1484 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
1485 ldb_dn_get_linearized(searched_msg->dn));
1487 return LDB_ERR_CONSTRAINT_VIOLATION;
1490 io->n.cleartext_utf8 = ldb_msg_find_ldb_val(orig_msg, "userPassword");
1491 io->n.cleartext_utf16 = ldb_msg_find_ldb_val(orig_msg, "clearTextPassword");
1493 /* this rather strange looking piece of code is there to
1494 handle a ldap client setting a password remotely using the
1495 unicodePwd ldap field. The syntax is that the password is
1496 in UTF-16LE, with a " at either end. Unfortunately the
1497 unicodePwd field is also used to store the nt hashes
1498 internally in Samba, and is used in the nt hash format on
1499 the wire in DRS replication, so we have a single name for
1500 two distinct values. The code below leaves us with a small
1501 chance (less than 1 in 2^32) of a mixup, if someone manages
1502 to create a MD4 hash which starts and ends in 0x22 0x00, as
1503 that would then be treated as a UTF16 password rather than
1504 a nthash */
1505 quoted_utf16 = ldb_msg_find_ldb_val(orig_msg, "unicodePwd");
1506 if (quoted_utf16 &&
1507 quoted_utf16->length >= 4 &&
1508 quoted_utf16->data[0] == '"' &&
1509 quoted_utf16->data[1] == 0 &&
1510 quoted_utf16->data[quoted_utf16->length-2] == '"' &&
1511 quoted_utf16->data[quoted_utf16->length-1] == 0) {
1512 io->n.quoted_utf16.data = talloc_memdup(io->ac, quoted_utf16->data+2, quoted_utf16->length-4);
1513 io->n.quoted_utf16.length = quoted_utf16->length-4;
1514 io->n.cleartext_utf16 = &io->n.quoted_utf16;
1515 io->n.nt_hash = NULL;
1516 } else {
1517 io->n.nt_hash = samdb_result_hash(io->ac, orig_msg, "unicodePwd");
1520 io->n.lm_hash = samdb_result_hash(io->ac, orig_msg, "dBCSPwd");
1522 return LDB_SUCCESS;
1525 static struct ph_context *ph_init_context(struct ldb_module *module,
1526 struct ldb_request *req)
1528 struct ldb_context *ldb;
1529 struct ph_context *ac;
1531 ldb = ldb_module_get_ctx(module);
1533 ac = talloc_zero(req, struct ph_context);
1534 if (ac == NULL) {
1535 ldb_set_errstring(ldb, "Out of Memory");
1536 return NULL;
1539 ac->module = module;
1540 ac->req = req;
1542 return ac;
1545 static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
1547 struct ph_context *ac;
1549 ac = talloc_get_type(req->context, struct ph_context);
1551 if (!ares) {
1552 return ldb_module_done(ac->req, NULL, NULL,
1553 LDB_ERR_OPERATIONS_ERROR);
1556 if (ares->type == LDB_REPLY_REFERRAL) {
1557 return ldb_module_send_referral(ac->req, ares->referral);
1560 if (ares->error != LDB_SUCCESS) {
1561 return ldb_module_done(ac->req, ares->controls,
1562 ares->response, ares->error);
1565 if (ares->type != LDB_REPLY_DONE) {
1566 talloc_free(ares);
1567 return ldb_module_done(ac->req, NULL, NULL,
1568 LDB_ERR_OPERATIONS_ERROR);
1571 return ldb_module_done(ac->req, ares->controls,
1572 ares->response, ares->error);
1575 static int password_hash_add_do_add(struct ph_context *ac);
1576 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
1577 static int password_hash_mod_search_self(struct ph_context *ac);
1578 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
1579 static int password_hash_mod_do_mod(struct ph_context *ac);
1581 static int get_domain_data_callback(struct ldb_request *req,
1582 struct ldb_reply *ares)
1584 struct ldb_context *ldb;
1585 struct domain_data *data;
1586 struct ph_context *ac;
1587 struct loadparm_context *lp_ctx;
1588 int ret;
1590 ac = talloc_get_type(req->context, struct ph_context);
1591 ldb = ldb_module_get_ctx(ac->module);
1593 if (!ares) {
1594 ret = LDB_ERR_OPERATIONS_ERROR;
1595 goto done;
1597 if (ares->error != LDB_SUCCESS) {
1598 return ldb_module_done(ac->req, ares->controls,
1599 ares->response, ares->error);
1602 switch (ares->type) {
1603 case LDB_REPLY_ENTRY:
1604 if (ac->domain != NULL) {
1605 talloc_free(ares);
1607 ldb_set_errstring(ldb, "Too many results");
1608 ret = LDB_ERR_OPERATIONS_ERROR;
1609 goto done;
1612 data = talloc_zero(ac, struct domain_data);
1613 if (data == NULL) {
1614 talloc_free(ares);
1616 ldb_oom(ldb);
1617 ret = LDB_ERR_OPERATIONS_ERROR;
1618 goto done;
1621 data->pwdProperties = samdb_result_uint(ares->message, "pwdProperties", -1);
1622 data->pwdHistoryLength = samdb_result_uint(ares->message, "pwdHistoryLength", -1);
1623 data->maxPwdAge = samdb_result_int64(ares->message, "maxPwdAge", -1);
1624 data->minPwdAge = samdb_result_int64(ares->message, "minPwdAge", -1);
1625 data->minPwdLength = samdb_result_uint(ares->message, "minPwdLength", -1);
1626 data->store_cleartext =
1627 data->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
1629 talloc_free(ares);
1631 /* For a domain DN, this puts things in dotted notation */
1632 /* For builtin domains, this will give details for the host,
1633 * but that doesn't really matter, as it's just used for salt
1634 * and kerberos principals, which don't exist here */
1636 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
1637 struct loadparm_context);
1639 data->dns_domain = lp_dnsdomain(lp_ctx);
1640 data->realm = lp_realm(lp_ctx);
1641 data->netbios_domain = lp_sam_name(lp_ctx);
1643 ac->domain = data;
1645 ret = LDB_SUCCESS;
1646 break;
1648 case LDB_REPLY_REFERRAL:
1649 /* ignore */
1650 talloc_free(ares);
1651 ret = LDB_SUCCESS;
1652 break;
1654 case LDB_REPLY_DONE:
1655 talloc_free(ares);
1656 /* call the next step */
1657 switch (ac->req->operation) {
1658 case LDB_ADD:
1659 ret = password_hash_add_do_add(ac);
1660 break;
1662 case LDB_MODIFY:
1663 ret = password_hash_mod_do_mod(ac);
1664 break;
1666 default:
1667 ret = LDB_ERR_OPERATIONS_ERROR;
1668 break;
1670 break;
1674 done:
1675 if (ret != LDB_SUCCESS) {
1676 return ldb_module_done(ac->req, NULL, NULL, ret);
1679 return LDB_SUCCESS;
1682 static int build_domain_data_request(struct ph_context *ac)
1684 /* attrs[] is returned from this function in
1685 ac->dom_req->op.search.attrs, so it must be static, as
1686 otherwise the compiler can put it on the stack */
1687 struct ldb_context *ldb;
1688 static const char * const attrs[] = { "pwdProperties",
1689 "pwdHistoryLength",
1690 "maxPwdAge",
1691 "minPwdAge",
1692 "minPwdLength",
1693 NULL };
1695 ldb = ldb_module_get_ctx(ac->module);
1697 return ldb_build_search_req(&ac->dom_req, ldb, ac,
1698 ldb_get_default_basedn(ldb),
1699 LDB_SCOPE_BASE,
1700 NULL, attrs,
1701 NULL,
1702 ac, get_domain_data_callback,
1703 ac->req);
1706 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
1708 struct ldb_context *ldb;
1709 struct ph_context *ac;
1710 struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
1711 *ntAttr, *lmAttr;
1712 int ret;
1714 ldb = ldb_module_get_ctx(module);
1716 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
1718 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
1719 return ldb_next_request(module, req);
1722 /* If the caller is manipulating the local passwords directly, let them pass */
1723 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1724 req->op.add.message->dn) == 0) {
1725 return ldb_next_request(module, req);
1728 /* nobody must touch password histories and 'supplementalCredentials' */
1729 if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
1730 return LDB_ERR_UNWILLING_TO_PERFORM;
1732 if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
1733 return LDB_ERR_UNWILLING_TO_PERFORM;
1735 if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
1736 return LDB_ERR_UNWILLING_TO_PERFORM;
1739 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
1740 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
1742 userPasswordAttr = ldb_msg_find_element(req->op.add.message, "userPassword");
1743 clearTextPasswordAttr = ldb_msg_find_element(req->op.add.message, "clearTextPassword");
1744 ntAttr = ldb_msg_find_element(req->op.add.message, "unicodePwd");
1745 lmAttr = ldb_msg_find_element(req->op.add.message, "dBCSPwd");
1747 if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
1748 return ldb_next_request(module, req);
1751 /* Make sure we are performing the password set action on a (for us)
1752 * valid object. Those are instances of either "user" and/or
1753 * "inetOrgPerson". Otherwise continue with the submodules. */
1754 if ((!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "user"))
1755 && (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "inetOrgPerson"))) {
1757 if (ldb_msg_find_element(req->op.add.message, "clearTextPassword") != NULL) {
1758 ldb_set_errstring(ldb,
1759 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
1760 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1763 return ldb_next_request(module, req);
1766 ac = ph_init_context(module, req);
1767 if (ac == NULL) {
1768 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1769 return LDB_ERR_OPERATIONS_ERROR;
1772 /* get user domain data */
1773 ret = build_domain_data_request(ac);
1774 if (ret != LDB_SUCCESS) {
1775 return ret;
1778 return ldb_next_request(module, ac->dom_req);
1781 static int password_hash_add_do_add(struct ph_context *ac)
1783 struct ldb_context *ldb;
1784 struct ldb_request *down_req;
1785 struct ldb_message *msg;
1786 struct setup_password_fields_io io;
1787 int ret;
1789 /* Prepare the internal data structure containing the passwords */
1790 ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
1791 if (ret != LDB_SUCCESS) {
1792 return ret;
1795 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
1796 if (msg == NULL) {
1797 return LDB_ERR_OPERATIONS_ERROR;
1800 /* remove attributes that we just read into 'io' */
1801 ldb_msg_remove_attr(msg, "userPassword");
1802 ldb_msg_remove_attr(msg, "clearTextPassword");
1803 ldb_msg_remove_attr(msg, "unicodePwd");
1804 ldb_msg_remove_attr(msg, "dBCSPwd");
1805 ldb_msg_remove_attr(msg, "pwdLastSet");
1807 ldb = ldb_module_get_ctx(ac->module);
1809 ret = setup_password_fields(&io);
1810 if (ret != LDB_SUCCESS) {
1811 return ret;
1814 if (io.g.nt_hash) {
1815 ret = samdb_msg_add_hash(ldb, ac, msg,
1816 "unicodePwd", io.g.nt_hash);
1817 if (ret != LDB_SUCCESS) {
1818 return ret;
1821 if (io.g.lm_hash) {
1822 ret = samdb_msg_add_hash(ldb, ac, msg,
1823 "dBCSPwd", io.g.lm_hash);
1824 if (ret != LDB_SUCCESS) {
1825 return ret;
1828 if (io.g.nt_history_len > 0) {
1829 ret = samdb_msg_add_hashes(ac, msg,
1830 "ntPwdHistory",
1831 io.g.nt_history,
1832 io.g.nt_history_len);
1833 if (ret != LDB_SUCCESS) {
1834 return ret;
1837 if (io.g.lm_history_len > 0) {
1838 ret = samdb_msg_add_hashes(ac, msg,
1839 "lmPwdHistory",
1840 io.g.lm_history,
1841 io.g.lm_history_len);
1842 if (ret != LDB_SUCCESS) {
1843 return ret;
1846 if (io.g.supplemental.length > 0) {
1847 ret = ldb_msg_add_value(msg, "supplementalCredentials",
1848 &io.g.supplemental, NULL);
1849 if (ret != LDB_SUCCESS) {
1850 return ret;
1853 ret = samdb_msg_add_uint64(ldb, ac, msg,
1854 "pwdLastSet",
1855 io.g.last_set);
1856 if (ret != LDB_SUCCESS) {
1857 return ret;
1860 ret = ldb_build_add_req(&down_req, ldb, ac,
1861 msg,
1862 ac->req->controls,
1863 ac, ph_op_callback,
1864 ac->req);
1865 if (ret != LDB_SUCCESS) {
1866 return ret;
1869 return ldb_next_request(ac->module, down_req);
1872 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
1874 struct ldb_context *ldb;
1875 struct ph_context *ac;
1876 const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
1877 "unicodePwd", "dBCSPwd", NULL }, **l;
1878 unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt;
1879 struct ldb_message_element *passwordAttr;
1880 struct ldb_message *msg;
1881 struct ldb_request *down_req;
1882 int ret;
1884 ldb = ldb_module_get_ctx(module);
1886 ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
1888 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
1889 return ldb_next_request(module, req);
1892 /* If the caller is manipulating the local passwords directly, let them pass */
1893 if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
1894 req->op.mod.message->dn) == 0) {
1895 return ldb_next_request(module, req);
1898 /* nobody must touch password histories and 'supplementalCredentials' */
1899 if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
1900 return LDB_ERR_UNWILLING_TO_PERFORM;
1902 if (ldb_msg_find_element(req->op.mod.message, "lmPwdHistory")) {
1903 return LDB_ERR_UNWILLING_TO_PERFORM;
1905 if (ldb_msg_find_element(req->op.mod.message, "supplementalCredentials")) {
1906 return LDB_ERR_UNWILLING_TO_PERFORM;
1909 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
1910 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
1911 * For password changes/set there should be a 'delete' or a 'modify'
1912 * on these attributes. */
1913 attr_cnt = 0;
1914 for (l = passwordAttrs; *l != NULL; l++) {
1915 if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) {
1916 ++attr_cnt;
1919 if (attr_cnt == 0) {
1920 return ldb_next_request(module, req);
1923 ac = ph_init_context(module, req);
1924 if (!ac) {
1925 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1926 return LDB_ERR_OPERATIONS_ERROR;
1929 /* use a new message structure so that we can modify it */
1930 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
1931 if (msg == NULL) {
1932 ldb_oom(ldb);
1933 return LDB_ERR_OPERATIONS_ERROR;
1936 /* - check for single-valued password attributes
1937 * (if not return "CONSTRAINT_VIOLATION")
1938 * - check that for a password change operation one add and one delete
1939 * operation exists
1940 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
1941 * - check that a password change and a password set operation cannot
1942 * be mixed
1943 * (if not return "UNWILLING_TO_PERFORM")
1944 * - remove all password attributes modifications from the first change
1945 * operation (anything without the passwords) - we will make the real
1946 * modification later */
1947 del_attr_cnt = 0;
1948 add_attr_cnt = 0;
1949 rep_attr_cnt = 0;
1950 for (l = passwordAttrs; *l != NULL; l++) {
1951 while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
1952 if (passwordAttr->flags == LDB_FLAG_MOD_DELETE) {
1953 ++del_attr_cnt;
1955 if (passwordAttr->flags == LDB_FLAG_MOD_ADD) {
1956 ++add_attr_cnt;
1958 if (passwordAttr->flags == LDB_FLAG_MOD_REPLACE) {
1959 ++rep_attr_cnt;
1961 if ((passwordAttr->num_values != 1) &&
1962 (passwordAttr->flags != LDB_FLAG_MOD_REPLACE)) {
1963 talloc_free(ac);
1964 ldb_asprintf_errstring(ldb,
1965 "'%s' attributes must have exactly one value!",
1966 *l);
1967 return LDB_ERR_CONSTRAINT_VIOLATION;
1969 ldb_msg_remove_attr(msg, *l);
1972 if ((del_attr_cnt > 0) && (add_attr_cnt == 0)) {
1973 talloc_free(ac);
1974 ldb_set_errstring(ldb,
1975 "Only the delete action for a password change specified!");
1976 return LDB_ERR_CONSTRAINT_VIOLATION;
1978 if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) {
1979 talloc_free(ac);
1980 ldb_set_errstring(ldb,
1981 "Only the add action for a password change specified!");
1982 return LDB_ERR_UNWILLING_TO_PERFORM;
1984 if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) {
1985 talloc_free(ac);
1986 ldb_set_errstring(ldb,
1987 "Only one delete and one add action for a password change allowed!");
1988 return LDB_ERR_UNWILLING_TO_PERFORM;
1990 if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) {
1991 talloc_free(ac);
1992 ldb_set_errstring(ldb,
1993 "Either a password change or a password set operation is allowed!");
1994 return LDB_ERR_UNWILLING_TO_PERFORM;
1997 /* if there was nothing else to be modified skip to next step */
1998 if (msg->num_elements == 0) {
1999 return password_hash_mod_search_self(ac);
2002 ret = ldb_build_mod_req(&down_req, ldb, ac,
2003 msg,
2004 req->controls,
2005 ac, ph_modify_callback,
2006 req);
2007 if (ret != LDB_SUCCESS) {
2008 return ret;
2011 return ldb_next_request(module, down_req);
2014 static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
2016 struct ph_context *ac;
2018 ac = talloc_get_type(req->context, struct ph_context);
2020 if (!ares) {
2021 return ldb_module_done(ac->req, NULL, NULL,
2022 LDB_ERR_OPERATIONS_ERROR);
2025 if (ares->type == LDB_REPLY_REFERRAL) {
2026 return ldb_module_send_referral(ac->req, ares->referral);
2029 if (ares->error != LDB_SUCCESS) {
2030 return ldb_module_done(ac->req, ares->controls,
2031 ares->response, ares->error);
2034 if (ares->type != LDB_REPLY_DONE) {
2035 talloc_free(ares);
2036 return ldb_module_done(ac->req, NULL, NULL,
2037 LDB_ERR_OPERATIONS_ERROR);
2040 talloc_free(ares);
2042 return password_hash_mod_search_self(ac);
2045 static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
2047 struct ldb_context *ldb;
2048 struct ph_context *ac;
2049 int ret;
2051 ac = talloc_get_type(req->context, struct ph_context);
2052 ldb = ldb_module_get_ctx(ac->module);
2054 if (!ares) {
2055 ret = LDB_ERR_OPERATIONS_ERROR;
2056 goto done;
2058 if (ares->error != LDB_SUCCESS) {
2059 return ldb_module_done(ac->req, ares->controls,
2060 ares->response, ares->error);
2063 /* we are interested only in the single reply (base search) */
2064 switch (ares->type) {
2065 case LDB_REPLY_ENTRY:
2066 /* Make sure we are performing the password change action on a
2067 * (for us) valid object. Those are instances of either "user"
2068 * and/or "inetOrgPerson". Otherwise continue with the
2069 * submodules. */
2070 if ((!ldb_msg_check_string_attribute(ares->message, "objectClass", "user"))
2071 && (!ldb_msg_check_string_attribute(ares->message, "objectClass", "inetOrgPerson"))) {
2072 talloc_free(ares);
2074 if (ldb_msg_find_element(ac->req->op.mod.message, "clearTextPassword") != NULL) {
2075 ldb_set_errstring(ldb,
2076 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2077 ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
2078 goto done;
2081 ret = ldb_next_request(ac->module, ac->req);
2082 goto done;
2085 if (ac->search_res != NULL) {
2086 talloc_free(ares);
2088 ldb_set_errstring(ldb, "Too many results");
2089 ret = LDB_ERR_OPERATIONS_ERROR;
2090 goto done;
2093 ac->search_res = talloc_steal(ac, ares);
2094 ret = LDB_SUCCESS;
2095 break;
2097 case LDB_REPLY_REFERRAL:
2098 /* ignore anything else for now */
2099 talloc_free(ares);
2100 ret = LDB_SUCCESS;
2101 break;
2103 case LDB_REPLY_DONE:
2104 talloc_free(ares);
2106 /* get user domain data */
2107 ret = build_domain_data_request(ac);
2108 if (ret != LDB_SUCCESS) {
2109 return ldb_module_done(ac->req, NULL, NULL, ret);
2112 ret = ldb_next_request(ac->module, ac->dom_req);
2113 break;
2116 done:
2117 if (ret != LDB_SUCCESS) {
2118 return ldb_module_done(ac->req, NULL, NULL, ret);
2121 return LDB_SUCCESS;
2124 static int password_hash_mod_search_self(struct ph_context *ac)
2126 struct ldb_context *ldb;
2127 static const char * const attrs[] = { "objectClass",
2128 "userAccountControl",
2129 "pwdLastSet",
2130 "sAMAccountName",
2131 "objectSid",
2132 "userPrincipalName",
2133 "supplementalCredentials",
2134 "lmPwdHistory",
2135 "ntPwdHistory",
2136 "dBCSPwd",
2137 "unicodePwd",
2138 NULL };
2139 struct ldb_request *search_req;
2140 int ret;
2142 ldb = ldb_module_get_ctx(ac->module);
2144 ret = ldb_build_search_req(&search_req, ldb, ac,
2145 ac->req->op.mod.message->dn,
2146 LDB_SCOPE_BASE,
2147 "(objectclass=*)",
2148 attrs,
2149 NULL,
2150 ac, ph_mod_search_callback,
2151 ac->req);
2153 if (ret != LDB_SUCCESS) {
2154 return ret;
2157 return ldb_next_request(ac->module, search_req);
2160 static int password_hash_mod_do_mod(struct ph_context *ac)
2162 struct ldb_context *ldb;
2163 struct ldb_request *mod_req;
2164 struct ldb_message *msg;
2165 const struct ldb_message *orig_msg, *searched_msg;
2166 struct setup_password_fields_io io;
2167 int ret;
2169 ldb = ldb_module_get_ctx(ac->module);
2171 /* use a new message structure so that we can modify it */
2172 msg = ldb_msg_new(ac);
2173 if (msg == NULL) {
2174 return LDB_ERR_OPERATIONS_ERROR;
2177 /* modify dn */
2178 msg->dn = ac->req->op.mod.message->dn;
2180 orig_msg = ac->req->op.mod.message;
2181 searched_msg = ac->search_res->message;
2183 /* Prepare the internal data structure containing the passwords */
2184 ret = setup_io(ac, orig_msg, searched_msg, &io);
2185 if (ret != LDB_SUCCESS) {
2186 return ret;
2189 /* Fill in some final details (only relevent once the password has been set) */
2190 io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
2191 io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
2192 io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
2194 ret = setup_password_fields(&io);
2195 if (ret != LDB_SUCCESS) {
2196 return ret;
2199 /* make sure we replace all the old attributes */
2200 ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
2201 ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
2202 ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2203 ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
2204 ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
2205 ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
2207 if (io.g.nt_hash) {
2208 ret = samdb_msg_add_hash(ldb, ac, msg,
2209 "unicodePwd", io.g.nt_hash);
2210 if (ret != LDB_SUCCESS) {
2211 return ret;
2214 if (io.g.lm_hash) {
2215 ret = samdb_msg_add_hash(ldb, ac, msg,
2216 "dBCSPwd", io.g.lm_hash);
2217 if (ret != LDB_SUCCESS) {
2218 return ret;
2221 if (io.g.nt_history_len > 0) {
2222 ret = samdb_msg_add_hashes(ac, msg,
2223 "ntPwdHistory",
2224 io.g.nt_history,
2225 io.g.nt_history_len);
2226 if (ret != LDB_SUCCESS) {
2227 return ret;
2230 if (io.g.lm_history_len > 0) {
2231 ret = samdb_msg_add_hashes(ac, msg,
2232 "lmPwdHistory",
2233 io.g.lm_history,
2234 io.g.lm_history_len);
2235 if (ret != LDB_SUCCESS) {
2236 return ret;
2239 if (io.g.supplemental.length > 0) {
2240 ret = ldb_msg_add_value(msg, "supplementalCredentials",
2241 &io.g.supplemental, NULL);
2242 if (ret != LDB_SUCCESS) {
2243 return ret;
2246 ret = samdb_msg_add_uint64(ldb, ac, msg,
2247 "pwdLastSet",
2248 io.g.last_set);
2249 if (ret != LDB_SUCCESS) {
2250 return ret;
2253 ret = ldb_build_mod_req(&mod_req, ldb, ac,
2254 msg,
2255 ac->req->controls,
2256 ac, ph_op_callback,
2257 ac->req);
2258 if (ret != LDB_SUCCESS) {
2259 return ret;
2262 return ldb_next_request(ac->module, mod_req);
2265 _PUBLIC_ const struct ldb_module_ops ldb_password_hash_module_ops = {
2266 .name = "password_hash",
2267 .add = password_hash_add,
2268 .modify = password_hash_modify