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 "rpc_server/common/common.h"
25 #include "rpc_server/samr/dcesrv_samr.h"
28 samr_ChangePasswordUser
30 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
31 struct samr_ChangePasswordUser *r)
33 struct dcesrv_handle *h;
34 struct samr_account_state *a_state;
35 struct ldb_message **res, mod, *msg;
37 struct samr_Hash new_lmPwdHash, new_ntPwdHash, checkHash;
38 uint8 *lm_pwd, *nt_pwd;
39 NTSTATUS status = NT_STATUS_OK;
40 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
42 DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_USER);
46 /* fetch the old hashes */
47 ret = samdb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
48 "dn=%s", a_state->account_dn);
50 return NT_STATUS_INTERNAL_DB_CORRUPTION;
54 /* basic sanity checking on parameters */
55 if (!r->in.lm_present || !r->in.nt_present ||
56 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
57 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
58 /* we should really handle a change with lm not
60 return NT_STATUS_INVALID_PARAMETER_MIX;
62 if (!r->in.cross1_present || !r->in.nt_cross) {
63 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
65 if (!r->in.cross2_present || !r->in.lm_cross) {
66 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
69 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
70 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
71 return NT_STATUS_WRONG_PASSWORD;
74 /* decrypt and check the new lm hash */
75 D_P16(lm_pwd, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
76 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
77 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
78 return NT_STATUS_WRONG_PASSWORD;
81 /* decrypt and check the new nt hash */
82 D_P16(nt_pwd, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
83 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
84 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
85 return NT_STATUS_WRONG_PASSWORD;
88 /* check the nt cross hash */
89 D_P16(lm_pwd, r->in.nt_cross->hash, checkHash.hash);
90 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
91 return NT_STATUS_WRONG_PASSWORD;
94 /* check the lm cross hash */
95 D_P16(nt_pwd, r->in.lm_cross->hash, checkHash.hash);
96 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
97 return NT_STATUS_WRONG_PASSWORD;
101 mod.dn = talloc_strdup(mem_ctx, a_state->account_dn);
103 return NT_STATUS_NO_MEMORY;
106 status = samdb_set_password(a_state->sam_ctx, mem_ctx,
107 a_state->account_dn, a_state->domain_state->domain_dn,
108 &mod, NULL, &new_lmPwdHash, &new_ntPwdHash,
110 if (!NT_STATUS_IS_OK(status)) {
114 /* modify the samdb record */
115 ret = samdb_replace(a_state->sam_ctx, mem_ctx, &mod);
117 return NT_STATUS_UNSUCCESSFUL;
124 samr_OemChangePasswordUser2
126 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
127 struct samr_OemChangePasswordUser2 *r)
131 uint32_t new_pass_len;
132 struct samr_CryptPassword *pwbuf = r->in.password;
134 const char *user_dn, *domain_dn;
136 struct ldb_message **res, mod;
137 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
138 const char *domain_sid;
142 return NT_STATUS_WRONG_PASSWORD;
145 /* this call doesn't take a policy handle, so we need to open
146 the sam db from scratch */
147 sam_ctx = samdb_connect();
148 if (sam_ctx == NULL) {
149 return NT_STATUS_INVALID_SYSTEM_SERVICE;
152 /* we need the users dn and the domain dn (derived from the
153 user SID). We also need the current lm password hash in
154 order to decrypt the incoming password */
155 ret = samdb_search(sam_ctx,
156 mem_ctx, NULL, &res, attrs,
157 "(&(sAMAccountName=%s)(objectclass=user))",
158 r->in.account->name);
160 samdb_close(sam_ctx);
161 return NT_STATUS_NO_SUCH_USER;
164 user_dn = res[0]->dn;
166 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
167 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
168 return NT_STATUS_WRONG_PASSWORD;
171 /* decrypt the password we have been given */
172 SamOEMhash(pwbuf->data, lm_pwd, 516);
174 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
175 &new_pass_len, STR_ASCII)) {
176 DEBUG(3,("samr: failed to decode password buffer\n"));
177 samdb_close(sam_ctx);
178 return NT_STATUS_WRONG_PASSWORD;
181 /* work out the domain dn */
182 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
183 if (domain_sid == NULL) {
184 samdb_close(sam_ctx);
185 return NT_STATUS_NO_SUCH_USER;
188 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
189 "(objectSid=%s)", domain_sid);
191 samdb_close(sam_ctx);
192 return NT_STATUS_INTERNAL_DB_CORRUPTION;
197 mod.dn = talloc_strdup(mem_ctx, user_dn);
199 samdb_close(sam_ctx);
200 return NT_STATUS_NO_MEMORY;
203 /* set the password - samdb needs to know both the domain and user DNs,
204 so the domain password policy can be used */
205 status = samdb_set_password(sam_ctx, mem_ctx,
210 if (!NT_STATUS_IS_OK(status)) {
211 samdb_close(sam_ctx);
215 /* modify the samdb record */
216 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
218 samdb_close(sam_ctx);
219 return NT_STATUS_UNSUCCESSFUL;
222 samdb_close(sam_ctx);
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;
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();
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->name);
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)) {
286 /* decrypt the password we have been given */
287 SamOEMhash(r->in.nt_password->data, nt_pwd, 516);
289 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
290 &new_pass_len, STR_UNICODE)) {
291 DEBUG(3,("samr: failed to decode password buffer\n"));
292 status = NT_STATUS_WRONG_PASSWORD;
296 /* work out the domain dn */
297 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
298 if (domain_sid == NULL) {
299 status = NT_STATUS_NO_SUCH_DOMAIN;
303 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
304 "(objectSid=%s)", domain_sid);
306 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
312 mod.dn = talloc_strdup(mem_ctx, user_dn);
314 status = NT_STATUS_NO_MEMORY;
318 /* set the password - samdb needs to know both the domain and user DNs,
319 so the domain password policy can be used */
320 status = samdb_set_password(sam_ctx, mem_ctx,
325 if (!NT_STATUS_IS_OK(status)) {
329 /* modify the samdb record */
330 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
332 status = NT_STATUS_UNSUCCESSFUL;
336 samdb_close(sam_ctx);
340 ret = samdb_search(sam_ctx,
341 mem_ctx, NULL, &res, dom_attrs,
344 samdb_close(sam_ctx);
351 /* on failure we need to fill in the reject reasons */
352 dominfo = talloc_p(mem_ctx, struct samr_DomInfo1);
353 if (dominfo == NULL) {
356 reject = talloc_p(mem_ctx, struct samr_ChangeReject);
357 if (reject == NULL) {
361 ZERO_STRUCTP(dominfo);
362 ZERO_STRUCTP(reject);
364 reject->reason = reason;
366 r->out.dominfo = dominfo;
367 r->out.reject = reject;
373 dominfo->min_pwd_len = samdb_result_uint (res[0], "minPwdLength", 0);
374 dominfo->password_properties = samdb_result_uint (res[0], "pwdProperties", 0);
375 dominfo->password_history = samdb_result_uint (res[0], "pwdHistoryLength", 0);
376 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
377 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
384 samr_ChangePasswordUser2
386 easy - just a subset of samr_ChangePasswordUser3
388 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
389 struct samr_ChangePasswordUser2 *r)
391 struct samr_ChangePasswordUser3 r2;
393 r2.in.server = r->in.server;
394 r2.in.account = r->in.account;
395 r2.in.nt_password = r->in.nt_password;
396 r2.in.nt_verifier = r->in.nt_verifier;
397 r2.in.lm_change = r->in.lm_change;
398 r2.in.lm_password = r->in.lm_password;
399 r2.in.lm_verifier = r->in.lm_verifier;
400 r2.in.password3 = NULL;
402 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
407 check that a password is sufficiently complex
409 BOOL samdb_password_complexity_ok(const char *pass)
411 return check_password_quality(pass);
415 set the user password using plaintext, obeying any user or domain
416 password restrictions
418 note that this function doesn't actually store the result in the
419 database, it just fills in the "mod" structure with ldb modify
420 elements to setup the correct change when samdb_replace() is
421 called. This allows the caller to combine the change with other
422 changes (as is needed by some of the set user info levels)
424 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
425 const char *user_dn, const char *domain_dn,
426 struct ldb_message *mod,
427 const char *new_pass,
428 struct samr_Hash *lmNewHash,
429 struct samr_Hash *ntNewHash,
431 uint32_t *reject_reason)
433 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
434 "ntPwdHistory", "unicodePwd",
435 "lmPwdHash", "ntPwdHash", "badPwdCount",
437 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
438 "maxPwdAge", "minPwdAge",
439 "minPwdLength", "pwdLastSet", NULL };
440 const char *unicodePwd;
443 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
444 uint_t userAccountControl, badPwdCount;
445 struct samr_Hash *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
446 struct samr_Hash *new_lmPwdHistory, *new_ntPwdHistory;
447 struct samr_Hash local_lmNewHash, local_ntNewHash;
448 int lmPwdHistory_len, ntPwdHistory_len;
449 struct ldb_message **res;
451 time_t now = time(NULL);
455 /* we need to know the time to compute password age */
456 unix_to_nt_time(&now_nt, now);
458 /* pull all the user parameters */
459 count = samdb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
461 return NT_STATUS_INTERNAL_DB_CORRUPTION;
463 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
464 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
465 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
466 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
467 "lmPwdHistory", &lmPwdHistory);
468 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
469 "ntPwdHistory", &ntPwdHistory);
470 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
471 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
472 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
474 /* pull the domain parameters */
475 count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
477 return NT_STATUS_INTERNAL_DB_CORRUPTION;
479 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
480 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
481 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
482 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
485 /* check the various password restrictions */
486 if (minPwdLength > str_charnum(new_pass)) {
488 *reject_reason = SAMR_REJECT_TOO_SHORT;
490 return NT_STATUS_PASSWORD_RESTRICTION;
493 /* possibly check password complexity */
494 if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
495 !samdb_password_complexity_ok(new_pass)) {
497 *reject_reason = SAMR_REJECT_COMPLEXITY;
499 return NT_STATUS_PASSWORD_RESTRICTION;
502 /* compute the new nt and lm hashes */
503 if (E_deshash(new_pass, local_lmNewHash.hash)) {
504 lmNewHash = &local_lmNewHash;
506 E_md4hash(new_pass, local_ntNewHash.hash);
507 ntNewHash = &local_ntNewHash;
511 /* are all password changes disallowed? */
512 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
514 *reject_reason = SAMR_REJECT_OTHER;
516 return NT_STATUS_PASSWORD_RESTRICTION;
519 /* can this user change password? */
520 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
522 *reject_reason = SAMR_REJECT_OTHER;
524 return NT_STATUS_PASSWORD_RESTRICTION;
527 /* yes, this is a minus. The ages are in negative 100nsec units! */
528 if (pwdLastSet - minPwdAge > now_nt) {
530 *reject_reason = SAMR_REJECT_OTHER;
532 return NT_STATUS_PASSWORD_RESTRICTION;
535 /* check the immediately past password */
536 if (pwdHistoryLength > 0) {
537 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
539 *reject_reason = SAMR_REJECT_COMPLEXITY;
541 return NT_STATUS_PASSWORD_RESTRICTION;
543 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
545 *reject_reason = SAMR_REJECT_COMPLEXITY;
547 return NT_STATUS_PASSWORD_RESTRICTION;
551 /* check the password history */
552 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
553 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
555 if (pwdHistoryLength > 0) {
556 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
558 *reject_reason = SAMR_REJECT_COMPLEXITY;
560 return NT_STATUS_PASSWORD_RESTRICTION;
562 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
564 *reject_reason = SAMR_REJECT_COMPLEXITY;
566 return NT_STATUS_PASSWORD_RESTRICTION;
568 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
570 *reject_reason = SAMR_REJECT_COMPLEXITY;
572 return NT_STATUS_PASSWORD_RESTRICTION;
576 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
577 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
579 *reject_reason = SAMR_REJECT_COMPLEXITY;
581 return NT_STATUS_PASSWORD_RESTRICTION;
584 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
585 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
587 *reject_reason = SAMR_REJECT_COMPLEXITY;
589 return NT_STATUS_PASSWORD_RESTRICTION;
594 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
596 /* the password is acceptable. Start forming the new fields */
598 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", *lmNewHash));
600 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
604 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", *ntNewHash));
606 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
609 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
610 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
611 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
612 "unicodePwd", new_pass));
614 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
617 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
619 if (pwdHistoryLength == 0) {
620 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
621 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
625 /* store the password history */
626 new_lmPwdHistory = talloc_array_p(mem_ctx, struct samr_Hash,
628 if (!new_lmPwdHistory) {
629 return NT_STATUS_NO_MEMORY;
631 new_ntPwdHistory = talloc_array_p(mem_ctx, struct samr_Hash,
633 if (!new_ntPwdHistory) {
634 return NT_STATUS_NO_MEMORY;
636 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
637 new_lmPwdHistory[i+1] = lmPwdHistory[i];
639 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
640 new_ntPwdHistory[i+1] = ntPwdHistory[i];
643 /* Don't store 'long' passwords in the LM history,
644 but make sure to 'expire' one password off the other end */
646 new_lmPwdHistory[0] = *lmNewHash;
648 ZERO_STRUCT(new_lmPwdHistory[0]);
650 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
653 new_ntPwdHistory[0] = *ntNewHash;
655 ZERO_STRUCT(new_ntPwdHistory[0]);
657 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
659 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
664 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
672 set password via a samr_CryptPassword buffer
673 this will in the 'msg' with modify operations that will update the user
674 password when applied
676 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
678 const char *account_dn, const char *domain_dn,
680 struct ldb_message *msg,
681 struct samr_CryptPassword *pwbuf)
684 uint32_t new_pass_len;
685 DATA_BLOB session_key = dce_call->conn->session_key;
687 SamOEMhashBlob(pwbuf->data, 516, &session_key);
689 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
690 &new_pass_len, STR_UNICODE)) {
691 DEBUG(3,("samr: failed to decode password buffer\n"));
692 return NT_STATUS_WRONG_PASSWORD;
695 /* set the password - samdb needs to know both the domain and user DNs,
696 so the domain password policy can be used */
697 return samdb_set_password(sam_ctx, mem_ctx,
698 account_dn, domain_dn,
701 False /* This is a password set, not change */,
707 set password via a samr_CryptPasswordEx buffer
708 this will in the 'msg' with modify operations that will update the user
709 password when applied
711 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
713 const char *account_dn, const char *domain_dn,
715 struct ldb_message *msg,
716 struct samr_CryptPasswordEx *pwbuf)
719 uint32_t new_pass_len;
720 DATA_BLOB co_session_key;
721 DATA_BLOB session_key = dce_call->conn->session_key;
722 struct MD5Context ctx;
724 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
725 if (!co_session_key.data) {
726 return NT_STATUS_NO_MEMORY;
730 MD5Update(&ctx, &pwbuf->data[516], 16);
731 MD5Update(&ctx, session_key.data, session_key.length);
732 MD5Final(co_session_key.data, &ctx);
734 SamOEMhashBlob(pwbuf->data, 516, &co_session_key);
736 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
737 &new_pass_len, STR_UNICODE)) {
738 DEBUG(3,("samr: failed to decode password buffer\n"));
739 return NT_STATUS_WRONG_PASSWORD;
742 /* set the password - samdb needs to know both the domain and user DNs,
743 so the domain password policy can be used */
744 return samdb_set_password(sam_ctx, mem_ctx,
745 account_dn, domain_dn,