4 Copyright (C) Simo Sorce 2004-2006
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * Component: ldb password_hash module
28 * Description: correctly update hash values based on changes to sambaPassword and friends
30 * Author: Andrew Bartlett
34 #include "libcli/ldap/ldap.h"
35 #include "ldb/include/ldb_errors.h"
36 #include "ldb/include/ldb_private.h"
37 #include "librpc/gen_ndr/misc.h"
38 #include "librpc/gen_ndr/samr.h"
39 #include "libcli/auth/libcli_auth.h"
40 #include "libcli/security/security.h"
41 #include "system/kerberos.h"
42 #include "auth/kerberos/kerberos.h"
43 #include "system/time.h"
44 #include "dsdb/samdb/samdb.h"
45 #include "dsdb/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"
51 /* If we have decided there is reason to work on this request, then
52 * setup all the password hash types correctly.
54 * If the administrator doesn't want the sambaPassword stored (set in the
55 * domain and per-account policies) then we must strip that out before
56 * we do the first operation.
58 * Once this is done (which could update anything at all), we
59 * calculate the password hashes.
61 * This function must not only update the unicodePwd, dBCSPwd and
62 * supplementalCredentials fields, it must also atomicly increment the
63 * msDS-KeyVersionNumber. We should be in a transaction, so all this
64 * should be quite safe...
66 * Finally, if the administrator has requested that a password history
67 * be maintained, then this should also be written out.
73 enum ph_type
{PH_ADD
, PH_MOD
} type
;
74 enum ph_step
{PH_ADD_SEARCH_DOM
, PH_ADD_DO_ADD
, PH_MOD_DO_REQ
, PH_MOD_SEARCH_SELF
, PH_MOD_SEARCH_DOM
, PH_MOD_DO_MOD
} step
;
76 struct ldb_module
*module
;
77 struct ldb_request
*orig_req
;
79 struct ldb_request
*dom_req
;
80 struct ldb_reply
*dom_res
;
82 struct ldb_request
*down_req
;
84 struct ldb_request
*search_req
;
85 struct ldb_reply
*search_res
;
87 struct ldb_request
*mod_req
;
89 struct dom_sid
*domain_sid
;
95 uint_t pwdHistoryLength
;
100 struct setup_password_fields_io
{
101 struct ph_context
*ac
;
102 struct domain_data
*domain
;
103 struct smb_krb5_context
*smb_krb5_context
;
105 /* infos about the user account */
107 uint32_t user_account_control
;
108 const char *sAMAccountName
;
109 const char *user_principal_name
;
113 /* new credentials */
115 const char *cleartext
;
116 struct samr_Password
*nt_hash
;
117 struct samr_Password
*lm_hash
;
120 /* old credentials */
122 uint32_t nt_history_len
;
123 struct samr_Password
*nt_history
;
124 uint32_t lm_history_len
;
125 struct samr_Password
*lm_history
;
126 const struct ldb_val
*supplemental
;
127 struct supplementalCredentialsBlob scb
;
131 /* generated credentials */
133 struct samr_Password
*nt_hash
;
134 struct samr_Password
*lm_hash
;
135 uint32_t nt_history_len
;
136 struct samr_Password
*nt_history
;
137 uint32_t lm_history_len
;
138 struct samr_Password
*lm_history
;
139 struct ldb_val supplemental
;
145 static int setup_nt_fields(struct setup_password_fields_io
*io
)
149 io
->g
.nt_hash
= io
->n
.nt_hash
;
151 if (io
->domain
->pwdHistoryLength
== 0) {
155 /* We might not have an old NT password */
156 io
->g
.nt_history
= talloc_array(io
->ac
,
157 struct samr_Password
,
158 io
->domain
->pwdHistoryLength
);
159 if (!io
->g
.nt_history
) {
160 ldb_oom(io
->ac
->module
->ldb
);
161 return LDB_ERR_OPERATIONS_ERROR
;
164 for (i
= 0; i
< MIN(io
->domain
->pwdHistoryLength
-1, io
->o
.nt_history_len
); i
++) {
165 io
->g
.nt_history
[i
+1] = io
->o
.nt_history
[i
];
167 io
->g
.nt_history_len
= i
+ 1;
170 io
->g
.nt_history
[0] = *io
->g
.nt_hash
;
173 * TODO: is this correct?
174 * the simular behavior is correct for the lm history case
176 E_md4hash("", io
->g
.nt_history
[0].hash
);
182 static int setup_lm_fields(struct setup_password_fields_io
*io
)
186 io
->g
.lm_hash
= io
->n
.lm_hash
;
188 if (io
->domain
->pwdHistoryLength
== 0) {
192 /* We might not have an old NT password */
193 io
->g
.lm_history
= talloc_array(io
->ac
,
194 struct samr_Password
,
195 io
->domain
->pwdHistoryLength
);
196 if (!io
->g
.lm_history
) {
197 ldb_oom(io
->ac
->module
->ldb
);
198 return LDB_ERR_OPERATIONS_ERROR
;
201 for (i
= 0; i
< MIN(io
->domain
->pwdHistoryLength
-1, io
->o
.lm_history_len
); i
++) {
202 io
->g
.lm_history
[i
+1] = io
->o
.lm_history
[i
];
204 io
->g
.lm_history_len
= i
+ 1;
207 io
->g
.lm_history
[0] = *io
->g
.lm_hash
;
209 E_deshash("", io
->g
.lm_history
[0].hash
);
215 static int setup_primary_kerberos(struct setup_password_fields_io
*io
,
216 const struct supplementalCredentialsBlob
*old_scb
,
217 struct package_PrimaryKerberosBlob
*pkb
)
219 krb5_error_code krb5_ret
;
220 Principal
*salt_principal
;
224 struct supplementalCredentialsPackage
*old_scp
= NULL
;
225 struct package_PrimaryKerberosBlob _old_pkb
;
226 struct package_PrimaryKerberosBlob
*old_pkb
= NULL
;
230 /* Many, many thanks to lukeh@padl.com for this
231 * algorithm, described in his Nov 10 2004 mail to
232 * samba-technical@samba.org */
235 * Determine a salting principal
237 if (io
->u
.is_computer
) {
241 name
= talloc_strdup(io
->ac
, io
->u
.sAMAccountName
);
243 ldb_oom(io
->ac
->module
->ldb
);
244 return LDB_ERR_OPERATIONS_ERROR
;
247 if (name
[strlen(name
)-1] == '$') {
248 name
[strlen(name
)-1] = '\0';
251 saltbody
= talloc_asprintf(io
->ac
, "%s.%s", name
, io
->domain
->dns_domain
);
253 ldb_oom(io
->ac
->module
->ldb
);
254 return LDB_ERR_OPERATIONS_ERROR
;
257 krb5_ret
= krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
259 io
->domain
->realm
, "host",
261 } else if (io
->u
.user_principal_name
) {
262 char *user_principal_name
;
265 user_principal_name
= talloc_strdup(io
->ac
, io
->u
.user_principal_name
);
266 if (!user_principal_name
) {
267 ldb_oom(io
->ac
->module
->ldb
);
268 return LDB_ERR_OPERATIONS_ERROR
;
271 p
= strchr(user_principal_name
, '@');
276 krb5_ret
= krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
278 io
->domain
->realm
, user_principal_name
,
281 krb5_ret
= krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
283 io
->domain
->realm
, io
->u
.sAMAccountName
,
287 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
288 "setup_primary_kerberos: "
289 "generation of a salting principal failed: %s",
290 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
, krb5_ret
, io
->ac
));
291 return LDB_ERR_OPERATIONS_ERROR
;
295 * create salt from salt_principal
297 krb5_ret
= krb5_get_pw_salt(io
->smb_krb5_context
->krb5_context
,
298 salt_principal
, &salt
);
299 krb5_free_principal(io
->smb_krb5_context
->krb5_context
, salt_principal
);
301 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
302 "setup_primary_kerberos: "
303 "generation of krb5_salt failed: %s",
304 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
, krb5_ret
, io
->ac
));
305 return LDB_ERR_OPERATIONS_ERROR
;
307 /* create a talloc copy */
308 pkb
->salt
.string
= talloc_strndup(io
->ac
,
310 salt
.saltvalue
.length
);
311 krb5_free_salt(io
->smb_krb5_context
->krb5_context
, salt
);
312 if (!pkb
->salt
.string
) {
313 ldb_oom(io
->ac
->module
->ldb
);
314 return LDB_ERR_OPERATIONS_ERROR
;
316 salt
.saltvalue
.data
= discard_const(pkb
->salt
.string
);
317 salt
.saltvalue
.length
= strlen(pkb
->salt
.string
);
320 * prepare generation of keys
322 * ENCTYPE_AES256_CTS_HMAC_SHA1_96 (disabled by default)
323 * ENCTYPE_DES_CBC_MD5
324 * ENCTYPE_DES_CBC_CRC
326 * NOTE: update num_keys1 when you add another enctype!
329 pkb
->keys1
= talloc_array(io
->ac
, struct package_PrimaryKerberosKey
, 3);
331 ldb_oom(io
->ac
->module
->ldb
);
332 return LDB_ERR_OPERATIONS_ERROR
;
334 pkb
->unknown3_1
= talloc_zero_array(io
->ac
, uint64_t, pkb
->num_keys1
);
335 if (!pkb
->unknown3_1
) {
336 ldb_oom(io
->ac
->module
->ldb
);
337 return LDB_ERR_OPERATIONS_ERROR
;
340 if (lp_parm_bool(-1, "password_hash", "create_aes_key", false)) {
344 * w2k and w2k3 doesn't support AES, so we'll not include
345 * the AES key here yet.
347 * Also we don't have an example supplementalCredentials blob
348 * from Windows Longhorn Server with AES support
352 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
353 * the salt and the cleartext password
355 krb5_ret
= krb5_string_to_key_salt(io
->smb_krb5_context
->krb5_context
,
356 ENCTYPE_AES256_CTS_HMAC_SHA1_96
,
360 pkb
->keys1
[k
].keytype
= ENCTYPE_AES256_CTS_HMAC_SHA1_96
;
361 pkb
->keys1
[k
].value
= talloc(pkb
->keys1
, DATA_BLOB
);
362 if (!pkb
->keys1
[k
].value
) {
363 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
364 ldb_oom(io
->ac
->module
->ldb
);
365 return LDB_ERR_OPERATIONS_ERROR
;
367 *pkb
->keys1
[k
].value
= data_blob_talloc(pkb
->keys1
[k
].value
,
369 key
.keyvalue
.length
);
370 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
371 if (!pkb
->keys1
[k
].value
->data
) {
372 ldb_oom(io
->ac
->module
->ldb
);
373 return LDB_ERR_OPERATIONS_ERROR
;
379 * create ENCTYPE_DES_CBC_MD5 key out of
380 * the salt and the cleartext password
382 krb5_ret
= krb5_string_to_key_salt(io
->smb_krb5_context
->krb5_context
,
387 pkb
->keys1
[k
].keytype
= ENCTYPE_DES_CBC_MD5
;
388 pkb
->keys1
[k
].value
= talloc(pkb
->keys1
, DATA_BLOB
);
389 if (!pkb
->keys1
[k
].value
) {
390 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
391 ldb_oom(io
->ac
->module
->ldb
);
392 return LDB_ERR_OPERATIONS_ERROR
;
394 *pkb
->keys1
[k
].value
= data_blob_talloc(pkb
->keys1
[k
].value
,
396 key
.keyvalue
.length
);
397 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
398 if (!pkb
->keys1
[k
].value
->data
) {
399 ldb_oom(io
->ac
->module
->ldb
);
400 return LDB_ERR_OPERATIONS_ERROR
;
405 * create ENCTYPE_DES_CBC_CRC key out of
406 * the salt and the cleartext password
408 krb5_ret
= krb5_string_to_key_salt(io
->smb_krb5_context
->krb5_context
,
413 pkb
->keys1
[k
].keytype
= ENCTYPE_DES_CBC_CRC
;
414 pkb
->keys1
[k
].value
= talloc(pkb
->keys1
, DATA_BLOB
);
415 if (!pkb
->keys1
[k
].value
) {
416 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
417 ldb_oom(io
->ac
->module
->ldb
);
418 return LDB_ERR_OPERATIONS_ERROR
;
420 *pkb
->keys1
[k
].value
= data_blob_talloc(pkb
->keys1
[k
].value
,
422 key
.keyvalue
.length
);
423 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
424 if (!pkb
->keys1
[k
].value
->data
) {
425 ldb_oom(io
->ac
->module
->ldb
);
426 return LDB_ERR_OPERATIONS_ERROR
;
430 /* fix up key number */
433 /* initialize the old keys to zero */
436 pkb
->unknown3_2
= NULL
;
438 /* if there're no old keys, then we're done */
443 for (i
=0; i
< old_scb
->sub
.num_packages
; i
++) {
444 if (old_scb
->sub
.packages
[i
].unknown1
!= 0x00000001) {
448 if (strcmp("Primary:Kerberos", old_scb
->sub
.packages
[i
].name
) != 0) {
452 if (!old_scb
->sub
.packages
[i
].data
|| !old_scb
->sub
.packages
[i
].data
[0]) {
456 old_scp
= &old_scb
->sub
.packages
[i
];
459 /* Primary:Kerberos element of supplementalCredentials */
463 blob
= strhex_to_data_blob(old_scp
->data
);
465 ldb_oom(io
->ac
->module
->ldb
);
466 return LDB_ERR_OPERATIONS_ERROR
;
468 talloc_steal(io
->ac
, blob
.data
);
470 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
471 status
= ndr_pull_struct_blob(&blob
, io
->ac
, &_old_pkb
,
472 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
473 if (!NT_STATUS_IS_OK(status
)) {
474 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
475 "setup_primary_kerberos: "
476 "failed to pull old package_PrimaryKerberosBlob: %s",
478 return LDB_ERR_OPERATIONS_ERROR
;
483 /* if we didn't found the old keys we're done */
488 /* fill in the old keys */
489 pkb
->num_keys2
= old_pkb
->num_keys1
;
490 pkb
->keys2
= old_pkb
->keys1
;
491 pkb
->unknown3_2
= old_pkb
->unknown3_1
;
496 static int setup_supplemental_field(struct setup_password_fields_io
*io
)
498 struct supplementalCredentialsBlob scb
;
499 struct supplementalCredentialsBlob _old_scb
;
500 struct supplementalCredentialsBlob
*old_scb
= NULL
;
501 /* Packages + (Kerberos and maybe CLEARTEXT) */
502 uint32_t num_packages
= 1 + 1;
503 struct supplementalCredentialsPackage packages
[1+2];
504 struct supplementalCredentialsPackage
*pp
= &packages
[0];
505 struct supplementalCredentialsPackage
*pk
= &packages
[1];
506 struct supplementalCredentialsPackage
*pc
= NULL
;
507 struct package_PackagesBlob pb
;
510 struct package_PrimaryKerberosBlob pkb
;
513 struct package_PrimaryCLEARTEXTBlob pcb
;
522 if (!io
->n
.cleartext
) {
524 * when we don't have a cleartext password
525 * we can't setup a supplementalCredential value
530 /* if there's an old supplementaCredentials blob then parse it */
531 if (io
->o
.supplemental
) {
532 status
= ndr_pull_struct_blob_all(io
->o
.supplemental
, io
->ac
, &_old_scb
,
533 (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
);
534 if (!NT_STATUS_IS_OK(status
)) {
535 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
536 "setup_supplemental_field: "
537 "failed to pull old supplementalCredentialsBlob: %s",
539 return LDB_ERR_OPERATIONS_ERROR
;
545 if (io
->domain
->store_cleartext
&&
546 (io
->u
.user_account_control
& UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
)) {
551 /* Kerberos, CLEARTEXT and termination(counted by the Packages element) */
552 pb
.names
= talloc_zero_array(io
->ac
, const char *, num_packages
);
555 * setup 'Primary:Kerberos' element
557 pb
.names
[0] = "Kerberos";
559 ret
= setup_primary_kerberos(io
, old_scb
, &pkb
);
560 if (ret
!= LDB_SUCCESS
) {
564 status
= ndr_push_struct_blob(&pkb_blob
, io
->ac
, &pkb
,
565 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryKerberosBlob
);
566 if (!NT_STATUS_IS_OK(status
)) {
567 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
568 "setup_supplemental_field: "
569 "failed to push package_PrimaryKerberosBlob: %s",
571 return LDB_ERR_OPERATIONS_ERROR
;
576 * This is ugly, but we want to generate the same blob as
577 * w2k and w2k3...we should handle this in the idl
579 status
= data_blob_append(io
->ac
, &pkb_blob
, zero16
, sizeof(zero16
));
580 if (!NT_STATUS_IS_OK(status
)) {
581 ldb_oom(io
->ac
->module
->ldb
);
582 return LDB_ERR_OPERATIONS_ERROR
;
584 pkb_hexstr
= data_blob_hex_string(io
->ac
, &pkb_blob
);
586 ldb_oom(io
->ac
->module
->ldb
);
587 return LDB_ERR_OPERATIONS_ERROR
;
589 pk
->name
= "Primary:Kerberos";
591 pk
->data
= pkb_hexstr
;
594 * setup 'Primary:CLEARTEXT' element
597 pb
.names
[1] = "CLEARTEXT";
599 pcb
.cleartext
= io
->n
.cleartext
;
601 status
= ndr_push_struct_blob(&pcb_blob
, io
->ac
, &pcb
,
602 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryCLEARTEXTBlob
);
603 if (!NT_STATUS_IS_OK(status
)) {
604 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
605 "setup_supplemental_field: "
606 "failed to push package_PrimaryCLEARTEXTBlob: %s",
608 return LDB_ERR_OPERATIONS_ERROR
;
610 pcb_hexstr
= data_blob_hex_string(io
->ac
, &pcb_blob
);
612 ldb_oom(io
->ac
->module
->ldb
);
613 return LDB_ERR_OPERATIONS_ERROR
;
615 pc
->name
= "Primary:CLEARTEXT";
617 pc
->data
= pcb_hexstr
;
621 * setup 'Packages' element
623 status
= ndr_push_struct_blob(&pb_blob
, io
->ac
, &pb
,
624 (ndr_push_flags_fn_t
)ndr_push_package_PackagesBlob
);
625 if (!NT_STATUS_IS_OK(status
)) {
626 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
627 "setup_supplemental_field: "
628 "failed to push package_PackagesBlob: %s",
630 return LDB_ERR_OPERATIONS_ERROR
;
632 pb_hexstr
= data_blob_hex_string(io
->ac
, &pb_blob
);
634 ldb_oom(io
->ac
->module
->ldb
);
635 return LDB_ERR_OPERATIONS_ERROR
;
637 pp
->name
= "Packages";
639 pp
->data
= pb_hexstr
;
642 * setup 'supplementalCredentials' value
644 scb
.sub
.num_packages
= num_packages
;
645 scb
.sub
.packages
= packages
;
647 status
= ndr_push_struct_blob(&io
->g
.supplemental
, io
->ac
, &scb
,
648 (ndr_push_flags_fn_t
)ndr_push_supplementalCredentialsBlob
);
649 if (!NT_STATUS_IS_OK(status
)) {
650 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
651 "setup_supplemental_field: "
652 "failed to push supplementalCredentialsBlob: %s",
654 return LDB_ERR_OPERATIONS_ERROR
;
660 static int setup_last_set_field(struct setup_password_fields_io
*io
)
663 unix_to_nt_time(&io
->g
.last_set
, time(NULL
));
668 static int setup_kvno_field(struct setup_password_fields_io
*io
)
670 /* increment by one */
671 io
->g
.kvno
= io
->o
.kvno
+ 1;
676 static int setup_password_fields(struct setup_password_fields_io
*io
)
682 * refuse the change if someone want to change the cleartext
683 * and supply his own hashes at the same time...
685 if (io
->n
.cleartext
&& (io
->n
.nt_hash
|| io
->n
.lm_hash
)) {
686 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
687 "setup_password_fields: "
688 "it's only allowed to set the cleartext password or the password hashes");
689 return LDB_ERR_UNWILLING_TO_PERFORM
;
692 if (io
->n
.cleartext
&& !io
->n
.nt_hash
) {
693 struct samr_Password
*hash
;
695 hash
= talloc(io
->ac
, struct samr_Password
);
697 ldb_oom(io
->ac
->module
->ldb
);
698 return LDB_ERR_OPERATIONS_ERROR
;
701 /* compute the new nt hash */
702 ok
= E_md4hash(io
->n
.cleartext
, hash
->hash
);
704 io
->n
.nt_hash
= hash
;
706 ldb_asprintf_errstring(io
->ac
->module
->ldb
,
707 "setup_password_fields: "
708 "failed to generate nthash from cleartext password");
709 return LDB_ERR_OPERATIONS_ERROR
;
713 if (io
->n
.cleartext
&& !io
->n
.lm_hash
) {
714 struct samr_Password
*hash
;
716 hash
= talloc(io
->ac
, struct samr_Password
);
718 ldb_oom(io
->ac
->module
->ldb
);
719 return LDB_ERR_OPERATIONS_ERROR
;
722 /* compute the new lm hash */
723 ok
= E_deshash(io
->n
.cleartext
, hash
->hash
);
725 io
->n
.lm_hash
= hash
;
727 talloc_free(hash
->hash
);
731 ret
= setup_nt_fields(io
);
736 ret
= setup_lm_fields(io
);
741 ret
= setup_supplemental_field(io
);
746 ret
= setup_last_set_field(io
);
751 ret
= setup_kvno_field(io
);
759 static struct ldb_handle
*ph_init_handle(struct ldb_request
*req
, struct ldb_module
*module
, enum ph_type type
)
761 struct ph_context
*ac
;
762 struct ldb_handle
*h
;
764 h
= talloc_zero(req
, struct ldb_handle
);
766 ldb_set_errstring(module
->ldb
, "Out of Memory");
772 ac
= talloc_zero(h
, struct ph_context
);
774 ldb_set_errstring(module
->ldb
, "Out of Memory");
779 h
->private_data
= (void *)ac
;
781 h
->state
= LDB_ASYNC_INIT
;
782 h
->status
= LDB_SUCCESS
;
791 static int get_domain_data_callback(struct ldb_context
*ldb
, void *context
, struct ldb_reply
*ares
)
793 struct ph_context
*ac
;
795 if (!context
|| !ares
) {
796 ldb_set_errstring(ldb
, "NULL Context or Result in callback");
797 return LDB_ERR_OPERATIONS_ERROR
;
800 ac
= talloc_get_type(context
, struct ph_context
);
802 /* we are interested only in the single reply (base search) we receive here */
803 if (ares
->type
== LDB_REPLY_ENTRY
) {
804 if (ac
->dom_res
!= NULL
) {
805 ldb_set_errstring(ldb
, "Too many results");
807 return LDB_ERR_OPERATIONS_ERROR
;
809 ac
->dom_res
= talloc_steal(ac
, ares
);
817 static int build_domain_data_request(struct ph_context
*ac
)
819 /* attrs[] is returned from this function in
820 ac->dom_req->op.search.attrs, so it must be static, as
821 otherwise the compiler can put it on the stack */
822 static const char * const attrs
[] = { "pwdProperties", "pwdHistoryLength", NULL
};
825 ac
->dom_req
= talloc_zero(ac
, struct ldb_request
);
826 if (ac
->dom_req
== NULL
) {
827 ldb_debug(ac
->module
->ldb
, LDB_DEBUG_ERROR
, "Out of Memory!\n");
828 return LDB_ERR_OPERATIONS_ERROR
;
830 ac
->dom_req
->operation
= LDB_SEARCH
;
831 ac
->dom_req
->op
.search
.base
= ldb_get_default_basedn(ac
->module
->ldb
);
832 ac
->dom_req
->op
.search
.scope
= LDB_SCOPE_SUBTREE
;
834 filter
= talloc_asprintf(ac
->dom_req
, "(&(objectSid=%s)(|(objectClass=domain)(objectClass=builtinDomain)))",
835 ldap_encode_ndr_dom_sid(ac
->dom_req
, ac
->domain_sid
));
836 if (filter
== NULL
) {
837 ldb_debug(ac
->module
->ldb
, LDB_DEBUG_ERROR
, "Out of Memory!\n");
838 talloc_free(ac
->dom_req
);
839 return LDB_ERR_OPERATIONS_ERROR
;
842 ac
->dom_req
->op
.search
.tree
= ldb_parse_tree(ac
->dom_req
, filter
);
843 if (ac
->dom_req
->op
.search
.tree
== NULL
) {
844 ldb_set_errstring(ac
->module
->ldb
, "Invalid search filter");
845 talloc_free(ac
->dom_req
);
846 return LDB_ERR_OPERATIONS_ERROR
;
848 ac
->dom_req
->op
.search
.attrs
= attrs
;
849 ac
->dom_req
->controls
= NULL
;
850 ac
->dom_req
->context
= ac
;
851 ac
->dom_req
->callback
= get_domain_data_callback
;
852 ldb_set_timeout_from_prev_req(ac
->module
->ldb
, ac
->orig_req
, ac
->dom_req
);
857 static struct domain_data
*get_domain_data(struct ldb_module
*module
, void *ctx
, struct ldb_reply
*res
)
859 struct domain_data
*data
;
861 struct ph_context
*ac
;
864 ac
= talloc_get_type(ctx
, struct ph_context
);
866 data
= talloc_zero(ac
, struct domain_data
);
872 ldb_debug(module
->ldb
, LDB_DEBUG_ERROR
, "Could not find this user's domain: %s!\n", dom_sid_string(data
, ac
->domain_sid
));
877 data
->pwdProperties
= samdb_result_uint(res
->message
, "pwdProperties", 0);
878 data
->store_cleartext
= data
->pwdProperties
& DOMAIN_PASSWORD_STORE_CLEARTEXT
;
879 data
->pwdHistoryLength
= samdb_result_uint(res
->message
, "pwdHistoryLength", 0);
881 /* For a domain DN, this puts things in dotted notation */
882 /* For builtin domains, this will give details for the host,
883 * but that doesn't really matter, as it's just used for salt
884 * and kerberos principals, which don't exist here */
886 tmp
= ldb_dn_canonical_string(ctx
, res
->message
->dn
);
891 /* But it puts a trailing (or just before 'builtin') / on things, so kill that */
892 p
= strchr(tmp
, '/');
898 data
->dns_domain
= strlower_talloc(data
, tmp
);
899 if (data
->dns_domain
== NULL
) {
900 ldb_debug(module
->ldb
, LDB_DEBUG_ERROR
, "Out of memory!\n");
903 data
->realm
= strupper_talloc(data
, tmp
);
904 if (data
->realm
== NULL
) {
905 ldb_debug(module
->ldb
, LDB_DEBUG_ERROR
, "Out of memory!\n");
913 static int password_hash_add(struct ldb_module
*module
, struct ldb_request
*req
)
915 struct ldb_handle
*h
;
916 struct ph_context
*ac
;
917 struct ldb_message_element
*sambaAttr
;
918 struct ldb_message_element
*ntAttr
;
919 struct ldb_message_element
*lmAttr
;
922 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "password_hash_add\n");
924 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) { /* do not manipulate our control entries */
925 return ldb_next_request(module
, req
);
928 /* If the caller is manipulating the local passwords directly, let them pass */
929 if (ldb_dn_compare_base(ldb_dn_new(req
, module
->ldb
, LOCAL_BASE
),
930 req
->op
.add
.message
->dn
) == 0) {
931 return ldb_next_request(module
, req
);
934 /* nobody must touch this fields */
935 if (ldb_msg_find_element(req
->op
.add
.message
, "ntPwdHistory")) {
936 return LDB_ERR_UNWILLING_TO_PERFORM
;
938 if (ldb_msg_find_element(req
->op
.add
.message
, "lmPwdHistory")) {
939 return LDB_ERR_UNWILLING_TO_PERFORM
;
941 if (ldb_msg_find_element(req
->op
.add
.message
, "supplementalCredentials")) {
942 return LDB_ERR_UNWILLING_TO_PERFORM
;
945 /* If no part of this ADD touches the sambaPassword, or the NT
946 * or LM hashes, then we don't need to make any changes. */
948 sambaAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "sambaPassword");
949 ntAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "unicodePwd");
950 lmAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "dBCSPwd");
952 if ((!sambaAttr
) && (!ntAttr
) && (!lmAttr
)) {
953 return ldb_next_request(module
, req
);
956 /* if it is not an entry of type person its an error */
957 /* TODO: remove this when sambaPassword will be in schema */
958 if (!ldb_msg_check_string_attribute(req
->op
.add
.message
, "objectClass", "person")) {
959 ldb_set_errstring(module
->ldb
, "Cannot set a password on entry that does not have objectClass 'person'");
960 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
963 /* check sambaPassword is single valued here */
964 /* TODO: remove this when sambaPassword will be single valued in schema */
965 if (sambaAttr
&& sambaAttr
->num_values
> 1) {
966 ldb_set_errstring(module
->ldb
, "mupltiple values for sambaPassword not allowed!\n");
967 return LDB_ERR_CONSTRAINT_VIOLATION
;
970 if (ntAttr
&& (ntAttr
->num_values
> 1)) {
971 ldb_set_errstring(module
->ldb
, "mupltiple values for unicodePwd not allowed!\n");
972 return LDB_ERR_CONSTRAINT_VIOLATION
;
974 if (lmAttr
&& (lmAttr
->num_values
> 1)) {
975 ldb_set_errstring(module
->ldb
, "mupltiple values for dBCSPwd not allowed!\n");
976 return LDB_ERR_CONSTRAINT_VIOLATION
;
979 if (sambaAttr
&& sambaAttr
->num_values
== 0) {
980 ldb_set_errstring(module
->ldb
, "sambaPassword must have a value!\n");
981 return LDB_ERR_CONSTRAINT_VIOLATION
;
984 if (ntAttr
&& (ntAttr
->num_values
== 0)) {
985 ldb_set_errstring(module
->ldb
, "unicodePwd must have a value!\n");
986 return LDB_ERR_CONSTRAINT_VIOLATION
;
988 if (lmAttr
&& (lmAttr
->num_values
== 0)) {
989 ldb_set_errstring(module
->ldb
, "dBCSPwd must have a value!\n");
990 return LDB_ERR_CONSTRAINT_VIOLATION
;
993 h
= ph_init_handle(req
, module
, PH_ADD
);
995 return LDB_ERR_OPERATIONS_ERROR
;
997 ac
= talloc_get_type(h
->private_data
, struct ph_context
);
999 /* get user domain data */
1000 ac
->domain_sid
= samdb_result_sid_prefix(ac
, req
->op
.add
.message
, "objectSid");
1001 if (ac
->domain_sid
== NULL
) {
1002 ldb_debug(module
->ldb
, LDB_DEBUG_ERROR
, "can't handle entry with missing objectSid!\n");
1003 return LDB_ERR_OPERATIONS_ERROR
;
1006 ret
= build_domain_data_request(ac
);
1007 if (ret
!= LDB_SUCCESS
) {
1011 ac
->step
= PH_ADD_SEARCH_DOM
;
1015 return ldb_next_request(module
, ac
->dom_req
);
1018 static int password_hash_add_do_add(struct ldb_handle
*h
) {
1020 struct ph_context
*ac
;
1021 struct domain_data
*domain
;
1022 struct smb_krb5_context
*smb_krb5_context
;
1023 struct ldb_message
*msg
;
1024 struct setup_password_fields_io io
;
1027 ac
= talloc_get_type(h
->private_data
, struct ph_context
);
1029 domain
= get_domain_data(ac
->module
, ac
, ac
->dom_res
);
1030 if (domain
== NULL
) {
1031 return LDB_ERR_OPERATIONS_ERROR
;
1034 ac
->down_req
= talloc(ac
, struct ldb_request
);
1035 if (ac
->down_req
== NULL
) {
1036 return LDB_ERR_OPERATIONS_ERROR
;
1039 *(ac
->down_req
) = *(ac
->orig_req
);
1040 ac
->down_req
->op
.add
.message
= msg
= ldb_msg_copy_shallow(ac
->down_req
, ac
->orig_req
->op
.add
.message
);
1041 if (ac
->down_req
->op
.add
.message
== NULL
) {
1042 return LDB_ERR_OPERATIONS_ERROR
;
1045 /* Some operations below require kerberos contexts */
1046 if (smb_krb5_init_context(ac
->down_req
, &smb_krb5_context
) != 0) {
1047 return LDB_ERR_OPERATIONS_ERROR
;
1053 io
.smb_krb5_context
= smb_krb5_context
;
1055 io
.u
.user_account_control
= samdb_result_uint(msg
, "userAccountControl", 0);
1056 io
.u
.sAMAccountName
= samdb_result_string(msg
, "samAccountName", NULL
);
1057 io
.u
.user_principal_name
= samdb_result_string(msg
, "userPrincipalName", NULL
);
1058 io
.u
.is_computer
= ldb_msg_check_string_attribute(msg
, "objectClass", "computer");
1060 io
.n
.cleartext
= samdb_result_string(msg
, "sambaPassword", NULL
);
1061 io
.n
.nt_hash
= samdb_result_hash(io
.ac
, msg
, "unicodePwd");
1062 io
.n
.lm_hash
= samdb_result_hash(io
.ac
, msg
, "dBCSPwd");
1064 /* remove attributes */
1065 if (io
.n
.cleartext
) ldb_msg_remove_attr(msg
, "sambaPassword");
1066 if (io
.n
.nt_hash
) ldb_msg_remove_attr(msg
, "unicodePwd");
1067 if (io
.n
.lm_hash
) ldb_msg_remove_attr(msg
, "dBCSPwd");
1068 ldb_msg_remove_attr(msg
, "pwdLastSet");
1069 io
.o
.kvno
= samdb_result_uint(msg
, "msDs-KeyVersionNumber", 1) - 1;
1070 ldb_msg_remove_attr(msg
, "msDs-KeyVersionNumber");
1072 ret
= setup_password_fields(&io
);
1073 if (ret
!= LDB_SUCCESS
) {
1078 ret
= samdb_msg_add_hash(ac
->module
->ldb
, ac
, msg
,
1079 "unicodePwd", io
.g
.nt_hash
);
1080 if (ret
!= LDB_SUCCESS
) {
1085 ret
= samdb_msg_add_hash(ac
->module
->ldb
, ac
, msg
,
1086 "dBCSPwd", io
.g
.lm_hash
);
1087 if (ret
!= LDB_SUCCESS
) {
1091 if (io
.g
.nt_history_len
> 0) {
1092 ret
= samdb_msg_add_hashes(ac
, msg
,
1095 io
.g
.nt_history_len
);
1096 if (ret
!= LDB_SUCCESS
) {
1100 if (io
.g
.lm_history_len
> 0) {
1101 ret
= samdb_msg_add_hashes(ac
, msg
,
1104 io
.g
.lm_history_len
);
1105 if (ret
!= LDB_SUCCESS
) {
1109 if (io
.g
.supplemental
.length
> 0) {
1110 ret
= ldb_msg_add_value(msg
, "supplementalCredentials",
1111 &io
.g
.supplemental
, NULL
);
1112 if (ret
!= LDB_SUCCESS
) {
1116 ret
= samdb_msg_add_uint64(ac
->module
->ldb
, ac
, msg
,
1119 if (ret
!= LDB_SUCCESS
) {
1122 ret
= samdb_msg_add_uint(ac
->module
->ldb
, ac
, msg
,
1123 "msDs-KeyVersionNumber",
1125 if (ret
!= LDB_SUCCESS
) {
1129 h
->state
= LDB_ASYNC_INIT
;
1130 h
->status
= LDB_SUCCESS
;
1132 ac
->step
= PH_ADD_DO_ADD
;
1134 ldb_set_timeout_from_prev_req(ac
->module
->ldb
, ac
->orig_req
, ac
->down_req
);
1136 /* perform the operation */
1137 return ldb_next_request(ac
->module
, ac
->down_req
);
1140 static int password_hash_mod_search_self(struct ldb_handle
*h
);
1142 static int password_hash_modify(struct ldb_module
*module
, struct ldb_request
*req
)
1144 struct ldb_handle
*h
;
1145 struct ph_context
*ac
;
1146 struct ldb_message_element
*sambaAttr
;
1147 struct ldb_message_element
*ntAttr
;
1148 struct ldb_message_element
*lmAttr
;
1149 struct ldb_message
*msg
;
1151 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "password_hash_modify\n");
1153 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) { /* do not manipulate our control entries */
1154 return ldb_next_request(module
, req
);
1157 /* If the caller is manipulating the local passwords directly, let them pass */
1158 if (ldb_dn_compare_base(ldb_dn_new(req
, module
->ldb
, LOCAL_BASE
),
1159 req
->op
.mod
.message
->dn
) == 0) {
1160 return ldb_next_request(module
, req
);
1163 /* nobody must touch password Histories */
1164 if (ldb_msg_find_element(req
->op
.add
.message
, "ntPwdHistory")) {
1165 return LDB_ERR_UNWILLING_TO_PERFORM
;
1167 if (ldb_msg_find_element(req
->op
.add
.message
, "lmPwdHistory")) {
1168 return LDB_ERR_UNWILLING_TO_PERFORM
;
1170 if (ldb_msg_find_element(req
->op
.add
.message
, "supplementalCredentials")) {
1171 return LDB_ERR_UNWILLING_TO_PERFORM
;
1174 sambaAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "sambaPassword");
1175 ntAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "unicodePwd");
1176 lmAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "dBCSPwd");
1178 /* If no part of this touches the sambaPassword OR unicodePwd and/or dBCSPwd, then we don't
1179 * need to make any changes. For password changes/set there should
1180 * be a 'delete' or a 'modify' on this attribute. */
1181 if ((!sambaAttr
) && (!ntAttr
) && (!lmAttr
)) {
1182 return ldb_next_request(module
, req
);
1185 /* check passwords are single valued here */
1186 /* TODO: remove this when passwords will be single valued in schema */
1187 if (sambaAttr
&& (sambaAttr
->num_values
> 1)) {
1188 return LDB_ERR_CONSTRAINT_VIOLATION
;
1190 if (ntAttr
&& (ntAttr
->num_values
> 1)) {
1191 return LDB_ERR_CONSTRAINT_VIOLATION
;
1193 if (lmAttr
&& (lmAttr
->num_values
> 1)) {
1194 return LDB_ERR_CONSTRAINT_VIOLATION
;
1197 h
= ph_init_handle(req
, module
, PH_MOD
);
1199 return LDB_ERR_OPERATIONS_ERROR
;
1201 ac
= talloc_get_type(h
->private_data
, struct ph_context
);
1203 /* return or own handle to deal with this call */
1206 /* prepare the first operation */
1207 ac
->down_req
= talloc_zero(ac
, struct ldb_request
);
1208 if (ac
->down_req
== NULL
) {
1209 ldb_set_errstring(module
->ldb
, "Out of memory!");
1210 return LDB_ERR_OPERATIONS_ERROR
;
1213 *(ac
->down_req
) = *req
; /* copy the request */
1215 /* use a new message structure so that we can modify it */
1216 ac
->down_req
->op
.mod
.message
= msg
= ldb_msg_copy_shallow(ac
->down_req
, req
->op
.mod
.message
);
1218 /* - remove any imodification to the password from the first commit
1219 * we will make the real modification later */
1220 if (sambaAttr
) ldb_msg_remove_attr(msg
, "sambaPassword");
1221 if (ntAttr
) ldb_msg_remove_attr(msg
, "unicodePwd");
1222 if (lmAttr
) ldb_msg_remove_attr(msg
, "dBCSPwd");
1224 /* if there was nothing else to be modify skip to next step */
1225 if (msg
->num_elements
== 0) {
1226 talloc_free(ac
->down_req
);
1227 ac
->down_req
= NULL
;
1228 return password_hash_mod_search_self(h
);
1231 ac
->down_req
->context
= NULL
;
1232 ac
->down_req
->callback
= NULL
;
1234 ac
->step
= PH_MOD_DO_REQ
;
1236 ldb_set_timeout_from_prev_req(module
->ldb
, req
, ac
->down_req
);
1238 return ldb_next_request(module
, ac
->down_req
);
1241 static int get_self_callback(struct ldb_context
*ldb
, void *context
, struct ldb_reply
*ares
)
1243 struct ph_context
*ac
;
1245 if (!context
|| !ares
) {
1246 ldb_set_errstring(ldb
, "NULL Context or Result in callback");
1247 return LDB_ERR_OPERATIONS_ERROR
;
1250 ac
= talloc_get_type(context
, struct ph_context
);
1252 /* we are interested only in the single reply (base search) we receive here */
1253 if (ares
->type
== LDB_REPLY_ENTRY
) {
1254 if (ac
->search_res
!= NULL
) {
1255 ldb_set_errstring(ldb
, "Too many results");
1257 return LDB_ERR_OPERATIONS_ERROR
;
1260 /* if it is not an entry of type person this is an error */
1261 /* TODO: remove this when sambaPassword will be in schema */
1262 if (!ldb_msg_check_string_attribute(ares
->message
, "objectClass", "person")) {
1263 ldb_set_errstring(ldb
, "Object class violation");
1265 return LDB_ERR_OBJECT_CLASS_VIOLATION
;
1268 ac
->search_res
= talloc_steal(ac
, ares
);
1276 static int password_hash_mod_search_self(struct ldb_handle
*h
) {
1278 struct ph_context
*ac
;
1279 static const char * const attrs
[] = { "userAccountControl", "lmPwdHistory",
1281 "objectSid", "msDS-KeyVersionNumber",
1282 "objectClass", "userPrincipalName",
1284 "dBCSPwd", "unicodePwd",
1285 "supplementalCredentials",
1288 ac
= talloc_get_type(h
->private_data
, struct ph_context
);
1290 /* prepare the search operation */
1291 ac
->search_req
= talloc_zero(ac
, struct ldb_request
);
1292 if (ac
->search_req
== NULL
) {
1293 ldb_debug(ac
->module
->ldb
, LDB_DEBUG_ERROR
, "Out of Memory!\n");
1294 return LDB_ERR_OPERATIONS_ERROR
;
1297 ac
->search_req
->operation
= LDB_SEARCH
;
1298 ac
->search_req
->op
.search
.base
= ac
->orig_req
->op
.mod
.message
->dn
;
1299 ac
->search_req
->op
.search
.scope
= LDB_SCOPE_BASE
;
1300 ac
->search_req
->op
.search
.tree
= ldb_parse_tree(ac
->search_req
, NULL
);
1301 if (ac
->search_req
->op
.search
.tree
== NULL
) {
1302 ldb_set_errstring(ac
->module
->ldb
, "Invalid search filter");
1303 return LDB_ERR_OPERATIONS_ERROR
;
1305 ac
->search_req
->op
.search
.attrs
= attrs
;
1306 ac
->search_req
->controls
= NULL
;
1307 ac
->search_req
->context
= ac
;
1308 ac
->search_req
->callback
= get_self_callback
;
1309 ldb_set_timeout_from_prev_req(ac
->module
->ldb
, ac
->orig_req
, ac
->search_req
);
1311 ac
->step
= PH_MOD_SEARCH_SELF
;
1313 return ldb_next_request(ac
->module
, ac
->search_req
);
1316 static int password_hash_mod_search_dom(struct ldb_handle
*h
) {
1318 struct ph_context
*ac
;
1321 ac
= talloc_get_type(h
->private_data
, struct ph_context
);
1323 /* get object domain sid */
1324 ac
->domain_sid
= samdb_result_sid_prefix(ac
, ac
->search_res
->message
, "objectSid");
1325 if (ac
->domain_sid
== NULL
) {
1326 ldb_debug(ac
->module
->ldb
, LDB_DEBUG_ERROR
, "can't handle entry with missing objectSid!\n");
1327 return LDB_ERR_OPERATIONS_ERROR
;
1330 /* get user domain data */
1331 ret
= build_domain_data_request(ac
);
1332 if (ret
!= LDB_SUCCESS
) {
1336 ac
->step
= PH_MOD_SEARCH_DOM
;
1338 return ldb_next_request(ac
->module
, ac
->dom_req
);
1341 static int password_hash_mod_do_mod(struct ldb_handle
*h
) {
1343 struct ph_context
*ac
;
1344 struct domain_data
*domain
;
1345 struct smb_krb5_context
*smb_krb5_context
;
1346 struct ldb_message
*msg
;
1347 struct ldb_message
*orig_msg
;
1348 struct ldb_message
*searched_msg
;
1349 struct setup_password_fields_io io
;
1352 ac
= talloc_get_type(h
->private_data
, struct ph_context
);
1354 domain
= get_domain_data(ac
->module
, ac
, ac
->dom_res
);
1355 if (domain
== NULL
) {
1356 return LDB_ERR_OPERATIONS_ERROR
;
1359 ac
->mod_req
= talloc(ac
, struct ldb_request
);
1360 if (ac
->mod_req
== NULL
) {
1361 return LDB_ERR_OPERATIONS_ERROR
;
1364 *(ac
->mod_req
) = *(ac
->orig_req
);
1366 /* use a new message structure so that we can modify it */
1367 ac
->mod_req
->op
.mod
.message
= msg
= ldb_msg_new(ac
->mod_req
);
1369 return LDB_ERR_OPERATIONS_ERROR
;
1373 msg
->dn
= ac
->orig_req
->op
.mod
.message
->dn
;
1375 /* Some operations below require kerberos contexts */
1376 if (smb_krb5_init_context(ac
->mod_req
, &smb_krb5_context
) != 0) {
1377 return LDB_ERR_OPERATIONS_ERROR
;
1380 orig_msg
= discard_const(ac
->orig_req
->op
.mod
.message
);
1381 searched_msg
= ac
->search_res
->message
;
1386 io
.smb_krb5_context
= smb_krb5_context
;
1388 io
.u
.user_account_control
= samdb_result_uint(searched_msg
, "userAccountControl", 0);
1389 io
.u
.sAMAccountName
= samdb_result_string(searched_msg
, "samAccountName", NULL
);
1390 io
.u
.user_principal_name
= samdb_result_string(searched_msg
, "userPrincipalName", NULL
);
1391 io
.u
.is_computer
= ldb_msg_check_string_attribute(searched_msg
, "objectClass", "computer");
1393 io
.n
.cleartext
= samdb_result_string(orig_msg
, "sambaPassword", NULL
);
1394 io
.n
.nt_hash
= samdb_result_hash(io
.ac
, orig_msg
, "unicodePwd");
1395 io
.n
.lm_hash
= samdb_result_hash(io
.ac
, orig_msg
, "dBCSPwd");
1397 io
.o
.kvno
= samdb_result_uint(searched_msg
, "msDs-KeyVersionNumber", 0);
1398 io
.o
.nt_history_len
= samdb_result_hashes(io
.ac
, searched_msg
, "ntPwdHistory", &io
.o
.nt_history
);
1399 io
.o
.lm_history_len
= samdb_result_hashes(io
.ac
, searched_msg
, "lmPwdHistory", &io
.o
.lm_history
);
1400 io
.o
.supplemental
= ldb_msg_find_ldb_val(searched_msg
, "supplementalCredentials");
1402 ret
= setup_password_fields(&io
);
1403 if (ret
!= LDB_SUCCESS
) {
1407 /* make sure we replace all the old attributes */
1408 ret
= ldb_msg_add_empty(msg
, "unicodePwd", LDB_FLAG_MOD_REPLACE
, NULL
);
1409 ret
= ldb_msg_add_empty(msg
, "dBCSPwd", LDB_FLAG_MOD_REPLACE
, NULL
);
1410 ret
= ldb_msg_add_empty(msg
, "ntPwdHistory", LDB_FLAG_MOD_REPLACE
, NULL
);
1411 ret
= ldb_msg_add_empty(msg
, "lmPwdHistory", LDB_FLAG_MOD_REPLACE
, NULL
);
1412 ret
= ldb_msg_add_empty(msg
, "supplementalCredentials", LDB_FLAG_MOD_REPLACE
, NULL
);
1413 ret
= ldb_msg_add_empty(msg
, "pwdLastSet", LDB_FLAG_MOD_REPLACE
, NULL
);
1414 ret
= ldb_msg_add_empty(msg
, "msDs-KeyVersionNumber", LDB_FLAG_MOD_REPLACE
, NULL
);
1417 ret
= samdb_msg_add_hash(ac
->module
->ldb
, ac
, msg
,
1418 "unicodePwd", io
.g
.nt_hash
);
1419 if (ret
!= LDB_SUCCESS
) {
1424 ret
= samdb_msg_add_hash(ac
->module
->ldb
, ac
, msg
,
1425 "dBCSPwd", io
.g
.lm_hash
);
1426 if (ret
!= LDB_SUCCESS
) {
1430 if (io
.g
.nt_history_len
> 0) {
1431 ret
= samdb_msg_add_hashes(ac
, msg
,
1434 io
.g
.nt_history_len
);
1435 if (ret
!= LDB_SUCCESS
) {
1439 if (io
.g
.lm_history_len
> 0) {
1440 ret
= samdb_msg_add_hashes(ac
, msg
,
1443 io
.g
.lm_history_len
);
1444 if (ret
!= LDB_SUCCESS
) {
1448 if (io
.g
.supplemental
.length
> 0) {
1449 ret
= ldb_msg_add_value(msg
, "supplementalCredentials",
1450 &io
.g
.supplemental
, NULL
);
1451 if (ret
!= LDB_SUCCESS
) {
1455 ret
= samdb_msg_add_uint64(ac
->module
->ldb
, ac
, msg
,
1458 if (ret
!= LDB_SUCCESS
) {
1461 ret
= samdb_msg_add_uint(ac
->module
->ldb
, ac
, msg
,
1462 "msDs-KeyVersionNumber",
1464 if (ret
!= LDB_SUCCESS
) {
1468 h
->state
= LDB_ASYNC_INIT
;
1469 h
->status
= LDB_SUCCESS
;
1471 ac
->step
= PH_MOD_DO_MOD
;
1473 ldb_set_timeout_from_prev_req(ac
->module
->ldb
, ac
->orig_req
, ac
->mod_req
);
1475 /* perform the search */
1476 return ldb_next_request(ac
->module
, ac
->mod_req
);
1479 static int ph_wait(struct ldb_handle
*handle
) {
1480 struct ph_context
*ac
;
1483 if (!handle
|| !handle
->private_data
) {
1484 return LDB_ERR_OPERATIONS_ERROR
;
1487 if (handle
->state
== LDB_ASYNC_DONE
) {
1488 return handle
->status
;
1491 handle
->state
= LDB_ASYNC_PENDING
;
1492 handle
->status
= LDB_SUCCESS
;
1494 ac
= talloc_get_type(handle
->private_data
, struct ph_context
);
1497 case PH_ADD_SEARCH_DOM
:
1498 ret
= ldb_wait(ac
->dom_req
->handle
, LDB_WAIT_NONE
);
1500 if (ret
!= LDB_SUCCESS
) {
1501 handle
->status
= ret
;
1504 if (ac
->dom_req
->handle
->status
!= LDB_SUCCESS
) {
1505 handle
->status
= ac
->dom_req
->handle
->status
;
1509 if (ac
->dom_req
->handle
->state
!= LDB_ASYNC_DONE
) {
1513 /* domain search done, go on */
1514 return password_hash_add_do_add(handle
);
1517 ret
= ldb_wait(ac
->down_req
->handle
, LDB_WAIT_NONE
);
1519 if (ret
!= LDB_SUCCESS
) {
1520 handle
->status
= ret
;
1523 if (ac
->down_req
->handle
->status
!= LDB_SUCCESS
) {
1524 handle
->status
= ac
->down_req
->handle
->status
;
1528 if (ac
->down_req
->handle
->state
!= LDB_ASYNC_DONE
) {
1535 ret
= ldb_wait(ac
->down_req
->handle
, LDB_WAIT_NONE
);
1537 if (ret
!= LDB_SUCCESS
) {
1538 handle
->status
= ret
;
1541 if (ac
->down_req
->handle
->status
!= LDB_SUCCESS
) {
1542 handle
->status
= ac
->down_req
->handle
->status
;
1546 if (ac
->down_req
->handle
->state
!= LDB_ASYNC_DONE
) {
1550 /* non-password mods done, go on */
1551 return password_hash_mod_search_self(handle
);
1553 case PH_MOD_SEARCH_SELF
:
1554 ret
= ldb_wait(ac
->search_req
->handle
, LDB_WAIT_NONE
);
1556 if (ret
!= LDB_SUCCESS
) {
1557 handle
->status
= ret
;
1560 if (ac
->search_req
->handle
->status
!= LDB_SUCCESS
) {
1561 handle
->status
= ac
->search_req
->handle
->status
;
1565 if (ac
->search_req
->handle
->state
!= LDB_ASYNC_DONE
) {
1569 if (ac
->search_res
== NULL
) {
1570 return LDB_ERR_NO_SUCH_OBJECT
;
1573 /* self search done, go on */
1574 return password_hash_mod_search_dom(handle
);
1576 case PH_MOD_SEARCH_DOM
:
1577 ret
= ldb_wait(ac
->dom_req
->handle
, LDB_WAIT_NONE
);
1579 if (ret
!= LDB_SUCCESS
) {
1580 handle
->status
= ret
;
1583 if (ac
->dom_req
->handle
->status
!= LDB_SUCCESS
) {
1584 handle
->status
= ac
->dom_req
->handle
->status
;
1588 if (ac
->dom_req
->handle
->state
!= LDB_ASYNC_DONE
) {
1592 /* domain search done, go on */
1593 return password_hash_mod_do_mod(handle
);
1596 ret
= ldb_wait(ac
->mod_req
->handle
, LDB_WAIT_NONE
);
1598 if (ret
!= LDB_SUCCESS
) {
1599 handle
->status
= ret
;
1602 if (ac
->mod_req
->handle
->status
!= LDB_SUCCESS
) {
1603 handle
->status
= ac
->mod_req
->handle
->status
;
1607 if (ac
->mod_req
->handle
->state
!= LDB_ASYNC_DONE
) {
1614 ret
= LDB_ERR_OPERATIONS_ERROR
;
1621 handle
->state
= LDB_ASYNC_DONE
;
1625 static int ph_wait_all(struct ldb_handle
*handle
) {
1629 while (handle
->state
!= LDB_ASYNC_DONE
) {
1630 ret
= ph_wait(handle
);
1631 if (ret
!= LDB_SUCCESS
) {
1636 return handle
->status
;
1639 static int password_hash_wait(struct ldb_handle
*handle
, enum ldb_wait_type type
)
1641 if (type
== LDB_WAIT_ALL
) {
1642 return ph_wait_all(handle
);
1644 return ph_wait(handle
);
1648 static const struct ldb_module_ops password_hash_ops
= {
1649 .name
= "password_hash",
1650 .add
= password_hash_add
,
1651 .modify
= password_hash_modify
,
1652 .wait
= password_hash_wait
1656 int password_hash_module_init(void)
1658 return ldb_register_module(&password_hash_ops
);