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"
32 samr_ChangePasswordUser
34 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
35 struct samr_ChangePasswordUser *r)
37 struct dcesrv_handle *h;
38 struct samr_account_state *a_state;
39 struct ldb_message **res, mod, *msg;
41 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
42 struct samr_Password *lm_pwd, *nt_pwd;
43 NTSTATUS status = NT_STATUS_OK;
44 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
46 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
50 /* fetch the old hashes */
51 ret = samdb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
52 "dn=%s", a_state->account_dn);
54 return NT_STATUS_INTERNAL_DB_CORRUPTION;
58 /* basic sanity checking on parameters */
59 if (!r->in.lm_present || !r->in.nt_present ||
60 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
61 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
62 /* we should really handle a change with lm not
64 return NT_STATUS_INVALID_PARAMETER_MIX;
66 if (!r->in.cross1_present || !r->in.nt_cross) {
67 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
69 if (!r->in.cross2_present || !r->in.lm_cross) {
70 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
73 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
74 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
75 return NT_STATUS_WRONG_PASSWORD;
78 /* decrypt and check the new lm hash */
79 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
80 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
81 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
82 return NT_STATUS_WRONG_PASSWORD;
85 /* decrypt and check the new nt hash */
86 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
87 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
88 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
89 return NT_STATUS_WRONG_PASSWORD;
92 /* check the nt cross hash */
93 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
94 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
95 return NT_STATUS_WRONG_PASSWORD;
98 /* check the lm cross hash */
99 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
100 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
101 return NT_STATUS_WRONG_PASSWORD;
105 mod.dn = talloc_strdup(mem_ctx, a_state->account_dn);
107 return NT_STATUS_NO_MEMORY;
110 status = samdb_set_password(a_state->sam_ctx, mem_ctx,
111 a_state->account_dn, a_state->domain_state->domain_dn,
112 &mod, NULL, &new_lmPwdHash, &new_ntPwdHash,
114 if (!NT_STATUS_IS_OK(status)) {
118 /* modify the samdb record */
119 ret = samdb_replace(a_state->sam_ctx, mem_ctx, &mod);
121 return NT_STATUS_UNSUCCESSFUL;
128 samr_OemChangePasswordUser2
130 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
131 struct samr_OemChangePasswordUser2 *r)
135 uint32_t new_pass_len;
136 struct samr_CryptPassword *pwbuf = r->in.password;
138 const char *user_dn, *domain_dn;
140 struct ldb_message **res, mod;
141 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
142 const char *domain_sid;
143 struct samr_Password *lm_pwd;
144 DATA_BLOB lm_pwd_blob;
147 return NT_STATUS_WRONG_PASSWORD;
150 /* this call doesn't take a policy handle, so we need to open
151 the sam db from scratch */
152 sam_ctx = samdb_connect(mem_ctx);
153 if (sam_ctx == NULL) {
154 return NT_STATUS_INVALID_SYSTEM_SERVICE;
157 /* we need the users dn and the domain dn (derived from the
158 user SID). We also need the current lm password hash in
159 order to decrypt the incoming password */
160 ret = samdb_search(sam_ctx,
161 mem_ctx, NULL, &res, attrs,
162 "(&(sAMAccountName=%s)(objectclass=user))",
163 r->in.account->string);
165 return NT_STATUS_NO_SUCH_USER;
168 user_dn = res[0]->dn;
170 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
171 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
172 return NT_STATUS_WRONG_PASSWORD;
175 /* decrypt the password we have been given */
176 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
177 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
178 data_blob_free(&lm_pwd_blob);
180 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
181 &new_pass_len, STR_ASCII)) {
182 DEBUG(3,("samr: failed to decode password buffer\n"));
183 return NT_STATUS_WRONG_PASSWORD;
186 /* work out the domain dn */
187 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
188 if (domain_sid == NULL) {
189 return NT_STATUS_NO_SUCH_USER;
192 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
193 "(objectSid=%s)", domain_sid);
195 return NT_STATUS_INTERNAL_DB_CORRUPTION;
200 mod.dn = talloc_strdup(mem_ctx, user_dn);
202 return NT_STATUS_NO_MEMORY;
205 /* set the password - samdb needs to know both the domain and user DNs,
206 so the domain password policy can be used */
207 status = samdb_set_password(sam_ctx, mem_ctx,
212 if (!NT_STATUS_IS_OK(status)) {
216 /* modify the samdb record */
217 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
219 return NT_STATUS_UNSUCCESSFUL;
227 samr_ChangePasswordUser3
229 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
231 struct samr_ChangePasswordUser3 *r)
235 uint32_t new_pass_len;
236 void *sam_ctx = NULL;
237 const char *user_dn, *domain_dn = NULL;
239 struct ldb_message **res, mod;
240 const char * const attrs[] = { "objectSid", "ntPwdHash", "unicodePwd", NULL };
241 const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength",
242 "pwdProperties", "minPwdAge", "maxPwdAge",
244 const char *domain_sid;
245 struct samr_Password *nt_pwd;
246 DATA_BLOB nt_pwd_blob;
247 struct samr_DomInfo1 *dominfo;
248 struct samr_ChangeReject *reject;
253 if (r->in.nt_password == NULL ||
254 r->in.nt_verifier == NULL) {
255 status = NT_STATUS_INVALID_PARAMETER;
259 /* this call doesn't take a policy handle, so we need to open
260 the sam db from scratch */
261 sam_ctx = samdb_connect(mem_ctx);
262 if (sam_ctx == NULL) {
263 status = NT_STATUS_INVALID_SYSTEM_SERVICE;
267 /* we need the users dn and the domain dn (derived from the
268 user SID). We also need the current lm and nt password hashes
269 in order to decrypt the incoming passwords */
270 ret = samdb_search(sam_ctx,
271 mem_ctx, NULL, &res, attrs,
272 "(&(sAMAccountName=%s)(objectclass=user))",
273 r->in.account->string);
275 status = NT_STATUS_NO_SUCH_USER;
279 user_dn = res[0]->dn;
281 status = samdb_result_passwords(mem_ctx, res[0], NULL, &nt_pwd);
282 if (!NT_STATUS_IS_OK(status) ) {
287 status = NT_STATUS_WRONG_PASSWORD;
291 /* decrypt the password we have been given */
292 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
293 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
294 data_blob_free(&nt_pwd_blob);
296 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
297 &new_pass_len, STR_UNICODE)) {
298 DEBUG(3,("samr: failed to decode password buffer\n"));
299 status = NT_STATUS_WRONG_PASSWORD;
303 /* work out the domain dn */
304 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
305 if (domain_sid == NULL) {
306 status = NT_STATUS_NO_SUCH_DOMAIN;
310 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
311 "(objectSid=%s)", domain_sid);
313 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
319 mod.dn = talloc_strdup(mem_ctx, user_dn);
321 status = NT_STATUS_NO_MEMORY;
325 /* set the password - samdb needs to know both the domain and user DNs,
326 so the domain password policy can be used */
327 status = samdb_set_password(sam_ctx, mem_ctx,
332 if (!NT_STATUS_IS_OK(status)) {
336 /* modify the samdb record */
337 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
339 status = NT_STATUS_UNSUCCESSFUL;
346 ret = samdb_search(sam_ctx,
347 mem_ctx, NULL, &res, dom_attrs,
354 /* on failure we need to fill in the reject reasons */
355 dominfo = talloc_p(mem_ctx, struct samr_DomInfo1);
356 if (dominfo == NULL) {
359 reject = talloc_p(mem_ctx, struct samr_ChangeReject);
360 if (reject == NULL) {
364 ZERO_STRUCTP(dominfo);
365 ZERO_STRUCTP(reject);
367 reject->reason = reason;
369 r->out.dominfo = dominfo;
370 r->out.reject = reject;
376 dominfo->min_password_len = samdb_result_uint (res[0], "minPwdLength", 0);
377 dominfo->password_properties = samdb_result_uint (res[0], "pwdProperties", 0);
378 dominfo->password_history = samdb_result_uint (res[0], "pwdHistoryLength", 0);
379 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
380 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
387 samr_ChangePasswordUser2
389 easy - just a subset of samr_ChangePasswordUser3
391 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
392 struct samr_ChangePasswordUser2 *r)
394 struct samr_ChangePasswordUser3 r2;
396 r2.in.server = r->in.server;
397 r2.in.account = r->in.account;
398 r2.in.nt_password = r->in.nt_password;
399 r2.in.nt_verifier = r->in.nt_verifier;
400 r2.in.lm_change = r->in.lm_change;
401 r2.in.lm_password = r->in.lm_password;
402 r2.in.lm_verifier = r->in.lm_verifier;
403 r2.in.password3 = NULL;
405 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
410 check that a password is sufficiently complex
412 static BOOL samdb_password_complexity_ok(const char *pass)
414 return check_password_quality(pass);
418 set the user password using plaintext, obeying any user or domain
419 password restrictions
421 note that this function doesn't actually store the result in the
422 database, it just fills in the "mod" structure with ldb modify
423 elements to setup the correct change when samdb_replace() is
424 called. This allows the caller to combine the change with other
425 changes (as is needed by some of the set user info levels)
427 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
428 const char *user_dn, const char *domain_dn,
429 struct ldb_message *mod,
430 const char *new_pass,
431 struct samr_Password *lmNewHash,
432 struct samr_Password *ntNewHash,
434 uint32_t *reject_reason)
436 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
437 "ntPwdHistory", "unicodePwd",
438 "lmPwdHash", "ntPwdHash", "badPwdCount",
440 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
441 "maxPwdAge", "minPwdAge",
442 "minPwdLength", "pwdLastSet", NULL };
443 const char *unicodePwd;
446 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
447 uint_t userAccountControl, badPwdCount;
448 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
449 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
450 struct samr_Password local_lmNewHash, local_ntNewHash;
451 int lmPwdHistory_len, ntPwdHistory_len;
452 struct ldb_message **res;
454 time_t now = time(NULL);
458 /* we need to know the time to compute password age */
459 unix_to_nt_time(&now_nt, now);
461 /* pull all the user parameters */
462 count = samdb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
464 return NT_STATUS_INTERNAL_DB_CORRUPTION;
466 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
467 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
468 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
469 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
470 "lmPwdHistory", &lmPwdHistory);
471 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
472 "ntPwdHistory", &ntPwdHistory);
473 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
474 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
475 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
477 /* pull the domain parameters */
478 count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
480 return NT_STATUS_INTERNAL_DB_CORRUPTION;
482 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
483 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
484 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
485 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
488 /* check the various password restrictions */
489 if (minPwdLength > strlen_m(new_pass)) {
491 *reject_reason = SAMR_REJECT_TOO_SHORT;
493 return NT_STATUS_PASSWORD_RESTRICTION;
496 /* possibly check password complexity */
497 if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
498 !samdb_password_complexity_ok(new_pass)) {
500 *reject_reason = SAMR_REJECT_COMPLEXITY;
502 return NT_STATUS_PASSWORD_RESTRICTION;
505 /* compute the new nt and lm hashes */
506 if (E_deshash(new_pass, local_lmNewHash.hash)) {
507 lmNewHash = &local_lmNewHash;
509 E_md4hash(new_pass, local_ntNewHash.hash);
510 ntNewHash = &local_ntNewHash;
514 /* are all password changes disallowed? */
515 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
517 *reject_reason = SAMR_REJECT_OTHER;
519 return NT_STATUS_PASSWORD_RESTRICTION;
522 /* can this user change password? */
523 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
525 *reject_reason = SAMR_REJECT_OTHER;
527 return NT_STATUS_PASSWORD_RESTRICTION;
530 /* yes, this is a minus. The ages are in negative 100nsec units! */
531 if (pwdLastSet - minPwdAge > now_nt) {
533 *reject_reason = SAMR_REJECT_OTHER;
535 return NT_STATUS_PASSWORD_RESTRICTION;
538 /* check the immediately past password */
539 if (pwdHistoryLength > 0) {
540 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
542 *reject_reason = SAMR_REJECT_COMPLEXITY;
544 return NT_STATUS_PASSWORD_RESTRICTION;
546 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
548 *reject_reason = SAMR_REJECT_COMPLEXITY;
550 return NT_STATUS_PASSWORD_RESTRICTION;
554 /* check the password history */
555 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
556 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
558 if (pwdHistoryLength > 0) {
559 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
561 *reject_reason = SAMR_REJECT_COMPLEXITY;
563 return NT_STATUS_PASSWORD_RESTRICTION;
565 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
567 *reject_reason = SAMR_REJECT_COMPLEXITY;
569 return NT_STATUS_PASSWORD_RESTRICTION;
571 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
573 *reject_reason = SAMR_REJECT_COMPLEXITY;
575 return NT_STATUS_PASSWORD_RESTRICTION;
579 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
580 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
582 *reject_reason = SAMR_REJECT_COMPLEXITY;
584 return NT_STATUS_PASSWORD_RESTRICTION;
587 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
588 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
590 *reject_reason = SAMR_REJECT_COMPLEXITY;
592 return NT_STATUS_PASSWORD_RESTRICTION;
597 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
599 /* the password is acceptable. Start forming the new fields */
601 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", *lmNewHash));
603 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
607 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", *ntNewHash));
609 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
612 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
613 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
614 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
615 "unicodePwd", new_pass));
617 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
620 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
622 if (pwdHistoryLength == 0) {
623 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
624 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
628 /* store the password history */
629 new_lmPwdHistory = talloc_array_p(mem_ctx, struct samr_Password,
631 if (!new_lmPwdHistory) {
632 return NT_STATUS_NO_MEMORY;
634 new_ntPwdHistory = talloc_array_p(mem_ctx, struct samr_Password,
636 if (!new_ntPwdHistory) {
637 return NT_STATUS_NO_MEMORY;
639 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
640 new_lmPwdHistory[i+1] = lmPwdHistory[i];
642 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
643 new_ntPwdHistory[i+1] = ntPwdHistory[i];
646 /* Don't store 'long' passwords in the LM history,
647 but make sure to 'expire' one password off the other end */
649 new_lmPwdHistory[0] = *lmNewHash;
651 ZERO_STRUCT(new_lmPwdHistory[0]);
653 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
656 new_ntPwdHistory[0] = *ntNewHash;
658 ZERO_STRUCT(new_ntPwdHistory[0]);
660 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
662 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
667 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
675 set password via a samr_CryptPassword buffer
676 this will in the 'msg' with modify operations that will update the user
677 password when applied
679 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
681 const char *account_dn, const char *domain_dn,
683 struct ldb_message *msg,
684 struct samr_CryptPassword *pwbuf)
688 uint32_t new_pass_len;
689 DATA_BLOB session_key = data_blob(NULL, 0);
691 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
692 if (!NT_STATUS_IS_OK(nt_status)) {
696 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
698 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
699 &new_pass_len, STR_UNICODE)) {
700 DEBUG(3,("samr: failed to decode password buffer\n"));
701 return NT_STATUS_WRONG_PASSWORD;
704 /* set the password - samdb needs to know both the domain and user DNs,
705 so the domain password policy can be used */
706 return samdb_set_password(sam_ctx, mem_ctx,
707 account_dn, domain_dn,
710 False /* This is a password set, not change */,
716 set password via a samr_CryptPasswordEx buffer
717 this will in the 'msg' with modify operations that will update the user
718 password when applied
720 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
722 const char *account_dn, const char *domain_dn,
724 struct ldb_message *msg,
725 struct samr_CryptPasswordEx *pwbuf)
729 uint32_t new_pass_len;
730 DATA_BLOB co_session_key;
731 DATA_BLOB session_key = data_blob(NULL, 0);
732 struct MD5Context ctx;
734 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
735 if (!NT_STATUS_IS_OK(nt_status)) {
739 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
740 if (!co_session_key.data) {
741 return NT_STATUS_NO_MEMORY;
745 MD5Update(&ctx, &pwbuf->data[516], 16);
746 MD5Update(&ctx, session_key.data, session_key.length);
747 MD5Final(co_session_key.data, &ctx);
749 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
751 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
752 &new_pass_len, STR_UNICODE)) {
753 DEBUG(3,("samr: failed to decode password buffer\n"));
754 return NT_STATUS_WRONG_PASSWORD;
757 /* set the password - samdb needs to know both the domain and user DNs,
758 so the domain password policy can be used */
759 return samdb_set_password(sam_ctx, mem_ctx,
760 account_dn, domain_dn,