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"
33 samr_ChangePasswordUser
35 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
36 struct samr_ChangePasswordUser *r)
38 struct dcesrv_handle *h;
39 struct samr_account_state *a_state;
40 struct ldb_message **res, mod, *msg;
42 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
43 struct samr_Password *lm_pwd, *nt_pwd;
44 NTSTATUS status = NT_STATUS_OK;
45 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
47 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
51 /* fetch the old hashes */
52 ret = samdb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
53 "dn=%s", a_state->account_dn);
55 return NT_STATUS_INTERNAL_DB_CORRUPTION;
59 /* basic sanity checking on parameters */
60 if (!r->in.lm_present || !r->in.nt_present ||
61 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
62 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
63 /* we should really handle a change with lm not
65 return NT_STATUS_INVALID_PARAMETER_MIX;
67 if (!r->in.cross1_present || !r->in.nt_cross) {
68 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
70 if (!r->in.cross2_present || !r->in.lm_cross) {
71 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
74 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
75 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
76 return NT_STATUS_WRONG_PASSWORD;
79 /* decrypt and check the new lm hash */
80 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
81 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
82 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
83 return NT_STATUS_WRONG_PASSWORD;
86 /* decrypt and check the new nt hash */
87 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
88 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
89 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
90 return NT_STATUS_WRONG_PASSWORD;
93 /* check the nt cross hash */
94 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
95 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
96 return NT_STATUS_WRONG_PASSWORD;
99 /* check the lm cross hash */
100 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
101 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
102 return NT_STATUS_WRONG_PASSWORD;
106 mod.dn = talloc_strdup(mem_ctx, a_state->account_dn);
108 return NT_STATUS_NO_MEMORY;
111 status = samdb_set_password(a_state->sam_ctx, mem_ctx,
112 a_state->account_dn, a_state->domain_state->domain_dn,
113 &mod, NULL, &new_lmPwdHash, &new_ntPwdHash,
115 if (!NT_STATUS_IS_OK(status)) {
119 /* modify the samdb record */
120 ret = samdb_replace(a_state->sam_ctx, mem_ctx, &mod);
122 return NT_STATUS_UNSUCCESSFUL;
129 samr_OemChangePasswordUser2
131 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
132 struct samr_OemChangePasswordUser2 *r)
136 uint32_t new_pass_len;
137 struct samr_CryptPassword *pwbuf = r->in.password;
139 const char *user_dn, *domain_dn;
141 struct ldb_message **res, mod;
142 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
143 const char *domain_sid;
144 struct samr_Password *lm_pwd;
145 DATA_BLOB lm_pwd_blob;
148 return NT_STATUS_WRONG_PASSWORD;
151 /* this call doesn't take a policy handle, so we need to open
152 the sam db from scratch */
153 sam_ctx = samdb_connect(mem_ctx);
154 if (sam_ctx == NULL) {
155 return NT_STATUS_INVALID_SYSTEM_SERVICE;
158 /* we need the users dn and the domain dn (derived from the
159 user SID). We also need the current lm password hash in
160 order to decrypt the incoming password */
161 ret = samdb_search(sam_ctx,
162 mem_ctx, NULL, &res, attrs,
163 "(&(sAMAccountName=%s)(objectclass=user))",
164 r->in.account->string);
166 return NT_STATUS_NO_SUCH_USER;
169 user_dn = res[0]->dn;
171 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
172 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
173 return NT_STATUS_WRONG_PASSWORD;
176 /* decrypt the password we have been given */
177 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
178 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
179 data_blob_free(&lm_pwd_blob);
181 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
182 &new_pass_len, STR_ASCII)) {
183 DEBUG(3,("samr: failed to decode password buffer\n"));
184 return NT_STATUS_WRONG_PASSWORD;
187 /* work out the domain dn */
188 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
189 if (domain_sid == NULL) {
190 return NT_STATUS_NO_SUCH_USER;
193 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
194 "(objectSid=%s)", domain_sid);
196 return NT_STATUS_INTERNAL_DB_CORRUPTION;
201 mod.dn = talloc_strdup(mem_ctx, user_dn);
203 return NT_STATUS_NO_MEMORY;
206 /* set the password - samdb needs to know both the domain and user DNs,
207 so the domain password policy can be used */
208 status = samdb_set_password(sam_ctx, mem_ctx,
213 if (!NT_STATUS_IS_OK(status)) {
217 /* modify the samdb record */
218 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
220 return NT_STATUS_UNSUCCESSFUL;
228 samr_ChangePasswordUser3
230 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
232 struct samr_ChangePasswordUser3 *r)
236 uint32_t new_pass_len;
237 void *sam_ctx = NULL;
238 const char *user_dn, *domain_dn = NULL;
240 struct ldb_message **res, mod;
241 const char * const attrs[] = { "objectSid", "ntPwdHash", "unicodePwd", NULL };
242 const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength",
243 "pwdProperties", "minPwdAge", "maxPwdAge",
245 const char *domain_sid;
246 struct samr_Password *nt_pwd;
247 DATA_BLOB nt_pwd_blob;
248 struct samr_DomInfo1 *dominfo;
249 struct samr_ChangeReject *reject;
254 if (r->in.nt_password == NULL ||
255 r->in.nt_verifier == NULL) {
256 status = NT_STATUS_INVALID_PARAMETER;
260 /* this call doesn't take a policy handle, so we need to open
261 the sam db from scratch */
262 sam_ctx = samdb_connect(mem_ctx);
263 if (sam_ctx == NULL) {
264 status = NT_STATUS_INVALID_SYSTEM_SERVICE;
268 /* we need the users dn and the domain dn (derived from the
269 user SID). We also need the current lm and nt password hashes
270 in order to decrypt the incoming passwords */
271 ret = samdb_search(sam_ctx,
272 mem_ctx, NULL, &res, attrs,
273 "(&(sAMAccountName=%s)(objectclass=user))",
274 r->in.account->string);
276 status = NT_STATUS_NO_SUCH_USER;
280 user_dn = res[0]->dn;
282 status = samdb_result_passwords(mem_ctx, res[0], NULL, &nt_pwd);
283 if (!NT_STATUS_IS_OK(status) ) {
288 status = NT_STATUS_WRONG_PASSWORD;
292 /* decrypt the password we have been given */
293 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
294 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
295 data_blob_free(&nt_pwd_blob);
297 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
298 &new_pass_len, STR_UNICODE)) {
299 DEBUG(3,("samr: failed to decode password buffer\n"));
300 status = NT_STATUS_WRONG_PASSWORD;
304 /* work out the domain dn */
305 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
306 if (domain_sid == NULL) {
307 status = NT_STATUS_NO_SUCH_DOMAIN;
311 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
312 "(objectSid=%s)", domain_sid);
314 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
320 mod.dn = talloc_strdup(mem_ctx, user_dn);
322 status = NT_STATUS_NO_MEMORY;
326 /* set the password - samdb needs to know both the domain and user DNs,
327 so the domain password policy can be used */
328 status = samdb_set_password(sam_ctx, mem_ctx,
333 if (!NT_STATUS_IS_OK(status)) {
337 /* modify the samdb record */
338 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
340 status = NT_STATUS_UNSUCCESSFUL;
347 ret = samdb_search(sam_ctx,
348 mem_ctx, NULL, &res, dom_attrs,
355 /* on failure we need to fill in the reject reasons */
356 dominfo = talloc_p(mem_ctx, struct samr_DomInfo1);
357 if (dominfo == NULL) {
360 reject = talloc_p(mem_ctx, struct samr_ChangeReject);
361 if (reject == NULL) {
365 ZERO_STRUCTP(dominfo);
366 ZERO_STRUCTP(reject);
368 reject->reason = reason;
370 r->out.dominfo = dominfo;
371 r->out.reject = reject;
377 dominfo->min_password_len = samdb_result_uint (res[0], "minPwdLength", 0);
378 dominfo->password_properties = samdb_result_uint (res[0], "pwdProperties", 0);
379 dominfo->password_history = samdb_result_uint (res[0], "pwdHistoryLength", 0);
380 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
381 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
388 samr_ChangePasswordUser2
390 easy - just a subset of samr_ChangePasswordUser3
392 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
393 struct samr_ChangePasswordUser2 *r)
395 struct samr_ChangePasswordUser3 r2;
397 r2.in.server = r->in.server;
398 r2.in.account = r->in.account;
399 r2.in.nt_password = r->in.nt_password;
400 r2.in.nt_verifier = r->in.nt_verifier;
401 r2.in.lm_change = r->in.lm_change;
402 r2.in.lm_password = r->in.lm_password;
403 r2.in.lm_verifier = r->in.lm_verifier;
404 r2.in.password3 = NULL;
406 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
411 check that a password is sufficiently complex
413 static BOOL samdb_password_complexity_ok(const char *pass)
415 return check_password_quality(pass);
419 set the user password using plaintext, obeying any user or domain
420 password restrictions
422 note that this function doesn't actually store the result in the
423 database, it just fills in the "mod" structure with ldb modify
424 elements to setup the correct change when samdb_replace() is
425 called. This allows the caller to combine the change with other
426 changes (as is needed by some of the set user info levels)
428 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
429 const char *user_dn, const char *domain_dn,
430 struct ldb_message *mod,
431 const char *new_pass,
432 struct samr_Password *lmNewHash,
433 struct samr_Password *ntNewHash,
435 uint32_t *reject_reason)
437 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
438 "ntPwdHistory", "unicodePwd",
439 "lmPwdHash", "ntPwdHash", "badPwdCount",
441 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
442 "maxPwdAge", "minPwdAge",
443 "minPwdLength", "pwdLastSet", NULL };
444 const char *unicodePwd;
447 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
448 uint_t userAccountControl, badPwdCount;
449 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
450 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
451 struct samr_Password local_lmNewHash, local_ntNewHash;
452 int lmPwdHistory_len, ntPwdHistory_len;
453 struct ldb_message **res;
455 time_t now = time(NULL);
459 /* we need to know the time to compute password age */
460 unix_to_nt_time(&now_nt, now);
462 /* pull all the user parameters */
463 count = samdb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
465 return NT_STATUS_INTERNAL_DB_CORRUPTION;
467 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
468 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
469 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
470 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
471 "lmPwdHistory", &lmPwdHistory);
472 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
473 "ntPwdHistory", &ntPwdHistory);
474 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
475 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
476 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
478 /* pull the domain parameters */
479 count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
481 return NT_STATUS_INTERNAL_DB_CORRUPTION;
483 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
484 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
485 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
486 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
489 /* check the various password restrictions */
490 if (minPwdLength > strlen_m(new_pass)) {
492 *reject_reason = SAMR_REJECT_TOO_SHORT;
494 return NT_STATUS_PASSWORD_RESTRICTION;
497 /* possibly check password complexity */
498 if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
499 !samdb_password_complexity_ok(new_pass)) {
501 *reject_reason = SAMR_REJECT_COMPLEXITY;
503 return NT_STATUS_PASSWORD_RESTRICTION;
506 /* compute the new nt and lm hashes */
507 if (E_deshash(new_pass, local_lmNewHash.hash)) {
508 lmNewHash = &local_lmNewHash;
510 E_md4hash(new_pass, local_ntNewHash.hash);
511 ntNewHash = &local_ntNewHash;
515 /* are all password changes disallowed? */
516 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
518 *reject_reason = SAMR_REJECT_OTHER;
520 return NT_STATUS_PASSWORD_RESTRICTION;
523 /* can this user change password? */
524 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
526 *reject_reason = SAMR_REJECT_OTHER;
528 return NT_STATUS_PASSWORD_RESTRICTION;
531 /* yes, this is a minus. The ages are in negative 100nsec units! */
532 if (pwdLastSet - minPwdAge > now_nt) {
534 *reject_reason = SAMR_REJECT_OTHER;
536 return NT_STATUS_PASSWORD_RESTRICTION;
539 /* check the immediately past password */
540 if (pwdHistoryLength > 0) {
541 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
543 *reject_reason = SAMR_REJECT_COMPLEXITY;
545 return NT_STATUS_PASSWORD_RESTRICTION;
547 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
549 *reject_reason = SAMR_REJECT_COMPLEXITY;
551 return NT_STATUS_PASSWORD_RESTRICTION;
555 /* check the password history */
556 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
557 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
559 if (pwdHistoryLength > 0) {
560 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
562 *reject_reason = SAMR_REJECT_COMPLEXITY;
564 return NT_STATUS_PASSWORD_RESTRICTION;
566 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
568 *reject_reason = SAMR_REJECT_COMPLEXITY;
570 return NT_STATUS_PASSWORD_RESTRICTION;
572 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
574 *reject_reason = SAMR_REJECT_COMPLEXITY;
576 return NT_STATUS_PASSWORD_RESTRICTION;
580 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
581 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
583 *reject_reason = SAMR_REJECT_COMPLEXITY;
585 return NT_STATUS_PASSWORD_RESTRICTION;
588 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
589 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
591 *reject_reason = SAMR_REJECT_COMPLEXITY;
593 return NT_STATUS_PASSWORD_RESTRICTION;
598 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
600 /* the password is acceptable. Start forming the new fields */
602 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", *lmNewHash));
604 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
608 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", *ntNewHash));
610 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
613 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
614 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
615 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
616 "unicodePwd", new_pass));
618 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
621 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
623 if (pwdHistoryLength == 0) {
624 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
625 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
629 /* store the password history */
630 new_lmPwdHistory = talloc_array_p(mem_ctx, struct samr_Password,
632 if (!new_lmPwdHistory) {
633 return NT_STATUS_NO_MEMORY;
635 new_ntPwdHistory = talloc_array_p(mem_ctx, struct samr_Password,
637 if (!new_ntPwdHistory) {
638 return NT_STATUS_NO_MEMORY;
640 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
641 new_lmPwdHistory[i+1] = lmPwdHistory[i];
643 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
644 new_ntPwdHistory[i+1] = ntPwdHistory[i];
647 /* Don't store 'long' passwords in the LM history,
648 but make sure to 'expire' one password off the other end */
650 new_lmPwdHistory[0] = *lmNewHash;
652 ZERO_STRUCT(new_lmPwdHistory[0]);
654 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
657 new_ntPwdHistory[0] = *ntNewHash;
659 ZERO_STRUCT(new_ntPwdHistory[0]);
661 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
663 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
668 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
676 set password via a samr_CryptPassword buffer
677 this will in the 'msg' with modify operations that will update the user
678 password when applied
680 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
682 const char *account_dn, const char *domain_dn,
684 struct ldb_message *msg,
685 struct samr_CryptPassword *pwbuf)
689 uint32_t new_pass_len;
690 DATA_BLOB session_key = data_blob(NULL, 0);
692 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
693 if (!NT_STATUS_IS_OK(nt_status)) {
697 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
699 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
700 &new_pass_len, STR_UNICODE)) {
701 DEBUG(3,("samr: failed to decode password buffer\n"));
702 return NT_STATUS_WRONG_PASSWORD;
705 /* set the password - samdb needs to know both the domain and user DNs,
706 so the domain password policy can be used */
707 return samdb_set_password(sam_ctx, mem_ctx,
708 account_dn, domain_dn,
711 False /* This is a password set, not change */,
717 set password via a samr_CryptPasswordEx buffer
718 this will in the 'msg' with modify operations that will update the user
719 password when applied
721 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
723 const char *account_dn, const char *domain_dn,
725 struct ldb_message *msg,
726 struct samr_CryptPasswordEx *pwbuf)
730 uint32_t new_pass_len;
731 DATA_BLOB co_session_key;
732 DATA_BLOB session_key = data_blob(NULL, 0);
733 struct MD5Context ctx;
735 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
736 if (!NT_STATUS_IS_OK(nt_status)) {
740 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
741 if (!co_session_key.data) {
742 return NT_STATUS_NO_MEMORY;
746 MD5Update(&ctx, &pwbuf->data[516], 16);
747 MD5Update(&ctx, session_key.data, session_key.length);
748 MD5Final(co_session_key.data, &ctx);
750 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
752 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
753 &new_pass_len, STR_UNICODE)) {
754 DEBUG(3,("samr: failed to decode password buffer\n"));
755 return NT_STATUS_WRONG_PASSWORD;
758 /* set the password - samdb needs to know both the domain and user DNs,
759 so the domain password policy can be used */
760 return samdb_set_password(sam_ctx, mem_ctx,
761 account_dn, domain_dn,