2 Unix SMB/CIFS implementation.
4 samr server password set/change handling
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "librpc/gen_ndr/ndr_samr.h"
25 #include "rpc_server/dcerpc_server.h"
26 #include "rpc_server/common/common.h"
27 #include "rpc_server/samr/dcesrv_samr.h"
28 #include "system/time.h"
29 #include "lib/crypto/crypto.h"
30 #include "lib/ldb/include/ldb.h"
34 samr_ChangePasswordUser
36 NTSTATUS
samr_ChangePasswordUser(struct dcesrv_call_state
*dce_call
, TALLOC_CTX
*mem_ctx
,
37 struct samr_ChangePasswordUser
*r
)
39 struct dcesrv_handle
*h
;
40 struct samr_account_state
*a_state
;
41 struct ldb_message
**res
, *msg
;
43 struct samr_Password new_lmPwdHash
, new_ntPwdHash
, checkHash
;
44 struct samr_Password
*lm_pwd
, *nt_pwd
;
45 NTSTATUS status
= NT_STATUS_OK
;
46 const char * const attrs
[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL
};
48 DCESRV_PULL_HANDLE(h
, r
->in
.user_handle
, SAMR_HANDLE_USER
);
52 /* fetch the old hashes */
53 ret
= gendb_search(a_state
->sam_ctx
, mem_ctx
, NULL
, &res
, attrs
,
54 "dn=%s", a_state
->account_dn
);
56 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
60 /* basic sanity checking on parameters */
61 if (!r
->in
.lm_present
|| !r
->in
.nt_present
||
62 !r
->in
.old_lm_crypted
|| !r
->in
.new_lm_crypted
||
63 !r
->in
.old_nt_crypted
|| !r
->in
.new_nt_crypted
) {
64 /* we should really handle a change with lm not
66 return NT_STATUS_INVALID_PARAMETER_MIX
;
68 if (!r
->in
.cross1_present
|| !r
->in
.nt_cross
) {
69 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED
;
71 if (!r
->in
.cross2_present
|| !r
->in
.lm_cross
) {
72 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED
;
75 status
= samdb_result_passwords(mem_ctx
, msg
, &lm_pwd
, &nt_pwd
);
76 if (!NT_STATUS_IS_OK(status
) || !lm_pwd
|| !nt_pwd
) {
77 return NT_STATUS_WRONG_PASSWORD
;
80 /* decrypt and check the new lm hash */
81 D_P16(lm_pwd
->hash
, r
->in
.new_lm_crypted
->hash
, new_lmPwdHash
.hash
);
82 D_P16(new_lmPwdHash
.hash
, r
->in
.old_lm_crypted
->hash
, checkHash
.hash
);
83 if (memcmp(checkHash
.hash
, lm_pwd
, 16) != 0) {
84 return NT_STATUS_WRONG_PASSWORD
;
87 /* decrypt and check the new nt hash */
88 D_P16(nt_pwd
->hash
, r
->in
.new_nt_crypted
->hash
, new_ntPwdHash
.hash
);
89 D_P16(new_ntPwdHash
.hash
, r
->in
.old_nt_crypted
->hash
, checkHash
.hash
);
90 if (memcmp(checkHash
.hash
, nt_pwd
, 16) != 0) {
91 return NT_STATUS_WRONG_PASSWORD
;
94 /* check the nt cross hash */
95 D_P16(lm_pwd
->hash
, r
->in
.nt_cross
->hash
, checkHash
.hash
);
96 if (memcmp(checkHash
.hash
, new_ntPwdHash
.hash
, 16) != 0) {
97 return NT_STATUS_WRONG_PASSWORD
;
100 /* check the lm cross hash */
101 D_P16(nt_pwd
->hash
, r
->in
.lm_cross
->hash
, checkHash
.hash
);
102 if (memcmp(checkHash
.hash
, new_lmPwdHash
.hash
, 16) != 0) {
103 return NT_STATUS_WRONG_PASSWORD
;
106 msg
= ldb_msg_new(mem_ctx
);
108 return NT_STATUS_NO_MEMORY
;
111 msg
->dn
= talloc_strdup(msg
, a_state
->account_dn
);
113 return NT_STATUS_NO_MEMORY
;
116 status
= samdb_set_password(a_state
->sam_ctx
, mem_ctx
,
117 a_state
->account_dn
, a_state
->domain_state
->domain_dn
,
118 msg
, NULL
, &new_lmPwdHash
, &new_ntPwdHash
,
119 True
, /* this is a user password change */
120 True
, /* run restriction tests */
122 if (!NT_STATUS_IS_OK(status
)) {
126 /* modify the samdb record */
127 ret
= samdb_replace(a_state
->sam_ctx
, mem_ctx
, msg
);
129 return NT_STATUS_UNSUCCESSFUL
;
136 samr_OemChangePasswordUser2
138 NTSTATUS
samr_OemChangePasswordUser2(struct dcesrv_call_state
*dce_call
, TALLOC_CTX
*mem_ctx
,
139 struct samr_OemChangePasswordUser2
*r
)
143 uint32_t new_pass_len
;
144 struct samr_CryptPassword
*pwbuf
= r
->in
.password
;
146 const char *user_dn
, *domain_dn
;
148 struct ldb_message
**res
, *mod
;
149 const char * const attrs
[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL
};
150 const char *domain_sid
;
151 struct samr_Password
*lm_pwd
;
152 DATA_BLOB lm_pwd_blob
;
153 uint8_t new_lm_hash
[16];
154 struct samr_Password lm_verifier
;
157 return NT_STATUS_WRONG_PASSWORD
;
160 /* this call doesn't take a policy handle, so we need to open
161 the sam db from scratch */
162 sam_ctx
= samdb_connect(mem_ctx
);
163 if (sam_ctx
== NULL
) {
164 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
167 /* we need the users dn and the domain dn (derived from the
168 user SID). We also need the current lm password hash in
169 order to decrypt the incoming password */
170 ret
= gendb_search(sam_ctx
,
171 mem_ctx
, NULL
, &res
, attrs
,
172 "(&(sAMAccountName=%s)(objectclass=user))",
173 r
->in
.account
->string
);
175 return NT_STATUS_NO_SUCH_USER
;
178 user_dn
= res
[0]->dn
;
180 status
= samdb_result_passwords(mem_ctx
, res
[0], &lm_pwd
, NULL
);
181 if (!NT_STATUS_IS_OK(status
) || !lm_pwd
) {
182 return NT_STATUS_WRONG_PASSWORD
;
185 /* decrypt the password we have been given */
186 lm_pwd_blob
= data_blob(lm_pwd
->hash
, sizeof(lm_pwd
->hash
));
187 arcfour_crypt_blob(pwbuf
->data
, 516, &lm_pwd_blob
);
188 data_blob_free(&lm_pwd_blob
);
190 if (!decode_pw_buffer(pwbuf
->data
, new_pass
, sizeof(new_pass
),
191 &new_pass_len
, STR_ASCII
)) {
192 DEBUG(3,("samr: failed to decode password buffer\n"));
193 return NT_STATUS_WRONG_PASSWORD
;
196 /* check LM verifier */
197 if (lm_pwd
== NULL
|| r
->in
.hash
== NULL
) {
198 return NT_STATUS_WRONG_PASSWORD
;
201 E_deshash(new_pass
, new_lm_hash
);
202 E_old_pw_hash(new_lm_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
203 if (memcmp(lm_verifier
.hash
, r
->in
.hash
->hash
, 16) != 0) {
204 return NT_STATUS_WRONG_PASSWORD
;
207 /* work out the domain dn */
208 domain_sid
= samdb_result_sid_prefix(mem_ctx
, res
[0], "objectSid");
209 if (domain_sid
== NULL
) {
210 return NT_STATUS_NO_SUCH_USER
;
213 domain_dn
= samdb_search_string(sam_ctx
, mem_ctx
, NULL
, "dn",
214 "(objectSid=%s)", domain_sid
);
216 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
219 mod
= ldb_msg_new(mem_ctx
);
221 return NT_STATUS_NO_MEMORY
;
224 mod
->dn
= talloc_strdup(mod
, user_dn
);
226 return NT_STATUS_NO_MEMORY
;
229 /* set the password - samdb needs to know both the domain and user DNs,
230 so the domain password policy can be used */
231 status
= samdb_set_password(sam_ctx
, mem_ctx
,
235 True
, /* this is a user password change */
236 True
, /* run restriction tests */
238 if (!NT_STATUS_IS_OK(status
)) {
242 /* modify the samdb record */
243 ret
= samdb_replace(sam_ctx
, mem_ctx
, mod
);
245 return NT_STATUS_UNSUCCESSFUL
;
253 samr_ChangePasswordUser3
255 NTSTATUS
samr_ChangePasswordUser3(struct dcesrv_call_state
*dce_call
,
257 struct samr_ChangePasswordUser3
*r
)
261 uint32_t new_pass_len
;
262 void *sam_ctx
= NULL
;
263 const char *user_dn
, *domain_dn
= NULL
;
265 struct ldb_message
**res
, *mod
;
266 const char * const attrs
[] = { "objectSid", "ntPwdHash", "lmPwdHash", "unicodePwd", NULL
};
267 const char * const dom_attrs
[] = { "minPwdLength", "pwdHistoryLength",
268 "pwdProperties", "minPwdAge", "maxPwdAge",
270 const char *domain_sid
;
271 struct samr_Password
*nt_pwd
, *lm_pwd
;
272 DATA_BLOB nt_pwd_blob
;
273 struct samr_DomInfo1
*dominfo
;
274 struct samr_ChangeReject
*reject
;
276 uint8_t new_nt_hash
[16], new_lm_hash
[16];
277 struct samr_Password nt_verifier
, lm_verifier
;
281 if (r
->in
.nt_password
== NULL
||
282 r
->in
.nt_verifier
== NULL
) {
283 status
= NT_STATUS_INVALID_PARAMETER
;
287 /* this call doesn't take a policy handle, so we need to open
288 the sam db from scratch */
289 sam_ctx
= samdb_connect(mem_ctx
);
290 if (sam_ctx
== NULL
) {
291 status
= NT_STATUS_INVALID_SYSTEM_SERVICE
;
295 /* we need the users dn and the domain dn (derived from the
296 user SID). We also need the current lm and nt password hashes
297 in order to decrypt the incoming passwords */
298 ret
= gendb_search(sam_ctx
,
299 mem_ctx
, NULL
, &res
, attrs
,
300 "(&(sAMAccountName=%s)(objectclass=user))",
301 r
->in
.account
->string
);
303 status
= NT_STATUS_NO_SUCH_USER
;
307 user_dn
= res
[0]->dn
;
309 status
= samdb_result_passwords(mem_ctx
, res
[0], &lm_pwd
, &nt_pwd
);
310 if (!NT_STATUS_IS_OK(status
) ) {
315 status
= NT_STATUS_WRONG_PASSWORD
;
319 /* decrypt the password we have been given */
320 nt_pwd_blob
= data_blob(nt_pwd
->hash
, sizeof(nt_pwd
->hash
));
321 arcfour_crypt_blob(r
->in
.nt_password
->data
, 516, &nt_pwd_blob
);
322 data_blob_free(&nt_pwd_blob
);
324 if (!decode_pw_buffer(r
->in
.nt_password
->data
, new_pass
, sizeof(new_pass
),
325 &new_pass_len
, STR_UNICODE
)) {
326 DEBUG(3,("samr: failed to decode password buffer\n"));
327 status
= NT_STATUS_WRONG_PASSWORD
;
331 if (r
->in
.nt_verifier
== NULL
) {
332 status
= NT_STATUS_WRONG_PASSWORD
;
336 /* check NT verifier */
337 E_md4hash(new_pass
, new_nt_hash
);
338 E_old_pw_hash(new_nt_hash
, nt_pwd
->hash
, nt_verifier
.hash
);
339 if (memcmp(nt_verifier
.hash
, r
->in
.nt_verifier
->hash
, 16) != 0) {
340 status
= NT_STATUS_WRONG_PASSWORD
;
344 /* check LM verifier */
345 if (lm_pwd
&& r
->in
.lm_verifier
!= NULL
) {
346 E_deshash(new_pass
, new_lm_hash
);
347 E_old_pw_hash(new_nt_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
348 if (memcmp(lm_verifier
.hash
, r
->in
.lm_verifier
->hash
, 16) != 0) {
349 status
= NT_STATUS_WRONG_PASSWORD
;
355 /* work out the domain dn */
356 domain_sid
= samdb_result_sid_prefix(mem_ctx
, res
[0], "objectSid");
357 if (domain_sid
== NULL
) {
358 status
= NT_STATUS_NO_SUCH_DOMAIN
;
362 domain_dn
= samdb_search_string(sam_ctx
, mem_ctx
, NULL
, "dn",
363 "(objectSid=%s)", domain_sid
);
365 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
369 mod
= ldb_msg_new(mem_ctx
);
371 return NT_STATUS_NO_MEMORY
;
374 mod
->dn
= talloc_strdup(mod
, user_dn
);
376 status
= NT_STATUS_NO_MEMORY
;
380 /* set the password - samdb needs to know both the domain and user DNs,
381 so the domain password policy can be used */
382 status
= samdb_set_password(sam_ctx
, mem_ctx
,
386 True
, /* this is a user password change */
387 True
, /* run restriction tests */
389 if (!NT_STATUS_IS_OK(status
)) {
393 /* modify the samdb record */
394 ret
= samdb_replace(sam_ctx
, mem_ctx
, mod
);
396 status
= NT_STATUS_UNSUCCESSFUL
;
403 ret
= gendb_search(sam_ctx
,
404 mem_ctx
, NULL
, &res
, dom_attrs
,
411 /* on failure we need to fill in the reject reasons */
412 dominfo
= talloc(mem_ctx
, struct samr_DomInfo1
);
413 if (dominfo
== NULL
) {
416 reject
= talloc(mem_ctx
, struct samr_ChangeReject
);
417 if (reject
== NULL
) {
421 ZERO_STRUCTP(dominfo
);
422 ZERO_STRUCTP(reject
);
424 reject
->reason
= reason
;
426 r
->out
.dominfo
= dominfo
;
427 r
->out
.reject
= reject
;
433 dominfo
->min_password_length
= samdb_result_uint (res
[0], "minPwdLength", 0);
434 dominfo
->password_properties
= samdb_result_uint (res
[0], "pwdProperties", 0);
435 dominfo
->password_history_length
= samdb_result_uint (res
[0], "pwdHistoryLength", 0);
436 dominfo
->max_password_age
= samdb_result_int64(res
[0], "maxPwdAge", 0);
437 dominfo
->min_password_age
= samdb_result_int64(res
[0], "minPwdAge", 0);
444 samr_ChangePasswordUser2
446 easy - just a subset of samr_ChangePasswordUser3
448 NTSTATUS
samr_ChangePasswordUser2(struct dcesrv_call_state
*dce_call
, TALLOC_CTX
*mem_ctx
,
449 struct samr_ChangePasswordUser2
*r
)
451 struct samr_ChangePasswordUser3 r2
;
453 r2
.in
.server
= r
->in
.server
;
454 r2
.in
.account
= r
->in
.account
;
455 r2
.in
.nt_password
= r
->in
.nt_password
;
456 r2
.in
.nt_verifier
= r
->in
.nt_verifier
;
457 r2
.in
.lm_change
= r
->in
.lm_change
;
458 r2
.in
.lm_password
= r
->in
.lm_password
;
459 r2
.in
.lm_verifier
= r
->in
.lm_verifier
;
460 r2
.in
.password3
= NULL
;
462 return samr_ChangePasswordUser3(dce_call
, mem_ctx
, &r2
);
467 check that a password is sufficiently complex
469 static BOOL
samdb_password_complexity_ok(const char *pass
)
471 return check_password_quality(pass
);
475 set the user password using plaintext, obeying any user or domain
476 password restrictions
478 note that this function doesn't actually store the result in the
479 database, it just fills in the "mod" structure with ldb modify
480 elements to setup the correct change when samdb_replace() is
481 called. This allows the caller to combine the change with other
482 changes (as is needed by some of the set user info levels)
484 NTSTATUS
samdb_set_password(void *ctx
, TALLOC_CTX
*mem_ctx
,
485 const char *user_dn
, const char *domain_dn
,
486 struct ldb_message
*mod
,
487 const char *new_pass
,
488 struct samr_Password
*lmNewHash
,
489 struct samr_Password
*ntNewHash
,
492 uint32_t *reject_reason
)
494 const char * const user_attrs
[] = { "userAccountControl", "lmPwdHistory",
495 "ntPwdHistory", "unicodePwd",
496 "lmPwdHash", "ntPwdHash", "badPwdCount",
498 const char * const domain_attrs
[] = { "pwdProperties", "pwdHistoryLength",
499 "maxPwdAge", "minPwdAge",
500 "minPwdLength", "pwdLastSet", NULL
};
501 const char *unicodePwd
;
504 uint_t minPwdLength
, pwdProperties
, pwdHistoryLength
;
505 uint_t userAccountControl
, badPwdCount
;
506 struct samr_Password
*lmPwdHistory
, *ntPwdHistory
, lmPwdHash
, ntPwdHash
;
507 struct samr_Password
*new_lmPwdHistory
, *new_ntPwdHistory
;
508 struct samr_Password local_lmNewHash
, local_ntNewHash
;
509 int lmPwdHistory_len
, ntPwdHistory_len
;
510 struct ldb_message
**res
;
512 time_t now
= time(NULL
);
516 /* we need to know the time to compute password age */
517 unix_to_nt_time(&now_nt
, now
);
519 /* pull all the user parameters */
520 count
= gendb_search(ctx
, mem_ctx
, NULL
, &res
, user_attrs
, "dn=%s", user_dn
);
522 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
524 unicodePwd
= samdb_result_string(res
[0], "unicodePwd", NULL
);
525 userAccountControl
= samdb_result_uint(res
[0], "userAccountControl", 0);
526 badPwdCount
= samdb_result_uint(res
[0], "badPwdCount", 0);
527 lmPwdHistory_len
= samdb_result_hashes(mem_ctx
, res
[0],
528 "lmPwdHistory", &lmPwdHistory
);
529 ntPwdHistory_len
= samdb_result_hashes(mem_ctx
, res
[0],
530 "ntPwdHistory", &ntPwdHistory
);
531 lmPwdHash
= samdb_result_hash(res
[0], "lmPwdHash");
532 ntPwdHash
= samdb_result_hash(res
[0], "ntPwdHash");
533 pwdLastSet
= samdb_result_uint64(res
[0], "pwdLastSet", 0);
535 /* pull the domain parameters */
536 count
= gendb_search(ctx
, mem_ctx
, NULL
, &res
, domain_attrs
, "dn=%s", domain_dn
);
538 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
540 pwdProperties
= samdb_result_uint(res
[0], "pwdProperties", 0);
541 pwdHistoryLength
= samdb_result_uint(res
[0], "pwdHistoryLength", 0);
542 minPwdLength
= samdb_result_uint(res
[0], "minPwdLength", 0);
543 minPwdAge
= samdb_result_int64(res
[0], "minPwdAge", 0);
546 /* check the various password restrictions */
547 if (restrictions
&& minPwdLength
> strlen_m(new_pass
)) {
549 *reject_reason
= SAMR_REJECT_TOO_SHORT
;
551 return NT_STATUS_PASSWORD_RESTRICTION
;
554 /* possibly check password complexity */
555 if (restrictions
&& pwdProperties
& DOMAIN_PASSWORD_COMPLEX
&&
556 !samdb_password_complexity_ok(new_pass
)) {
558 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
560 return NT_STATUS_PASSWORD_RESTRICTION
;
563 /* compute the new nt and lm hashes */
564 if (E_deshash(new_pass
, local_lmNewHash
.hash
)) {
565 lmNewHash
= &local_lmNewHash
;
567 E_md4hash(new_pass
, local_ntNewHash
.hash
);
568 ntNewHash
= &local_ntNewHash
;
571 if (restrictions
&& user_change
) {
572 /* are all password changes disallowed? */
573 if (pwdProperties
& DOMAIN_REFUSE_PASSWORD_CHANGE
) {
575 *reject_reason
= SAMR_REJECT_OTHER
;
577 return NT_STATUS_PASSWORD_RESTRICTION
;
580 /* can this user change password? */
581 if (userAccountControl
& UF_PASSWD_CANT_CHANGE
) {
583 *reject_reason
= SAMR_REJECT_OTHER
;
585 return NT_STATUS_PASSWORD_RESTRICTION
;
588 /* yes, this is a minus. The ages are in negative 100nsec units! */
589 if (pwdLastSet
- minPwdAge
> now_nt
) {
591 *reject_reason
= SAMR_REJECT_OTHER
;
593 return NT_STATUS_PASSWORD_RESTRICTION
;
596 /* check the immediately past password */
597 if (pwdHistoryLength
> 0) {
598 if (lmNewHash
&& memcmp(lmNewHash
->hash
, lmPwdHash
.hash
, 16) == 0) {
600 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
602 return NT_STATUS_PASSWORD_RESTRICTION
;
604 if (ntNewHash
&& memcmp(ntNewHash
->hash
, ntPwdHash
.hash
, 16) == 0) {
606 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
608 return NT_STATUS_PASSWORD_RESTRICTION
;
612 /* check the password history */
613 lmPwdHistory_len
= MIN(lmPwdHistory_len
, pwdHistoryLength
);
614 ntPwdHistory_len
= MIN(ntPwdHistory_len
, pwdHistoryLength
);
616 if (pwdHistoryLength
> 0) {
617 if (unicodePwd
&& new_pass
&& strcmp(unicodePwd
, new_pass
) == 0) {
619 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
621 return NT_STATUS_PASSWORD_RESTRICTION
;
623 if (lmNewHash
&& memcmp(lmNewHash
->hash
, lmPwdHash
.hash
, 16) == 0) {
625 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
627 return NT_STATUS_PASSWORD_RESTRICTION
;
629 if (ntNewHash
&& memcmp(ntNewHash
->hash
, ntPwdHash
.hash
, 16) == 0) {
631 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
633 return NT_STATUS_PASSWORD_RESTRICTION
;
637 for (i
=0; lmNewHash
&& i
<lmPwdHistory_len
;i
++) {
638 if (memcmp(lmNewHash
->hash
, lmPwdHistory
[i
].hash
, 16) == 0) {
640 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
642 return NT_STATUS_PASSWORD_RESTRICTION
;
645 for (i
=0; ntNewHash
&& i
<ntPwdHistory_len
;i
++) {
646 if (memcmp(ntNewHash
->hash
, ntPwdHistory
[i
].hash
, 16) == 0) {
648 *reject_reason
= SAMR_REJECT_COMPLEXITY
;
650 return NT_STATUS_PASSWORD_RESTRICTION
;
655 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
657 /* the password is acceptable. Start forming the new fields */
659 CHECK_RET(samdb_msg_add_hash(ctx
, mem_ctx
, mod
, "lmPwdHash", lmNewHash
));
661 CHECK_RET(samdb_msg_add_delete(ctx
, mem_ctx
, mod
, "lmPwdHash"));
665 CHECK_RET(samdb_msg_add_hash(ctx
, mem_ctx
, mod
, "ntPwdHash", ntNewHash
));
667 CHECK_RET(samdb_msg_add_delete(ctx
, mem_ctx
, mod
, "ntPwdHash"));
670 if (new_pass
&& (pwdProperties
& DOMAIN_PASSWORD_STORE_CLEARTEXT
) &&
671 (userAccountControl
& UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
)) {
672 CHECK_RET(samdb_msg_add_string(ctx
, mem_ctx
, mod
,
673 "unicodePwd", new_pass
));
675 CHECK_RET(samdb_msg_add_delete(ctx
, mem_ctx
, mod
, "unicodePwd"));
678 CHECK_RET(samdb_msg_add_uint64(ctx
, mem_ctx
, mod
, "pwdLastSet", now_nt
));
680 if (pwdHistoryLength
== 0) {
681 CHECK_RET(samdb_msg_add_delete(ctx
, mem_ctx
, mod
, "lmPwdHistory"));
682 CHECK_RET(samdb_msg_add_delete(ctx
, mem_ctx
, mod
, "ntPwdHistory"));
686 /* store the password history */
687 new_lmPwdHistory
= talloc_array(mem_ctx
, struct samr_Password
,
689 if (!new_lmPwdHistory
) {
690 return NT_STATUS_NO_MEMORY
;
692 new_ntPwdHistory
= talloc_array(mem_ctx
, struct samr_Password
,
694 if (!new_ntPwdHistory
) {
695 return NT_STATUS_NO_MEMORY
;
697 for (i
=0;i
<MIN(pwdHistoryLength
-1, lmPwdHistory_len
);i
++) {
698 new_lmPwdHistory
[i
+1] = lmPwdHistory
[i
];
700 for (i
=0;i
<MIN(pwdHistoryLength
-1, ntPwdHistory_len
);i
++) {
701 new_ntPwdHistory
[i
+1] = ntPwdHistory
[i
];
704 /* Don't store 'long' passwords in the LM history,
705 but make sure to 'expire' one password off the other end */
707 new_lmPwdHistory
[0] = *lmNewHash
;
709 ZERO_STRUCT(new_lmPwdHistory
[0]);
711 lmPwdHistory_len
= MIN(lmPwdHistory_len
+ 1, pwdHistoryLength
);
714 new_ntPwdHistory
[0] = *ntNewHash
;
716 ZERO_STRUCT(new_ntPwdHistory
[0]);
718 ntPwdHistory_len
= MIN(ntPwdHistory_len
+ 1, pwdHistoryLength
);
720 CHECK_RET(samdb_msg_add_hashes(ctx
, mem_ctx
, mod
,
725 CHECK_RET(samdb_msg_add_hashes(ctx
, mem_ctx
, mod
,
733 set password via a samr_CryptPassword buffer
734 this will in the 'msg' with modify operations that will update the user
735 password when applied
737 NTSTATUS
samr_set_password(struct dcesrv_call_state
*dce_call
,
739 const char *account_dn
, const char *domain_dn
,
741 struct ldb_message
*msg
,
742 struct samr_CryptPassword
*pwbuf
)
746 uint32_t new_pass_len
;
747 DATA_BLOB session_key
= data_blob(NULL
, 0);
749 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
750 if (!NT_STATUS_IS_OK(nt_status
)) {
754 arcfour_crypt_blob(pwbuf
->data
, 516, &session_key
);
756 if (!decode_pw_buffer(pwbuf
->data
, new_pass
, sizeof(new_pass
),
757 &new_pass_len
, STR_UNICODE
)) {
758 DEBUG(3,("samr: failed to decode password buffer\n"));
759 return NT_STATUS_WRONG_PASSWORD
;
762 /* set the password - samdb needs to know both the domain and user DNs,
763 so the domain password policy can be used */
764 return samdb_set_password(sam_ctx
, mem_ctx
,
765 account_dn
, domain_dn
,
768 False
, /* This is a password set, not change */
769 True
, /* run restriction tests */
775 set password via a samr_CryptPasswordEx buffer
776 this will in the 'msg' with modify operations that will update the user
777 password when applied
779 NTSTATUS
samr_set_password_ex(struct dcesrv_call_state
*dce_call
,
781 const char *account_dn
, const char *domain_dn
,
783 struct ldb_message
*msg
,
784 struct samr_CryptPasswordEx
*pwbuf
)
788 uint32_t new_pass_len
;
789 DATA_BLOB co_session_key
;
790 DATA_BLOB session_key
= data_blob(NULL
, 0);
791 struct MD5Context ctx
;
793 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
794 if (!NT_STATUS_IS_OK(nt_status
)) {
798 co_session_key
= data_blob_talloc(mem_ctx
, NULL
, 16);
799 if (!co_session_key
.data
) {
800 return NT_STATUS_NO_MEMORY
;
804 MD5Update(&ctx
, &pwbuf
->data
[516], 16);
805 MD5Update(&ctx
, session_key
.data
, session_key
.length
);
806 MD5Final(co_session_key
.data
, &ctx
);
808 arcfour_crypt_blob(pwbuf
->data
, 516, &co_session_key
);
810 if (!decode_pw_buffer(pwbuf
->data
, new_pass
, sizeof(new_pass
),
811 &new_pass_len
, STR_UNICODE
)) {
812 DEBUG(3,("samr: failed to decode password buffer\n"));
813 return NT_STATUS_WRONG_PASSWORD
;
816 /* set the password - samdb needs to know both the domain and user DNs,
817 so the domain password policy can be used */
818 return samdb_set_password(sam_ctx
, mem_ctx
,
819 account_dn
, domain_dn
,
822 False
, /* This is a password set, not change */
823 True
, /* run restriction tests */