4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Stefan Metzmacher 2007-2010
8 Copyright (C) Matthias Dieter Wallnöfer 2009-2010
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb password_hash module
29 * Description: correctly handle AD password changes fields
31 * Author: Andrew Bartlett
32 * Author: Stefan Metzmacher
36 #include "ldb_module.h"
37 #include "libcli/auth/libcli_auth.h"
38 #include "system/kerberos.h"
39 #include "auth/kerberos/kerberos.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/samdb/ldb_modules/util.h"
42 #include "dsdb/samdb/ldb_modules/password_modules.h"
43 #include "librpc/gen_ndr/ndr_drsblobs.h"
44 #include "../lib/crypto/crypto.h"
45 #include "param/param.h"
47 /* If we have decided there is a reason to work on this request, then
48 * setup all the password hash types correctly.
50 * If we haven't the hashes yet but the password given as plain-text (attributes
51 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
52 * the constraints. Once this is done, we calculate the password hashes.
54 * Notice: unlike the real AD which only supports the UTF16 special based
55 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
56 * understand also a UTF16 based 'clearTextPassword' one.
57 * The latter is also accessible through LDAP so it can also be set by external
58 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
60 * Also when the module receives only the password hashes (possible through
61 * specifying an internal LDB control - for security reasons) some checks are
62 * performed depending on the operation mode (see below) (e.g. if the password
63 * has been in use before if the password memory policy was activated).
65 * Attention: There is a difference between "modify" and "reset" operations
66 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
67 * operation for a password attribute we thread this as a "modify"; if it sends
68 * only a "replace" one we have an (administrative) reset.
70 * Finally, if the administrator has requested that a password history
71 * be maintained, then this should also be written out.
75 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
76 * - Check for right connection encryption
79 /* Notice: Definition of "dsdb_control_password_change_status" moved into
83 struct ldb_module
*module
;
84 struct ldb_request
*req
;
86 struct ldb_request
*dom_req
;
87 struct ldb_reply
*dom_res
;
89 struct ldb_reply
*search_res
;
91 struct dsdb_control_password_change_status
*status
;
92 struct dsdb_control_password_change
*change
;
98 bool pwd_last_set_bypass
;
102 struct setup_password_fields_io
{
103 struct ph_context
*ac
;
105 struct smb_krb5_context
*smb_krb5_context
;
107 /* infos about the user account */
109 uint32_t userAccountControl
;
111 const char *sAMAccountName
;
112 const char *user_principal_name
;
114 uint32_t restrictions
;
117 /* new credentials and old given credentials */
118 struct setup_password_fields_given
{
119 const struct ldb_val
*cleartext_utf8
;
120 const struct ldb_val
*cleartext_utf16
;
121 struct samr_Password
*nt_hash
;
122 struct samr_Password
*lm_hash
;
125 /* old credentials */
127 struct samr_Password
*nt_hash
;
128 struct samr_Password
*lm_hash
;
129 uint32_t nt_history_len
;
130 struct samr_Password
*nt_history
;
131 uint32_t lm_history_len
;
132 struct samr_Password
*lm_history
;
133 const struct ldb_val
*supplemental
;
134 struct supplementalCredentialsBlob scb
;
137 /* generated credentials */
139 struct samr_Password
*nt_hash
;
140 struct samr_Password
*lm_hash
;
141 uint32_t nt_history_len
;
142 struct samr_Password
*nt_history
;
143 uint32_t lm_history_len
;
144 struct samr_Password
*lm_history
;
150 struct ldb_val supplemental
;
155 static int password_hash_bypass(struct ldb_module
*module
, struct ldb_request
*request
)
157 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
158 const struct ldb_message
*msg
;
159 struct ldb_message_element
*nte
;
160 struct ldb_message_element
*lme
;
161 struct ldb_message_element
*nthe
;
162 struct ldb_message_element
*lmhe
;
163 struct ldb_message_element
*sce
;
165 switch (request
->operation
) {
167 msg
= request
->op
.add
.message
;
170 msg
= request
->op
.mod
.message
;
173 return ldb_next_request(module
, request
);
176 /* nobody must touch password histories and 'supplementalCredentials' */
177 nte
= dsdb_get_single_valued_attr(msg
, "unicodePwd",
179 lme
= dsdb_get_single_valued_attr(msg
, "dBCSPwd",
181 nthe
= dsdb_get_single_valued_attr(msg
, "ntPwdHistory",
183 lmhe
= dsdb_get_single_valued_attr(msg
, "lmPwdHistory",
185 sce
= dsdb_get_single_valued_attr(msg
, "supplementalCredentials",
188 #define CHECK_HASH_ELEMENT(e, min, max) do {\
189 if (e && e->num_values) { \
190 unsigned int _count; \
191 if (e->num_values != 1) { \
192 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
193 "num_values != 1"); \
195 if ((e->values[0].length % 16) != 0) { \
196 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
197 "length % 16 != 0"); \
199 _count = e->values[0].length / 16; \
200 if (_count < min) { \
201 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
204 if (_count > max) { \
205 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
211 CHECK_HASH_ELEMENT(nte
, 1, 1);
212 CHECK_HASH_ELEMENT(lme
, 1, 1);
213 CHECK_HASH_ELEMENT(nthe
, 1, INT32_MAX
);
214 CHECK_HASH_ELEMENT(lmhe
, 1, INT32_MAX
);
216 if (sce
&& sce
->num_values
) {
217 enum ndr_err_code ndr_err
;
218 struct supplementalCredentialsBlob
*scb
;
219 struct supplementalCredentialsPackage
*scpp
= NULL
;
220 struct supplementalCredentialsPackage
*scpk
= NULL
;
221 struct supplementalCredentialsPackage
*scpkn
= NULL
;
222 struct supplementalCredentialsPackage
*scpct
= NULL
;
223 DATA_BLOB scpbp
= data_blob_null
;
224 DATA_BLOB scpbk
= data_blob_null
;
225 DATA_BLOB scpbkn
= data_blob_null
;
226 DATA_BLOB scpbct
= data_blob_null
;
230 if (sce
->num_values
!= 1) {
231 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
235 scb
= talloc_zero(request
, struct supplementalCredentialsBlob
);
237 return ldb_module_oom(module
);
240 ndr_err
= ndr_pull_struct_blob_all(&sce
->values
[0], scb
, scb
,
241 (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
);
242 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
243 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
244 "ndr_pull_struct_blob_all");
247 if (scb
->sub
.num_packages
< 2) {
248 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
252 for (i
=0; i
< scb
->sub
.num_packages
; i
++) {
255 subblob
= strhex_to_data_blob(scb
, scb
->sub
.packages
[i
].data
);
256 if (subblob
.data
== NULL
) {
257 return ldb_module_oom(module
);
260 if (strcmp(scb
->sub
.packages
[i
].name
, "Packages") == 0) {
262 return ldb_error(ldb
,
263 LDB_ERR_CONSTRAINT_VIOLATION
,
266 scpp
= &scb
->sub
.packages
[i
];
270 if (strcmp(scb
->sub
.packages
[i
].name
, "Primary:Kerberos") == 0) {
272 return ldb_error(ldb
,
273 LDB_ERR_CONSTRAINT_VIOLATION
,
274 "Primary:Kerberos twice");
276 scpk
= &scb
->sub
.packages
[i
];
280 if (strcmp(scb
->sub
.packages
[i
].name
, "Primary:Kerberos-Newer-Keys") == 0) {
282 return ldb_error(ldb
,
283 LDB_ERR_CONSTRAINT_VIOLATION
,
284 "Primary:Kerberos-Newer-Keys twice");
286 scpkn
= &scb
->sub
.packages
[i
];
290 if (strcmp(scb
->sub
.packages
[i
].name
, "Primary:CLEARTEXT") == 0) {
292 return ldb_error(ldb
,
293 LDB_ERR_CONSTRAINT_VIOLATION
,
294 "Primary:CLEARTEXT twice");
296 scpct
= &scb
->sub
.packages
[i
];
301 data_blob_free(&subblob
);
305 return ldb_error(ldb
,
306 LDB_ERR_CONSTRAINT_VIOLATION
,
307 "Primary:Packages missing");
312 * If Primary:Kerberos is missing w2k8r2 reboots
313 * when a password is changed.
315 return ldb_error(ldb
,
316 LDB_ERR_CONSTRAINT_VIOLATION
,
317 "Primary:Kerberos missing");
321 struct package_PackagesBlob
*p
;
324 p
= talloc_zero(scb
, struct package_PackagesBlob
);
326 return ldb_module_oom(module
);
329 ndr_err
= ndr_pull_struct_blob(&scpbp
, p
, p
,
330 (ndr_pull_flags_fn_t
)ndr_pull_package_PackagesBlob
);
331 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
332 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
333 "ndr_pull_struct_blob Packages");
336 if (p
->names
== NULL
) {
337 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
338 "Packages names == NULL");
341 for (n
= 0; p
->names
[n
]; n
++) {
345 if (scb
->sub
.num_packages
!= (n
+ 1)) {
346 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
347 "Packages num_packages != num_names + 1");
354 struct package_PrimaryKerberosBlob
*k
;
356 k
= talloc_zero(scb
, struct package_PrimaryKerberosBlob
);
358 return ldb_module_oom(module
);
361 ndr_err
= ndr_pull_struct_blob(&scpbk
, k
, k
,
362 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
363 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
364 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
365 "ndr_pull_struct_blob PrimaryKerberos");
368 if (k
->version
!= 3) {
369 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
370 "PrimaryKerberos version != 3");
373 if (k
->ctr
.ctr3
.salt
.string
== NULL
) {
374 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
375 "PrimaryKerberos salt == NULL");
378 if (strlen(k
->ctr
.ctr3
.salt
.string
) == 0) {
379 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
380 "PrimaryKerberos strlen(salt) == 0");
383 if (k
->ctr
.ctr3
.num_keys
!= 2) {
384 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
385 "PrimaryKerberos num_keys != 2");
388 if (k
->ctr
.ctr3
.num_old_keys
> k
->ctr
.ctr3
.num_keys
) {
389 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
390 "PrimaryKerberos num_old_keys > num_keys");
393 if (k
->ctr
.ctr3
.keys
[0].keytype
!= ENCTYPE_DES_CBC_MD5
) {
394 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
395 "PrimaryKerberos key[0] != DES_CBC_MD5");
397 if (k
->ctr
.ctr3
.keys
[1].keytype
!= ENCTYPE_DES_CBC_CRC
) {
398 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
399 "PrimaryKerberos key[1] != DES_CBC_CRC");
402 if (k
->ctr
.ctr3
.keys
[0].value_len
!= 8) {
403 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
404 "PrimaryKerberos key[0] value_len != 8");
406 if (k
->ctr
.ctr3
.keys
[1].value_len
!= 8) {
407 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
408 "PrimaryKerberos key[1] value_len != 8");
411 for (i
= 0; i
< k
->ctr
.ctr3
.num_old_keys
; i
++) {
412 if (k
->ctr
.ctr3
.old_keys
[i
].keytype
==
413 k
->ctr
.ctr3
.keys
[i
].keytype
&&
414 k
->ctr
.ctr3
.old_keys
[i
].value_len
==
415 k
->ctr
.ctr3
.keys
[i
].value_len
) {
419 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
420 "PrimaryKerberos old_keys type/value_len doesn't match");
427 struct package_PrimaryKerberosBlob
*k
;
429 k
= talloc_zero(scb
, struct package_PrimaryKerberosBlob
);
431 return ldb_module_oom(module
);
434 ndr_err
= ndr_pull_struct_blob(&scpbkn
, k
, k
,
435 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
436 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
437 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
438 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
441 if (k
->version
!= 4) {
442 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
443 "KerberosNerverKeys version != 4");
446 if (k
->ctr
.ctr4
.salt
.string
== NULL
) {
447 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
448 "KerberosNewerKeys salt == NULL");
451 if (strlen(k
->ctr
.ctr4
.salt
.string
) == 0) {
452 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
453 "KerberosNewerKeys strlen(salt) == 0");
456 if (k
->ctr
.ctr4
.num_keys
!= 4) {
457 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
458 "KerberosNewerKeys num_keys != 2");
461 if (k
->ctr
.ctr4
.num_old_keys
> k
->ctr
.ctr4
.num_keys
) {
462 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
463 "KerberosNewerKeys num_old_keys > num_keys");
466 if (k
->ctr
.ctr4
.num_older_keys
> k
->ctr
.ctr4
.num_old_keys
) {
467 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
468 "KerberosNewerKeys num_older_keys > num_old_keys");
471 if (k
->ctr
.ctr4
.keys
[0].keytype
!= ENCTYPE_AES256_CTS_HMAC_SHA1_96
) {
472 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
473 "KerberosNewerKeys key[0] != AES256");
475 if (k
->ctr
.ctr4
.keys
[1].keytype
!= ENCTYPE_AES128_CTS_HMAC_SHA1_96
) {
476 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
477 "KerberosNewerKeys key[1] != AES128");
479 if (k
->ctr
.ctr4
.keys
[2].keytype
!= ENCTYPE_DES_CBC_MD5
) {
480 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
481 "KerberosNewerKeys key[2] != DES_CBC_MD5");
483 if (k
->ctr
.ctr4
.keys
[3].keytype
!= ENCTYPE_DES_CBC_CRC
) {
484 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
485 "KerberosNewerKeys key[3] != DES_CBC_CRC");
488 if (k
->ctr
.ctr4
.keys
[0].value_len
!= 32) {
489 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
490 "KerberosNewerKeys key[0] value_len != 32");
492 if (k
->ctr
.ctr4
.keys
[1].value_len
!= 16) {
493 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
494 "KerberosNewerKeys key[1] value_len != 16");
496 if (k
->ctr
.ctr4
.keys
[2].value_len
!= 8) {
497 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
498 "KerberosNewerKeys key[2] value_len != 8");
500 if (k
->ctr
.ctr4
.keys
[3].value_len
!= 8) {
501 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
502 "KerberosNewerKeys key[3] value_len != 8");
507 * Maybe we can check old and older keys here.
508 * But we need to do some tests, if the old keys
509 * can be taken from the PrimaryKerberos blob
510 * (with only des keys), when the domain was upgraded
518 struct package_PrimaryCLEARTEXTBlob
*ct
;
520 ct
= talloc_zero(scb
, struct package_PrimaryCLEARTEXTBlob
);
522 return ldb_module_oom(module
);
525 ndr_err
= ndr_pull_struct_blob(&scpbct
, ct
, ct
,
526 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryCLEARTEXTBlob
);
527 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
528 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
529 "ndr_pull_struct_blob PrimaryCLEARTEXT");
532 if ((ct
->cleartext
.length
% 2) != 0) {
533 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
534 "PrimaryCLEARTEXT length % 2 != 0");
540 ndr_err
= ndr_push_struct_blob(&blob
, scb
, scb
,
541 (ndr_push_flags_fn_t
)ndr_push_supplementalCredentialsBlob
);
542 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
543 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
544 "ndr_pull_struct_blob_all");
547 if (sce
->values
[0].length
!= blob
.length
) {
548 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
549 "supplementalCredentialsBlob length differ");
552 if (memcmp(sce
->values
[0].data
, blob
.data
, blob
.length
) != 0) {
553 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
554 "supplementalCredentialsBlob memcmp differ");
560 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_bypass - validated\n");
561 return ldb_next_request(module
, request
);
564 /* Get the NT hash, and fill it in as an entry in the password history,
565 and specify it into io->g.nt_hash */
567 static int setup_nt_fields(struct setup_password_fields_io
*io
)
569 struct ldb_context
*ldb
;
572 io
->g
.nt_hash
= io
->n
.nt_hash
;
573 ldb
= ldb_module_get_ctx(io
->ac
->module
);
575 if (io
->ac
->status
->domain_data
.pwdHistoryLength
== 0) {
579 /* We might not have an old NT password */
580 io
->g
.nt_history
= talloc_array(io
->ac
,
581 struct samr_Password
,
582 io
->ac
->status
->domain_data
.pwdHistoryLength
);
583 if (!io
->g
.nt_history
) {
587 for (i
= 0; i
< MIN(io
->ac
->status
->domain_data
.pwdHistoryLength
-1,
588 io
->o
.nt_history_len
); i
++) {
589 io
->g
.nt_history
[i
+1] = io
->o
.nt_history
[i
];
591 io
->g
.nt_history_len
= i
+ 1;
594 io
->g
.nt_history
[0] = *io
->g
.nt_hash
;
597 * TODO: is this correct?
598 * the simular behavior is correct for the lm history case
600 E_md4hash("", io
->g
.nt_history
[0].hash
);
606 /* Get the LANMAN hash, and fill it in as an entry in the password history,
607 and specify it into io->g.lm_hash */
609 static int setup_lm_fields(struct setup_password_fields_io
*io
)
611 struct ldb_context
*ldb
;
614 io
->g
.lm_hash
= io
->n
.lm_hash
;
615 ldb
= ldb_module_get_ctx(io
->ac
->module
);
617 if (io
->ac
->status
->domain_data
.pwdHistoryLength
== 0) {
621 /* We might not have an old LM password */
622 io
->g
.lm_history
= talloc_array(io
->ac
,
623 struct samr_Password
,
624 io
->ac
->status
->domain_data
.pwdHistoryLength
);
625 if (!io
->g
.lm_history
) {
629 for (i
= 0; i
< MIN(io
->ac
->status
->domain_data
.pwdHistoryLength
-1,
630 io
->o
.lm_history_len
); i
++) {
631 io
->g
.lm_history
[i
+1] = io
->o
.lm_history
[i
];
633 io
->g
.lm_history_len
= i
+ 1;
636 io
->g
.lm_history
[0] = *io
->g
.lm_hash
;
638 E_deshash("", io
->g
.lm_history
[0].hash
);
644 static int setup_kerberos_keys(struct setup_password_fields_io
*io
)
646 struct ldb_context
*ldb
;
647 krb5_error_code krb5_ret
;
648 Principal
*salt_principal
;
651 krb5_data cleartext_data
;
653 ldb
= ldb_module_get_ctx(io
->ac
->module
);
654 cleartext_data
.data
= io
->n
.cleartext_utf8
->data
;
655 cleartext_data
.length
= io
->n
.cleartext_utf8
->length
;
657 /* Many, many thanks to lukeh@padl.com for this
658 * algorithm, described in his Nov 10 2004 mail to
659 * samba-technical@samba.org */
662 * Determine a salting principal
664 if (io
->u
.is_computer
) {
668 name
= strlower_talloc(io
->ac
, io
->u
.sAMAccountName
);
673 if (name
[strlen(name
)-1] == '$') {
674 name
[strlen(name
)-1] = '\0';
677 saltbody
= talloc_asprintf(io
->ac
, "%s.%s", name
,
678 io
->ac
->status
->domain_data
.dns_domain
);
683 krb5_ret
= krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
685 io
->ac
->status
->domain_data
.realm
,
686 "host", saltbody
, NULL
);
687 } else if (io
->u
.user_principal_name
) {
688 char *user_principal_name
;
691 user_principal_name
= talloc_strdup(io
->ac
, io
->u
.user_principal_name
);
692 if (!user_principal_name
) {
696 p
= strchr(user_principal_name
, '@');
701 krb5_ret
= krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
703 io
->ac
->status
->domain_data
.realm
,
704 user_principal_name
, NULL
);
706 krb5_ret
= krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
708 io
->ac
->status
->domain_data
.realm
,
709 io
->u
.sAMAccountName
, NULL
);
712 ldb_asprintf_errstring(ldb
,
713 "setup_kerberos_keys: "
714 "generation of a salting principal failed: %s",
715 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
717 return LDB_ERR_OPERATIONS_ERROR
;
721 * create salt from salt_principal
723 krb5_ret
= krb5_get_pw_salt(io
->smb_krb5_context
->krb5_context
,
724 salt_principal
, &salt
);
725 krb5_free_principal(io
->smb_krb5_context
->krb5_context
, salt_principal
);
727 ldb_asprintf_errstring(ldb
,
728 "setup_kerberos_keys: "
729 "generation of krb5_salt failed: %s",
730 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
732 return LDB_ERR_OPERATIONS_ERROR
;
734 /* create a talloc copy */
735 io
->g
.salt
= talloc_strndup(io
->ac
,
736 (char *)salt
.saltvalue
.data
,
737 salt
.saltvalue
.length
);
738 krb5_free_salt(io
->smb_krb5_context
->krb5_context
, salt
);
742 salt
.saltvalue
.data
= discard_const(io
->g
.salt
);
743 salt
.saltvalue
.length
= strlen(io
->g
.salt
);
746 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
747 * the salt and the cleartext password
749 krb5_ret
= krb5_string_to_key_data_salt(io
->smb_krb5_context
->krb5_context
,
750 ENCTYPE_AES256_CTS_HMAC_SHA1_96
,
755 ldb_asprintf_errstring(ldb
,
756 "setup_kerberos_keys: "
757 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
758 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
760 return LDB_ERR_OPERATIONS_ERROR
;
762 io
->g
.aes_256
= data_blob_talloc(io
->ac
,
764 KRB5_KEY_LENGTH(&key
));
765 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
766 if (!io
->g
.aes_256
.data
) {
771 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
772 * the salt and the cleartext password
774 krb5_ret
= krb5_string_to_key_data_salt(io
->smb_krb5_context
->krb5_context
,
775 ENCTYPE_AES128_CTS_HMAC_SHA1_96
,
780 ldb_asprintf_errstring(ldb
,
781 "setup_kerberos_keys: "
782 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
783 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
785 return LDB_ERR_OPERATIONS_ERROR
;
787 io
->g
.aes_128
= data_blob_talloc(io
->ac
,
789 KRB5_KEY_LENGTH(&key
));
790 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
791 if (!io
->g
.aes_128
.data
) {
796 * create ENCTYPE_DES_CBC_MD5 key out of
797 * the salt and the cleartext password
799 krb5_ret
= krb5_string_to_key_data_salt(io
->smb_krb5_context
->krb5_context
,
805 ldb_asprintf_errstring(ldb
,
806 "setup_kerberos_keys: "
807 "generation of a des-cbc-md5 key failed: %s",
808 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
810 return LDB_ERR_OPERATIONS_ERROR
;
812 io
->g
.des_md5
= data_blob_talloc(io
->ac
,
814 KRB5_KEY_LENGTH(&key
));
815 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
816 if (!io
->g
.des_md5
.data
) {
821 * create ENCTYPE_DES_CBC_CRC key out of
822 * the salt and the cleartext password
824 krb5_ret
= krb5_string_to_key_data_salt(io
->smb_krb5_context
->krb5_context
,
830 ldb_asprintf_errstring(ldb
,
831 "setup_kerberos_keys: "
832 "generation of a des-cbc-crc key failed: %s",
833 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
835 return LDB_ERR_OPERATIONS_ERROR
;
837 io
->g
.des_crc
= data_blob_talloc(io
->ac
,
839 KRB5_KEY_LENGTH(&key
));
840 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
841 if (!io
->g
.des_crc
.data
) {
848 static int setup_primary_kerberos(struct setup_password_fields_io
*io
,
849 const struct supplementalCredentialsBlob
*old_scb
,
850 struct package_PrimaryKerberosBlob
*pkb
)
852 struct ldb_context
*ldb
;
853 struct package_PrimaryKerberosCtr3
*pkb3
= &pkb
->ctr
.ctr3
;
854 struct supplementalCredentialsPackage
*old_scp
= NULL
;
855 struct package_PrimaryKerberosBlob _old_pkb
;
856 struct package_PrimaryKerberosCtr3
*old_pkb3
= NULL
;
858 enum ndr_err_code ndr_err
;
860 ldb
= ldb_module_get_ctx(io
->ac
->module
);
863 * prepare generation of keys
865 * ENCTYPE_DES_CBC_MD5
866 * ENCTYPE_DES_CBC_CRC
869 pkb3
->salt
.string
= io
->g
.salt
;
871 pkb3
->keys
= talloc_array(io
->ac
,
872 struct package_PrimaryKerberosKey3
,
878 pkb3
->keys
[0].keytype
= ENCTYPE_DES_CBC_MD5
;
879 pkb3
->keys
[0].value
= &io
->g
.des_md5
;
880 pkb3
->keys
[1].keytype
= ENCTYPE_DES_CBC_CRC
;
881 pkb3
->keys
[1].value
= &io
->g
.des_crc
;
883 /* initialize the old keys to zero */
884 pkb3
->num_old_keys
= 0;
885 pkb3
->old_keys
= NULL
;
887 /* if there're no old keys, then we're done */
892 for (i
=0; i
< old_scb
->sub
.num_packages
; i
++) {
893 if (strcmp("Primary:Kerberos", old_scb
->sub
.packages
[i
].name
) != 0) {
897 if (!old_scb
->sub
.packages
[i
].data
|| !old_scb
->sub
.packages
[i
].data
[0]) {
901 old_scp
= &old_scb
->sub
.packages
[i
];
904 /* Primary:Kerberos element of supplementalCredentials */
908 blob
= strhex_to_data_blob(io
->ac
, old_scp
->data
);
913 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
914 ndr_err
= ndr_pull_struct_blob(&blob
, io
->ac
, &_old_pkb
,
915 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
916 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
917 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
918 ldb_asprintf_errstring(ldb
,
919 "setup_primary_kerberos: "
920 "failed to pull old package_PrimaryKerberosBlob: %s",
922 return LDB_ERR_OPERATIONS_ERROR
;
925 if (_old_pkb
.version
!= 3) {
926 ldb_asprintf_errstring(ldb
,
927 "setup_primary_kerberos: "
928 "package_PrimaryKerberosBlob version[%u] expected[3]",
930 return LDB_ERR_OPERATIONS_ERROR
;
933 old_pkb3
= &_old_pkb
.ctr
.ctr3
;
936 /* if we didn't found the old keys we're done */
941 /* fill in the old keys */
942 pkb3
->num_old_keys
= old_pkb3
->num_keys
;
943 pkb3
->old_keys
= old_pkb3
->keys
;
948 static int setup_primary_kerberos_newer(struct setup_password_fields_io
*io
,
949 const struct supplementalCredentialsBlob
*old_scb
,
950 struct package_PrimaryKerberosBlob
*pkb
)
952 struct ldb_context
*ldb
;
953 struct package_PrimaryKerberosCtr4
*pkb4
= &pkb
->ctr
.ctr4
;
954 struct supplementalCredentialsPackage
*old_scp
= NULL
;
955 struct package_PrimaryKerberosBlob _old_pkb
;
956 struct package_PrimaryKerberosCtr4
*old_pkb4
= NULL
;
958 enum ndr_err_code ndr_err
;
960 ldb
= ldb_module_get_ctx(io
->ac
->module
);
963 * prepare generation of keys
965 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
966 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
967 * ENCTYPE_DES_CBC_MD5
968 * ENCTYPE_DES_CBC_CRC
971 pkb4
->salt
.string
= io
->g
.salt
;
972 pkb4
->default_iteration_count
= 4096;
975 pkb4
->keys
= talloc_array(io
->ac
,
976 struct package_PrimaryKerberosKey4
,
982 pkb4
->keys
[0].iteration_count
= 4096;
983 pkb4
->keys
[0].keytype
= ENCTYPE_AES256_CTS_HMAC_SHA1_96
;
984 pkb4
->keys
[0].value
= &io
->g
.aes_256
;
985 pkb4
->keys
[1].iteration_count
= 4096;
986 pkb4
->keys
[1].keytype
= ENCTYPE_AES128_CTS_HMAC_SHA1_96
;
987 pkb4
->keys
[1].value
= &io
->g
.aes_128
;
988 pkb4
->keys
[2].iteration_count
= 4096;
989 pkb4
->keys
[2].keytype
= ENCTYPE_DES_CBC_MD5
;
990 pkb4
->keys
[2].value
= &io
->g
.des_md5
;
991 pkb4
->keys
[3].iteration_count
= 4096;
992 pkb4
->keys
[3].keytype
= ENCTYPE_DES_CBC_CRC
;
993 pkb4
->keys
[3].value
= &io
->g
.des_crc
;
995 /* initialize the old keys to zero */
996 pkb4
->num_old_keys
= 0;
997 pkb4
->old_keys
= NULL
;
998 pkb4
->num_older_keys
= 0;
999 pkb4
->older_keys
= NULL
;
1001 /* if there're no old keys, then we're done */
1006 for (i
=0; i
< old_scb
->sub
.num_packages
; i
++) {
1007 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb
->sub
.packages
[i
].name
) != 0) {
1011 if (!old_scb
->sub
.packages
[i
].data
|| !old_scb
->sub
.packages
[i
].data
[0]) {
1015 old_scp
= &old_scb
->sub
.packages
[i
];
1018 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1022 blob
= strhex_to_data_blob(io
->ac
, old_scp
->data
);
1024 return ldb_oom(ldb
);
1027 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1028 ndr_err
= ndr_pull_struct_blob(&blob
, io
->ac
,
1030 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
1031 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1032 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1033 ldb_asprintf_errstring(ldb
,
1034 "setup_primary_kerberos_newer: "
1035 "failed to pull old package_PrimaryKerberosBlob: %s",
1037 return LDB_ERR_OPERATIONS_ERROR
;
1040 if (_old_pkb
.version
!= 4) {
1041 ldb_asprintf_errstring(ldb
,
1042 "setup_primary_kerberos_newer: "
1043 "package_PrimaryKerberosBlob version[%u] expected[4]",
1045 return LDB_ERR_OPERATIONS_ERROR
;
1048 old_pkb4
= &_old_pkb
.ctr
.ctr4
;
1051 /* if we didn't found the old keys we're done */
1056 /* fill in the old keys */
1057 pkb4
->num_old_keys
= old_pkb4
->num_keys
;
1058 pkb4
->old_keys
= old_pkb4
->keys
;
1059 pkb4
->num_older_keys
= old_pkb4
->num_old_keys
;
1060 pkb4
->older_keys
= old_pkb4
->old_keys
;
1065 static int setup_primary_wdigest(struct setup_password_fields_io
*io
,
1066 const struct supplementalCredentialsBlob
*old_scb
,
1067 struct package_PrimaryWDigestBlob
*pdb
)
1069 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
1070 DATA_BLOB sAMAccountName
;
1071 DATA_BLOB sAMAccountName_l
;
1072 DATA_BLOB sAMAccountName_u
;
1073 const char *user_principal_name
= io
->u
.user_principal_name
;
1074 DATA_BLOB userPrincipalName
;
1075 DATA_BLOB userPrincipalName_l
;
1076 DATA_BLOB userPrincipalName_u
;
1077 DATA_BLOB netbios_domain
;
1078 DATA_BLOB netbios_domain_l
;
1079 DATA_BLOB netbios_domain_u
;
1080 DATA_BLOB dns_domain
;
1081 DATA_BLOB dns_domain_l
;
1082 DATA_BLOB dns_domain_u
;
1085 DATA_BLOB backslash
;
1094 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1095 * for what precalculated hashes are supposed to be stored...
1097 * I can't reproduce all values which should contain "Digest" as realm,
1098 * am I doing something wrong or is w2k3 just broken...?
1100 * W2K3 fills in following for a user:
1102 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1103 * sAMAccountName: NewUser2Sam
1104 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1106 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1107 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1108 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1109 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1110 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1111 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1112 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1113 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1114 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1115 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1116 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1117 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1118 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1119 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1120 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1121 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1122 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1123 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1124 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1125 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1126 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1127 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1128 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1129 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1130 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1131 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1132 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1133 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1134 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1136 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1137 * sAMAccountName: NewUser2Sam
1139 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1140 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1141 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1142 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1143 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1144 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1145 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1146 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1147 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1148 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1149 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1150 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1151 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1152 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1153 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1154 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1155 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1156 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1157 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1158 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1159 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1160 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1161 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1162 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1163 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1164 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1165 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1166 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1167 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1171 * sAMAccountName, netbios_domain
1174 .user
= &sAMAccountName
,
1175 .realm
= &netbios_domain
,
1178 .user
= &sAMAccountName_l
,
1179 .realm
= &netbios_domain_l
,
1182 .user
= &sAMAccountName_u
,
1183 .realm
= &netbios_domain_u
,
1186 .user
= &sAMAccountName
,
1187 .realm
= &netbios_domain_u
,
1190 .user
= &sAMAccountName
,
1191 .realm
= &netbios_domain_l
,
1194 .user
= &sAMAccountName_u
,
1195 .realm
= &netbios_domain_l
,
1198 .user
= &sAMAccountName_l
,
1199 .realm
= &netbios_domain_u
,
1202 * sAMAccountName, dns_domain
1205 .user
= &sAMAccountName
,
1206 .realm
= &dns_domain
,
1209 .user
= &sAMAccountName_l
,
1210 .realm
= &dns_domain_l
,
1213 .user
= &sAMAccountName_u
,
1214 .realm
= &dns_domain_u
,
1217 .user
= &sAMAccountName
,
1218 .realm
= &dns_domain_u
,
1221 .user
= &sAMAccountName
,
1222 .realm
= &dns_domain_l
,
1225 .user
= &sAMAccountName_u
,
1226 .realm
= &dns_domain_l
,
1229 .user
= &sAMAccountName_l
,
1230 .realm
= &dns_domain_u
,
1233 * userPrincipalName, no realm
1236 .user
= &userPrincipalName
,
1240 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1241 * the fallback to the sAMAccountName based userPrincipalName is correct
1243 .user
= &userPrincipalName_l
,
1246 .user
= &userPrincipalName_u
,
1249 * nt4dom\sAMAccountName, no realm
1252 .user
= &sAMAccountName
,
1253 .nt4dom
= &netbios_domain
1256 .user
= &sAMAccountName_l
,
1257 .nt4dom
= &netbios_domain_l
1260 .user
= &sAMAccountName_u
,
1261 .nt4dom
= &netbios_domain_u
1265 * the following ones are guessed depending on the technet2 article
1266 * but not reproducable on a w2k3 server
1268 /* sAMAccountName with "Digest" realm */
1270 .user
= &sAMAccountName
,
1274 .user
= &sAMAccountName_l
,
1278 .user
= &sAMAccountName_u
,
1281 /* userPrincipalName with "Digest" realm */
1283 .user
= &userPrincipalName
,
1287 .user
= &userPrincipalName_l
,
1291 .user
= &userPrincipalName_u
,
1294 /* nt4dom\\sAMAccountName with "Digest" realm */
1296 .user
= &sAMAccountName
,
1297 .nt4dom
= &netbios_domain
,
1301 .user
= &sAMAccountName_l
,
1302 .nt4dom
= &netbios_domain_l
,
1306 .user
= &sAMAccountName_u
,
1307 .nt4dom
= &netbios_domain_u
,
1312 /* prepare DATA_BLOB's used in the combinations array */
1313 sAMAccountName
= data_blob_string_const(io
->u
.sAMAccountName
);
1314 sAMAccountName_l
= data_blob_string_const(strlower_talloc(io
->ac
, io
->u
.sAMAccountName
));
1315 if (!sAMAccountName_l
.data
) {
1316 return ldb_oom(ldb
);
1318 sAMAccountName_u
= data_blob_string_const(strupper_talloc(io
->ac
, io
->u
.sAMAccountName
));
1319 if (!sAMAccountName_u
.data
) {
1320 return ldb_oom(ldb
);
1323 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1324 if (!user_principal_name
) {
1325 user_principal_name
= talloc_asprintf(io
->ac
, "%s@%s",
1326 io
->u
.sAMAccountName
,
1327 io
->ac
->status
->domain_data
.dns_domain
);
1328 if (!user_principal_name
) {
1329 return ldb_oom(ldb
);
1332 userPrincipalName
= data_blob_string_const(user_principal_name
);
1333 userPrincipalName_l
= data_blob_string_const(strlower_talloc(io
->ac
, user_principal_name
));
1334 if (!userPrincipalName_l
.data
) {
1335 return ldb_oom(ldb
);
1337 userPrincipalName_u
= data_blob_string_const(strupper_talloc(io
->ac
, user_principal_name
));
1338 if (!userPrincipalName_u
.data
) {
1339 return ldb_oom(ldb
);
1342 netbios_domain
= data_blob_string_const(io
->ac
->status
->domain_data
.netbios_domain
);
1343 netbios_domain_l
= data_blob_string_const(strlower_talloc(io
->ac
,
1344 io
->ac
->status
->domain_data
.netbios_domain
));
1345 if (!netbios_domain_l
.data
) {
1346 return ldb_oom(ldb
);
1348 netbios_domain_u
= data_blob_string_const(strupper_talloc(io
->ac
,
1349 io
->ac
->status
->domain_data
.netbios_domain
));
1350 if (!netbios_domain_u
.data
) {
1351 return ldb_oom(ldb
);
1354 dns_domain
= data_blob_string_const(io
->ac
->status
->domain_data
.dns_domain
);
1355 dns_domain_l
= data_blob_string_const(io
->ac
->status
->domain_data
.dns_domain
);
1356 dns_domain_u
= data_blob_string_const(io
->ac
->status
->domain_data
.realm
);
1358 digest
= data_blob_string_const("Digest");
1360 delim
= data_blob_string_const(":");
1361 backslash
= data_blob_string_const("\\");
1363 pdb
->num_hashes
= ARRAY_SIZE(wdigest
);
1364 pdb
->hashes
= talloc_array(io
->ac
, struct package_PrimaryWDigestHash
,
1367 return ldb_oom(ldb
);
1370 for (i
=0; i
< ARRAY_SIZE(wdigest
); i
++) {
1373 if (wdigest
[i
].nt4dom
) {
1374 MD5Update(&md5
, wdigest
[i
].nt4dom
->data
, wdigest
[i
].nt4dom
->length
);
1375 MD5Update(&md5
, backslash
.data
, backslash
.length
);
1377 MD5Update(&md5
, wdigest
[i
].user
->data
, wdigest
[i
].user
->length
);
1378 MD5Update(&md5
, delim
.data
, delim
.length
);
1379 if (wdigest
[i
].realm
) {
1380 MD5Update(&md5
, wdigest
[i
].realm
->data
, wdigest
[i
].realm
->length
);
1382 MD5Update(&md5
, delim
.data
, delim
.length
);
1383 MD5Update(&md5
, io
->n
.cleartext_utf8
->data
, io
->n
.cleartext_utf8
->length
);
1384 MD5Final(pdb
->hashes
[i
].hash
, &md5
);
1390 static int setup_supplemental_field(struct setup_password_fields_io
*io
)
1392 struct ldb_context
*ldb
;
1393 struct supplementalCredentialsBlob scb
;
1394 struct supplementalCredentialsBlob _old_scb
;
1395 struct supplementalCredentialsBlob
*old_scb
= NULL
;
1396 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1397 uint32_t num_names
= 0;
1398 const char *names
[1+4];
1399 uint32_t num_packages
= 0;
1400 struct supplementalCredentialsPackage packages
[1+4];
1402 struct supplementalCredentialsPackage
*pp
= NULL
;
1403 struct package_PackagesBlob pb
;
1406 /* Primary:Kerberos-Newer-Keys */
1407 const char **nkn
= NULL
;
1408 struct supplementalCredentialsPackage
*pkn
= NULL
;
1409 struct package_PrimaryKerberosBlob pknb
;
1410 DATA_BLOB pknb_blob
;
1412 /* Primary:Kerberos */
1413 const char **nk
= NULL
;
1414 struct supplementalCredentialsPackage
*pk
= NULL
;
1415 struct package_PrimaryKerberosBlob pkb
;
1418 /* Primary:WDigest */
1419 const char **nd
= NULL
;
1420 struct supplementalCredentialsPackage
*pd
= NULL
;
1421 struct package_PrimaryWDigestBlob pdb
;
1424 /* Primary:CLEARTEXT */
1425 const char **nc
= NULL
;
1426 struct supplementalCredentialsPackage
*pc
= NULL
;
1427 struct package_PrimaryCLEARTEXTBlob pcb
;
1431 enum ndr_err_code ndr_err
;
1433 bool do_newer_keys
= false;
1434 bool do_cleartext
= false;
1436 ZERO_STRUCT(zero16
);
1439 ldb
= ldb_module_get_ctx(io
->ac
->module
);
1441 if (!io
->n
.cleartext_utf8
) {
1443 * when we don't have a cleartext password
1444 * we can't setup a supplementalCredential value
1449 /* if there's an old supplementaCredentials blob then parse it */
1450 if (io
->o
.supplemental
) {
1451 ndr_err
= ndr_pull_struct_blob_all(io
->o
.supplemental
, io
->ac
,
1453 (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
);
1454 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1455 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1456 ldb_asprintf_errstring(ldb
,
1457 "setup_supplemental_field: "
1458 "failed to pull old supplementalCredentialsBlob: %s",
1460 return LDB_ERR_OPERATIONS_ERROR
;
1463 if (_old_scb
.sub
.signature
== SUPPLEMENTAL_CREDENTIALS_SIGNATURE
) {
1464 old_scb
= &_old_scb
;
1466 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
1467 "setup_supplemental_field: "
1468 "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
1469 _old_scb
.sub
.signature
, SUPPLEMENTAL_CREDENTIALS_SIGNATURE
);
1472 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1473 do_newer_keys
= (dsdb_functional_level(ldb
) >= DS_DOMAIN_FUNCTION_2008
);
1475 if (io
->ac
->status
->domain_data
.store_cleartext
&&
1476 (io
->u
.userAccountControl
& UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
)) {
1477 do_cleartext
= true;
1481 * The ordering is this
1483 * Primary:Kerberos-Newer-Keys (optional)
1486 * Primary:CLEARTEXT (optional)
1488 * And the 'Packages' package is insert before the last
1491 if (do_newer_keys
) {
1492 /* Primary:Kerberos-Newer-Keys */
1493 nkn
= &names
[num_names
++];
1494 pkn
= &packages
[num_packages
++];
1497 /* Primary:Kerberos */
1498 nk
= &names
[num_names
++];
1499 pk
= &packages
[num_packages
++];
1501 if (!do_cleartext
) {
1503 pp
= &packages
[num_packages
++];
1506 /* Primary:WDigest */
1507 nd
= &names
[num_names
++];
1508 pd
= &packages
[num_packages
++];
1512 pp
= &packages
[num_packages
++];
1514 /* Primary:CLEARTEXT */
1515 nc
= &names
[num_names
++];
1516 pc
= &packages
[num_packages
++];
1521 * setup 'Primary:Kerberos-Newer-Keys' element
1523 *nkn
= "Kerberos-Newer-Keys";
1525 ret
= setup_primary_kerberos_newer(io
, old_scb
, &pknb
);
1526 if (ret
!= LDB_SUCCESS
) {
1530 ndr_err
= ndr_push_struct_blob(&pknb_blob
, io
->ac
,
1532 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryKerberosBlob
);
1533 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1534 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1535 ldb_asprintf_errstring(ldb
,
1536 "setup_supplemental_field: "
1537 "failed to push package_PrimaryKerberosNeverBlob: %s",
1539 return LDB_ERR_OPERATIONS_ERROR
;
1541 pknb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pknb_blob
);
1543 return ldb_oom(ldb
);
1545 pkn
->name
= "Primary:Kerberos-Newer-Keys";
1547 pkn
->data
= pknb_hexstr
;
1551 * setup 'Primary:Kerberos' element
1555 ret
= setup_primary_kerberos(io
, old_scb
, &pkb
);
1556 if (ret
!= LDB_SUCCESS
) {
1560 ndr_err
= ndr_push_struct_blob(&pkb_blob
, io
->ac
,
1562 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryKerberosBlob
);
1563 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1564 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1565 ldb_asprintf_errstring(ldb
,
1566 "setup_supplemental_field: "
1567 "failed to push package_PrimaryKerberosBlob: %s",
1569 return LDB_ERR_OPERATIONS_ERROR
;
1571 pkb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pkb_blob
);
1573 return ldb_oom(ldb
);
1575 pk
->name
= "Primary:Kerberos";
1577 pk
->data
= pkb_hexstr
;
1580 * setup 'Primary:WDigest' element
1584 ret
= setup_primary_wdigest(io
, old_scb
, &pdb
);
1585 if (ret
!= LDB_SUCCESS
) {
1589 ndr_err
= ndr_push_struct_blob(&pdb_blob
, io
->ac
,
1591 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryWDigestBlob
);
1592 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1593 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1594 ldb_asprintf_errstring(ldb
,
1595 "setup_supplemental_field: "
1596 "failed to push package_PrimaryWDigestBlob: %s",
1598 return LDB_ERR_OPERATIONS_ERROR
;
1600 pdb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pdb_blob
);
1602 return ldb_oom(ldb
);
1604 pd
->name
= "Primary:WDigest";
1606 pd
->data
= pdb_hexstr
;
1609 * setup 'Primary:CLEARTEXT' element
1614 pcb
.cleartext
= *io
->n
.cleartext_utf16
;
1616 ndr_err
= ndr_push_struct_blob(&pcb_blob
, io
->ac
,
1618 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryCLEARTEXTBlob
);
1619 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1620 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1621 ldb_asprintf_errstring(ldb
,
1622 "setup_supplemental_field: "
1623 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1625 return LDB_ERR_OPERATIONS_ERROR
;
1627 pcb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pcb_blob
);
1629 return ldb_oom(ldb
);
1631 pc
->name
= "Primary:CLEARTEXT";
1633 pc
->data
= pcb_hexstr
;
1637 * setup 'Packages' element
1640 ndr_err
= ndr_push_struct_blob(&pb_blob
, io
->ac
,
1642 (ndr_push_flags_fn_t
)ndr_push_package_PackagesBlob
);
1643 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1644 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1645 ldb_asprintf_errstring(ldb
,
1646 "setup_supplemental_field: "
1647 "failed to push package_PackagesBlob: %s",
1649 return LDB_ERR_OPERATIONS_ERROR
;
1651 pb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pb_blob
);
1653 return ldb_oom(ldb
);
1655 pp
->name
= "Packages";
1657 pp
->data
= pb_hexstr
;
1660 * setup 'supplementalCredentials' value
1663 scb
.sub
.num_packages
= num_packages
;
1664 scb
.sub
.packages
= packages
;
1666 ndr_err
= ndr_push_struct_blob(&io
->g
.supplemental
, io
->ac
,
1668 (ndr_push_flags_fn_t
)ndr_push_supplementalCredentialsBlob
);
1669 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1670 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1671 ldb_asprintf_errstring(ldb
,
1672 "setup_supplemental_field: "
1673 "failed to push supplementalCredentialsBlob: %s",
1675 return LDB_ERR_OPERATIONS_ERROR
;
1681 static int setup_last_set_field(struct setup_password_fields_io
*io
)
1683 const struct ldb_message
*msg
= NULL
;
1685 switch (io
->ac
->req
->operation
) {
1687 msg
= io
->ac
->req
->op
.add
.message
;
1690 msg
= io
->ac
->req
->op
.mod
.message
;
1693 return LDB_ERR_OPERATIONS_ERROR
;
1697 if (io
->ac
->pwd_last_set_bypass
) {
1698 struct ldb_message_element
*el
;
1701 return LDB_ERR_CONSTRAINT_VIOLATION
;
1704 el
= ldb_msg_find_element(msg
, "pwdLastSet");
1706 return LDB_ERR_CONSTRAINT_VIOLATION
;
1709 io
->g
.last_set
= samdb_result_nttime(msg
, "pwdLastSet", 0);
1714 unix_to_nt_time(&io
->g
.last_set
, time(NULL
));
1719 static int setup_given_passwords(struct setup_password_fields_io
*io
,
1720 struct setup_password_fields_given
*g
)
1722 struct ldb_context
*ldb
;
1725 ldb
= ldb_module_get_ctx(io
->ac
->module
);
1727 if (g
->cleartext_utf8
) {
1728 struct ldb_val
*cleartext_utf16_blob
;
1730 cleartext_utf16_blob
= talloc(io
->ac
, struct ldb_val
);
1731 if (!cleartext_utf16_blob
) {
1732 return ldb_oom(ldb
);
1734 if (!convert_string_talloc(io
->ac
,
1736 g
->cleartext_utf8
->data
,
1737 g
->cleartext_utf8
->length
,
1738 (void *)&cleartext_utf16_blob
->data
,
1739 &cleartext_utf16_blob
->length
)) {
1740 if (g
->cleartext_utf8
->length
!= 0) {
1741 talloc_free(cleartext_utf16_blob
);
1742 ldb_asprintf_errstring(ldb
,
1743 "setup_password_fields: "
1744 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1745 io
->u
.sAMAccountName
);
1746 return LDB_ERR_CONSTRAINT_VIOLATION
;
1748 /* passwords with length "0" are valid! */
1749 cleartext_utf16_blob
->data
= NULL
;
1750 cleartext_utf16_blob
->length
= 0;
1753 g
->cleartext_utf16
= cleartext_utf16_blob
;
1754 } else if (g
->cleartext_utf16
) {
1755 struct ldb_val
*cleartext_utf8_blob
;
1757 cleartext_utf8_blob
= talloc(io
->ac
, struct ldb_val
);
1758 if (!cleartext_utf8_blob
) {
1759 return ldb_oom(ldb
);
1761 if (!convert_string_talloc(io
->ac
,
1762 CH_UTF16MUNGED
, CH_UTF8
,
1763 g
->cleartext_utf16
->data
,
1764 g
->cleartext_utf16
->length
,
1765 (void *)&cleartext_utf8_blob
->data
,
1766 &cleartext_utf8_blob
->length
)) {
1767 if (g
->cleartext_utf16
->length
!= 0) {
1768 /* We must bail out here, the input wasn't even
1769 * a multiple of 2 bytes */
1770 talloc_free(cleartext_utf8_blob
);
1771 ldb_asprintf_errstring(ldb
,
1772 "setup_password_fields: "
1773 "failed to generate UTF8 password from cleartext UTF 16 one for user '%s' - the latter had odd length (length must be a multiple of 2)!",
1774 io
->u
.sAMAccountName
);
1775 return LDB_ERR_CONSTRAINT_VIOLATION
;
1777 /* passwords with length "0" are valid! */
1778 cleartext_utf8_blob
->data
= NULL
;
1779 cleartext_utf8_blob
->length
= 0;
1782 g
->cleartext_utf8
= cleartext_utf8_blob
;
1785 if (g
->cleartext_utf16
) {
1786 struct samr_Password
*nt_hash
;
1788 nt_hash
= talloc(io
->ac
, struct samr_Password
);
1790 return ldb_oom(ldb
);
1792 g
->nt_hash
= nt_hash
;
1794 /* compute the new nt hash */
1795 mdfour(nt_hash
->hash
,
1796 g
->cleartext_utf16
->data
,
1797 g
->cleartext_utf16
->length
);
1800 if (g
->cleartext_utf8
) {
1801 struct samr_Password
*lm_hash
;
1803 lm_hash
= talloc(io
->ac
, struct samr_Password
);
1805 return ldb_oom(ldb
);
1808 /* compute the new lm hash */
1809 ok
= E_deshash((char *)g
->cleartext_utf8
->data
, lm_hash
->hash
);
1811 g
->lm_hash
= lm_hash
;
1813 talloc_free(lm_hash
);
1820 static int setup_password_fields(struct setup_password_fields_io
*io
)
1822 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
1823 struct loadparm_context
*lp_ctx
=
1824 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
1825 struct loadparm_context
);
1828 /* transform the old password (for password changes) */
1829 ret
= setup_given_passwords(io
, &io
->og
);
1830 if (ret
!= LDB_SUCCESS
) {
1834 /* transform the new password */
1835 ret
= setup_given_passwords(io
, &io
->n
);
1836 if (ret
!= LDB_SUCCESS
) {
1840 if (io
->n
.cleartext_utf8
) {
1841 ret
= setup_kerberos_keys(io
);
1842 if (ret
!= LDB_SUCCESS
) {
1847 ret
= setup_nt_fields(io
);
1848 if (ret
!= LDB_SUCCESS
) {
1852 if (lpcfg_lanman_auth(lp_ctx
)) {
1853 ret
= setup_lm_fields(io
);
1854 if (ret
!= LDB_SUCCESS
) {
1858 io
->g
.lm_hash
= NULL
;
1859 io
->g
.lm_history_len
= 0;
1862 ret
= setup_supplemental_field(io
);
1863 if (ret
!= LDB_SUCCESS
) {
1867 ret
= setup_last_set_field(io
);
1868 if (ret
!= LDB_SUCCESS
) {
1875 static int make_error_and_update_badPwdCount(struct setup_password_fields_io
*io
)
1877 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
1878 struct ldb_message
*mod_msg
= NULL
;
1882 status
= dsdb_update_bad_pwd_count(io
->ac
, ldb
,
1883 io
->ac
->search_res
->message
,
1884 io
->ac
->dom_res
->message
,
1886 if (!NT_STATUS_IS_OK(status
)) {
1890 if (mod_msg
== NULL
) {
1895 * OK, horrible semantics ahead.
1897 * - We need to abort any existing transaction
1898 * - create a transaction arround the badPwdCount update
1899 * - re-open the transaction so the upper layer
1900 * doesn't know what happened.
1902 * This is needed because returning an error to the upper
1903 * layer will cancel the transaction and undo the badPwdCount
1908 * Checking errors here is a bit pointless.
1909 * What can we do if we can't end the transaction?
1911 ret
= ldb_next_del_trans(io
->ac
->module
);
1912 if (ret
!= LDB_SUCCESS
) {
1913 ldb_debug(ldb
, LDB_DEBUG_FATAL
,
1914 "Failed to abort transaction prior to update of badPwdCount of %s: %s",
1915 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
1916 ldb_errstring(ldb
));
1918 * just return the original error
1923 /* Likewise, what should we do if we can't open a new transaction? */
1924 ret
= ldb_next_start_trans(io
->ac
->module
);
1925 if (ret
!= LDB_SUCCESS
) {
1926 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
1927 "Failed to open transaction to update badPwdCount of %s: %s",
1928 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
1929 ldb_errstring(ldb
));
1931 * just return the original error
1936 ret
= dsdb_module_modify(io
->ac
->module
, mod_msg
,
1937 DSDB_FLAG_NEXT_MODULE
,
1939 if (ret
!= LDB_SUCCESS
) {
1940 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
1941 "Failed to update badPwdCount of %s: %s",
1942 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
1943 ldb_errstring(ldb
));
1945 * We can only ignore this...
1949 ret
= ldb_next_end_trans(io
->ac
->module
);
1950 if (ret
!= LDB_SUCCESS
) {
1951 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
1952 "Failed to close transaction to update badPwdCount of %s: %s",
1953 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
1954 ldb_errstring(ldb
));
1956 * We can only ignore this...
1960 ret
= ldb_next_start_trans(io
->ac
->module
);
1961 if (ret
!= LDB_SUCCESS
) {
1962 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
1963 "Failed to open transaction after update of badPwdCount of %s: %s",
1964 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
1965 ldb_errstring(ldb
));
1967 * We can only ignore this...
1972 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
1973 ldb_asprintf_errstring(ldb
,
1974 "%08X: %s - check_password_restrictions: "
1975 "The old password specified doesn't match!",
1976 W_ERROR_V(WERR_INVALID_PASSWORD
),
1981 static int check_password_restrictions(struct setup_password_fields_io
*io
)
1983 struct ldb_context
*ldb
;
1986 ldb
= ldb_module_get_ctx(io
->ac
->module
);
1988 /* First check the old password is correct, for password changes */
1989 if (!io
->ac
->pwd_reset
) {
1990 bool nt_hash_checked
= false;
1992 /* we need the old nt or lm hash given by the client */
1993 if (!io
->og
.nt_hash
&& !io
->og
.lm_hash
) {
1994 ldb_asprintf_errstring(ldb
,
1995 "check_password_restrictions: "
1996 "You need to provide the old password in order "
1998 return LDB_ERR_UNWILLING_TO_PERFORM
;
2001 /* The password modify through the NT hash is encouraged and
2002 has no problems at all */
2003 if (io
->og
.nt_hash
) {
2004 if (!io
->o
.nt_hash
|| memcmp(io
->og
.nt_hash
->hash
, io
->o
.nt_hash
->hash
, 16) != 0) {
2005 return make_error_and_update_badPwdCount(io
);
2008 nt_hash_checked
= true;
2011 /* But it is also possible to change a password by the LM hash
2012 * alone for compatibility reasons. This check is optional if
2013 * the NT hash was already checked - otherwise it's mandatory.
2014 * (as the SAMR operations request it). */
2015 if (io
->og
.lm_hash
) {
2016 if ((!io
->o
.lm_hash
&& !nt_hash_checked
)
2017 || (io
->o
.lm_hash
&& memcmp(io
->og
.lm_hash
->hash
, io
->o
.lm_hash
->hash
, 16) != 0)) {
2018 return make_error_and_update_badPwdCount(io
);
2023 if (io
->u
.restrictions
== 0) {
2024 /* FIXME: Is this right? */
2028 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2029 if ((io
->u
.pwdLastSet
- io
->ac
->status
->domain_data
.minPwdAge
> io
->g
.last_set
) &&
2032 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2033 ldb_asprintf_errstring(ldb
,
2034 "%08X: %s - check_password_restrictions: "
2035 "password is too young to change!",
2036 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2042 * Fundamental password checks done by the call
2043 * "samdb_check_password".
2044 * It is also in use by "dcesrv_samr_ValidatePassword".
2046 if (io
->n
.cleartext_utf8
!= NULL
) {
2047 enum samr_ValidationStatus vstat
;
2048 vstat
= samdb_check_password(io
->n
.cleartext_utf8
,
2049 io
->ac
->status
->domain_data
.pwdProperties
,
2050 io
->ac
->status
->domain_data
.minPwdLength
);
2052 case SAMR_VALIDATION_STATUS_SUCCESS
:
2053 /* perfect -> proceed! */
2056 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT
:
2057 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2058 ldb_asprintf_errstring(ldb
,
2059 "%08X: %s - check_password_restrictions: "
2060 "the password is too short. It should be equal or longer than %u characters!",
2061 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2063 io
->ac
->status
->domain_data
.minPwdLength
);
2064 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_PASSWORD_TOO_SHORT
;
2067 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH
:
2068 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2069 ldb_asprintf_errstring(ldb
,
2070 "%08X: %s - check_password_restrictions: "
2071 "the password does not meet the complexity criteria!",
2072 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2074 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_NOT_COMPLEX
;
2078 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2079 ldb_asprintf_errstring(ldb
,
2080 "%08X: %s - check_password_restrictions: "
2081 "the password doesn't fit by a certain reason!",
2082 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2088 if (io
->ac
->pwd_reset
) {
2092 if (io
->n
.nt_hash
) {
2095 /* checks the NT hash password history */
2096 for (i
= 0; i
< io
->o
.nt_history_len
; i
++) {
2097 ret
= memcmp(io
->n
.nt_hash
, io
->o
.nt_history
[i
].hash
, 16);
2099 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2100 ldb_asprintf_errstring(ldb
,
2101 "%08X: %s - check_password_restrictions: "
2102 "the password was already used (in history)!",
2103 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2105 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_PWD_IN_HISTORY
;
2111 if (io
->n
.lm_hash
) {
2114 /* checks the LM hash password history */
2115 for (i
= 0; i
< io
->o
.lm_history_len
; i
++) {
2116 ret
= memcmp(io
->n
.nt_hash
, io
->o
.lm_history
[i
].hash
, 16);
2118 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2119 ldb_asprintf_errstring(ldb
,
2120 "%08X: %s - check_password_restrictions: "
2121 "the password was already used (in history)!",
2122 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2124 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_PWD_IN_HISTORY
;
2130 /* are all password changes disallowed? */
2131 if (io
->ac
->status
->domain_data
.pwdProperties
& DOMAIN_REFUSE_PASSWORD_CHANGE
) {
2132 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2133 ldb_asprintf_errstring(ldb
,
2134 "%08X: %s - check_password_restrictions: "
2135 "password changes disabled!",
2136 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2141 /* can this user change the password? */
2142 if (io
->u
.userAccountControl
& UF_PASSWD_CANT_CHANGE
) {
2143 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2144 ldb_asprintf_errstring(ldb
,
2145 "%08X: %s - check_password_restrictions: "
2146 "password can't be changed on this account!",
2147 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2156 * This is intended for use by the "password_hash" module since there
2157 * password changes can be specified through one message element with the
2158 * new password (to set) and another one with the old password (to unset).
2160 * The first which sets a password (new value) can have flags
2161 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2162 * for entries). The latter (old value) has always specified
2163 * LDB_FLAG_MOD_DELETE.
2165 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2166 * matching message elements are malformed in respect to the set/change rules.
2167 * Otherwise it returns LDB_SUCCESS.
2169 static int msg_find_old_and_new_pwd_val(const struct ldb_message
*msg
,
2171 enum ldb_request_type operation
,
2172 const struct ldb_val
**new_val
,
2173 const struct ldb_val
**old_val
)
2184 for (i
= 0; i
< msg
->num_elements
; i
++) {
2185 if (ldb_attr_cmp(msg
->elements
[i
].name
, name
) != 0) {
2189 if ((operation
== LDB_MODIFY
) &&
2190 (LDB_FLAG_MOD_TYPE(msg
->elements
[i
].flags
) == LDB_FLAG_MOD_DELETE
)) {
2191 /* 0 values are allowed */
2192 if (msg
->elements
[i
].num_values
== 1) {
2193 *old_val
= &msg
->elements
[i
].values
[0];
2194 } else if (msg
->elements
[i
].num_values
> 1) {
2195 return LDB_ERR_CONSTRAINT_VIOLATION
;
2197 } else if ((operation
== LDB_MODIFY
) &&
2198 (LDB_FLAG_MOD_TYPE(msg
->elements
[i
].flags
) == LDB_FLAG_MOD_REPLACE
)) {
2199 if (msg
->elements
[i
].num_values
> 0) {
2200 *new_val
= &msg
->elements
[i
].values
[msg
->elements
[i
].num_values
- 1];
2202 return LDB_ERR_UNWILLING_TO_PERFORM
;
2205 /* Add operations and LDB_FLAG_MOD_ADD */
2206 if (msg
->elements
[i
].num_values
> 0) {
2207 *new_val
= &msg
->elements
[i
].values
[msg
->elements
[i
].num_values
- 1];
2209 return LDB_ERR_CONSTRAINT_VIOLATION
;
2217 static int setup_io(struct ph_context
*ac
,
2218 const struct ldb_message
*orig_msg
,
2219 const struct ldb_message
*searched_msg
,
2220 struct setup_password_fields_io
*io
)
2222 const struct ldb_val
*quoted_utf16
, *old_quoted_utf16
, *lm_hash
, *old_lm_hash
;
2223 struct ldb_context
*ldb
= ldb_module_get_ctx(ac
->module
);
2224 struct loadparm_context
*lp_ctx
=
2225 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
2226 struct loadparm_context
);
2231 /* Some operations below require kerberos contexts */
2233 if (smb_krb5_init_context(ac
,
2234 (struct loadparm_context
*)ldb_get_opaque(ldb
, "loadparm"),
2235 &io
->smb_krb5_context
) != 0) {
2236 return ldb_operr(ldb
);
2241 io
->u
.userAccountControl
= ldb_msg_find_attr_as_uint(searched_msg
,
2242 "userAccountControl", 0);
2243 io
->u
.pwdLastSet
= samdb_result_nttime(searched_msg
, "pwdLastSet", 0);
2244 io
->u
.sAMAccountName
= ldb_msg_find_attr_as_string(searched_msg
,
2245 "sAMAccountName", NULL
);
2246 io
->u
.user_principal_name
= ldb_msg_find_attr_as_string(searched_msg
,
2247 "userPrincipalName", NULL
);
2248 io
->u
.is_computer
= ldb_msg_check_string_attribute(searched_msg
, "objectClass", "computer");
2250 if (io
->u
.sAMAccountName
== NULL
) {
2251 ldb_asprintf_errstring(ldb
,
2252 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2253 ldb_dn_get_linearized(searched_msg
->dn
));
2255 return LDB_ERR_CONSTRAINT_VIOLATION
;
2258 /* Only non-trust accounts have restrictions (possibly this test is the
2259 * wrong way around, but we like to be restrictive if possible */
2260 io
->u
.restrictions
= !(io
->u
.userAccountControl
2261 & (UF_INTERDOMAIN_TRUST_ACCOUNT
| UF_WORKSTATION_TRUST_ACCOUNT
2262 | UF_SERVER_TRUST_ACCOUNT
));
2264 if (ac
->userPassword
) {
2265 ret
= msg_find_old_and_new_pwd_val(orig_msg
, "userPassword",
2267 &io
->n
.cleartext_utf8
,
2268 &io
->og
.cleartext_utf8
);
2269 if (ret
!= LDB_SUCCESS
) {
2270 ldb_asprintf_errstring(ldb
,
2272 "it's only allowed to set the old password once!");
2277 if (io
->n
.cleartext_utf8
!= NULL
) {
2278 struct ldb_val
*cleartext_utf8_blob
;
2281 cleartext_utf8_blob
= talloc(io
->ac
, struct ldb_val
);
2282 if (!cleartext_utf8_blob
) {
2283 return ldb_oom(ldb
);
2286 *cleartext_utf8_blob
= *io
->n
.cleartext_utf8
;
2288 /* make sure we have a null terminated string */
2289 p
= talloc_strndup(cleartext_utf8_blob
,
2290 (const char *)io
->n
.cleartext_utf8
->data
,
2291 io
->n
.cleartext_utf8
->length
);
2292 if ((p
== NULL
) && (io
->n
.cleartext_utf8
->length
> 0)) {
2293 return ldb_oom(ldb
);
2295 cleartext_utf8_blob
->data
= (uint8_t *)p
;
2297 io
->n
.cleartext_utf8
= cleartext_utf8_blob
;
2300 ret
= msg_find_old_and_new_pwd_val(orig_msg
, "clearTextPassword",
2302 &io
->n
.cleartext_utf16
,
2303 &io
->og
.cleartext_utf16
);
2304 if (ret
!= LDB_SUCCESS
) {
2305 ldb_asprintf_errstring(ldb
,
2307 "it's only allowed to set the old password once!");
2311 /* this rather strange looking piece of code is there to
2312 handle a ldap client setting a password remotely using the
2313 unicodePwd ldap field. The syntax is that the password is
2314 in UTF-16LE, with a " at either end. Unfortunately the
2315 unicodePwd field is also used to store the nt hashes
2316 internally in Samba, and is used in the nt hash format on
2317 the wire in DRS replication, so we have a single name for
2318 two distinct values. The code below leaves us with a small
2319 chance (less than 1 in 2^32) of a mixup, if someone manages
2320 to create a MD4 hash which starts and ends in 0x22 0x00, as
2321 that would then be treated as a UTF16 password rather than
2324 ret
= msg_find_old_and_new_pwd_val(orig_msg
, "unicodePwd",
2328 if (ret
!= LDB_SUCCESS
) {
2329 ldb_asprintf_errstring(ldb
,
2331 "it's only allowed to set the old password once!");
2335 /* Checks and converts the actual "unicodePwd" attribute */
2336 if (!ac
->hash_values
&&
2338 quoted_utf16
->length
>= 4 &&
2339 quoted_utf16
->data
[0] == '"' &&
2340 quoted_utf16
->data
[1] == 0 &&
2341 quoted_utf16
->data
[quoted_utf16
->length
-2] == '"' &&
2342 quoted_utf16
->data
[quoted_utf16
->length
-1] == 0) {
2343 struct ldb_val
*quoted_utf16_2
;
2345 if (io
->n
.cleartext_utf16
) {
2346 /* refuse the change if someone wants to change with
2347 with both UTF16 possibilities at the same time... */
2348 ldb_asprintf_errstring(ldb
,
2350 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2351 return LDB_ERR_UNWILLING_TO_PERFORM
;
2355 * adapt the quoted UTF16 string to be a real
2358 quoted_utf16_2
= talloc(io
->ac
, struct ldb_val
);
2359 if (quoted_utf16_2
== NULL
) {
2360 return ldb_oom(ldb
);
2363 quoted_utf16_2
->data
= quoted_utf16
->data
+ 2;
2364 quoted_utf16_2
->length
= quoted_utf16
->length
-4;
2365 io
->n
.cleartext_utf16
= quoted_utf16_2
;
2366 io
->n
.nt_hash
= NULL
;
2368 } else if (quoted_utf16
) {
2369 /* We have only the hash available -> so no plaintext here */
2370 if (!ac
->hash_values
) {
2371 /* refuse the change if someone wants to change
2372 the hash without control specified... */
2373 ldb_asprintf_errstring(ldb
,
2375 "it's not allowed to set the NT hash password directly'");
2376 /* this looks odd but this is what Windows does:
2377 returns "UNWILLING_TO_PERFORM" on wrong
2378 password sets and "CONSTRAINT_VIOLATION" on
2379 wrong password changes. */
2380 if (old_quoted_utf16
== NULL
) {
2381 return LDB_ERR_UNWILLING_TO_PERFORM
;
2384 return LDB_ERR_CONSTRAINT_VIOLATION
;
2387 io
->n
.nt_hash
= talloc(io
->ac
, struct samr_Password
);
2388 memcpy(io
->n
.nt_hash
->hash
, quoted_utf16
->data
,
2389 MIN(quoted_utf16
->length
, sizeof(io
->n
.nt_hash
->hash
)));
2392 /* Checks and converts the previous "unicodePwd" attribute */
2393 if (!ac
->hash_values
&&
2395 old_quoted_utf16
->length
>= 4 &&
2396 old_quoted_utf16
->data
[0] == '"' &&
2397 old_quoted_utf16
->data
[1] == 0 &&
2398 old_quoted_utf16
->data
[old_quoted_utf16
->length
-2] == '"' &&
2399 old_quoted_utf16
->data
[old_quoted_utf16
->length
-1] == 0) {
2400 struct ldb_val
*old_quoted_utf16_2
;
2402 if (io
->og
.cleartext_utf16
) {
2403 /* refuse the change if someone wants to change with
2404 both UTF16 possibilities at the same time... */
2405 ldb_asprintf_errstring(ldb
,
2407 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2408 return LDB_ERR_UNWILLING_TO_PERFORM
;
2412 * adapt the quoted UTF16 string to be a real
2415 old_quoted_utf16_2
= talloc(io
->ac
, struct ldb_val
);
2416 if (old_quoted_utf16_2
== NULL
) {
2417 return ldb_oom(ldb
);
2420 old_quoted_utf16_2
->data
= old_quoted_utf16
->data
+ 2;
2421 old_quoted_utf16_2
->length
= old_quoted_utf16
->length
-4;
2423 io
->og
.cleartext_utf16
= old_quoted_utf16_2
;
2424 io
->og
.nt_hash
= NULL
;
2425 } else if (old_quoted_utf16
) {
2426 /* We have only the hash available -> so no plaintext here */
2427 if (!ac
->hash_values
) {
2428 /* refuse the change if someone wants to change
2429 the hash without control specified... */
2430 ldb_asprintf_errstring(ldb
,
2432 "it's not allowed to set the NT hash password directly'");
2433 return LDB_ERR_UNWILLING_TO_PERFORM
;
2436 io
->og
.nt_hash
= talloc(io
->ac
, struct samr_Password
);
2437 memcpy(io
->og
.nt_hash
->hash
, old_quoted_utf16
->data
,
2438 MIN(old_quoted_utf16
->length
, sizeof(io
->og
.nt_hash
->hash
)));
2441 /* Handles the "dBCSPwd" attribute (LM hash) */
2442 io
->n
.lm_hash
= NULL
; io
->og
.lm_hash
= NULL
;
2443 ret
= msg_find_old_and_new_pwd_val(orig_msg
, "dBCSPwd",
2445 &lm_hash
, &old_lm_hash
);
2446 if (ret
!= LDB_SUCCESS
) {
2447 ldb_asprintf_errstring(ldb
,
2449 "it's only allowed to set the old password once!");
2453 if (((lm_hash
!= NULL
) || (old_lm_hash
!= NULL
)) && (!ac
->hash_values
)) {
2454 /* refuse the change if someone wants to change the hash
2455 without control specified... */
2456 ldb_asprintf_errstring(ldb
,
2458 "it's not allowed to set the LM hash password directly'");
2459 return LDB_ERR_UNWILLING_TO_PERFORM
;
2462 if (lpcfg_lanman_auth(lp_ctx
) && (lm_hash
!= NULL
)) {
2463 io
->n
.lm_hash
= talloc(io
->ac
, struct samr_Password
);
2464 memcpy(io
->n
.lm_hash
->hash
, lm_hash
->data
, MIN(lm_hash
->length
,
2465 sizeof(io
->n
.lm_hash
->hash
)));
2467 if (lpcfg_lanman_auth(lp_ctx
) && (old_lm_hash
!= NULL
)) {
2468 io
->og
.lm_hash
= talloc(io
->ac
, struct samr_Password
);
2469 memcpy(io
->og
.lm_hash
->hash
, old_lm_hash
->data
, MIN(old_lm_hash
->length
,
2470 sizeof(io
->og
.lm_hash
->hash
)));
2474 * Handles the password change control if it's specified. It has the
2475 * precedance and overrides already specified old password values of
2476 * change requests (but that shouldn't happen since the control is
2477 * fully internal and only used in conjunction with replace requests!).
2479 if (ac
->change
!= NULL
) {
2480 io
->og
.nt_hash
= NULL
;
2481 if (ac
->change
->old_nt_pwd_hash
!= NULL
) {
2482 io
->og
.nt_hash
= talloc_memdup(io
->ac
,
2483 ac
->change
->old_nt_pwd_hash
,
2484 sizeof(struct samr_Password
));
2486 io
->og
.lm_hash
= NULL
;
2487 if (lpcfg_lanman_auth(lp_ctx
) && (ac
->change
->old_lm_pwd_hash
!= NULL
)) {
2488 io
->og
.lm_hash
= talloc_memdup(io
->ac
,
2489 ac
->change
->old_lm_pwd_hash
,
2490 sizeof(struct samr_Password
));
2494 /* refuse the change if someone wants to change the clear-
2495 text and supply his own hashes at the same time... */
2496 if ((io
->n
.cleartext_utf8
|| io
->n
.cleartext_utf16
)
2497 && (io
->n
.nt_hash
|| io
->n
.lm_hash
)) {
2498 ldb_asprintf_errstring(ldb
,
2500 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2501 return LDB_ERR_UNWILLING_TO_PERFORM
;
2504 /* refuse the change if someone wants to change the password
2505 using both plaintext methods (UTF8 and UTF16) at the same time... */
2506 if (io
->n
.cleartext_utf8
&& io
->n
.cleartext_utf16
) {
2507 ldb_asprintf_errstring(ldb
,
2509 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2510 return LDB_ERR_UNWILLING_TO_PERFORM
;
2513 /* refuse the change if someone tries to set/change the password by
2514 * the lanman hash alone and we've deactivated that mechanism. This
2515 * would end in an account without any password! */
2516 if ((!io
->n
.cleartext_utf8
) && (!io
->n
.cleartext_utf16
)
2517 && (!io
->n
.nt_hash
) && (!io
->n
.lm_hash
)) {
2518 ldb_asprintf_errstring(ldb
,
2520 "It' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2521 /* on "userPassword" and "clearTextPassword" we've to return
2522 * something different, since these are virtual attributes */
2523 if ((ldb_msg_find_element(orig_msg
, "userPassword") != NULL
) ||
2524 (ldb_msg_find_element(orig_msg
, "clearTextPassword") != NULL
)) {
2525 return LDB_ERR_CONSTRAINT_VIOLATION
;
2527 return LDB_ERR_UNWILLING_TO_PERFORM
;
2530 /* refuse the change if someone wants to compare against a plaintext
2531 or hash at the same time for a "password modify" operation... */
2532 if ((io
->og
.cleartext_utf8
|| io
->og
.cleartext_utf16
)
2533 && (io
->og
.nt_hash
|| io
->og
.lm_hash
)) {
2534 ldb_asprintf_errstring(ldb
,
2536 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2537 return LDB_ERR_UNWILLING_TO_PERFORM
;
2540 /* refuse the change if someone wants to compare against both
2541 * plaintexts at the same time for a "password modify" operation... */
2542 if (io
->og
.cleartext_utf8
&& io
->og
.cleartext_utf16
) {
2543 ldb_asprintf_errstring(ldb
,
2545 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2546 return LDB_ERR_UNWILLING_TO_PERFORM
;
2549 /* Decides if we have a password modify or password reset operation */
2550 if (ac
->req
->operation
== LDB_ADD
) {
2551 /* On "add" we have only "password reset" */
2552 ac
->pwd_reset
= true;
2553 } else if (ac
->req
->operation
== LDB_MODIFY
) {
2554 if (io
->og
.cleartext_utf8
|| io
->og
.cleartext_utf16
2555 || io
->og
.nt_hash
|| io
->og
.lm_hash
) {
2556 /* If we have an old password specified then for sure it
2557 * is a user "password change" */
2558 ac
->pwd_reset
= false;
2560 /* Otherwise we have also here a "password reset" */
2561 ac
->pwd_reset
= true;
2564 /* this shouldn't happen */
2565 return ldb_operr(ldb
);
2571 static struct ph_context
*ph_init_context(struct ldb_module
*module
,
2572 struct ldb_request
*req
,
2575 struct ldb_context
*ldb
;
2576 struct ph_context
*ac
;
2578 ldb
= ldb_module_get_ctx(module
);
2580 ac
= talloc_zero(req
, struct ph_context
);
2582 ldb_set_errstring(ldb
, "Out of Memory");
2586 ac
->module
= module
;
2588 ac
->userPassword
= userPassword
;
2593 static void ph_apply_controls(struct ph_context
*ac
)
2595 struct ldb_control
*ctrl
;
2597 ac
->change_status
= false;
2598 ctrl
= ldb_request_get_control(ac
->req
,
2599 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID
);
2601 ac
->change_status
= true;
2603 /* Mark the "change status" control as uncritical (done) */
2604 ctrl
->critical
= false;
2607 ac
->hash_values
= false;
2608 ctrl
= ldb_request_get_control(ac
->req
,
2609 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID
);
2611 ac
->hash_values
= true;
2613 /* Mark the "hash values" control as uncritical (done) */
2614 ctrl
->critical
= false;
2617 ctrl
= ldb_request_get_control(ac
->req
,
2618 DSDB_CONTROL_PASSWORD_CHANGE_OID
);
2620 ac
->change
= (struct dsdb_control_password_change
*) ctrl
->data
;
2622 /* Mark the "change" control as uncritical (done) */
2623 ctrl
->critical
= false;
2626 ac
->pwd_last_set_bypass
= false;
2627 ctrl
= ldb_request_get_control(ac
->req
,
2628 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID
);
2630 ac
->pwd_last_set_bypass
= true;
2632 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
2633 ctrl
->critical
= false;
2637 static int ph_op_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
2639 struct ph_context
*ac
;
2641 ac
= talloc_get_type(req
->context
, struct ph_context
);
2644 return ldb_module_done(ac
->req
, NULL
, NULL
,
2645 LDB_ERR_OPERATIONS_ERROR
);
2648 if (ares
->type
== LDB_REPLY_REFERRAL
) {
2649 return ldb_module_send_referral(ac
->req
, ares
->referral
);
2652 if ((ares
->error
!= LDB_ERR_OPERATIONS_ERROR
) && (ac
->change_status
)) {
2653 /* On success and trivial errors a status control is being
2654 * added (used for example by the "samdb_set_password" call) */
2655 ldb_reply_add_control(ares
,
2656 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID
,
2661 if (ares
->error
!= LDB_SUCCESS
) {
2662 return ldb_module_done(ac
->req
, ares
->controls
,
2663 ares
->response
, ares
->error
);
2666 if (ares
->type
!= LDB_REPLY_DONE
) {
2668 return ldb_module_done(ac
->req
, NULL
, NULL
,
2669 LDB_ERR_OPERATIONS_ERROR
);
2672 return ldb_module_done(ac
->req
, ares
->controls
,
2673 ares
->response
, ares
->error
);
2676 static int password_hash_add_do_add(struct ph_context
*ac
);
2677 static int ph_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
2678 static int password_hash_mod_search_self(struct ph_context
*ac
);
2679 static int ph_mod_search_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
2680 static int password_hash_mod_do_mod(struct ph_context
*ac
);
2682 static int get_domain_data_callback(struct ldb_request
*req
,
2683 struct ldb_reply
*ares
)
2685 struct ldb_context
*ldb
;
2686 struct ph_context
*ac
;
2687 struct loadparm_context
*lp_ctx
;
2688 int ret
= LDB_SUCCESS
;
2690 ac
= talloc_get_type(req
->context
, struct ph_context
);
2691 ldb
= ldb_module_get_ctx(ac
->module
);
2694 ret
= LDB_ERR_OPERATIONS_ERROR
;
2697 if (ares
->error
!= LDB_SUCCESS
) {
2698 return ldb_module_done(ac
->req
, ares
->controls
,
2699 ares
->response
, ares
->error
);
2702 switch (ares
->type
) {
2703 case LDB_REPLY_ENTRY
:
2704 if (ac
->status
!= NULL
) {
2707 ldb_set_errstring(ldb
, "Too many results");
2708 ret
= LDB_ERR_OPERATIONS_ERROR
;
2712 /* Setup the "status" structure (used as control later) */
2713 ac
->status
= talloc_zero(ac
->req
,
2714 struct dsdb_control_password_change_status
);
2715 if (ac
->status
== NULL
) {
2719 ret
= LDB_ERR_OPERATIONS_ERROR
;
2723 /* Setup the "domain data" structure */
2724 ac
->status
->domain_data
.pwdProperties
=
2725 ldb_msg_find_attr_as_uint(ares
->message
, "pwdProperties", -1);
2726 ac
->status
->domain_data
.pwdHistoryLength
=
2727 ldb_msg_find_attr_as_uint(ares
->message
, "pwdHistoryLength", -1);
2728 ac
->status
->domain_data
.maxPwdAge
=
2729 ldb_msg_find_attr_as_int64(ares
->message
, "maxPwdAge", -1);
2730 ac
->status
->domain_data
.minPwdAge
=
2731 ldb_msg_find_attr_as_int64(ares
->message
, "minPwdAge", -1);
2732 ac
->status
->domain_data
.minPwdLength
=
2733 ldb_msg_find_attr_as_uint(ares
->message
, "minPwdLength", -1);
2734 ac
->status
->domain_data
.store_cleartext
=
2735 ac
->status
->domain_data
.pwdProperties
& DOMAIN_PASSWORD_STORE_CLEARTEXT
;
2737 /* For a domain DN, this puts things in dotted notation */
2738 /* For builtin domains, this will give details for the host,
2739 * but that doesn't really matter, as it's just used for salt
2740 * and kerberos principals, which don't exist here */
2742 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
2743 struct loadparm_context
);
2745 ac
->status
->domain_data
.dns_domain
= lpcfg_dnsdomain(lp_ctx
);
2746 ac
->status
->domain_data
.realm
= lpcfg_realm(lp_ctx
);
2747 ac
->status
->domain_data
.netbios_domain
= lpcfg_sam_name(lp_ctx
);
2749 ac
->status
->reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
2751 if (ac
->dom_res
!= NULL
) {
2754 ldb_set_errstring(ldb
, "Too many results");
2755 ret
= LDB_ERR_OPERATIONS_ERROR
;
2759 ac
->dom_res
= talloc_steal(ac
, ares
);
2763 case LDB_REPLY_REFERRAL
:
2769 case LDB_REPLY_DONE
:
2771 /* call the next step */
2772 switch (ac
->req
->operation
) {
2774 ret
= password_hash_add_do_add(ac
);
2778 ret
= password_hash_mod_do_mod(ac
);
2782 ret
= LDB_ERR_OPERATIONS_ERROR
;
2789 if (ret
!= LDB_SUCCESS
) {
2790 struct ldb_reply
*new_ares
;
2792 new_ares
= talloc_zero(ac
->req
, struct ldb_reply
);
2793 if (new_ares
== NULL
) {
2795 return ldb_module_done(ac
->req
, NULL
, NULL
,
2796 LDB_ERR_OPERATIONS_ERROR
);
2799 new_ares
->error
= ret
;
2800 if ((ret
!= LDB_ERR_OPERATIONS_ERROR
) && (ac
->change_status
)) {
2801 /* On success and trivial errors a status control is being
2802 * added (used for example by the "samdb_set_password" call) */
2803 ldb_reply_add_control(new_ares
,
2804 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID
,
2809 return ldb_module_done(ac
->req
, new_ares
->controls
,
2810 new_ares
->response
, new_ares
->error
);
2816 static int build_domain_data_request(struct ph_context
*ac
)
2818 /* attrs[] is returned from this function in
2819 ac->dom_req->op.search.attrs, so it must be static, as
2820 otherwise the compiler can put it on the stack */
2821 struct ldb_context
*ldb
;
2822 static const char * const attrs
[] = { "pwdProperties",
2828 "lockOutObservationWindow",
2832 ldb
= ldb_module_get_ctx(ac
->module
);
2834 ret
= ldb_build_search_req(&ac
->dom_req
, ldb
, ac
,
2835 ldb_get_default_basedn(ldb
),
2839 ac
, get_domain_data_callback
,
2841 LDB_REQ_SET_LOCATION(ac
->dom_req
);
2845 static int password_hash_add(struct ldb_module
*module
, struct ldb_request
*req
)
2847 struct ldb_context
*ldb
;
2848 struct ph_context
*ac
;
2849 struct ldb_message_element
*userPasswordAttr
, *clearTextPasswordAttr
,
2852 struct ldb_control
*bypass
= NULL
;
2853 bool userPassword
= dsdb_user_password_support(module
, req
, req
);
2855 ldb
= ldb_module_get_ctx(module
);
2857 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_add\n");
2859 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) { /* do not manipulate our control entries */
2860 return ldb_next_request(module
, req
);
2863 bypass
= ldb_request_get_control(req
,
2864 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID
);
2865 if (bypass
!= NULL
) {
2866 /* Mark the "bypass" control as uncritical (done) */
2867 bypass
->critical
= false;
2868 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_add (bypassing)\n");
2869 return password_hash_bypass(module
, req
);
2872 /* nobody must touch password histories and 'supplementalCredentials' */
2873 if (ldb_msg_find_element(req
->op
.add
.message
, "ntPwdHistory")) {
2874 return LDB_ERR_UNWILLING_TO_PERFORM
;
2876 if (ldb_msg_find_element(req
->op
.add
.message
, "lmPwdHistory")) {
2877 return LDB_ERR_UNWILLING_TO_PERFORM
;
2879 if (ldb_msg_find_element(req
->op
.add
.message
, "supplementalCredentials")) {
2880 return LDB_ERR_UNWILLING_TO_PERFORM
;
2883 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
2884 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. */
2886 userPasswordAttr
= NULL
;
2888 userPasswordAttr
= ldb_msg_find_element(req
->op
.add
.message
,
2890 /* MS-ADTS 3.1.1.3.1.5.2 */
2891 if ((userPasswordAttr
!= NULL
) &&
2892 (dsdb_functional_level(ldb
) < DS_DOMAIN_FUNCTION_2003
)) {
2893 return LDB_ERR_CONSTRAINT_VIOLATION
;
2896 clearTextPasswordAttr
= ldb_msg_find_element(req
->op
.add
.message
, "clearTextPassword");
2897 ntAttr
= ldb_msg_find_element(req
->op
.add
.message
, "unicodePwd");
2898 lmAttr
= ldb_msg_find_element(req
->op
.add
.message
, "dBCSPwd");
2900 if ((!userPasswordAttr
) && (!clearTextPasswordAttr
) && (!ntAttr
) && (!lmAttr
)) {
2901 return ldb_next_request(module
, req
);
2904 /* Make sure we are performing the password set action on a (for us)
2905 * valid object. Those are instances of either "user" and/or
2906 * "inetOrgPerson". Otherwise continue with the submodules. */
2907 if ((!ldb_msg_check_string_attribute(req
->op
.add
.message
, "objectClass", "user"))
2908 && (!ldb_msg_check_string_attribute(req
->op
.add
.message
, "objectClass", "inetOrgPerson"))) {
2910 if (ldb_msg_find_element(req
->op
.add
.message
, "clearTextPassword") != NULL
) {
2911 ldb_set_errstring(ldb
,
2912 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
2913 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
2916 return ldb_next_request(module
, req
);
2919 ac
= ph_init_context(module
, req
, userPassword
);
2921 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
2922 return ldb_operr(ldb
);
2924 ph_apply_controls(ac
);
2926 /* get user domain data */
2927 ret
= build_domain_data_request(ac
);
2928 if (ret
!= LDB_SUCCESS
) {
2932 return ldb_next_request(module
, ac
->dom_req
);
2935 static int password_hash_add_do_add(struct ph_context
*ac
)
2937 struct ldb_context
*ldb
;
2938 struct ldb_request
*down_req
;
2939 struct ldb_message
*msg
;
2940 struct setup_password_fields_io io
;
2943 /* Prepare the internal data structure containing the passwords */
2944 ret
= setup_io(ac
, ac
->req
->op
.add
.message
, ac
->req
->op
.add
.message
, &io
);
2945 if (ret
!= LDB_SUCCESS
) {
2949 ldb
= ldb_module_get_ctx(ac
->module
);
2951 msg
= ldb_msg_copy_shallow(ac
, ac
->req
->op
.add
.message
);
2953 return ldb_operr(ldb
);
2956 /* remove attributes that we just read into 'io' */
2957 if (ac
->userPassword
) {
2958 ldb_msg_remove_attr(msg
, "userPassword");
2960 ldb_msg_remove_attr(msg
, "clearTextPassword");
2961 ldb_msg_remove_attr(msg
, "unicodePwd");
2962 ldb_msg_remove_attr(msg
, "dBCSPwd");
2963 ldb_msg_remove_attr(msg
, "pwdLastSet");
2965 ret
= setup_password_fields(&io
);
2966 if (ret
!= LDB_SUCCESS
) {
2970 ret
= check_password_restrictions(&io
);
2971 if (ret
!= LDB_SUCCESS
) {
2976 ret
= samdb_msg_add_hash(ldb
, ac
, msg
,
2977 "unicodePwd", io
.g
.nt_hash
);
2978 if (ret
!= LDB_SUCCESS
) {
2983 ret
= samdb_msg_add_hash(ldb
, ac
, msg
,
2984 "dBCSPwd", io
.g
.lm_hash
);
2985 if (ret
!= LDB_SUCCESS
) {
2989 if (io
.g
.nt_history_len
> 0) {
2990 ret
= samdb_msg_add_hashes(ldb
, ac
, msg
,
2993 io
.g
.nt_history_len
);
2994 if (ret
!= LDB_SUCCESS
) {
2998 if (io
.g
.lm_history_len
> 0) {
2999 ret
= samdb_msg_add_hashes(ldb
, ac
, msg
,
3002 io
.g
.lm_history_len
);
3003 if (ret
!= LDB_SUCCESS
) {
3007 if (io
.g
.supplemental
.length
> 0) {
3008 ret
= ldb_msg_add_value(msg
, "supplementalCredentials",
3009 &io
.g
.supplemental
, NULL
);
3010 if (ret
!= LDB_SUCCESS
) {
3014 ret
= samdb_msg_add_uint64(ldb
, ac
, msg
,
3017 if (ret
!= LDB_SUCCESS
) {
3021 ret
= ldb_build_add_req(&down_req
, ldb
, ac
,
3026 LDB_REQ_SET_LOCATION(down_req
);
3027 if (ret
!= LDB_SUCCESS
) {
3031 return ldb_next_request(ac
->module
, down_req
);
3034 static int password_hash_modify(struct ldb_module
*module
, struct ldb_request
*req
)
3036 struct ldb_context
*ldb
;
3037 struct ph_context
*ac
;
3038 const char *passwordAttrs
[] = { "userPassword", "clearTextPassword",
3039 "unicodePwd", "dBCSPwd", NULL
}, **l
;
3040 unsigned int attr_cnt
, del_attr_cnt
, add_attr_cnt
, rep_attr_cnt
;
3041 struct ldb_message_element
*passwordAttr
;
3042 struct ldb_message
*msg
;
3043 struct ldb_request
*down_req
;
3045 struct ldb_control
*bypass
= NULL
;
3046 bool userPassword
= dsdb_user_password_support(module
, req
, req
);
3048 ldb
= ldb_module_get_ctx(module
);
3050 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_modify\n");
3052 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) { /* do not manipulate our control entries */
3053 return ldb_next_request(module
, req
);
3056 bypass
= ldb_request_get_control(req
,
3057 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID
);
3058 if (bypass
!= NULL
) {
3059 /* Mark the "bypass" control as uncritical (done) */
3060 bypass
->critical
= false;
3061 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_modify (bypassing)\n");
3062 return password_hash_bypass(module
, req
);
3065 /* nobody must touch password histories and 'supplementalCredentials' */
3066 if (ldb_msg_find_element(req
->op
.mod
.message
, "ntPwdHistory")) {
3067 return LDB_ERR_UNWILLING_TO_PERFORM
;
3069 if (ldb_msg_find_element(req
->op
.mod
.message
, "lmPwdHistory")) {
3070 return LDB_ERR_UNWILLING_TO_PERFORM
;
3072 if (ldb_msg_find_element(req
->op
.mod
.message
, "supplementalCredentials")) {
3073 return LDB_ERR_UNWILLING_TO_PERFORM
;
3076 /* If no part of this touches the 'userPassword' OR 'clearTextPassword'
3077 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
3078 * For password changes/set there should be a 'delete' or a 'modify'
3079 * on these attributes. */
3081 for (l
= passwordAttrs
; *l
!= NULL
; l
++) {
3082 if ((!userPassword
) && (ldb_attr_cmp(*l
, "userPassword") == 0)) {
3086 if (ldb_msg_find_element(req
->op
.mod
.message
, *l
) != NULL
) {
3087 /* MS-ADTS 3.1.1.3.1.5.2 */
3088 if ((ldb_attr_cmp(*l
, "userPassword") == 0) &&
3089 (dsdb_functional_level(ldb
) < DS_DOMAIN_FUNCTION_2003
)) {
3090 return LDB_ERR_CONSTRAINT_VIOLATION
;
3096 if (attr_cnt
== 0) {
3097 return ldb_next_request(module
, req
);
3100 ac
= ph_init_context(module
, req
, userPassword
);
3102 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
3103 return ldb_operr(ldb
);
3105 ph_apply_controls(ac
);
3107 /* use a new message structure so that we can modify it */
3108 msg
= ldb_msg_copy_shallow(ac
, req
->op
.mod
.message
);
3110 return ldb_oom(ldb
);
3113 /* - check for single-valued password attributes
3114 * (if not return "CONSTRAINT_VIOLATION")
3115 * - check that for a password change operation one add and one delete
3117 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3118 * - check that a password change and a password set operation cannot
3120 * (if not return "UNWILLING_TO_PERFORM")
3121 * - remove all password attributes modifications from the first change
3122 * operation (anything without the passwords) - we will make the real
3123 * modification later */
3127 for (l
= passwordAttrs
; *l
!= NULL
; l
++) {
3128 if ((!ac
->userPassword
) &&
3129 (ldb_attr_cmp(*l
, "userPassword") == 0)) {
3133 while ((passwordAttr
= ldb_msg_find_element(msg
, *l
)) != NULL
) {
3134 if (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_DELETE
) {
3137 if (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_ADD
) {
3140 if (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_REPLACE
) {
3143 if ((passwordAttr
->num_values
!= 1) &&
3144 (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_ADD
)) {
3146 ldb_asprintf_errstring(ldb
,
3147 "'%s' attribute must have exactly one value on add operations!",
3149 return LDB_ERR_CONSTRAINT_VIOLATION
;
3151 if ((passwordAttr
->num_values
> 1) &&
3152 (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_DELETE
)) {
3154 ldb_asprintf_errstring(ldb
,
3155 "'%s' attribute must have zero or one value(s) on delete operations!",
3157 return LDB_ERR_CONSTRAINT_VIOLATION
;
3159 ldb_msg_remove_element(msg
, passwordAttr
);
3162 if ((del_attr_cnt
== 0) && (add_attr_cnt
> 0)) {
3164 ldb_set_errstring(ldb
,
3165 "Only the add action for a password change specified!");
3166 return LDB_ERR_UNWILLING_TO_PERFORM
;
3168 if ((del_attr_cnt
> 1) || (add_attr_cnt
> 1)) {
3170 ldb_set_errstring(ldb
,
3171 "Only one delete and one add action for a password change allowed!");
3172 return LDB_ERR_UNWILLING_TO_PERFORM
;
3174 if ((rep_attr_cnt
> 0) && ((del_attr_cnt
> 0) || (add_attr_cnt
> 0))) {
3176 ldb_set_errstring(ldb
,
3177 "Either a password change or a password set operation is allowed!");
3178 return LDB_ERR_UNWILLING_TO_PERFORM
;
3181 /* if there was nothing else to be modified skip to next step */
3182 if (msg
->num_elements
== 0) {
3183 return password_hash_mod_search_self(ac
);
3186 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3189 ac
, ph_modify_callback
,
3191 LDB_REQ_SET_LOCATION(down_req
);
3192 if (ret
!= LDB_SUCCESS
) {
3196 return ldb_next_request(module
, down_req
);
3199 static int ph_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3201 struct ph_context
*ac
;
3203 ac
= talloc_get_type(req
->context
, struct ph_context
);
3206 return ldb_module_done(ac
->req
, NULL
, NULL
,
3207 LDB_ERR_OPERATIONS_ERROR
);
3210 if (ares
->type
== LDB_REPLY_REFERRAL
) {
3211 return ldb_module_send_referral(ac
->req
, ares
->referral
);
3214 if (ares
->error
!= LDB_SUCCESS
) {
3215 return ldb_module_done(ac
->req
, ares
->controls
,
3216 ares
->response
, ares
->error
);
3219 if (ares
->type
!= LDB_REPLY_DONE
) {
3221 return ldb_module_done(ac
->req
, NULL
, NULL
,
3222 LDB_ERR_OPERATIONS_ERROR
);
3227 return password_hash_mod_search_self(ac
);
3230 static int ph_mod_search_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3232 struct ldb_context
*ldb
;
3233 struct ph_context
*ac
;
3234 int ret
= LDB_SUCCESS
;
3236 ac
= talloc_get_type(req
->context
, struct ph_context
);
3237 ldb
= ldb_module_get_ctx(ac
->module
);
3240 ret
= LDB_ERR_OPERATIONS_ERROR
;
3243 if (ares
->error
!= LDB_SUCCESS
) {
3244 return ldb_module_done(ac
->req
, ares
->controls
,
3245 ares
->response
, ares
->error
);
3248 /* we are interested only in the single reply (base search) */
3249 switch (ares
->type
) {
3250 case LDB_REPLY_ENTRY
:
3251 /* Make sure we are performing the password change action on a
3252 * (for us) valid object. Those are instances of either "user"
3253 * and/or "inetOrgPerson". Otherwise continue with the
3255 if ((!ldb_msg_check_string_attribute(ares
->message
, "objectClass", "user"))
3256 && (!ldb_msg_check_string_attribute(ares
->message
, "objectClass", "inetOrgPerson"))) {
3259 if (ldb_msg_find_element(ac
->req
->op
.mod
.message
, "clearTextPassword") != NULL
) {
3260 ldb_set_errstring(ldb
,
3261 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3262 ret
= LDB_ERR_NO_SUCH_ATTRIBUTE
;
3266 ret
= ldb_next_request(ac
->module
, ac
->req
);
3270 if (ac
->search_res
!= NULL
) {
3273 ldb_set_errstring(ldb
, "Too many results");
3274 ret
= LDB_ERR_OPERATIONS_ERROR
;
3278 ac
->search_res
= talloc_steal(ac
, ares
);
3282 case LDB_REPLY_REFERRAL
:
3283 /* ignore anything else for now */
3288 case LDB_REPLY_DONE
:
3291 /* get user domain data */
3292 ret
= build_domain_data_request(ac
);
3293 if (ret
!= LDB_SUCCESS
) {
3294 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
3297 ret
= ldb_next_request(ac
->module
, ac
->dom_req
);
3302 if (ret
!= LDB_SUCCESS
) {
3303 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
3309 static int password_hash_mod_search_self(struct ph_context
*ac
)
3311 struct ldb_context
*ldb
;
3312 static const char * const attrs
[] = { "objectClass",
3313 "userAccountControl",
3314 "msDS-User-Account-Control-Computed",
3318 "userPrincipalName",
3319 "supplementalCredentials",
3328 struct ldb_request
*search_req
;
3331 ldb
= ldb_module_get_ctx(ac
->module
);
3333 ret
= ldb_build_search_req(&search_req
, ldb
, ac
,
3334 ac
->req
->op
.mod
.message
->dn
,
3339 ac
, ph_mod_search_callback
,
3341 LDB_REQ_SET_LOCATION(search_req
);
3342 if (ret
!= LDB_SUCCESS
) {
3346 return ldb_next_request(ac
->module
, search_req
);
3349 static int password_hash_mod_do_mod(struct ph_context
*ac
)
3351 struct ldb_context
*ldb
= ldb_module_get_ctx(ac
->module
);
3352 struct loadparm_context
*lp_ctx
=
3353 talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
3354 struct loadparm_context
);
3355 struct ldb_request
*mod_req
;
3356 struct ldb_message
*msg
;
3357 const struct ldb_message
*orig_msg
, *searched_msg
;
3358 struct setup_password_fields_io io
;
3362 /* use a new message structure so that we can modify it */
3363 msg
= ldb_msg_new(ac
);
3365 return ldb_operr(ldb
);
3369 msg
->dn
= ac
->req
->op
.mod
.message
->dn
;
3371 orig_msg
= ac
->req
->op
.mod
.message
;
3372 searched_msg
= ac
->search_res
->message
;
3374 /* Prepare the internal data structure containing the passwords */
3375 ret
= setup_io(ac
, orig_msg
, searched_msg
, &io
);
3376 if (ret
!= LDB_SUCCESS
) {
3380 if (io
.ac
->pwd_reset
) {
3381 /* Get the old password from the database */
3382 status
= samdb_result_passwords_no_lockout(io
.ac
,
3384 discard_const_p(struct ldb_message
, searched_msg
),
3388 /* Get the old password from the database */
3389 status
= samdb_result_passwords(io
.ac
,
3391 discard_const_p(struct ldb_message
, searched_msg
),
3392 &io
.o
.lm_hash
, &io
.o
.nt_hash
);
3395 if (NT_STATUS_EQUAL(status
, NT_STATUS_ACCOUNT_LOCKED_OUT
)) {
3396 ldb_asprintf_errstring(ldb
,
3397 "%08X: check_password: "
3398 "Password change not permitted, account locked out!",
3399 W_ERROR_V(WERR_ACCOUNT_LOCKED_OUT
));
3400 return LDB_ERR_CONSTRAINT_VIOLATION
;
3403 if (!NT_STATUS_IS_OK(status
)) {
3405 * This only happens if the database has gone weird,
3406 * not if we are just missing the passwords
3408 return ldb_operr(ldb
);
3411 io
.o
.nt_history_len
= samdb_result_hashes(io
.ac
, searched_msg
, "ntPwdHistory", &io
.o
.nt_history
);
3412 io
.o
.lm_history_len
= samdb_result_hashes(io
.ac
, searched_msg
, "lmPwdHistory", &io
.o
.lm_history
);
3413 io
.o
.supplemental
= ldb_msg_find_ldb_val(searched_msg
, "supplementalCredentials");
3415 ret
= setup_password_fields(&io
);
3416 if (ret
!= LDB_SUCCESS
) {
3420 ret
= check_password_restrictions(&io
);
3421 if (ret
!= LDB_SUCCESS
) {
3425 /* make sure we replace all the old attributes */
3426 ret
= ldb_msg_add_empty(msg
, "unicodePwd", LDB_FLAG_MOD_REPLACE
, NULL
);
3427 ret
= ldb_msg_add_empty(msg
, "dBCSPwd", LDB_FLAG_MOD_REPLACE
, NULL
);
3428 ret
= ldb_msg_add_empty(msg
, "ntPwdHistory", LDB_FLAG_MOD_REPLACE
, NULL
);
3429 ret
= ldb_msg_add_empty(msg
, "lmPwdHistory", LDB_FLAG_MOD_REPLACE
, NULL
);
3430 ret
= ldb_msg_add_empty(msg
, "supplementalCredentials", LDB_FLAG_MOD_REPLACE
, NULL
);
3431 ret
= ldb_msg_add_empty(msg
, "pwdLastSet", LDB_FLAG_MOD_REPLACE
, NULL
);
3434 ret
= samdb_msg_add_hash(ldb
, ac
, msg
,
3435 "unicodePwd", io
.g
.nt_hash
);
3436 if (ret
!= LDB_SUCCESS
) {
3441 ret
= samdb_msg_add_hash(ldb
, ac
, msg
,
3442 "dBCSPwd", io
.g
.lm_hash
);
3443 if (ret
!= LDB_SUCCESS
) {
3447 if (io
.g
.nt_history_len
> 0) {
3448 ret
= samdb_msg_add_hashes(ldb
, ac
, msg
,
3451 io
.g
.nt_history_len
);
3452 if (ret
!= LDB_SUCCESS
) {
3456 if (io
.g
.lm_history_len
> 0) {
3457 ret
= samdb_msg_add_hashes(ldb
, ac
, msg
,
3460 io
.g
.lm_history_len
);
3461 if (ret
!= LDB_SUCCESS
) {
3465 if (io
.g
.supplemental
.length
> 0) {
3466 ret
= ldb_msg_add_value(msg
, "supplementalCredentials",
3467 &io
.g
.supplemental
, NULL
);
3468 if (ret
!= LDB_SUCCESS
) {
3472 ret
= samdb_msg_add_uint64(ldb
, ac
, msg
,
3475 if (ret
!= LDB_SUCCESS
) {
3479 ret
= ldb_build_mod_req(&mod_req
, ldb
, ac
,
3484 LDB_REQ_SET_LOCATION(mod_req
);
3485 if (ret
!= LDB_SUCCESS
) {
3489 return ldb_next_request(ac
->module
, mod_req
);
3492 static const struct ldb_module_ops ldb_password_hash_module_ops
= {
3493 .name
= "password_hash",
3494 .add
= password_hash_add
,
3495 .modify
= password_hash_modify
3498 int ldb_password_hash_module_init(const char *version
)
3500 LDB_MODULE_CHECK_VERSION(version
);
3501 return ldb_register_module(&ldb_password_hash_module_ops
);