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"
46 #include "lib/krb5_wrap/krb5_samba.h"
48 /* If we have decided there is a reason to work on this request, then
49 * setup all the password hash types correctly.
51 * If we haven't the hashes yet but the password given as plain-text (attributes
52 * 'unicodePwd', 'userPassword' and 'clearTextPassword') we have to check for
53 * the constraints. Once this is done, we calculate the password hashes.
55 * Notice: unlike the real AD which only supports the UTF16 special based
56 * 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
57 * understand also a UTF16 based 'clearTextPassword' one.
58 * The latter is also accessible through LDAP so it can also be set by external
59 * tools and scripts. But be aware that this isn't portable on non SAMBA 4 ADs!
61 * Also when the module receives only the password hashes (possible through
62 * specifying an internal LDB control - for security reasons) some checks are
63 * performed depending on the operation mode (see below) (e.g. if the password
64 * has been in use before if the password memory policy was activated).
66 * Attention: There is a difference between "modify" and "reset" operations
67 * (see MS-ADTS 3.1.1.3.1.5). If the client sends a "add" and "remove"
68 * operation for a password attribute we thread this as a "modify"; if it sends
69 * only a "replace" one we have an (administrative) reset.
71 * Finally, if the administrator has requested that a password history
72 * be maintained, then this should also be written out.
76 /* TODO: [consider always MS-ADTS 3.1.1.3.1.5]
77 * - Check for right connection encryption
80 /* Notice: Definition of "dsdb_control_password_change_status" moved into
84 struct ldb_module
*module
;
85 struct ldb_request
*req
;
87 struct ldb_request
*dom_req
;
88 struct ldb_reply
*dom_res
;
90 struct ldb_reply
*search_res
;
92 struct ldb_message
*update_msg
;
94 struct dsdb_control_password_change_status
*status
;
95 struct dsdb_control_password_change
*change
;
101 bool update_password
;
103 bool pwd_last_set_bypass
;
104 bool pwd_last_set_default
;
108 struct setup_password_fields_io
{
109 struct ph_context
*ac
;
111 struct smb_krb5_context
*smb_krb5_context
;
113 /* infos about the user account */
115 uint32_t userAccountControl
;
117 const char *sAMAccountName
;
118 const char *user_principal_name
;
120 uint32_t restrictions
;
123 /* new credentials and old given credentials */
124 struct setup_password_fields_given
{
125 const struct ldb_val
*cleartext_utf8
;
126 const struct ldb_val
*cleartext_utf16
;
127 struct samr_Password
*nt_hash
;
128 struct samr_Password
*lm_hash
;
131 /* old credentials */
133 struct samr_Password
*nt_hash
;
134 struct samr_Password
*lm_hash
;
135 uint32_t nt_history_len
;
136 struct samr_Password
*nt_history
;
137 uint32_t lm_history_len
;
138 struct samr_Password
*lm_history
;
139 const struct ldb_val
*supplemental
;
140 struct supplementalCredentialsBlob scb
;
143 /* generated credentials */
145 struct samr_Password
*nt_hash
;
146 struct samr_Password
*lm_hash
;
147 uint32_t nt_history_len
;
148 struct samr_Password
*nt_history
;
149 uint32_t lm_history_len
;
150 struct samr_Password
*lm_history
;
156 struct ldb_val supplemental
;
161 static int msg_find_old_and_new_pwd_val(const struct ldb_message
*msg
,
163 enum ldb_request_type operation
,
164 const struct ldb_val
**new_val
,
165 const struct ldb_val
**old_val
);
167 static int password_hash_bypass(struct ldb_module
*module
, struct ldb_request
*request
)
169 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
170 const struct ldb_message
*msg
;
171 struct ldb_message_element
*nte
;
172 struct ldb_message_element
*lme
;
173 struct ldb_message_element
*nthe
;
174 struct ldb_message_element
*lmhe
;
175 struct ldb_message_element
*sce
;
177 switch (request
->operation
) {
179 msg
= request
->op
.add
.message
;
182 msg
= request
->op
.mod
.message
;
185 return ldb_next_request(module
, request
);
188 /* nobody must touch password histories and 'supplementalCredentials' */
189 nte
= dsdb_get_single_valued_attr(msg
, "unicodePwd",
191 lme
= dsdb_get_single_valued_attr(msg
, "dBCSPwd",
193 nthe
= dsdb_get_single_valued_attr(msg
, "ntPwdHistory",
195 lmhe
= dsdb_get_single_valued_attr(msg
, "lmPwdHistory",
197 sce
= dsdb_get_single_valued_attr(msg
, "supplementalCredentials",
200 #define CHECK_HASH_ELEMENT(e, min, max) do {\
201 if (e && e->num_values) { \
202 unsigned int _count; \
203 if (e->num_values != 1) { \
204 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
205 "num_values != 1"); \
207 if ((e->values[0].length % 16) != 0) { \
208 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
209 "length % 16 != 0"); \
211 _count = e->values[0].length / 16; \
212 if (_count < min) { \
213 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
216 if (_count > max) { \
217 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, \
223 CHECK_HASH_ELEMENT(nte
, 1, 1);
224 CHECK_HASH_ELEMENT(lme
, 1, 1);
225 CHECK_HASH_ELEMENT(nthe
, 1, INT32_MAX
);
226 CHECK_HASH_ELEMENT(lmhe
, 1, INT32_MAX
);
228 if (sce
&& sce
->num_values
) {
229 enum ndr_err_code ndr_err
;
230 struct supplementalCredentialsBlob
*scb
;
231 struct supplementalCredentialsPackage
*scpp
= NULL
;
232 struct supplementalCredentialsPackage
*scpk
= NULL
;
233 struct supplementalCredentialsPackage
*scpkn
= NULL
;
234 struct supplementalCredentialsPackage
*scpct
= NULL
;
235 DATA_BLOB scpbp
= data_blob_null
;
236 DATA_BLOB scpbk
= data_blob_null
;
237 DATA_BLOB scpbkn
= data_blob_null
;
238 DATA_BLOB scpbct
= data_blob_null
;
242 if (sce
->num_values
!= 1) {
243 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
247 scb
= talloc_zero(request
, struct supplementalCredentialsBlob
);
249 return ldb_module_oom(module
);
252 ndr_err
= ndr_pull_struct_blob_all(&sce
->values
[0], scb
, scb
,
253 (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
);
254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
255 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
256 "ndr_pull_struct_blob_all");
259 if (scb
->sub
.num_packages
< 2) {
260 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
264 for (i
=0; i
< scb
->sub
.num_packages
; i
++) {
267 subblob
= strhex_to_data_blob(scb
, scb
->sub
.packages
[i
].data
);
268 if (subblob
.data
== NULL
) {
269 return ldb_module_oom(module
);
272 if (strcmp(scb
->sub
.packages
[i
].name
, "Packages") == 0) {
274 return ldb_error(ldb
,
275 LDB_ERR_CONSTRAINT_VIOLATION
,
278 scpp
= &scb
->sub
.packages
[i
];
282 if (strcmp(scb
->sub
.packages
[i
].name
, "Primary:Kerberos") == 0) {
284 return ldb_error(ldb
,
285 LDB_ERR_CONSTRAINT_VIOLATION
,
286 "Primary:Kerberos twice");
288 scpk
= &scb
->sub
.packages
[i
];
292 if (strcmp(scb
->sub
.packages
[i
].name
, "Primary:Kerberos-Newer-Keys") == 0) {
294 return ldb_error(ldb
,
295 LDB_ERR_CONSTRAINT_VIOLATION
,
296 "Primary:Kerberos-Newer-Keys twice");
298 scpkn
= &scb
->sub
.packages
[i
];
302 if (strcmp(scb
->sub
.packages
[i
].name
, "Primary:CLEARTEXT") == 0) {
304 return ldb_error(ldb
,
305 LDB_ERR_CONSTRAINT_VIOLATION
,
306 "Primary:CLEARTEXT twice");
308 scpct
= &scb
->sub
.packages
[i
];
313 data_blob_free(&subblob
);
317 return ldb_error(ldb
,
318 LDB_ERR_CONSTRAINT_VIOLATION
,
319 "Primary:Packages missing");
324 * If Primary:Kerberos is missing w2k8r2 reboots
325 * when a password is changed.
327 return ldb_error(ldb
,
328 LDB_ERR_CONSTRAINT_VIOLATION
,
329 "Primary:Kerberos missing");
333 struct package_PackagesBlob
*p
;
336 p
= talloc_zero(scb
, struct package_PackagesBlob
);
338 return ldb_module_oom(module
);
341 ndr_err
= ndr_pull_struct_blob(&scpbp
, p
, p
,
342 (ndr_pull_flags_fn_t
)ndr_pull_package_PackagesBlob
);
343 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
344 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
345 "ndr_pull_struct_blob Packages");
348 if (p
->names
== NULL
) {
349 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
350 "Packages names == NULL");
353 for (n
= 0; p
->names
[n
]; n
++) {
357 if (scb
->sub
.num_packages
!= (n
+ 1)) {
358 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
359 "Packages num_packages != num_names + 1");
366 struct package_PrimaryKerberosBlob
*k
;
368 k
= talloc_zero(scb
, struct package_PrimaryKerberosBlob
);
370 return ldb_module_oom(module
);
373 ndr_err
= ndr_pull_struct_blob(&scpbk
, k
, k
,
374 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
375 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
376 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
377 "ndr_pull_struct_blob PrimaryKerberos");
380 if (k
->version
!= 3) {
381 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
382 "PrimaryKerberos version != 3");
385 if (k
->ctr
.ctr3
.salt
.string
== NULL
) {
386 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
387 "PrimaryKerberos salt == NULL");
390 if (strlen(k
->ctr
.ctr3
.salt
.string
) == 0) {
391 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
392 "PrimaryKerberos strlen(salt) == 0");
395 if (k
->ctr
.ctr3
.num_keys
!= 2) {
396 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
397 "PrimaryKerberos num_keys != 2");
400 if (k
->ctr
.ctr3
.num_old_keys
> k
->ctr
.ctr3
.num_keys
) {
401 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
402 "PrimaryKerberos num_old_keys > num_keys");
405 if (k
->ctr
.ctr3
.keys
[0].keytype
!= ENCTYPE_DES_CBC_MD5
) {
406 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
407 "PrimaryKerberos key[0] != DES_CBC_MD5");
409 if (k
->ctr
.ctr3
.keys
[1].keytype
!= ENCTYPE_DES_CBC_CRC
) {
410 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
411 "PrimaryKerberos key[1] != DES_CBC_CRC");
414 if (k
->ctr
.ctr3
.keys
[0].value_len
!= 8) {
415 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
416 "PrimaryKerberos key[0] value_len != 8");
418 if (k
->ctr
.ctr3
.keys
[1].value_len
!= 8) {
419 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
420 "PrimaryKerberos key[1] value_len != 8");
423 for (i
= 0; i
< k
->ctr
.ctr3
.num_old_keys
; i
++) {
424 if (k
->ctr
.ctr3
.old_keys
[i
].keytype
==
425 k
->ctr
.ctr3
.keys
[i
].keytype
&&
426 k
->ctr
.ctr3
.old_keys
[i
].value_len
==
427 k
->ctr
.ctr3
.keys
[i
].value_len
) {
431 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
432 "PrimaryKerberos old_keys type/value_len doesn't match");
439 struct package_PrimaryKerberosBlob
*k
;
441 k
= talloc_zero(scb
, struct package_PrimaryKerberosBlob
);
443 return ldb_module_oom(module
);
446 ndr_err
= ndr_pull_struct_blob(&scpbkn
, k
, k
,
447 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
448 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
449 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
450 "ndr_pull_struct_blob PrimaryKerberosNeverKeys");
453 if (k
->version
!= 4) {
454 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
455 "KerberosNerverKeys version != 4");
458 if (k
->ctr
.ctr4
.salt
.string
== NULL
) {
459 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
460 "KerberosNewerKeys salt == NULL");
463 if (strlen(k
->ctr
.ctr4
.salt
.string
) == 0) {
464 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
465 "KerberosNewerKeys strlen(salt) == 0");
468 if (k
->ctr
.ctr4
.num_keys
!= 4) {
469 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
470 "KerberosNewerKeys num_keys != 2");
473 if (k
->ctr
.ctr4
.num_old_keys
> k
->ctr
.ctr4
.num_keys
) {
474 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
475 "KerberosNewerKeys num_old_keys > num_keys");
478 if (k
->ctr
.ctr4
.num_older_keys
> k
->ctr
.ctr4
.num_old_keys
) {
479 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
480 "KerberosNewerKeys num_older_keys > num_old_keys");
483 if (k
->ctr
.ctr4
.keys
[0].keytype
!= ENCTYPE_AES256_CTS_HMAC_SHA1_96
) {
484 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
485 "KerberosNewerKeys key[0] != AES256");
487 if (k
->ctr
.ctr4
.keys
[1].keytype
!= ENCTYPE_AES128_CTS_HMAC_SHA1_96
) {
488 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
489 "KerberosNewerKeys key[1] != AES128");
491 if (k
->ctr
.ctr4
.keys
[2].keytype
!= ENCTYPE_DES_CBC_MD5
) {
492 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
493 "KerberosNewerKeys key[2] != DES_CBC_MD5");
495 if (k
->ctr
.ctr4
.keys
[3].keytype
!= ENCTYPE_DES_CBC_CRC
) {
496 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
497 "KerberosNewerKeys key[3] != DES_CBC_CRC");
500 if (k
->ctr
.ctr4
.keys
[0].value_len
!= 32) {
501 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
502 "KerberosNewerKeys key[0] value_len != 32");
504 if (k
->ctr
.ctr4
.keys
[1].value_len
!= 16) {
505 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
506 "KerberosNewerKeys key[1] value_len != 16");
508 if (k
->ctr
.ctr4
.keys
[2].value_len
!= 8) {
509 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
510 "KerberosNewerKeys key[2] value_len != 8");
512 if (k
->ctr
.ctr4
.keys
[3].value_len
!= 8) {
513 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
514 "KerberosNewerKeys key[3] value_len != 8");
519 * Maybe we can check old and older keys here.
520 * But we need to do some tests, if the old keys
521 * can be taken from the PrimaryKerberos blob
522 * (with only des keys), when the domain was upgraded
530 struct package_PrimaryCLEARTEXTBlob
*ct
;
532 ct
= talloc_zero(scb
, struct package_PrimaryCLEARTEXTBlob
);
534 return ldb_module_oom(module
);
537 ndr_err
= ndr_pull_struct_blob(&scpbct
, ct
, ct
,
538 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryCLEARTEXTBlob
);
539 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
540 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
541 "ndr_pull_struct_blob PrimaryCLEARTEXT");
544 if ((ct
->cleartext
.length
% 2) != 0) {
545 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
546 "PrimaryCLEARTEXT length % 2 != 0");
552 ndr_err
= ndr_push_struct_blob(&blob
, scb
, scb
,
553 (ndr_push_flags_fn_t
)ndr_push_supplementalCredentialsBlob
);
554 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
555 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
556 "ndr_pull_struct_blob_all");
559 if (sce
->values
[0].length
!= blob
.length
) {
560 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
561 "supplementalCredentialsBlob length differ");
564 if (memcmp(sce
->values
[0].data
, blob
.data
, blob
.length
) != 0) {
565 return ldb_error(ldb
, LDB_ERR_CONSTRAINT_VIOLATION
,
566 "supplementalCredentialsBlob memcmp differ");
572 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_bypass - validated\n");
573 return ldb_next_request(module
, request
);
576 /* Get the NT hash, and fill it in as an entry in the password history,
577 and specify it into io->g.nt_hash */
579 static int setup_nt_fields(struct setup_password_fields_io
*io
)
581 struct ldb_context
*ldb
;
584 io
->g
.nt_hash
= io
->n
.nt_hash
;
585 ldb
= ldb_module_get_ctx(io
->ac
->module
);
587 if (io
->ac
->status
->domain_data
.pwdHistoryLength
== 0) {
591 /* We might not have an old NT password */
592 io
->g
.nt_history
= talloc_array(io
->ac
,
593 struct samr_Password
,
594 io
->ac
->status
->domain_data
.pwdHistoryLength
);
595 if (!io
->g
.nt_history
) {
599 for (i
= 0; i
< MIN(io
->ac
->status
->domain_data
.pwdHistoryLength
-1,
600 io
->o
.nt_history_len
); i
++) {
601 io
->g
.nt_history
[i
+1] = io
->o
.nt_history
[i
];
603 io
->g
.nt_history_len
= i
+ 1;
606 io
->g
.nt_history
[0] = *io
->g
.nt_hash
;
609 * TODO: is this correct?
610 * the simular behavior is correct for the lm history case
612 E_md4hash("", io
->g
.nt_history
[0].hash
);
618 /* Get the LANMAN hash, and fill it in as an entry in the password history,
619 and specify it into io->g.lm_hash */
621 static int setup_lm_fields(struct setup_password_fields_io
*io
)
623 struct ldb_context
*ldb
;
626 io
->g
.lm_hash
= io
->n
.lm_hash
;
627 ldb
= ldb_module_get_ctx(io
->ac
->module
);
629 if (io
->ac
->status
->domain_data
.pwdHistoryLength
== 0) {
633 /* We might not have an old LM password */
634 io
->g
.lm_history
= talloc_array(io
->ac
,
635 struct samr_Password
,
636 io
->ac
->status
->domain_data
.pwdHistoryLength
);
637 if (!io
->g
.lm_history
) {
641 for (i
= 0; i
< MIN(io
->ac
->status
->domain_data
.pwdHistoryLength
-1,
642 io
->o
.lm_history_len
); i
++) {
643 io
->g
.lm_history
[i
+1] = io
->o
.lm_history
[i
];
645 io
->g
.lm_history_len
= i
+ 1;
648 io
->g
.lm_history
[0] = *io
->g
.lm_hash
;
650 E_deshash("", io
->g
.lm_history
[0].hash
);
656 static int setup_kerberos_keys(struct setup_password_fields_io
*io
)
658 struct ldb_context
*ldb
;
659 krb5_error_code krb5_ret
;
660 krb5_principal salt_principal
;
663 krb5_data cleartext_data
;
665 ldb
= ldb_module_get_ctx(io
->ac
->module
);
666 cleartext_data
.data
= (char *)io
->n
.cleartext_utf8
->data
;
667 cleartext_data
.length
= io
->n
.cleartext_utf8
->length
;
669 /* Many, many thanks to lukeh@padl.com for this
670 * algorithm, described in his Nov 10 2004 mail to
671 * samba-technical@lists.samba.org */
674 * Determine a salting principal
676 if (io
->u
.is_computer
) {
680 name
= strlower_talloc(io
->ac
, io
->u
.sAMAccountName
);
685 if (name
[strlen(name
)-1] == '$') {
686 name
[strlen(name
)-1] = '\0';
689 saltbody
= talloc_asprintf(io
->ac
, "%s.%s", name
,
690 io
->ac
->status
->domain_data
.dns_domain
);
695 krb5_ret
= smb_krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
697 io
->ac
->status
->domain_data
.realm
,
698 "host", saltbody
, NULL
);
699 } else if (io
->u
.user_principal_name
) {
700 char *user_principal_name
;
703 user_principal_name
= talloc_strdup(io
->ac
, io
->u
.user_principal_name
);
704 if (!user_principal_name
) {
708 p
= strchr(user_principal_name
, '@');
713 krb5_ret
= smb_krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
715 io
->ac
->status
->domain_data
.realm
,
716 user_principal_name
, NULL
);
718 krb5_ret
= smb_krb5_make_principal(io
->smb_krb5_context
->krb5_context
,
720 io
->ac
->status
->domain_data
.realm
,
721 io
->u
.sAMAccountName
, NULL
);
724 ldb_asprintf_errstring(ldb
,
725 "setup_kerberos_keys: "
726 "generation of a salting principal failed: %s",
727 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
729 return LDB_ERR_OPERATIONS_ERROR
;
733 * create salt from salt_principal
735 krb5_ret
= smb_krb5_get_pw_salt(io
->smb_krb5_context
->krb5_context
,
736 salt_principal
, &salt
);
737 krb5_free_principal(io
->smb_krb5_context
->krb5_context
, salt_principal
);
739 ldb_asprintf_errstring(ldb
,
740 "setup_kerberos_keys: "
741 "generation of krb5_salt failed: %s",
742 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
744 return LDB_ERR_OPERATIONS_ERROR
;
746 /* create a talloc copy */
747 io
->g
.salt
= talloc_strndup(io
->ac
,
750 kerberos_free_data_contents(io
->smb_krb5_context
->krb5_context
, &salt
);
754 /* now use the talloced copy of the salt */
755 salt
.data
= discard_const(io
->g
.salt
);
756 salt
.length
= strlen(io
->g
.salt
);
759 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
760 * the salt and the cleartext password
762 krb5_ret
= smb_krb5_create_key_from_string(io
->smb_krb5_context
->krb5_context
,
766 ENCTYPE_AES256_CTS_HMAC_SHA1_96
,
769 ldb_asprintf_errstring(ldb
,
770 "setup_kerberos_keys: "
771 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
772 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
774 return LDB_ERR_OPERATIONS_ERROR
;
776 io
->g
.aes_256
= data_blob_talloc(io
->ac
,
778 KRB5_KEY_LENGTH(&key
));
779 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
780 if (!io
->g
.aes_256
.data
) {
785 * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
786 * the salt and the cleartext password
788 krb5_ret
= smb_krb5_create_key_from_string(io
->smb_krb5_context
->krb5_context
,
792 ENCTYPE_AES128_CTS_HMAC_SHA1_96
,
795 ldb_asprintf_errstring(ldb
,
796 "setup_kerberos_keys: "
797 "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
798 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
800 return LDB_ERR_OPERATIONS_ERROR
;
802 io
->g
.aes_128
= data_blob_talloc(io
->ac
,
804 KRB5_KEY_LENGTH(&key
));
805 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
806 if (!io
->g
.aes_128
.data
) {
811 * create ENCTYPE_DES_CBC_MD5 key out of
812 * the salt and the cleartext password
814 krb5_ret
= smb_krb5_create_key_from_string(io
->smb_krb5_context
->krb5_context
,
821 ldb_asprintf_errstring(ldb
,
822 "setup_kerberos_keys: "
823 "generation of a des-cbc-md5 key failed: %s",
824 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
826 return LDB_ERR_OPERATIONS_ERROR
;
828 io
->g
.des_md5
= data_blob_talloc(io
->ac
,
830 KRB5_KEY_LENGTH(&key
));
831 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
832 if (!io
->g
.des_md5
.data
) {
837 * create ENCTYPE_DES_CBC_CRC key out of
838 * the salt and the cleartext password
840 krb5_ret
= smb_krb5_create_key_from_string(io
->smb_krb5_context
->krb5_context
,
847 ldb_asprintf_errstring(ldb
,
848 "setup_kerberos_keys: "
849 "generation of a des-cbc-crc key failed: %s",
850 smb_get_krb5_error_message(io
->smb_krb5_context
->krb5_context
,
852 return LDB_ERR_OPERATIONS_ERROR
;
854 io
->g
.des_crc
= data_blob_talloc(io
->ac
,
856 KRB5_KEY_LENGTH(&key
));
857 krb5_free_keyblock_contents(io
->smb_krb5_context
->krb5_context
, &key
);
858 if (!io
->g
.des_crc
.data
) {
865 static int setup_primary_kerberos(struct setup_password_fields_io
*io
,
866 const struct supplementalCredentialsBlob
*old_scb
,
867 struct package_PrimaryKerberosBlob
*pkb
)
869 struct ldb_context
*ldb
;
870 struct package_PrimaryKerberosCtr3
*pkb3
= &pkb
->ctr
.ctr3
;
871 struct supplementalCredentialsPackage
*old_scp
= NULL
;
872 struct package_PrimaryKerberosBlob _old_pkb
;
873 struct package_PrimaryKerberosCtr3
*old_pkb3
= NULL
;
875 enum ndr_err_code ndr_err
;
877 ldb
= ldb_module_get_ctx(io
->ac
->module
);
880 * prepare generation of keys
882 * ENCTYPE_DES_CBC_MD5
883 * ENCTYPE_DES_CBC_CRC
886 pkb3
->salt
.string
= io
->g
.salt
;
888 pkb3
->keys
= talloc_array(io
->ac
,
889 struct package_PrimaryKerberosKey3
,
895 pkb3
->keys
[0].keytype
= ENCTYPE_DES_CBC_MD5
;
896 pkb3
->keys
[0].value
= &io
->g
.des_md5
;
897 pkb3
->keys
[1].keytype
= ENCTYPE_DES_CBC_CRC
;
898 pkb3
->keys
[1].value
= &io
->g
.des_crc
;
900 /* initialize the old keys to zero */
901 pkb3
->num_old_keys
= 0;
902 pkb3
->old_keys
= NULL
;
904 /* if there're no old keys, then we're done */
909 for (i
=0; i
< old_scb
->sub
.num_packages
; i
++) {
910 if (strcmp("Primary:Kerberos", old_scb
->sub
.packages
[i
].name
) != 0) {
914 if (!old_scb
->sub
.packages
[i
].data
|| !old_scb
->sub
.packages
[i
].data
[0]) {
918 old_scp
= &old_scb
->sub
.packages
[i
];
921 /* Primary:Kerberos element of supplementalCredentials */
925 blob
= strhex_to_data_blob(io
->ac
, old_scp
->data
);
930 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
931 ndr_err
= ndr_pull_struct_blob(&blob
, io
->ac
, &_old_pkb
,
932 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
933 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
934 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
935 ldb_asprintf_errstring(ldb
,
936 "setup_primary_kerberos: "
937 "failed to pull old package_PrimaryKerberosBlob: %s",
939 return LDB_ERR_OPERATIONS_ERROR
;
942 if (_old_pkb
.version
!= 3) {
943 ldb_asprintf_errstring(ldb
,
944 "setup_primary_kerberos: "
945 "package_PrimaryKerberosBlob version[%u] expected[3]",
947 return LDB_ERR_OPERATIONS_ERROR
;
950 old_pkb3
= &_old_pkb
.ctr
.ctr3
;
953 /* if we didn't found the old keys we're done */
958 /* fill in the old keys */
959 pkb3
->num_old_keys
= old_pkb3
->num_keys
;
960 pkb3
->old_keys
= old_pkb3
->keys
;
965 static int setup_primary_kerberos_newer(struct setup_password_fields_io
*io
,
966 const struct supplementalCredentialsBlob
*old_scb
,
967 struct package_PrimaryKerberosBlob
*pkb
)
969 struct ldb_context
*ldb
;
970 struct package_PrimaryKerberosCtr4
*pkb4
= &pkb
->ctr
.ctr4
;
971 struct supplementalCredentialsPackage
*old_scp
= NULL
;
972 struct package_PrimaryKerberosBlob _old_pkb
;
973 struct package_PrimaryKerberosCtr4
*old_pkb4
= NULL
;
975 enum ndr_err_code ndr_err
;
977 ldb
= ldb_module_get_ctx(io
->ac
->module
);
980 * prepare generation of keys
982 * ENCTYPE_AES256_CTS_HMAC_SHA1_96
983 * ENCTYPE_AES128_CTS_HMAC_SHA1_96
984 * ENCTYPE_DES_CBC_MD5
985 * ENCTYPE_DES_CBC_CRC
988 pkb4
->salt
.string
= io
->g
.salt
;
989 pkb4
->default_iteration_count
= 4096;
992 pkb4
->keys
= talloc_array(io
->ac
,
993 struct package_PrimaryKerberosKey4
,
999 pkb4
->keys
[0].iteration_count
= 4096;
1000 pkb4
->keys
[0].keytype
= ENCTYPE_AES256_CTS_HMAC_SHA1_96
;
1001 pkb4
->keys
[0].value
= &io
->g
.aes_256
;
1002 pkb4
->keys
[1].iteration_count
= 4096;
1003 pkb4
->keys
[1].keytype
= ENCTYPE_AES128_CTS_HMAC_SHA1_96
;
1004 pkb4
->keys
[1].value
= &io
->g
.aes_128
;
1005 pkb4
->keys
[2].iteration_count
= 4096;
1006 pkb4
->keys
[2].keytype
= ENCTYPE_DES_CBC_MD5
;
1007 pkb4
->keys
[2].value
= &io
->g
.des_md5
;
1008 pkb4
->keys
[3].iteration_count
= 4096;
1009 pkb4
->keys
[3].keytype
= ENCTYPE_DES_CBC_CRC
;
1010 pkb4
->keys
[3].value
= &io
->g
.des_crc
;
1012 /* initialize the old keys to zero */
1013 pkb4
->num_old_keys
= 0;
1014 pkb4
->old_keys
= NULL
;
1015 pkb4
->num_older_keys
= 0;
1016 pkb4
->older_keys
= NULL
;
1018 /* if there're no old keys, then we're done */
1023 for (i
=0; i
< old_scb
->sub
.num_packages
; i
++) {
1024 if (strcmp("Primary:Kerberos-Newer-Keys", old_scb
->sub
.packages
[i
].name
) != 0) {
1028 if (!old_scb
->sub
.packages
[i
].data
|| !old_scb
->sub
.packages
[i
].data
[0]) {
1032 old_scp
= &old_scb
->sub
.packages
[i
];
1035 /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
1039 blob
= strhex_to_data_blob(io
->ac
, old_scp
->data
);
1041 return ldb_oom(ldb
);
1044 /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
1045 ndr_err
= ndr_pull_struct_blob(&blob
, io
->ac
,
1047 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
1048 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1049 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1050 ldb_asprintf_errstring(ldb
,
1051 "setup_primary_kerberos_newer: "
1052 "failed to pull old package_PrimaryKerberosBlob: %s",
1054 return LDB_ERR_OPERATIONS_ERROR
;
1057 if (_old_pkb
.version
!= 4) {
1058 ldb_asprintf_errstring(ldb
,
1059 "setup_primary_kerberos_newer: "
1060 "package_PrimaryKerberosBlob version[%u] expected[4]",
1062 return LDB_ERR_OPERATIONS_ERROR
;
1065 old_pkb4
= &_old_pkb
.ctr
.ctr4
;
1068 /* if we didn't found the old keys we're done */
1073 /* fill in the old keys */
1074 pkb4
->num_old_keys
= old_pkb4
->num_keys
;
1075 pkb4
->old_keys
= old_pkb4
->keys
;
1076 pkb4
->num_older_keys
= old_pkb4
->num_old_keys
;
1077 pkb4
->older_keys
= old_pkb4
->old_keys
;
1082 static int setup_primary_wdigest(struct setup_password_fields_io
*io
,
1083 const struct supplementalCredentialsBlob
*old_scb
,
1084 struct package_PrimaryWDigestBlob
*pdb
)
1086 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
1087 DATA_BLOB sAMAccountName
;
1088 DATA_BLOB sAMAccountName_l
;
1089 DATA_BLOB sAMAccountName_u
;
1090 const char *user_principal_name
= io
->u
.user_principal_name
;
1091 DATA_BLOB userPrincipalName
;
1092 DATA_BLOB userPrincipalName_l
;
1093 DATA_BLOB userPrincipalName_u
;
1094 DATA_BLOB netbios_domain
;
1095 DATA_BLOB netbios_domain_l
;
1096 DATA_BLOB netbios_domain_u
;
1097 DATA_BLOB dns_domain
;
1098 DATA_BLOB dns_domain_l
;
1099 DATA_BLOB dns_domain_u
;
1102 DATA_BLOB backslash
;
1111 * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
1112 * for what precalculated hashes are supposed to be stored...
1114 * I can't reproduce all values which should contain "Digest" as realm,
1115 * am I doing something wrong or is w2k3 just broken...?
1117 * W2K3 fills in following for a user:
1119 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1120 * sAMAccountName: NewUser2Sam
1121 * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
1123 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1124 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1125 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1126 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1127 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1128 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1129 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1130 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1131 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1132 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1133 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1134 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1135 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1136 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1137 * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1138 * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1139 * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1140 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1141 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1142 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1143 * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
1144 * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
1145 * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
1146 * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
1147 * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
1148 * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
1149 * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
1150 * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
1151 * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
1153 * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
1154 * sAMAccountName: NewUser2Sam
1156 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1157 * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
1158 * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
1159 * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
1160 * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
1161 * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
1162 * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
1163 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1164 * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1165 * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1166 * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1167 * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1168 * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
1169 * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
1170 * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1171 * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
1172 * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
1173 * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
1174 * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
1175 * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
1176 * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
1177 * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
1178 * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
1179 * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
1180 * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
1181 * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
1182 * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
1183 * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
1184 * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
1188 * sAMAccountName, netbios_domain
1191 .user
= &sAMAccountName
,
1192 .realm
= &netbios_domain
,
1195 .user
= &sAMAccountName_l
,
1196 .realm
= &netbios_domain_l
,
1199 .user
= &sAMAccountName_u
,
1200 .realm
= &netbios_domain_u
,
1203 .user
= &sAMAccountName
,
1204 .realm
= &netbios_domain_u
,
1207 .user
= &sAMAccountName
,
1208 .realm
= &netbios_domain_l
,
1211 .user
= &sAMAccountName_u
,
1212 .realm
= &netbios_domain_l
,
1215 .user
= &sAMAccountName_l
,
1216 .realm
= &netbios_domain_u
,
1219 * sAMAccountName, dns_domain
1222 .user
= &sAMAccountName
,
1223 .realm
= &dns_domain
,
1226 .user
= &sAMAccountName_l
,
1227 .realm
= &dns_domain_l
,
1230 .user
= &sAMAccountName_u
,
1231 .realm
= &dns_domain_u
,
1234 .user
= &sAMAccountName
,
1235 .realm
= &dns_domain_u
,
1238 .user
= &sAMAccountName
,
1239 .realm
= &dns_domain_l
,
1242 .user
= &sAMAccountName_u
,
1243 .realm
= &dns_domain_l
,
1246 .user
= &sAMAccountName_l
,
1247 .realm
= &dns_domain_u
,
1250 * userPrincipalName, no realm
1253 .user
= &userPrincipalName
,
1257 * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
1258 * the fallback to the sAMAccountName based userPrincipalName is correct
1260 .user
= &userPrincipalName_l
,
1263 .user
= &userPrincipalName_u
,
1266 * nt4dom\sAMAccountName, no realm
1269 .user
= &sAMAccountName
,
1270 .nt4dom
= &netbios_domain
1273 .user
= &sAMAccountName_l
,
1274 .nt4dom
= &netbios_domain_l
1277 .user
= &sAMAccountName_u
,
1278 .nt4dom
= &netbios_domain_u
1282 * the following ones are guessed depending on the technet2 article
1283 * but not reproducable on a w2k3 server
1285 /* sAMAccountName with "Digest" realm */
1287 .user
= &sAMAccountName
,
1291 .user
= &sAMAccountName_l
,
1295 .user
= &sAMAccountName_u
,
1298 /* userPrincipalName with "Digest" realm */
1300 .user
= &userPrincipalName
,
1304 .user
= &userPrincipalName_l
,
1308 .user
= &userPrincipalName_u
,
1311 /* nt4dom\\sAMAccountName with "Digest" realm */
1313 .user
= &sAMAccountName
,
1314 .nt4dom
= &netbios_domain
,
1318 .user
= &sAMAccountName_l
,
1319 .nt4dom
= &netbios_domain_l
,
1323 .user
= &sAMAccountName_u
,
1324 .nt4dom
= &netbios_domain_u
,
1329 /* prepare DATA_BLOB's used in the combinations array */
1330 sAMAccountName
= data_blob_string_const(io
->u
.sAMAccountName
);
1331 sAMAccountName_l
= data_blob_string_const(strlower_talloc(io
->ac
, io
->u
.sAMAccountName
));
1332 if (!sAMAccountName_l
.data
) {
1333 return ldb_oom(ldb
);
1335 sAMAccountName_u
= data_blob_string_const(strupper_talloc(io
->ac
, io
->u
.sAMAccountName
));
1336 if (!sAMAccountName_u
.data
) {
1337 return ldb_oom(ldb
);
1340 /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
1341 if (!user_principal_name
) {
1342 user_principal_name
= talloc_asprintf(io
->ac
, "%s@%s",
1343 io
->u
.sAMAccountName
,
1344 io
->ac
->status
->domain_data
.dns_domain
);
1345 if (!user_principal_name
) {
1346 return ldb_oom(ldb
);
1349 userPrincipalName
= data_blob_string_const(user_principal_name
);
1350 userPrincipalName_l
= data_blob_string_const(strlower_talloc(io
->ac
, user_principal_name
));
1351 if (!userPrincipalName_l
.data
) {
1352 return ldb_oom(ldb
);
1354 userPrincipalName_u
= data_blob_string_const(strupper_talloc(io
->ac
, user_principal_name
));
1355 if (!userPrincipalName_u
.data
) {
1356 return ldb_oom(ldb
);
1359 netbios_domain
= data_blob_string_const(io
->ac
->status
->domain_data
.netbios_domain
);
1360 netbios_domain_l
= data_blob_string_const(strlower_talloc(io
->ac
,
1361 io
->ac
->status
->domain_data
.netbios_domain
));
1362 if (!netbios_domain_l
.data
) {
1363 return ldb_oom(ldb
);
1365 netbios_domain_u
= data_blob_string_const(strupper_talloc(io
->ac
,
1366 io
->ac
->status
->domain_data
.netbios_domain
));
1367 if (!netbios_domain_u
.data
) {
1368 return ldb_oom(ldb
);
1371 dns_domain
= data_blob_string_const(io
->ac
->status
->domain_data
.dns_domain
);
1372 dns_domain_l
= data_blob_string_const(io
->ac
->status
->domain_data
.dns_domain
);
1373 dns_domain_u
= data_blob_string_const(io
->ac
->status
->domain_data
.realm
);
1375 digest
= data_blob_string_const("Digest");
1377 delim
= data_blob_string_const(":");
1378 backslash
= data_blob_string_const("\\");
1380 pdb
->num_hashes
= ARRAY_SIZE(wdigest
);
1381 pdb
->hashes
= talloc_array(io
->ac
, struct package_PrimaryWDigestHash
,
1384 return ldb_oom(ldb
);
1387 for (i
=0; i
< ARRAY_SIZE(wdigest
); i
++) {
1390 if (wdigest
[i
].nt4dom
) {
1391 MD5Update(&md5
, wdigest
[i
].nt4dom
->data
, wdigest
[i
].nt4dom
->length
);
1392 MD5Update(&md5
, backslash
.data
, backslash
.length
);
1394 MD5Update(&md5
, wdigest
[i
].user
->data
, wdigest
[i
].user
->length
);
1395 MD5Update(&md5
, delim
.data
, delim
.length
);
1396 if (wdigest
[i
].realm
) {
1397 MD5Update(&md5
, wdigest
[i
].realm
->data
, wdigest
[i
].realm
->length
);
1399 MD5Update(&md5
, delim
.data
, delim
.length
);
1400 MD5Update(&md5
, io
->n
.cleartext_utf8
->data
, io
->n
.cleartext_utf8
->length
);
1401 MD5Final(pdb
->hashes
[i
].hash
, &md5
);
1407 static int setup_supplemental_field(struct setup_password_fields_io
*io
)
1409 struct ldb_context
*ldb
;
1410 struct supplementalCredentialsBlob scb
;
1411 struct supplementalCredentialsBlob
*old_scb
= NULL
;
1412 /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
1413 uint32_t num_names
= 0;
1414 const char *names
[1+4];
1415 uint32_t num_packages
= 0;
1416 struct supplementalCredentialsPackage packages
[1+4];
1418 struct supplementalCredentialsPackage
*pp
= NULL
;
1419 struct package_PackagesBlob pb
;
1422 /* Primary:Kerberos-Newer-Keys */
1423 const char **nkn
= NULL
;
1424 struct supplementalCredentialsPackage
*pkn
= NULL
;
1425 struct package_PrimaryKerberosBlob pknb
;
1426 DATA_BLOB pknb_blob
;
1428 /* Primary:Kerberos */
1429 const char **nk
= NULL
;
1430 struct supplementalCredentialsPackage
*pk
= NULL
;
1431 struct package_PrimaryKerberosBlob pkb
;
1434 /* Primary:WDigest */
1435 const char **nd
= NULL
;
1436 struct supplementalCredentialsPackage
*pd
= NULL
;
1437 struct package_PrimaryWDigestBlob pdb
;
1440 /* Primary:CLEARTEXT */
1441 const char **nc
= NULL
;
1442 struct supplementalCredentialsPackage
*pc
= NULL
;
1443 struct package_PrimaryCLEARTEXTBlob pcb
;
1447 enum ndr_err_code ndr_err
;
1449 bool do_newer_keys
= false;
1450 bool do_cleartext
= false;
1452 ZERO_STRUCT(zero16
);
1455 ldb
= ldb_module_get_ctx(io
->ac
->module
);
1457 if (!io
->n
.cleartext_utf8
) {
1459 * when we don't have a cleartext password
1460 * we can't setup a supplementalCredential value
1465 /* if there's an old supplementaCredentials blob then use it */
1466 if (io
->o
.supplemental
) {
1467 if (io
->o
.scb
.sub
.signature
== SUPPLEMENTAL_CREDENTIALS_SIGNATURE
) {
1468 old_scb
= &io
->o
.scb
;
1470 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
1471 "setup_supplemental_field: "
1472 "supplementalCredentialsBlob "
1473 "signature[0x%04X] expected[0x%04X]",
1474 io
->o
.scb
.sub
.signature
,
1475 SUPPLEMENTAL_CREDENTIALS_SIGNATURE
);
1478 /* Per MS-SAMR 3.1.1.8.11.6 we create AES keys if our domain functionality level is 2008 or higher */
1479 do_newer_keys
= (dsdb_functional_level(ldb
) >= DS_DOMAIN_FUNCTION_2008
);
1481 if (io
->ac
->status
->domain_data
.store_cleartext
&&
1482 (io
->u
.userAccountControl
& UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
)) {
1483 do_cleartext
= true;
1487 * The ordering is this
1489 * Primary:Kerberos-Newer-Keys (optional)
1492 * Primary:CLEARTEXT (optional)
1494 * And the 'Packages' package is insert before the last
1497 if (do_newer_keys
) {
1498 /* Primary:Kerberos-Newer-Keys */
1499 nkn
= &names
[num_names
++];
1500 pkn
= &packages
[num_packages
++];
1503 /* Primary:Kerberos */
1504 nk
= &names
[num_names
++];
1505 pk
= &packages
[num_packages
++];
1507 if (!do_cleartext
) {
1509 pp
= &packages
[num_packages
++];
1512 /* Primary:WDigest */
1513 nd
= &names
[num_names
++];
1514 pd
= &packages
[num_packages
++];
1518 pp
= &packages
[num_packages
++];
1520 /* Primary:CLEARTEXT */
1521 nc
= &names
[num_names
++];
1522 pc
= &packages
[num_packages
++];
1527 * setup 'Primary:Kerberos-Newer-Keys' element
1529 *nkn
= "Kerberos-Newer-Keys";
1531 ret
= setup_primary_kerberos_newer(io
, old_scb
, &pknb
);
1532 if (ret
!= LDB_SUCCESS
) {
1536 ndr_err
= ndr_push_struct_blob(&pknb_blob
, io
->ac
,
1538 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryKerberosBlob
);
1539 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1540 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1541 ldb_asprintf_errstring(ldb
,
1542 "setup_supplemental_field: "
1543 "failed to push package_PrimaryKerberosNeverBlob: %s",
1545 return LDB_ERR_OPERATIONS_ERROR
;
1547 pknb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pknb_blob
);
1549 return ldb_oom(ldb
);
1551 pkn
->name
= "Primary:Kerberos-Newer-Keys";
1553 pkn
->data
= pknb_hexstr
;
1557 * setup 'Primary:Kerberos' element
1561 ret
= setup_primary_kerberos(io
, old_scb
, &pkb
);
1562 if (ret
!= LDB_SUCCESS
) {
1566 ndr_err
= ndr_push_struct_blob(&pkb_blob
, io
->ac
,
1568 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryKerberosBlob
);
1569 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1570 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1571 ldb_asprintf_errstring(ldb
,
1572 "setup_supplemental_field: "
1573 "failed to push package_PrimaryKerberosBlob: %s",
1575 return LDB_ERR_OPERATIONS_ERROR
;
1577 pkb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pkb_blob
);
1579 return ldb_oom(ldb
);
1581 pk
->name
= "Primary:Kerberos";
1583 pk
->data
= pkb_hexstr
;
1586 * setup 'Primary:WDigest' element
1590 ret
= setup_primary_wdigest(io
, old_scb
, &pdb
);
1591 if (ret
!= LDB_SUCCESS
) {
1595 ndr_err
= ndr_push_struct_blob(&pdb_blob
, io
->ac
,
1597 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryWDigestBlob
);
1598 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1599 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1600 ldb_asprintf_errstring(ldb
,
1601 "setup_supplemental_field: "
1602 "failed to push package_PrimaryWDigestBlob: %s",
1604 return LDB_ERR_OPERATIONS_ERROR
;
1606 pdb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pdb_blob
);
1608 return ldb_oom(ldb
);
1610 pd
->name
= "Primary:WDigest";
1612 pd
->data
= pdb_hexstr
;
1615 * setup 'Primary:CLEARTEXT' element
1620 pcb
.cleartext
= *io
->n
.cleartext_utf16
;
1622 ndr_err
= ndr_push_struct_blob(&pcb_blob
, io
->ac
,
1624 (ndr_push_flags_fn_t
)ndr_push_package_PrimaryCLEARTEXTBlob
);
1625 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1626 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1627 ldb_asprintf_errstring(ldb
,
1628 "setup_supplemental_field: "
1629 "failed to push package_PrimaryCLEARTEXTBlob: %s",
1631 return LDB_ERR_OPERATIONS_ERROR
;
1633 pcb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pcb_blob
);
1635 return ldb_oom(ldb
);
1637 pc
->name
= "Primary:CLEARTEXT";
1639 pc
->data
= pcb_hexstr
;
1643 * setup 'Packages' element
1646 ndr_err
= ndr_push_struct_blob(&pb_blob
, io
->ac
,
1648 (ndr_push_flags_fn_t
)ndr_push_package_PackagesBlob
);
1649 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1650 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1651 ldb_asprintf_errstring(ldb
,
1652 "setup_supplemental_field: "
1653 "failed to push package_PackagesBlob: %s",
1655 return LDB_ERR_OPERATIONS_ERROR
;
1657 pb_hexstr
= data_blob_hex_string_upper(io
->ac
, &pb_blob
);
1659 return ldb_oom(ldb
);
1661 pp
->name
= "Packages";
1663 pp
->data
= pb_hexstr
;
1666 * setup 'supplementalCredentials' value
1669 scb
.sub
.num_packages
= num_packages
;
1670 scb
.sub
.packages
= packages
;
1672 ndr_err
= ndr_push_struct_blob(&io
->g
.supplemental
, io
->ac
,
1674 (ndr_push_flags_fn_t
)ndr_push_supplementalCredentialsBlob
);
1675 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1676 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
1677 ldb_asprintf_errstring(ldb
,
1678 "setup_supplemental_field: "
1679 "failed to push supplementalCredentialsBlob: %s",
1681 return LDB_ERR_OPERATIONS_ERROR
;
1687 static int setup_last_set_field(struct setup_password_fields_io
*io
)
1689 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
1690 const struct ldb_message
*msg
= NULL
;
1691 struct timeval tv
= { .tv_sec
= 0 };
1692 const struct ldb_val
*old_val
= NULL
;
1693 const struct ldb_val
*new_val
= NULL
;
1696 switch (io
->ac
->req
->operation
) {
1698 msg
= io
->ac
->req
->op
.add
.message
;
1701 msg
= io
->ac
->req
->op
.mod
.message
;
1704 return LDB_ERR_OPERATIONS_ERROR
;
1708 if (io
->ac
->pwd_last_set_bypass
) {
1709 struct ldb_message_element
*el1
= NULL
;
1710 struct ldb_message_element
*el2
= NULL
;
1713 return LDB_ERR_CONSTRAINT_VIOLATION
;
1716 el1
= dsdb_get_single_valued_attr(msg
, "pwdLastSet",
1717 io
->ac
->req
->operation
);
1719 return LDB_ERR_CONSTRAINT_VIOLATION
;
1721 el2
= ldb_msg_find_element(msg
, "pwdLastSet");
1723 return LDB_ERR_CONSTRAINT_VIOLATION
;
1726 return LDB_ERR_CONSTRAINT_VIOLATION
;
1729 io
->g
.last_set
= samdb_result_nttime(msg
, "pwdLastSet", 0);
1733 ret
= msg_find_old_and_new_pwd_val(msg
, "pwdLastSet",
1734 io
->ac
->req
->operation
,
1735 &new_val
, &old_val
);
1736 if (ret
!= LDB_SUCCESS
) {
1740 if (old_val
!= NULL
&& new_val
== NULL
) {
1741 ldb_set_errstring(ldb
,
1742 "'pwdLastSet' deletion is not allowed!");
1743 return LDB_ERR_UNWILLING_TO_PERFORM
;
1746 io
->g
.last_set
= UINT64_MAX
;
1747 if (new_val
!= NULL
) {
1748 struct ldb_message
*tmp_msg
= NULL
;
1750 tmp_msg
= ldb_msg_new(io
->ac
);
1751 if (tmp_msg
== NULL
) {
1752 return ldb_module_oom(io
->ac
->module
);
1755 if (old_val
!= NULL
) {
1756 NTTIME old_last_set
= 0;
1758 ret
= ldb_msg_add_value(tmp_msg
, "oldval",
1760 if (ret
!= LDB_SUCCESS
) {
1764 old_last_set
= samdb_result_nttime(tmp_msg
,
1767 if (io
->u
.pwdLastSet
!= old_last_set
) {
1768 return dsdb_module_werror(io
->ac
->module
,
1769 LDB_ERR_NO_SUCH_ATTRIBUTE
,
1770 WERR_DS_CANT_REM_MISSING_ATT_VAL
,
1771 "setup_last_set_field: old pwdLastSet "
1772 "value not found!");
1776 ret
= ldb_msg_add_value(tmp_msg
, "newval",
1778 if (ret
!= LDB_SUCCESS
) {
1782 io
->g
.last_set
= samdb_result_nttime(tmp_msg
,
1785 } else if (ldb_msg_find_element(msg
, "pwdLastSet")) {
1786 ldb_set_errstring(ldb
,
1787 "'pwdLastSet' deletion is not allowed!");
1788 return LDB_ERR_UNWILLING_TO_PERFORM
;
1791 /* only 0 or -1 (0xFFFFFFFFFFFFFFFF) are allowed */
1792 switch (io
->g
.last_set
) {
1794 if (!io
->ac
->pwd_last_set_default
) {
1797 if (!io
->ac
->update_password
) {
1802 if (!io
->ac
->update_password
&&
1803 io
->u
.pwdLastSet
!= 0 &&
1804 io
->u
.pwdLastSet
!= UINT64_MAX
)
1807 * Just setting pwdLastSet to -1, while not changing
1808 * any password field has no effect if pwdLastSet
1809 * is already non-zero.
1811 io
->ac
->update_lastset
= false;
1814 /* -1 means set it as now */
1816 io
->g
.last_set
= timeval_to_nttime(&tv
);
1819 return dsdb_module_werror(io
->ac
->module
,
1822 "setup_last_set_field: "
1823 "pwdLastSet must be 0 or -1 only!");
1826 if (io
->ac
->req
->operation
== LDB_ADD
) {
1828 * We always need to store the value on add
1834 if (io
->g
.last_set
== io
->u
.pwdLastSet
) {
1836 * Just setting pwdLastSet to 0, is no-op if it's already 0.
1838 io
->ac
->update_lastset
= false;
1844 static int setup_given_passwords(struct setup_password_fields_io
*io
,
1845 struct setup_password_fields_given
*g
)
1847 struct ldb_context
*ldb
;
1850 ldb
= ldb_module_get_ctx(io
->ac
->module
);
1852 if (g
->cleartext_utf8
) {
1853 struct ldb_val
*cleartext_utf16_blob
;
1855 cleartext_utf16_blob
= talloc(io
->ac
, struct ldb_val
);
1856 if (!cleartext_utf16_blob
) {
1857 return ldb_oom(ldb
);
1859 if (!convert_string_talloc(io
->ac
,
1861 g
->cleartext_utf8
->data
,
1862 g
->cleartext_utf8
->length
,
1863 (void *)&cleartext_utf16_blob
->data
,
1864 &cleartext_utf16_blob
->length
)) {
1865 if (g
->cleartext_utf8
->length
!= 0) {
1866 talloc_free(cleartext_utf16_blob
);
1867 ldb_asprintf_errstring(ldb
,
1868 "setup_password_fields: "
1869 "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
1870 io
->u
.sAMAccountName
);
1871 return LDB_ERR_CONSTRAINT_VIOLATION
;
1873 /* passwords with length "0" are valid! */
1874 cleartext_utf16_blob
->data
= NULL
;
1875 cleartext_utf16_blob
->length
= 0;
1878 g
->cleartext_utf16
= cleartext_utf16_blob
;
1879 } else if (g
->cleartext_utf16
) {
1880 struct ldb_val
*cleartext_utf8_blob
;
1882 cleartext_utf8_blob
= talloc(io
->ac
, struct ldb_val
);
1883 if (!cleartext_utf8_blob
) {
1884 return ldb_oom(ldb
);
1886 if (!convert_string_talloc(io
->ac
,
1887 CH_UTF16MUNGED
, CH_UTF8
,
1888 g
->cleartext_utf16
->data
,
1889 g
->cleartext_utf16
->length
,
1890 (void *)&cleartext_utf8_blob
->data
,
1891 &cleartext_utf8_blob
->length
)) {
1892 if (g
->cleartext_utf16
->length
!= 0) {
1893 /* We must bail out here, the input wasn't even
1894 * a multiple of 2 bytes */
1895 talloc_free(cleartext_utf8_blob
);
1896 ldb_asprintf_errstring(ldb
,
1897 "setup_password_fields: "
1898 "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)!",
1899 io
->u
.sAMAccountName
);
1900 return LDB_ERR_CONSTRAINT_VIOLATION
;
1902 /* passwords with length "0" are valid! */
1903 cleartext_utf8_blob
->data
= NULL
;
1904 cleartext_utf8_blob
->length
= 0;
1907 g
->cleartext_utf8
= cleartext_utf8_blob
;
1910 if (g
->cleartext_utf16
) {
1911 struct samr_Password
*nt_hash
;
1913 nt_hash
= talloc(io
->ac
, struct samr_Password
);
1915 return ldb_oom(ldb
);
1917 g
->nt_hash
= nt_hash
;
1919 /* compute the new nt hash */
1920 mdfour(nt_hash
->hash
,
1921 g
->cleartext_utf16
->data
,
1922 g
->cleartext_utf16
->length
);
1925 if (g
->cleartext_utf8
) {
1926 struct samr_Password
*lm_hash
;
1928 lm_hash
= talloc(io
->ac
, struct samr_Password
);
1930 return ldb_oom(ldb
);
1933 /* compute the new lm hash */
1934 ok
= E_deshash((char *)g
->cleartext_utf8
->data
, lm_hash
->hash
);
1936 g
->lm_hash
= lm_hash
;
1938 talloc_free(lm_hash
);
1945 static int setup_password_fields(struct setup_password_fields_io
*io
)
1947 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
1948 struct loadparm_context
*lp_ctx
=
1949 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
1950 struct loadparm_context
);
1953 ret
= setup_last_set_field(io
);
1954 if (ret
!= LDB_SUCCESS
) {
1958 if (!io
->ac
->update_password
) {
1962 /* transform the old password (for password changes) */
1963 ret
= setup_given_passwords(io
, &io
->og
);
1964 if (ret
!= LDB_SUCCESS
) {
1968 /* transform the new password */
1969 ret
= setup_given_passwords(io
, &io
->n
);
1970 if (ret
!= LDB_SUCCESS
) {
1974 if (io
->n
.cleartext_utf8
) {
1975 ret
= setup_kerberos_keys(io
);
1976 if (ret
!= LDB_SUCCESS
) {
1981 ret
= setup_nt_fields(io
);
1982 if (ret
!= LDB_SUCCESS
) {
1986 if (lpcfg_lanman_auth(lp_ctx
)) {
1987 ret
= setup_lm_fields(io
);
1988 if (ret
!= LDB_SUCCESS
) {
1992 io
->g
.lm_hash
= NULL
;
1993 io
->g
.lm_history_len
= 0;
1996 ret
= setup_supplemental_field(io
);
1997 if (ret
!= LDB_SUCCESS
) {
2004 static int make_error_and_update_badPwdCount(struct setup_password_fields_io
*io
)
2006 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
2007 struct ldb_message
*mod_msg
= NULL
;
2011 status
= dsdb_update_bad_pwd_count(io
->ac
, ldb
,
2012 io
->ac
->search_res
->message
,
2013 io
->ac
->dom_res
->message
,
2015 if (!NT_STATUS_IS_OK(status
)) {
2019 if (mod_msg
== NULL
) {
2024 * OK, horrible semantics ahead.
2026 * - We need to abort any existing transaction
2027 * - create a transaction arround the badPwdCount update
2028 * - re-open the transaction so the upper layer
2029 * doesn't know what happened.
2031 * This is needed because returning an error to the upper
2032 * layer will cancel the transaction and undo the badPwdCount
2037 * Checking errors here is a bit pointless.
2038 * What can we do if we can't end the transaction?
2040 ret
= ldb_next_del_trans(io
->ac
->module
);
2041 if (ret
!= LDB_SUCCESS
) {
2042 ldb_debug(ldb
, LDB_DEBUG_FATAL
,
2043 "Failed to abort transaction prior to update of badPwdCount of %s: %s",
2044 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
2045 ldb_errstring(ldb
));
2047 * just return the original error
2052 /* Likewise, what should we do if we can't open a new transaction? */
2053 ret
= ldb_next_start_trans(io
->ac
->module
);
2054 if (ret
!= LDB_SUCCESS
) {
2055 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
2056 "Failed to open transaction to update badPwdCount of %s: %s",
2057 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
2058 ldb_errstring(ldb
));
2060 * just return the original error
2065 ret
= dsdb_module_modify(io
->ac
->module
, mod_msg
,
2066 DSDB_FLAG_NEXT_MODULE
,
2068 if (ret
!= LDB_SUCCESS
) {
2069 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
2070 "Failed to update badPwdCount of %s: %s",
2071 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
2072 ldb_errstring(ldb
));
2074 * We can only ignore this...
2078 ret
= ldb_next_end_trans(io
->ac
->module
);
2079 if (ret
!= LDB_SUCCESS
) {
2080 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
2081 "Failed to close transaction to update badPwdCount of %s: %s",
2082 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
2083 ldb_errstring(ldb
));
2085 * We can only ignore this...
2089 ret
= ldb_next_start_trans(io
->ac
->module
);
2090 if (ret
!= LDB_SUCCESS
) {
2091 ldb_debug(ldb
, LDB_DEBUG_ERROR
,
2092 "Failed to open transaction after update of badPwdCount of %s: %s",
2093 ldb_dn_get_linearized(io
->ac
->search_res
->message
->dn
),
2094 ldb_errstring(ldb
));
2096 * We can only ignore this...
2101 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2102 ldb_asprintf_errstring(ldb
,
2103 "%08X: %s - check_password_restrictions: "
2104 "The old password specified doesn't match!",
2105 W_ERROR_V(WERR_INVALID_PASSWORD
),
2110 static int check_password_restrictions(struct setup_password_fields_io
*io
)
2112 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
2114 struct loadparm_context
*lp_ctx
=
2115 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
2116 struct loadparm_context
);
2118 if (!io
->ac
->update_password
) {
2122 /* First check the old password is correct, for password changes */
2123 if (!io
->ac
->pwd_reset
) {
2124 bool nt_hash_checked
= false;
2126 /* we need the old nt or lm hash given by the client */
2127 if (!io
->og
.nt_hash
&& !io
->og
.lm_hash
) {
2128 ldb_asprintf_errstring(ldb
,
2129 "check_password_restrictions: "
2130 "You need to provide the old password in order "
2132 return LDB_ERR_UNWILLING_TO_PERFORM
;
2135 /* The password modify through the NT hash is encouraged and
2136 has no problems at all */
2137 if (io
->og
.nt_hash
) {
2138 if (!io
->o
.nt_hash
|| memcmp(io
->og
.nt_hash
->hash
, io
->o
.nt_hash
->hash
, 16) != 0) {
2139 return make_error_and_update_badPwdCount(io
);
2142 nt_hash_checked
= true;
2145 /* But it is also possible to change a password by the LM hash
2146 * alone for compatibility reasons. This check is optional if
2147 * the NT hash was already checked - otherwise it's mandatory.
2148 * (as the SAMR operations request it). */
2149 if (io
->og
.lm_hash
) {
2150 if ((!io
->o
.lm_hash
&& !nt_hash_checked
)
2151 || (io
->o
.lm_hash
&& memcmp(io
->og
.lm_hash
->hash
, io
->o
.lm_hash
->hash
, 16) != 0)) {
2152 return make_error_and_update_badPwdCount(io
);
2157 if (io
->u
.restrictions
== 0) {
2158 /* FIXME: Is this right? */
2162 /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
2163 if ((io
->u
.pwdLastSet
- io
->ac
->status
->domain_data
.minPwdAge
> io
->g
.last_set
) &&
2166 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2167 ldb_asprintf_errstring(ldb
,
2168 "%08X: %s - check_password_restrictions: "
2169 "password is too young to change!",
2170 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2176 * Fundamental password checks done by the call
2177 * "samdb_check_password".
2178 * It is also in use by "dcesrv_samr_ValidatePassword".
2180 if (io
->n
.cleartext_utf8
!= NULL
) {
2181 enum samr_ValidationStatus vstat
;
2182 vstat
= samdb_check_password(io
->ac
, lp_ctx
,
2183 io
->n
.cleartext_utf8
,
2184 io
->ac
->status
->domain_data
.pwdProperties
,
2185 io
->ac
->status
->domain_data
.minPwdLength
);
2187 case SAMR_VALIDATION_STATUS_SUCCESS
:
2188 /* perfect -> proceed! */
2191 case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT
:
2192 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2193 ldb_asprintf_errstring(ldb
,
2194 "%08X: %s - check_password_restrictions: "
2195 "the password is too short. It should be equal or longer than %u characters!",
2196 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2198 io
->ac
->status
->domain_data
.minPwdLength
);
2199 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_PASSWORD_TOO_SHORT
;
2202 case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH
:
2203 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2204 ldb_asprintf_errstring(ldb
,
2205 "%08X: %s - check_password_restrictions: "
2206 "the password does not meet the complexity criteria!",
2207 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2209 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_NOT_COMPLEX
;
2213 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2214 ldb_asprintf_errstring(ldb
,
2215 "%08X: %s - check_password_restrictions: "
2216 "the password doesn't fit due to a miscellaneous restriction!",
2217 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2223 if (io
->ac
->pwd_reset
) {
2227 if (io
->n
.nt_hash
) {
2230 /* checks the NT hash password history */
2231 for (i
= 0; i
< io
->o
.nt_history_len
; i
++) {
2232 ret
= memcmp(io
->n
.nt_hash
, io
->o
.nt_history
[i
].hash
, 16);
2234 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2235 ldb_asprintf_errstring(ldb
,
2236 "%08X: %s - check_password_restrictions: "
2237 "the password was already used (in history)!",
2238 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2240 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_PWD_IN_HISTORY
;
2246 if (io
->n
.lm_hash
) {
2249 /* checks the LM hash password history */
2250 for (i
= 0; i
< io
->o
.lm_history_len
; i
++) {
2251 ret
= memcmp(io
->n
.lm_hash
, io
->o
.lm_history
[i
].hash
, 16);
2253 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2254 ldb_asprintf_errstring(ldb
,
2255 "%08X: %s - check_password_restrictions: "
2256 "the password was already used (in history)!",
2257 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2259 io
->ac
->status
->reject_reason
= SAM_PWD_CHANGE_PWD_IN_HISTORY
;
2265 /* are all password changes disallowed? */
2266 if (io
->ac
->status
->domain_data
.pwdProperties
& DOMAIN_REFUSE_PASSWORD_CHANGE
) {
2267 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2268 ldb_asprintf_errstring(ldb
,
2269 "%08X: %s - check_password_restrictions: "
2270 "password changes disabled!",
2271 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2276 /* can this user change the password? */
2277 if (io
->u
.userAccountControl
& UF_PASSWD_CANT_CHANGE
) {
2278 ret
= LDB_ERR_CONSTRAINT_VIOLATION
;
2279 ldb_asprintf_errstring(ldb
,
2280 "%08X: %s - check_password_restrictions: "
2281 "password can't be changed on this account!",
2282 W_ERROR_V(WERR_PASSWORD_RESTRICTION
),
2290 static int update_final_msg(struct setup_password_fields_io
*io
)
2292 struct ldb_context
*ldb
= ldb_module_get_ctx(io
->ac
->module
);
2295 bool update_password
= io
->ac
->update_password
;
2296 bool update_scb
= io
->ac
->update_password
;
2299 * If we add a user without initial password,
2300 * we need to add replication meta data for
2301 * following attributes:
2307 * If we add a user with initial password or a
2308 * password is changed of an existing user,
2309 * we need to replace the following attributes
2310 * with a forced meta data update, e.g. also
2311 * when updating an empty attribute with an empty value:
2316 * - supplementalCredentials
2319 switch (io
->ac
->req
->operation
) {
2321 update_password
= true;
2322 el_flags
|= DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
2325 el_flags
|= LDB_FLAG_MOD_REPLACE
;
2326 el_flags
|= DSDB_FLAG_INTERNAL_FORCE_META_DATA
;
2329 return ldb_module_operr(io
->ac
->module
);
2332 if (update_password
) {
2333 ret
= ldb_msg_add_empty(io
->ac
->update_msg
,
2336 if (ret
!= LDB_SUCCESS
) {
2339 ret
= ldb_msg_add_empty(io
->ac
->update_msg
,
2342 if (ret
!= LDB_SUCCESS
) {
2345 ret
= ldb_msg_add_empty(io
->ac
->update_msg
,
2348 if (ret
!= LDB_SUCCESS
) {
2351 ret
= ldb_msg_add_empty(io
->ac
->update_msg
,
2354 if (ret
!= LDB_SUCCESS
) {
2359 ret
= ldb_msg_add_empty(io
->ac
->update_msg
,
2360 "supplementalCredentials",
2362 if (ret
!= LDB_SUCCESS
) {
2366 if (io
->ac
->update_lastset
) {
2367 ret
= ldb_msg_add_empty(io
->ac
->update_msg
,
2370 if (ret
!= LDB_SUCCESS
) {
2375 if (io
->g
.nt_hash
!= NULL
) {
2376 ret
= samdb_msg_add_hash(ldb
, io
->ac
,
2380 if (ret
!= LDB_SUCCESS
) {
2384 if (io
->g
.lm_hash
!= NULL
) {
2385 ret
= samdb_msg_add_hash(ldb
, io
->ac
,
2389 if (ret
!= LDB_SUCCESS
) {
2393 if (io
->g
.nt_history_len
> 0) {
2394 ret
= samdb_msg_add_hashes(ldb
, io
->ac
,
2398 io
->g
.nt_history_len
);
2399 if (ret
!= LDB_SUCCESS
) {
2403 if (io
->g
.lm_history_len
> 0) {
2404 ret
= samdb_msg_add_hashes(ldb
, io
->ac
,
2408 io
->g
.lm_history_len
);
2409 if (ret
!= LDB_SUCCESS
) {
2413 if (io
->g
.supplemental
.length
> 0) {
2414 ret
= ldb_msg_add_value(io
->ac
->update_msg
,
2415 "supplementalCredentials",
2416 &io
->g
.supplemental
, NULL
);
2417 if (ret
!= LDB_SUCCESS
) {
2421 if (io
->ac
->update_lastset
) {
2422 ret
= samdb_msg_add_uint64(ldb
, io
->ac
,
2426 if (ret
!= LDB_SUCCESS
) {
2435 * This is intended for use by the "password_hash" module since there
2436 * password changes can be specified through one message element with the
2437 * new password (to set) and another one with the old password (to unset).
2439 * The first which sets a password (new value) can have flags
2440 * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations
2441 * for entries). The latter (old value) has always specified
2442 * LDB_FLAG_MOD_DELETE.
2444 * Returns LDB_ERR_CONSTRAINT_VIOLATION and LDB_ERR_UNWILLING_TO_PERFORM if
2445 * matching message elements are malformed in respect to the set/change rules.
2446 * Otherwise it returns LDB_SUCCESS.
2448 static int msg_find_old_and_new_pwd_val(const struct ldb_message
*msg
,
2450 enum ldb_request_type operation
,
2451 const struct ldb_val
**new_val
,
2452 const struct ldb_val
**old_val
)
2463 for (i
= 0; i
< msg
->num_elements
; i
++) {
2464 if (ldb_attr_cmp(msg
->elements
[i
].name
, name
) != 0) {
2468 if ((operation
== LDB_MODIFY
) &&
2469 (LDB_FLAG_MOD_TYPE(msg
->elements
[i
].flags
) == LDB_FLAG_MOD_DELETE
)) {
2470 /* 0 values are allowed */
2471 if (msg
->elements
[i
].num_values
== 1) {
2472 *old_val
= &msg
->elements
[i
].values
[0];
2473 } else if (msg
->elements
[i
].num_values
> 1) {
2474 return LDB_ERR_CONSTRAINT_VIOLATION
;
2476 } else if ((operation
== LDB_MODIFY
) &&
2477 (LDB_FLAG_MOD_TYPE(msg
->elements
[i
].flags
) == LDB_FLAG_MOD_REPLACE
)) {
2478 if (msg
->elements
[i
].num_values
> 0) {
2479 *new_val
= &msg
->elements
[i
].values
[msg
->elements
[i
].num_values
- 1];
2481 return LDB_ERR_UNWILLING_TO_PERFORM
;
2484 /* Add operations and LDB_FLAG_MOD_ADD */
2485 if (msg
->elements
[i
].num_values
> 0) {
2486 *new_val
= &msg
->elements
[i
].values
[msg
->elements
[i
].num_values
- 1];
2488 return LDB_ERR_CONSTRAINT_VIOLATION
;
2496 static int setup_io(struct ph_context
*ac
,
2497 const struct ldb_message
*client_msg
,
2498 const struct ldb_message
*existing_msg
,
2499 struct setup_password_fields_io
*io
)
2501 const struct ldb_val
*quoted_utf16
, *old_quoted_utf16
, *lm_hash
, *old_lm_hash
;
2502 struct ldb_context
*ldb
= ldb_module_get_ctx(ac
->module
);
2503 struct loadparm_context
*lp_ctx
= talloc_get_type(
2504 ldb_get_opaque(ldb
, "loadparm"), struct loadparm_context
);
2506 const struct ldb_message
*info_msg
= NULL
;
2510 /* Some operations below require kerberos contexts */
2512 if (existing_msg
!= NULL
) {
2514 * This is a modify operation
2516 info_msg
= existing_msg
;
2519 * This is an add operation
2521 info_msg
= client_msg
;
2524 if (smb_krb5_init_context(ac
,
2525 (struct loadparm_context
*)ldb_get_opaque(ldb
, "loadparm"),
2526 &io
->smb_krb5_context
) != 0) {
2527 return ldb_operr(ldb
);
2532 io
->u
.userAccountControl
= ldb_msg_find_attr_as_uint(info_msg
,
2533 "userAccountControl", 0);
2534 if (info_msg
== existing_msg
) {
2536 * We only take pwdLastSet from the existing object
2537 * otherwise we leave it as 0.
2539 * If no attribute is available, e.g. on deleted objects
2540 * we remember that as UINT64_MAX.
2542 io
->u
.pwdLastSet
= samdb_result_nttime(info_msg
, "pwdLastSet",
2545 io
->u
.sAMAccountName
= ldb_msg_find_attr_as_string(info_msg
,
2546 "sAMAccountName", NULL
);
2547 io
->u
.user_principal_name
= ldb_msg_find_attr_as_string(info_msg
,
2548 "userPrincipalName", NULL
);
2549 io
->u
.is_computer
= ldb_msg_check_string_attribute(info_msg
, "objectClass", "computer");
2551 if (io
->u
.sAMAccountName
== NULL
) {
2552 ldb_asprintf_errstring(ldb
,
2553 "setup_io: sAMAccountName attribute is missing on %s for attempted password set/change",
2554 ldb_dn_get_linearized(info_msg
->dn
));
2556 return LDB_ERR_CONSTRAINT_VIOLATION
;
2559 if (io
->u
.userAccountControl
& UF_INTERDOMAIN_TRUST_ACCOUNT
) {
2560 struct ldb_control
*permit_trust
= ldb_request_get_control(ac
->req
,
2561 DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID
);
2563 if (permit_trust
== NULL
) {
2564 ret
= LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS
;
2565 ldb_asprintf_errstring(ldb
,
2566 "%08X: %s - setup_io: changing the interdomain trust password "
2567 "on %s not allowed via LDAP. Use LSA or NETLOGON",
2568 W_ERROR_V(WERR_ACCESS_DENIED
),
2570 ldb_dn_get_linearized(info_msg
->dn
));
2575 /* Only non-trust accounts have restrictions (possibly this test is the
2576 * wrong way around, but we like to be restrictive if possible */
2577 io
->u
.restrictions
= !(io
->u
.userAccountControl
2578 & (UF_INTERDOMAIN_TRUST_ACCOUNT
| UF_WORKSTATION_TRUST_ACCOUNT
2579 | UF_SERVER_TRUST_ACCOUNT
));
2581 if (ac
->userPassword
) {
2582 ret
= msg_find_old_and_new_pwd_val(client_msg
, "userPassword",
2584 &io
->n
.cleartext_utf8
,
2585 &io
->og
.cleartext_utf8
);
2586 if (ret
!= LDB_SUCCESS
) {
2587 ldb_asprintf_errstring(ldb
,
2589 "it's only allowed to set the old password once!");
2594 if (io
->n
.cleartext_utf8
!= NULL
) {
2595 struct ldb_val
*cleartext_utf8_blob
;
2598 cleartext_utf8_blob
= talloc(io
->ac
, struct ldb_val
);
2599 if (!cleartext_utf8_blob
) {
2600 return ldb_oom(ldb
);
2603 *cleartext_utf8_blob
= *io
->n
.cleartext_utf8
;
2605 /* make sure we have a null terminated string */
2606 p
= talloc_strndup(cleartext_utf8_blob
,
2607 (const char *)io
->n
.cleartext_utf8
->data
,
2608 io
->n
.cleartext_utf8
->length
);
2609 if ((p
== NULL
) && (io
->n
.cleartext_utf8
->length
> 0)) {
2610 return ldb_oom(ldb
);
2612 cleartext_utf8_blob
->data
= (uint8_t *)p
;
2614 io
->n
.cleartext_utf8
= cleartext_utf8_blob
;
2617 ret
= msg_find_old_and_new_pwd_val(client_msg
, "clearTextPassword",
2619 &io
->n
.cleartext_utf16
,
2620 &io
->og
.cleartext_utf16
);
2621 if (ret
!= LDB_SUCCESS
) {
2622 ldb_asprintf_errstring(ldb
,
2624 "it's only allowed to set the old password once!");
2628 /* this rather strange looking piece of code is there to
2629 handle a ldap client setting a password remotely using the
2630 unicodePwd ldap field. The syntax is that the password is
2631 in UTF-16LE, with a " at either end. Unfortunately the
2632 unicodePwd field is also used to store the nt hashes
2633 internally in Samba, and is used in the nt hash format on
2634 the wire in DRS replication, so we have a single name for
2635 two distinct values. The code below leaves us with a small
2636 chance (less than 1 in 2^32) of a mixup, if someone manages
2637 to create a MD4 hash which starts and ends in 0x22 0x00, as
2638 that would then be treated as a UTF16 password rather than
2641 ret
= msg_find_old_and_new_pwd_val(client_msg
, "unicodePwd",
2645 if (ret
!= LDB_SUCCESS
) {
2646 ldb_asprintf_errstring(ldb
,
2648 "it's only allowed to set the old password once!");
2652 /* Checks and converts the actual "unicodePwd" attribute */
2653 if (!ac
->hash_values
&&
2655 quoted_utf16
->length
>= 4 &&
2656 quoted_utf16
->data
[0] == '"' &&
2657 quoted_utf16
->data
[1] == 0 &&
2658 quoted_utf16
->data
[quoted_utf16
->length
-2] == '"' &&
2659 quoted_utf16
->data
[quoted_utf16
->length
-1] == 0) {
2660 struct ldb_val
*quoted_utf16_2
;
2662 if (io
->n
.cleartext_utf16
) {
2663 /* refuse the change if someone wants to change with
2664 with both UTF16 possibilities at the same time... */
2665 ldb_asprintf_errstring(ldb
,
2667 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2668 return LDB_ERR_UNWILLING_TO_PERFORM
;
2672 * adapt the quoted UTF16 string to be a real
2675 quoted_utf16_2
= talloc(io
->ac
, struct ldb_val
);
2676 if (quoted_utf16_2
== NULL
) {
2677 return ldb_oom(ldb
);
2680 quoted_utf16_2
->data
= quoted_utf16
->data
+ 2;
2681 quoted_utf16_2
->length
= quoted_utf16
->length
-4;
2682 io
->n
.cleartext_utf16
= quoted_utf16_2
;
2683 io
->n
.nt_hash
= NULL
;
2685 } else if (quoted_utf16
) {
2686 /* We have only the hash available -> so no plaintext here */
2687 if (!ac
->hash_values
) {
2688 /* refuse the change if someone wants to change
2689 the hash without control specified... */
2690 ldb_asprintf_errstring(ldb
,
2692 "it's not allowed to set the NT hash password directly'");
2693 /* this looks odd but this is what Windows does:
2694 returns "UNWILLING_TO_PERFORM" on wrong
2695 password sets and "CONSTRAINT_VIOLATION" on
2696 wrong password changes. */
2697 if (old_quoted_utf16
== NULL
) {
2698 return LDB_ERR_UNWILLING_TO_PERFORM
;
2701 return LDB_ERR_CONSTRAINT_VIOLATION
;
2704 io
->n
.nt_hash
= talloc(io
->ac
, struct samr_Password
);
2705 memcpy(io
->n
.nt_hash
->hash
, quoted_utf16
->data
,
2706 MIN(quoted_utf16
->length
, sizeof(io
->n
.nt_hash
->hash
)));
2709 /* Checks and converts the previous "unicodePwd" attribute */
2710 if (!ac
->hash_values
&&
2712 old_quoted_utf16
->length
>= 4 &&
2713 old_quoted_utf16
->data
[0] == '"' &&
2714 old_quoted_utf16
->data
[1] == 0 &&
2715 old_quoted_utf16
->data
[old_quoted_utf16
->length
-2] == '"' &&
2716 old_quoted_utf16
->data
[old_quoted_utf16
->length
-1] == 0) {
2717 struct ldb_val
*old_quoted_utf16_2
;
2719 if (io
->og
.cleartext_utf16
) {
2720 /* refuse the change if someone wants to change with
2721 both UTF16 possibilities at the same time... */
2722 ldb_asprintf_errstring(ldb
,
2724 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'clearTextPassword'");
2725 return LDB_ERR_UNWILLING_TO_PERFORM
;
2729 * adapt the quoted UTF16 string to be a real
2732 old_quoted_utf16_2
= talloc(io
->ac
, struct ldb_val
);
2733 if (old_quoted_utf16_2
== NULL
) {
2734 return ldb_oom(ldb
);
2737 old_quoted_utf16_2
->data
= old_quoted_utf16
->data
+ 2;
2738 old_quoted_utf16_2
->length
= old_quoted_utf16
->length
-4;
2740 io
->og
.cleartext_utf16
= old_quoted_utf16_2
;
2741 io
->og
.nt_hash
= NULL
;
2742 } else if (old_quoted_utf16
) {
2743 /* We have only the hash available -> so no plaintext here */
2744 if (!ac
->hash_values
) {
2745 /* refuse the change if someone wants to change
2746 the hash without control specified... */
2747 ldb_asprintf_errstring(ldb
,
2749 "it's not allowed to set the NT hash password directly'");
2750 return LDB_ERR_UNWILLING_TO_PERFORM
;
2753 io
->og
.nt_hash
= talloc(io
->ac
, struct samr_Password
);
2754 memcpy(io
->og
.nt_hash
->hash
, old_quoted_utf16
->data
,
2755 MIN(old_quoted_utf16
->length
, sizeof(io
->og
.nt_hash
->hash
)));
2758 /* Handles the "dBCSPwd" attribute (LM hash) */
2759 io
->n
.lm_hash
= NULL
; io
->og
.lm_hash
= NULL
;
2760 ret
= msg_find_old_and_new_pwd_val(client_msg
, "dBCSPwd",
2762 &lm_hash
, &old_lm_hash
);
2763 if (ret
!= LDB_SUCCESS
) {
2764 ldb_asprintf_errstring(ldb
,
2766 "it's only allowed to set the old password once!");
2770 if (((lm_hash
!= NULL
) || (old_lm_hash
!= NULL
)) && (!ac
->hash_values
)) {
2771 /* refuse the change if someone wants to change the hash
2772 without control specified... */
2773 ldb_asprintf_errstring(ldb
,
2775 "it's not allowed to set the LM hash password directly'");
2776 return LDB_ERR_UNWILLING_TO_PERFORM
;
2779 if (lpcfg_lanman_auth(lp_ctx
) && (lm_hash
!= NULL
)) {
2780 io
->n
.lm_hash
= talloc(io
->ac
, struct samr_Password
);
2781 memcpy(io
->n
.lm_hash
->hash
, lm_hash
->data
, MIN(lm_hash
->length
,
2782 sizeof(io
->n
.lm_hash
->hash
)));
2784 if (lpcfg_lanman_auth(lp_ctx
) && (old_lm_hash
!= NULL
)) {
2785 io
->og
.lm_hash
= talloc(io
->ac
, struct samr_Password
);
2786 memcpy(io
->og
.lm_hash
->hash
, old_lm_hash
->data
, MIN(old_lm_hash
->length
,
2787 sizeof(io
->og
.lm_hash
->hash
)));
2791 * Handles the password change control if it's specified. It has the
2792 * precedance and overrides already specified old password values of
2793 * change requests (but that shouldn't happen since the control is
2794 * fully internal and only used in conjunction with replace requests!).
2796 if (ac
->change
!= NULL
) {
2797 io
->og
.nt_hash
= NULL
;
2798 if (ac
->change
->old_nt_pwd_hash
!= NULL
) {
2799 io
->og
.nt_hash
= talloc_memdup(io
->ac
,
2800 ac
->change
->old_nt_pwd_hash
,
2801 sizeof(struct samr_Password
));
2803 io
->og
.lm_hash
= NULL
;
2804 if (lpcfg_lanman_auth(lp_ctx
) && (ac
->change
->old_lm_pwd_hash
!= NULL
)) {
2805 io
->og
.lm_hash
= talloc_memdup(io
->ac
,
2806 ac
->change
->old_lm_pwd_hash
,
2807 sizeof(struct samr_Password
));
2811 /* refuse the change if someone wants to change the clear-
2812 text and supply his own hashes at the same time... */
2813 if ((io
->n
.cleartext_utf8
|| io
->n
.cleartext_utf16
)
2814 && (io
->n
.nt_hash
|| io
->n
.lm_hash
)) {
2815 ldb_asprintf_errstring(ldb
,
2817 "it's only allowed to set the password in form of cleartext attributes or as hashes");
2818 return LDB_ERR_UNWILLING_TO_PERFORM
;
2821 /* refuse the change if someone wants to change the password
2822 using both plaintext methods (UTF8 and UTF16) at the same time... */
2823 if (io
->n
.cleartext_utf8
&& io
->n
.cleartext_utf16
) {
2824 ldb_asprintf_errstring(ldb
,
2826 "it's only allowed to set the cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2827 return LDB_ERR_UNWILLING_TO_PERFORM
;
2830 /* refuse the change if someone tries to set/change the password by
2831 * the lanman hash alone and we've deactivated that mechanism. This
2832 * would end in an account without any password! */
2833 if (io
->ac
->update_password
2834 && (!io
->n
.cleartext_utf8
) && (!io
->n
.cleartext_utf16
)
2835 && (!io
->n
.nt_hash
) && (!io
->n
.lm_hash
)) {
2836 ldb_asprintf_errstring(ldb
,
2838 "It's not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!");
2839 /* on "userPassword" and "clearTextPassword" we've to return
2840 * something different, since these are virtual attributes */
2841 if ((ldb_msg_find_element(client_msg
, "userPassword") != NULL
) ||
2842 (ldb_msg_find_element(client_msg
, "clearTextPassword") != NULL
)) {
2843 return LDB_ERR_CONSTRAINT_VIOLATION
;
2845 return LDB_ERR_UNWILLING_TO_PERFORM
;
2848 /* refuse the change if someone wants to compare against a plaintext
2849 or hash at the same time for a "password modify" operation... */
2850 if ((io
->og
.cleartext_utf8
|| io
->og
.cleartext_utf16
)
2851 && (io
->og
.nt_hash
|| io
->og
.lm_hash
)) {
2852 ldb_asprintf_errstring(ldb
,
2854 "it's only allowed to provide the old password in form of cleartext attributes or as hashes");
2855 return LDB_ERR_UNWILLING_TO_PERFORM
;
2858 /* refuse the change if someone wants to compare against both
2859 * plaintexts at the same time for a "password modify" operation... */
2860 if (io
->og
.cleartext_utf8
&& io
->og
.cleartext_utf16
) {
2861 ldb_asprintf_errstring(ldb
,
2863 "it's only allowed to provide the old cleartext password as 'unicodePwd' or as 'userPassword' or as 'clearTextPassword'");
2864 return LDB_ERR_UNWILLING_TO_PERFORM
;
2867 /* Decides if we have a password modify or password reset operation */
2868 if (ac
->req
->operation
== LDB_ADD
) {
2869 /* On "add" we have only "password reset" */
2870 ac
->pwd_reset
= true;
2871 } else if (ac
->req
->operation
== LDB_MODIFY
) {
2872 if (io
->og
.cleartext_utf8
|| io
->og
.cleartext_utf16
2873 || io
->og
.nt_hash
|| io
->og
.lm_hash
) {
2874 /* If we have an old password specified then for sure it
2875 * is a user "password change" */
2876 ac
->pwd_reset
= false;
2878 /* Otherwise we have also here a "password reset" */
2879 ac
->pwd_reset
= true;
2882 /* this shouldn't happen */
2883 return ldb_operr(ldb
);
2886 if (existing_msg
!= NULL
) {
2889 if (ac
->pwd_reset
) {
2890 /* Get the old password from the database */
2891 status
= samdb_result_passwords_no_lockout(ac
,
2897 /* Get the old password from the database */
2898 status
= samdb_result_passwords(ac
,
2905 if (NT_STATUS_EQUAL(status
, NT_STATUS_ACCOUNT_LOCKED_OUT
)) {
2906 return dsdb_module_werror(ac
->module
,
2907 LDB_ERR_CONSTRAINT_VIOLATION
,
2908 WERR_ACCOUNT_LOCKED_OUT
,
2909 "Password change not permitted,"
2910 " account locked out!");
2913 if (!NT_STATUS_IS_OK(status
)) {
2915 * This only happens if the database has gone weird,
2916 * not if we are just missing the passwords
2918 return ldb_operr(ldb
);
2921 io
->o
.nt_history_len
= samdb_result_hashes(ac
, existing_msg
,
2924 io
->o
.lm_history_len
= samdb_result_hashes(ac
, existing_msg
,
2927 io
->o
.supplemental
= ldb_msg_find_ldb_val(existing_msg
,
2928 "supplementalCredentials");
2930 if (io
->o
.supplemental
!= NULL
) {
2931 enum ndr_err_code ndr_err
;
2933 ndr_err
= ndr_pull_struct_blob_all(io
->o
.supplemental
, io
->ac
,
2935 (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
);
2936 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
2937 status
= ndr_map_error2ntstatus(ndr_err
);
2938 ldb_asprintf_errstring(ldb
,
2939 "setup_io: failed to pull "
2940 "old supplementalCredentialsBlob: %s",
2942 return LDB_ERR_OPERATIONS_ERROR
;
2950 static struct ph_context
*ph_init_context(struct ldb_module
*module
,
2951 struct ldb_request
*req
,
2953 bool update_password
)
2955 struct ldb_context
*ldb
;
2956 struct ph_context
*ac
;
2958 ldb
= ldb_module_get_ctx(module
);
2960 ac
= talloc_zero(req
, struct ph_context
);
2962 ldb_set_errstring(ldb
, "Out of Memory");
2966 ac
->module
= module
;
2968 ac
->userPassword
= userPassword
;
2969 ac
->update_password
= update_password
;
2970 ac
->update_lastset
= true;
2975 static void ph_apply_controls(struct ph_context
*ac
)
2977 struct ldb_control
*ctrl
;
2979 ac
->change_status
= false;
2980 ctrl
= ldb_request_get_control(ac
->req
,
2981 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID
);
2983 ac
->change_status
= true;
2985 /* Mark the "change status" control as uncritical (done) */
2986 ctrl
->critical
= false;
2989 ac
->hash_values
= false;
2990 ctrl
= ldb_request_get_control(ac
->req
,
2991 DSDB_CONTROL_PASSWORD_HASH_VALUES_OID
);
2993 ac
->hash_values
= true;
2995 /* Mark the "hash values" control as uncritical (done) */
2996 ctrl
->critical
= false;
2999 ctrl
= ldb_request_get_control(ac
->req
,
3000 DSDB_CONTROL_PASSWORD_CHANGE_OID
);
3002 ac
->change
= (struct dsdb_control_password_change
*) ctrl
->data
;
3004 /* Mark the "change" control as uncritical (done) */
3005 ctrl
->critical
= false;
3008 ac
->pwd_last_set_bypass
= false;
3009 ctrl
= ldb_request_get_control(ac
->req
,
3010 DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID
);
3012 ac
->pwd_last_set_bypass
= true;
3014 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
3015 ctrl
->critical
= false;
3018 ac
->pwd_last_set_default
= false;
3019 ctrl
= ldb_request_get_control(ac
->req
,
3020 DSDB_CONTROL_PASSWORD_DEFAULT_LAST_SET_OID
);
3022 ac
->pwd_last_set_default
= true;
3024 /* Mark the "bypass pwdLastSet" control as uncritical (done) */
3025 ctrl
->critical
= false;
3029 static int ph_op_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3031 struct ph_context
*ac
;
3033 ac
= talloc_get_type(req
->context
, struct ph_context
);
3036 return ldb_module_done(ac
->req
, NULL
, NULL
,
3037 LDB_ERR_OPERATIONS_ERROR
);
3040 if (ares
->type
== LDB_REPLY_REFERRAL
) {
3041 return ldb_module_send_referral(ac
->req
, ares
->referral
);
3044 if ((ares
->error
!= LDB_ERR_OPERATIONS_ERROR
) && (ac
->change_status
)) {
3045 /* On success and trivial errors a status control is being
3046 * added (used for example by the "samdb_set_password" call) */
3047 ldb_reply_add_control(ares
,
3048 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID
,
3053 if (ares
->error
!= LDB_SUCCESS
) {
3054 return ldb_module_done(ac
->req
, ares
->controls
,
3055 ares
->response
, ares
->error
);
3058 if (ares
->type
!= LDB_REPLY_DONE
) {
3060 return ldb_module_done(ac
->req
, NULL
, NULL
,
3061 LDB_ERR_OPERATIONS_ERROR
);
3064 return ldb_module_done(ac
->req
, ares
->controls
,
3065 ares
->response
, ares
->error
);
3068 static int password_hash_add_do_add(struct ph_context
*ac
);
3069 static int ph_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
3070 static int password_hash_mod_search_self(struct ph_context
*ac
);
3071 static int ph_mod_search_callback(struct ldb_request
*req
, struct ldb_reply
*ares
);
3072 static int password_hash_mod_do_mod(struct ph_context
*ac
);
3074 static int get_domain_data_callback(struct ldb_request
*req
,
3075 struct ldb_reply
*ares
)
3077 struct ldb_context
*ldb
;
3078 struct ph_context
*ac
;
3079 struct loadparm_context
*lp_ctx
;
3080 int ret
= LDB_SUCCESS
;
3082 ac
= talloc_get_type(req
->context
, struct ph_context
);
3083 ldb
= ldb_module_get_ctx(ac
->module
);
3086 ret
= LDB_ERR_OPERATIONS_ERROR
;
3089 if (ares
->error
!= LDB_SUCCESS
) {
3090 return ldb_module_done(ac
->req
, ares
->controls
,
3091 ares
->response
, ares
->error
);
3094 switch (ares
->type
) {
3095 case LDB_REPLY_ENTRY
:
3096 if (ac
->status
!= NULL
) {
3099 ldb_set_errstring(ldb
, "Too many results");
3100 ret
= LDB_ERR_OPERATIONS_ERROR
;
3104 /* Setup the "status" structure (used as control later) */
3105 ac
->status
= talloc_zero(ac
->req
,
3106 struct dsdb_control_password_change_status
);
3107 if (ac
->status
== NULL
) {
3111 ret
= LDB_ERR_OPERATIONS_ERROR
;
3115 /* Setup the "domain data" structure */
3116 ac
->status
->domain_data
.pwdProperties
=
3117 ldb_msg_find_attr_as_uint(ares
->message
, "pwdProperties", -1);
3118 ac
->status
->domain_data
.pwdHistoryLength
=
3119 ldb_msg_find_attr_as_uint(ares
->message
, "pwdHistoryLength", -1);
3120 ac
->status
->domain_data
.maxPwdAge
=
3121 ldb_msg_find_attr_as_int64(ares
->message
, "maxPwdAge", -1);
3122 ac
->status
->domain_data
.minPwdAge
=
3123 ldb_msg_find_attr_as_int64(ares
->message
, "minPwdAge", -1);
3124 ac
->status
->domain_data
.minPwdLength
=
3125 ldb_msg_find_attr_as_uint(ares
->message
, "minPwdLength", -1);
3126 ac
->status
->domain_data
.store_cleartext
=
3127 ac
->status
->domain_data
.pwdProperties
& DOMAIN_PASSWORD_STORE_CLEARTEXT
;
3129 /* For a domain DN, this puts things in dotted notation */
3130 /* For builtin domains, this will give details for the host,
3131 * but that doesn't really matter, as it's just used for salt
3132 * and kerberos principals, which don't exist here */
3134 lp_ctx
= talloc_get_type(ldb_get_opaque(ldb
, "loadparm"),
3135 struct loadparm_context
);
3137 ac
->status
->domain_data
.dns_domain
= lpcfg_dnsdomain(lp_ctx
);
3138 ac
->status
->domain_data
.realm
= lpcfg_realm(lp_ctx
);
3139 ac
->status
->domain_data
.netbios_domain
= lpcfg_sam_name(lp_ctx
);
3141 ac
->status
->reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
3143 if (ac
->dom_res
!= NULL
) {
3146 ldb_set_errstring(ldb
, "Too many results");
3147 ret
= LDB_ERR_OPERATIONS_ERROR
;
3151 ac
->dom_res
= talloc_steal(ac
, ares
);
3155 case LDB_REPLY_REFERRAL
:
3161 case LDB_REPLY_DONE
:
3163 /* call the next step */
3164 switch (ac
->req
->operation
) {
3166 ret
= password_hash_add_do_add(ac
);
3170 ret
= password_hash_mod_do_mod(ac
);
3174 ret
= LDB_ERR_OPERATIONS_ERROR
;
3181 if (ret
!= LDB_SUCCESS
) {
3182 struct ldb_reply
*new_ares
;
3184 new_ares
= talloc_zero(ac
->req
, struct ldb_reply
);
3185 if (new_ares
== NULL
) {
3187 return ldb_module_done(ac
->req
, NULL
, NULL
,
3188 LDB_ERR_OPERATIONS_ERROR
);
3191 new_ares
->error
= ret
;
3192 if ((ret
!= LDB_ERR_OPERATIONS_ERROR
) && (ac
->change_status
)) {
3193 /* On success and trivial errors a status control is being
3194 * added (used for example by the "samdb_set_password" call) */
3195 ldb_reply_add_control(new_ares
,
3196 DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID
,
3201 return ldb_module_done(ac
->req
, new_ares
->controls
,
3202 new_ares
->response
, new_ares
->error
);
3208 static int build_domain_data_request(struct ph_context
*ac
)
3210 /* attrs[] is returned from this function in
3211 ac->dom_req->op.search.attrs, so it must be static, as
3212 otherwise the compiler can put it on the stack */
3213 struct ldb_context
*ldb
;
3214 static const char * const attrs
[] = { "pwdProperties",
3220 "lockOutObservationWindow",
3224 ldb
= ldb_module_get_ctx(ac
->module
);
3226 ret
= ldb_build_search_req(&ac
->dom_req
, ldb
, ac
,
3227 ldb_get_default_basedn(ldb
),
3231 ac
, get_domain_data_callback
,
3233 LDB_REQ_SET_LOCATION(ac
->dom_req
);
3237 static int password_hash_needed(struct ldb_module
*module
,
3238 struct ldb_request
*req
,
3239 struct ph_context
**_ac
)
3241 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3242 const char *operation
= NULL
;
3243 const struct ldb_message
*msg
= NULL
;
3244 struct ph_context
*ac
= NULL
;
3245 const char *passwordAttrs
[] = {
3247 "clearTextPassword",
3252 const char **a
= NULL
;
3253 unsigned int attr_cnt
= 0;
3254 struct ldb_control
*bypass
= NULL
;
3255 bool userPassword
= dsdb_user_password_support(module
, req
, req
);
3256 bool update_password
= false;
3257 bool processing_needed
= false;
3261 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_needed\n");
3263 switch (req
->operation
) {
3266 msg
= req
->op
.add
.message
;
3269 operation
= "modify";
3270 msg
= req
->op
.mod
.message
;
3273 return ldb_next_request(module
, req
);
3276 if (ldb_dn_is_special(msg
->dn
)) { /* do not manipulate our control entries */
3277 return ldb_next_request(module
, req
);
3280 bypass
= ldb_request_get_control(req
,
3281 DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID
);
3282 if (bypass
!= NULL
) {
3283 /* Mark the "bypass" control as uncritical (done) */
3284 bypass
->critical
= false;
3285 ldb_debug(ldb
, LDB_DEBUG_TRACE
,
3286 "password_hash_needed(%s) (bypassing)\n",
3288 return password_hash_bypass(module
, req
);
3291 /* nobody must touch password histories and 'supplementalCredentials' */
3292 if (ldb_msg_find_element(msg
, "ntPwdHistory")) {
3293 return LDB_ERR_UNWILLING_TO_PERFORM
;
3295 if (ldb_msg_find_element(msg
, "lmPwdHistory")) {
3296 return LDB_ERR_UNWILLING_TO_PERFORM
;
3298 if (ldb_msg_find_element(msg
, "supplementalCredentials")) {
3299 return LDB_ERR_UNWILLING_TO_PERFORM
;
3303 * If no part of this touches the 'userPassword' OR 'clearTextPassword'
3304 * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes.
3305 * For password changes/set there should be a 'delete' or a 'modify'
3306 * on these attributes.
3308 for (a
= passwordAttrs
; *a
!= NULL
; a
++) {
3309 if ((!userPassword
) && (ldb_attr_cmp(*a
, "userPassword") == 0)) {
3313 if (ldb_msg_find_element(msg
, *a
) != NULL
) {
3314 /* MS-ADTS 3.1.1.3.1.5.2 */
3315 if ((ldb_attr_cmp(*a
, "userPassword") == 0) &&
3316 (dsdb_functional_level(ldb
) < DS_DOMAIN_FUNCTION_2003
)) {
3317 return LDB_ERR_CONSTRAINT_VIOLATION
;
3325 update_password
= true;
3326 processing_needed
= true;
3329 if (ldb_msg_find_element(msg
, "pwdLastSet")) {
3330 processing_needed
= true;
3333 if (!processing_needed
) {
3334 return ldb_next_request(module
, req
);
3337 ac
= ph_init_context(module
, req
, userPassword
, update_password
);
3339 DEBUG(0,(__location__
": %s\n", ldb_errstring(ldb
)));
3340 return ldb_operr(ldb
);
3342 ph_apply_controls(ac
);
3345 * Make a copy in order to apply our modifications
3346 * to the final update
3348 ac
->update_msg
= ldb_msg_copy_shallow(ac
, msg
);
3349 if (ac
->update_msg
== NULL
) {
3350 return ldb_oom(ldb
);
3354 * Remove all password related attributes.
3356 if (ac
->userPassword
) {
3357 ldb_msg_remove_attr(ac
->update_msg
, "userPassword");
3359 ldb_msg_remove_attr(ac
->update_msg
, "clearTextPassword");
3360 ldb_msg_remove_attr(ac
->update_msg
, "unicodePwd");
3361 ldb_msg_remove_attr(ac
->update_msg
, "ntPwdHistory");
3362 ldb_msg_remove_attr(ac
->update_msg
, "dBCSPwd");
3363 ldb_msg_remove_attr(ac
->update_msg
, "lmPwdHistory");
3364 ldb_msg_remove_attr(ac
->update_msg
, "supplementalCredentials");
3365 ldb_msg_remove_attr(ac
->update_msg
, "pwdLastSet");
3371 static int password_hash_add(struct ldb_module
*module
, struct ldb_request
*req
)
3373 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3374 struct ph_context
*ac
= NULL
;
3377 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_add\n");
3379 ret
= password_hash_needed(module
, req
, &ac
);
3380 if (ret
!= LDB_SUCCESS
) {
3387 /* Make sure we are performing the password set action on a (for us)
3388 * valid object. Those are instances of either "user" and/or
3389 * "inetOrgPerson". Otherwise continue with the submodules. */
3390 if ((!ldb_msg_check_string_attribute(req
->op
.add
.message
, "objectClass", "user"))
3391 && (!ldb_msg_check_string_attribute(req
->op
.add
.message
, "objectClass", "inetOrgPerson"))) {
3395 if (ldb_msg_find_element(req
->op
.add
.message
, "clearTextPassword") != NULL
) {
3396 ldb_set_errstring(ldb
,
3397 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3398 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
3401 return ldb_next_request(module
, req
);
3404 /* get user domain data */
3405 ret
= build_domain_data_request(ac
);
3406 if (ret
!= LDB_SUCCESS
) {
3410 return ldb_next_request(module
, ac
->dom_req
);
3413 static int password_hash_add_do_add(struct ph_context
*ac
)
3415 struct ldb_context
*ldb
= ldb_module_get_ctx(ac
->module
);
3416 struct ldb_request
*down_req
;
3417 struct setup_password_fields_io io
;
3420 /* Prepare the internal data structure containing the passwords */
3421 ret
= setup_io(ac
, ac
->req
->op
.add
.message
, NULL
, &io
);
3422 if (ret
!= LDB_SUCCESS
) {
3426 ret
= setup_password_fields(&io
);
3427 if (ret
!= LDB_SUCCESS
) {
3431 ret
= check_password_restrictions(&io
);
3432 if (ret
!= LDB_SUCCESS
) {
3436 ret
= update_final_msg(&io
);
3437 if (ret
!= LDB_SUCCESS
) {
3441 ret
= ldb_build_add_req(&down_req
, ldb
, ac
,
3446 LDB_REQ_SET_LOCATION(down_req
);
3447 if (ret
!= LDB_SUCCESS
) {
3451 return ldb_next_request(ac
->module
, down_req
);
3454 static int password_hash_modify(struct ldb_module
*module
, struct ldb_request
*req
)
3456 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
3457 struct ph_context
*ac
= NULL
;
3458 const char *passwordAttrs
[] = { "userPassword", "clearTextPassword",
3459 "unicodePwd", "dBCSPwd", NULL
}, **l
;
3460 unsigned int del_attr_cnt
, add_attr_cnt
, rep_attr_cnt
;
3461 struct ldb_message_element
*passwordAttr
;
3462 struct ldb_message
*msg
;
3463 struct ldb_request
*down_req
;
3464 struct ldb_control
*restore
= NULL
;
3468 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "password_hash_modify\n");
3470 ret
= password_hash_needed(module
, req
, &ac
);
3471 if (ret
!= LDB_SUCCESS
) {
3478 /* use a new message structure so that we can modify it */
3479 msg
= ldb_msg_copy_shallow(ac
, req
->op
.mod
.message
);
3481 return ldb_oom(ldb
);
3484 /* - check for single-valued password attributes
3485 * (if not return "CONSTRAINT_VIOLATION")
3486 * - check that for a password change operation one add and one delete
3488 * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM")
3489 * - check that a password change and a password set operation cannot
3491 * (if not return "UNWILLING_TO_PERFORM")
3492 * - remove all password attributes modifications from the first change
3493 * operation (anything without the passwords) - we will make the real
3494 * modification later */
3498 for (l
= passwordAttrs
; *l
!= NULL
; l
++) {
3499 if ((!ac
->userPassword
) &&
3500 (ldb_attr_cmp(*l
, "userPassword") == 0)) {
3504 while ((passwordAttr
= ldb_msg_find_element(msg
, *l
)) != NULL
) {
3505 if (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_DELETE
) {
3508 if (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_ADD
) {
3511 if (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_REPLACE
) {
3514 if ((passwordAttr
->num_values
!= 1) &&
3515 (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_ADD
)) {
3517 ldb_asprintf_errstring(ldb
,
3518 "'%s' attribute must have exactly one value on add operations!",
3520 return LDB_ERR_CONSTRAINT_VIOLATION
;
3522 if ((passwordAttr
->num_values
> 1) &&
3523 (LDB_FLAG_MOD_TYPE(passwordAttr
->flags
) == LDB_FLAG_MOD_DELETE
)) {
3525 ldb_asprintf_errstring(ldb
,
3526 "'%s' attribute must have zero or one value(s) on delete operations!",
3528 return LDB_ERR_CONSTRAINT_VIOLATION
;
3530 ldb_msg_remove_element(msg
, passwordAttr
);
3533 if ((del_attr_cnt
== 0) && (add_attr_cnt
> 0)) {
3535 ldb_set_errstring(ldb
,
3536 "Only the add action for a password change specified!");
3537 return LDB_ERR_UNWILLING_TO_PERFORM
;
3539 if ((del_attr_cnt
> 1) || (add_attr_cnt
> 1)) {
3541 ldb_set_errstring(ldb
,
3542 "Only one delete and one add action for a password change allowed!");
3543 return LDB_ERR_UNWILLING_TO_PERFORM
;
3545 if ((rep_attr_cnt
> 0) && ((del_attr_cnt
> 0) || (add_attr_cnt
> 0))) {
3547 ldb_set_errstring(ldb
,
3548 "Either a password change or a password set operation is allowed!");
3549 return LDB_ERR_UNWILLING_TO_PERFORM
;
3552 restore
= ldb_request_get_control(req
,
3553 DSDB_CONTROL_RESTORE_TOMBSTONE_OID
);
3554 if (restore
== NULL
) {
3556 * A tomstone reanimation generates a double update
3559 * So we only remove it without the
3560 * DSDB_CONTROL_RESTORE_TOMBSTONE_OID control.
3562 ldb_msg_remove_attr(msg
, "pwdLastSet");
3566 /* if there was nothing else to be modified skip to next step */
3567 if (msg
->num_elements
== 0) {
3568 return password_hash_mod_search_self(ac
);
3572 * Now we apply all changes remaining in msg
3573 * and remove them from our final update_msg
3576 for (i
= 0; i
< msg
->num_elements
; i
++) {
3577 ldb_msg_remove_attr(ac
->update_msg
,
3578 msg
->elements
[i
].name
);
3581 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
3584 ac
, ph_modify_callback
,
3586 LDB_REQ_SET_LOCATION(down_req
);
3587 if (ret
!= LDB_SUCCESS
) {
3591 return ldb_next_request(module
, down_req
);
3594 static int ph_modify_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3596 struct ph_context
*ac
;
3598 ac
= talloc_get_type(req
->context
, struct ph_context
);
3601 return ldb_module_done(ac
->req
, NULL
, NULL
,
3602 LDB_ERR_OPERATIONS_ERROR
);
3605 if (ares
->type
== LDB_REPLY_REFERRAL
) {
3606 return ldb_module_send_referral(ac
->req
, ares
->referral
);
3609 if (ares
->error
!= LDB_SUCCESS
) {
3610 return ldb_module_done(ac
->req
, ares
->controls
,
3611 ares
->response
, ares
->error
);
3614 if (ares
->type
!= LDB_REPLY_DONE
) {
3616 return ldb_module_done(ac
->req
, NULL
, NULL
,
3617 LDB_ERR_OPERATIONS_ERROR
);
3622 return password_hash_mod_search_self(ac
);
3625 static int ph_mod_search_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
3627 struct ldb_context
*ldb
;
3628 struct ph_context
*ac
;
3629 int ret
= LDB_SUCCESS
;
3631 ac
= talloc_get_type(req
->context
, struct ph_context
);
3632 ldb
= ldb_module_get_ctx(ac
->module
);
3635 ret
= LDB_ERR_OPERATIONS_ERROR
;
3638 if (ares
->error
!= LDB_SUCCESS
) {
3639 return ldb_module_done(ac
->req
, ares
->controls
,
3640 ares
->response
, ares
->error
);
3643 /* we are interested only in the single reply (base search) */
3644 switch (ares
->type
) {
3645 case LDB_REPLY_ENTRY
:
3646 /* Make sure we are performing the password change action on a
3647 * (for us) valid object. Those are instances of either "user"
3648 * and/or "inetOrgPerson". Otherwise continue with the
3650 if ((!ldb_msg_check_string_attribute(ares
->message
, "objectClass", "user"))
3651 && (!ldb_msg_check_string_attribute(ares
->message
, "objectClass", "inetOrgPerson"))) {
3654 if (ldb_msg_find_element(ac
->req
->op
.mod
.message
, "clearTextPassword") != NULL
) {
3655 ldb_set_errstring(ldb
,
3656 "'clearTextPassword' is only allowed on objects of class 'user' and/or 'inetOrgPerson'!");
3657 ret
= LDB_ERR_NO_SUCH_ATTRIBUTE
;
3661 ret
= ldb_next_request(ac
->module
, ac
->req
);
3665 if (ac
->search_res
!= NULL
) {
3668 ldb_set_errstring(ldb
, "Too many results");
3669 ret
= LDB_ERR_OPERATIONS_ERROR
;
3673 ac
->search_res
= talloc_steal(ac
, ares
);
3677 case LDB_REPLY_REFERRAL
:
3678 /* ignore anything else for now */
3683 case LDB_REPLY_DONE
:
3686 /* get user domain data */
3687 ret
= build_domain_data_request(ac
);
3688 if (ret
!= LDB_SUCCESS
) {
3689 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
3692 ret
= ldb_next_request(ac
->module
, ac
->dom_req
);
3697 if (ret
!= LDB_SUCCESS
) {
3698 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
3704 static int password_hash_mod_search_self(struct ph_context
*ac
)
3706 struct ldb_context
*ldb
;
3707 static const char * const attrs
[] = { "objectClass",
3708 "userAccountControl",
3709 "msDS-User-Account-Control-Computed",
3713 "userPrincipalName",
3714 "supplementalCredentials",
3723 struct ldb_request
*search_req
;
3726 ldb
= ldb_module_get_ctx(ac
->module
);
3728 ret
= ldb_build_search_req(&search_req
, ldb
, ac
,
3729 ac
->req
->op
.mod
.message
->dn
,
3734 ac
, ph_mod_search_callback
,
3736 LDB_REQ_SET_LOCATION(search_req
);
3737 if (ret
!= LDB_SUCCESS
) {
3741 return ldb_next_request(ac
->module
, search_req
);
3744 static int password_hash_mod_do_mod(struct ph_context
*ac
)
3746 struct ldb_context
*ldb
= ldb_module_get_ctx(ac
->module
);
3747 struct ldb_request
*mod_req
;
3748 struct setup_password_fields_io io
;
3751 /* Prepare the internal data structure containing the passwords */
3752 ret
= setup_io(ac
, ac
->req
->op
.mod
.message
,
3753 ac
->search_res
->message
, &io
);
3754 if (ret
!= LDB_SUCCESS
) {
3758 ret
= setup_password_fields(&io
);
3759 if (ret
!= LDB_SUCCESS
) {
3763 ret
= check_password_restrictions(&io
);
3764 if (ret
!= LDB_SUCCESS
) {
3768 ret
= update_final_msg(&io
);
3769 if (ret
!= LDB_SUCCESS
) {
3773 ret
= ldb_build_mod_req(&mod_req
, ldb
, ac
,
3778 LDB_REQ_SET_LOCATION(mod_req
);
3779 if (ret
!= LDB_SUCCESS
) {
3783 return ldb_next_request(ac
->module
, mod_req
);
3786 static const struct ldb_module_ops ldb_password_hash_module_ops
= {
3787 .name
= "password_hash",
3788 .add
= password_hash_add
,
3789 .modify
= password_hash_modify
3792 int ldb_password_hash_module_init(const char *version
)
3794 LDB_MODULE_CHECK_VERSION(version
);
3795 return ldb_register_module(&ldb_password_hash_module_ops
);