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_dn(a_state->sam_ctx, mem_ctx,
54 a_state->account_dn, &res, attrs);
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 = ldb_dn_copy(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 struct ldb_dn *user_dn, *domain_dn;
148 struct ldb_message **res, *mod;
149 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
150 struct samr_Password *lm_pwd;
151 DATA_BLOB lm_pwd_blob;
152 uint8_t new_lm_hash[16];
153 struct samr_Password lm_verifier;
154 struct dom_sid *domain_sid;
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_dn(sam_ctx, mem_ctx, NULL,
215 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
217 return NT_STATUS_INTERNAL_DB_CORRUPTION;
220 mod = ldb_msg_new(mem_ctx);
222 return NT_STATUS_NO_MEMORY;
225 mod->dn = ldb_dn_copy(mod, user_dn);
227 return NT_STATUS_NO_MEMORY;
230 /* set the password - samdb needs to know both the domain and user DNs,
231 so the domain password policy can be used */
232 status = samdb_set_password(sam_ctx, mem_ctx,
236 True, /* this is a user password change */
237 True, /* run restriction tests */
239 if (!NT_STATUS_IS_OK(status)) {
243 /* modify the samdb record */
244 ret = samdb_replace(sam_ctx, mem_ctx, mod);
246 return NT_STATUS_UNSUCCESSFUL;
254 samr_ChangePasswordUser3
256 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
258 struct samr_ChangePasswordUser3 *r)
262 uint32_t new_pass_len;
263 void *sam_ctx = NULL;
264 const struct ldb_dn *user_dn, *domain_dn = NULL;
266 struct ldb_message **res, *mod;
267 const char * const attrs[] = { "objectSid", "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
268 const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength",
269 "pwdProperties", "minPwdAge", "maxPwdAge",
271 struct dom_sid *domain_sid;
272 struct samr_Password *nt_pwd, *lm_pwd;
273 DATA_BLOB nt_pwd_blob;
274 struct samr_DomInfo1 *dominfo;
275 struct samr_ChangeReject *reject;
277 uint8_t new_nt_hash[16], new_lm_hash[16];
278 struct samr_Password nt_verifier, lm_verifier;
282 if (r->in.nt_password == NULL ||
283 r->in.nt_verifier == NULL) {
284 status = NT_STATUS_INVALID_PARAMETER;
288 /* this call doesn't take a policy handle, so we need to open
289 the sam db from scratch */
290 sam_ctx = samdb_connect(mem_ctx);
291 if (sam_ctx == NULL) {
292 status = NT_STATUS_INVALID_SYSTEM_SERVICE;
296 /* we need the users dn and the domain dn (derived from the
297 user SID). We also need the current lm and nt password hashes
298 in order to decrypt the incoming passwords */
299 ret = gendb_search(sam_ctx,
300 mem_ctx, NULL, &res, attrs,
301 "(&(sAMAccountName=%s)(objectclass=user))",
302 r->in.account->string);
304 status = NT_STATUS_NO_SUCH_USER;
308 user_dn = res[0]->dn;
310 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
311 if (!NT_STATUS_IS_OK(status) ) {
316 status = NT_STATUS_WRONG_PASSWORD;
320 /* decrypt the password we have been given */
321 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
322 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
323 data_blob_free(&nt_pwd_blob);
325 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
326 &new_pass_len, STR_UNICODE)) {
327 DEBUG(3,("samr: failed to decode password buffer\n"));
328 status = NT_STATUS_WRONG_PASSWORD;
332 if (r->in.nt_verifier == NULL) {
333 status = NT_STATUS_WRONG_PASSWORD;
337 /* check NT verifier */
338 E_md4hash(new_pass, new_nt_hash);
339 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
340 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
341 status = NT_STATUS_WRONG_PASSWORD;
345 /* check LM verifier */
346 if (lm_pwd && r->in.lm_verifier != NULL) {
347 E_deshash(new_pass, new_lm_hash);
348 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
349 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
350 status = NT_STATUS_WRONG_PASSWORD;
356 /* work out the domain dn */
357 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
358 if (domain_sid == NULL) {
359 status = NT_STATUS_NO_SUCH_DOMAIN;
363 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
365 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
367 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
371 mod = ldb_msg_new(mem_ctx);
373 return NT_STATUS_NO_MEMORY;
376 mod->dn = ldb_dn_copy(mod, user_dn);
378 status = NT_STATUS_NO_MEMORY;
382 /* set the password - samdb needs to know both the domain and user DNs,
383 so the domain password policy can be used */
384 status = samdb_set_password(sam_ctx, mem_ctx,
388 True, /* this is a user password change */
389 True, /* run restriction tests */
391 if (!NT_STATUS_IS_OK(status)) {
395 /* modify the samdb record */
396 ret = samdb_replace(sam_ctx, mem_ctx, mod);
398 status = NT_STATUS_UNSUCCESSFUL;
406 ret = gendb_search_dn(sam_ctx, mem_ctx,
407 domain_dn, &res, dom_attrs);
414 /* on failure we need to fill in the reject reasons */
415 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
416 if (dominfo == NULL) {
419 reject = talloc(mem_ctx, struct samr_ChangeReject);
420 if (reject == NULL) {
424 ZERO_STRUCTP(dominfo);
425 ZERO_STRUCTP(reject);
427 reject->reason = reason;
429 r->out.dominfo = dominfo;
430 r->out.reject = reject;
436 dominfo->min_password_length = samdb_result_uint (res[0], "minPwdLength", 0);
437 dominfo->password_properties = samdb_result_uint (res[0], "pwdProperties", 0);
438 dominfo->password_history_length = samdb_result_uint (res[0], "pwdHistoryLength", 0);
439 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
440 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
447 samr_ChangePasswordUser2
449 easy - just a subset of samr_ChangePasswordUser3
451 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
452 struct samr_ChangePasswordUser2 *r)
454 struct samr_ChangePasswordUser3 r2;
456 r2.in.server = r->in.server;
457 r2.in.account = r->in.account;
458 r2.in.nt_password = r->in.nt_password;
459 r2.in.nt_verifier = r->in.nt_verifier;
460 r2.in.lm_change = r->in.lm_change;
461 r2.in.lm_password = r->in.lm_password;
462 r2.in.lm_verifier = r->in.lm_verifier;
463 r2.in.password3 = NULL;
465 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
470 check that a password is sufficiently complex
472 static BOOL samdb_password_complexity_ok(const char *pass)
474 return check_password_quality(pass);
478 set the user password using plaintext, obeying any user or domain
479 password restrictions
481 note that this function doesn't actually store the result in the
482 database, it just fills in the "mod" structure with ldb modify
483 elements to setup the correct change when samdb_replace() is
484 called. This allows the caller to combine the change with other
485 changes (as is needed by some of the set user info levels)
487 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
488 const struct ldb_dn *user_dn,
489 const struct ldb_dn *domain_dn,
490 struct ldb_message *mod,
491 const char *new_pass,
492 struct samr_Password *lmNewHash,
493 struct samr_Password *ntNewHash,
496 uint32_t *reject_reason)
498 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
499 "ntPwdHistory", "unicodePwd",
500 "lmPwdHash", "ntPwdHash", "badPwdCount",
502 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
503 "maxPwdAge", "minPwdAge",
504 "minPwdLength", "pwdLastSet", NULL };
505 const char *unicodePwd;
508 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
509 uint_t userAccountControl, badPwdCount;
510 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
511 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
512 struct samr_Password local_lmNewHash, local_ntNewHash;
513 int lmPwdHistory_len, ntPwdHistory_len;
515 struct ldb_message **res;
517 time_t now = time(NULL);
521 /* we need to know the time to compute password age */
522 unix_to_nt_time(&now_nt, now);
524 /* pull all the user parameters */
525 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
527 return NT_STATUS_INTERNAL_DB_CORRUPTION;
529 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
530 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
531 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
532 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
533 "lmPwdHistory", &lmPwdHistory);
534 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
535 "ntPwdHistory", &ntPwdHistory);
536 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
537 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
538 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
539 kvno = samdb_result_uint(res[0], "msDS-KeyVersionNumber", 0);
541 /* pull the domain parameters */
542 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
544 return NT_STATUS_INTERNAL_DB_CORRUPTION;
546 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
547 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
548 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
549 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
552 /* check the various password restrictions */
553 if (restrictions && minPwdLength > strlen_m(new_pass)) {
555 *reject_reason = SAMR_REJECT_TOO_SHORT;
557 return NT_STATUS_PASSWORD_RESTRICTION;
560 /* possibly check password complexity */
561 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
562 !samdb_password_complexity_ok(new_pass)) {
564 *reject_reason = SAMR_REJECT_COMPLEXITY;
566 return NT_STATUS_PASSWORD_RESTRICTION;
569 /* compute the new nt and lm hashes */
570 if (E_deshash(new_pass, local_lmNewHash.hash)) {
571 lmNewHash = &local_lmNewHash;
573 E_md4hash(new_pass, local_ntNewHash.hash);
574 ntNewHash = &local_ntNewHash;
577 if (restrictions && user_change) {
578 /* are all password changes disallowed? */
579 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
581 *reject_reason = SAMR_REJECT_OTHER;
583 return NT_STATUS_PASSWORD_RESTRICTION;
586 /* can this user change password? */
587 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
589 *reject_reason = SAMR_REJECT_OTHER;
591 return NT_STATUS_PASSWORD_RESTRICTION;
594 /* yes, this is a minus. The ages are in negative 100nsec units! */
595 if (pwdLastSet - minPwdAge > now_nt) {
597 *reject_reason = SAMR_REJECT_OTHER;
599 return NT_STATUS_PASSWORD_RESTRICTION;
602 /* check the immediately past password */
603 if (pwdHistoryLength > 0) {
604 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
606 *reject_reason = SAMR_REJECT_COMPLEXITY;
608 return NT_STATUS_PASSWORD_RESTRICTION;
610 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
612 *reject_reason = SAMR_REJECT_COMPLEXITY;
614 return NT_STATUS_PASSWORD_RESTRICTION;
618 /* check the password history */
619 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
620 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
622 if (pwdHistoryLength > 0) {
623 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
625 *reject_reason = SAMR_REJECT_COMPLEXITY;
627 return NT_STATUS_PASSWORD_RESTRICTION;
629 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
631 *reject_reason = SAMR_REJECT_COMPLEXITY;
633 return NT_STATUS_PASSWORD_RESTRICTION;
635 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
637 *reject_reason = SAMR_REJECT_COMPLEXITY;
639 return NT_STATUS_PASSWORD_RESTRICTION;
643 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
644 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
646 *reject_reason = SAMR_REJECT_COMPLEXITY;
648 return NT_STATUS_PASSWORD_RESTRICTION;
651 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
652 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
654 *reject_reason = SAMR_REJECT_COMPLEXITY;
656 return NT_STATUS_PASSWORD_RESTRICTION;
661 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
663 /* the password is acceptable. Start forming the new fields */
665 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
667 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
671 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
673 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
676 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
677 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
678 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
679 "unicodePwd", new_pass));
681 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
684 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
686 CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
688 if (pwdHistoryLength == 0) {
689 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
690 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
694 /* store the password history */
695 new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
697 if (!new_lmPwdHistory) {
698 return NT_STATUS_NO_MEMORY;
700 new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
702 if (!new_ntPwdHistory) {
703 return NT_STATUS_NO_MEMORY;
705 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
706 new_lmPwdHistory[i+1] = lmPwdHistory[i];
708 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
709 new_ntPwdHistory[i+1] = ntPwdHistory[i];
712 /* Don't store 'long' passwords in the LM history,
713 but make sure to 'expire' one password off the other end */
715 new_lmPwdHistory[0] = *lmNewHash;
717 ZERO_STRUCT(new_lmPwdHistory[0]);
719 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
722 new_ntPwdHistory[0] = *ntNewHash;
724 ZERO_STRUCT(new_ntPwdHistory[0]);
726 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
728 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
733 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
741 set password via a samr_CryptPassword buffer
742 this will in the 'msg' with modify operations that will update the user
743 password when applied
745 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
747 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
749 struct ldb_message *msg,
750 struct samr_CryptPassword *pwbuf)
754 uint32_t new_pass_len;
755 DATA_BLOB session_key = data_blob(NULL, 0);
757 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
758 if (!NT_STATUS_IS_OK(nt_status)) {
762 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
764 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
765 &new_pass_len, STR_UNICODE)) {
766 DEBUG(3,("samr: failed to decode password buffer\n"));
767 return NT_STATUS_WRONG_PASSWORD;
770 /* set the password - samdb needs to know both the domain and user DNs,
771 so the domain password policy can be used */
772 return samdb_set_password(sam_ctx, mem_ctx,
773 account_dn, domain_dn,
776 False, /* This is a password set, not change */
777 True, /* run restriction tests */
783 set password via a samr_CryptPasswordEx buffer
784 this will in the 'msg' with modify operations that will update the user
785 password when applied
787 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
789 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
791 struct ldb_message *msg,
792 struct samr_CryptPasswordEx *pwbuf)
796 uint32_t new_pass_len;
797 DATA_BLOB co_session_key;
798 DATA_BLOB session_key = data_blob(NULL, 0);
799 struct MD5Context ctx;
801 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
802 if (!NT_STATUS_IS_OK(nt_status)) {
806 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
807 if (!co_session_key.data) {
808 return NT_STATUS_NO_MEMORY;
812 MD5Update(&ctx, &pwbuf->data[516], 16);
813 MD5Update(&ctx, session_key.data, session_key.length);
814 MD5Final(co_session_key.data, &ctx);
816 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
818 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
819 &new_pass_len, STR_UNICODE)) {
820 DEBUG(3,("samr: failed to decode password buffer\n"));
821 return NT_STATUS_WRONG_PASSWORD;
824 /* set the password - samdb needs to know both the domain and user DNs,
825 so the domain password policy can be used */
826 return samdb_set_password(sam_ctx, mem_ctx,
827 account_dn, domain_dn,
830 False, /* This is a password set, not change */
831 True, /* run restriction tests */