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 = 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_dn(sam_ctx, mem_ctx,
404 domain_dn, &res, dom_attrs);
410 /* on failure we need to fill in the reject reasons */
411 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
412 if (dominfo == NULL) {
415 reject = talloc(mem_ctx, struct samr_ChangeReject);
416 if (reject == NULL) {
420 ZERO_STRUCTP(dominfo);
421 ZERO_STRUCTP(reject);
423 reject->reason = reason;
425 r->out.dominfo = dominfo;
426 r->out.reject = reject;
432 dominfo->min_password_length = samdb_result_uint (res[0], "minPwdLength", 0);
433 dominfo->password_properties = samdb_result_uint (res[0], "pwdProperties", 0);
434 dominfo->password_history_length = samdb_result_uint (res[0], "pwdHistoryLength", 0);
435 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
436 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
443 samr_ChangePasswordUser2
445 easy - just a subset of samr_ChangePasswordUser3
447 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
448 struct samr_ChangePasswordUser2 *r)
450 struct samr_ChangePasswordUser3 r2;
452 r2.in.server = r->in.server;
453 r2.in.account = r->in.account;
454 r2.in.nt_password = r->in.nt_password;
455 r2.in.nt_verifier = r->in.nt_verifier;
456 r2.in.lm_change = r->in.lm_change;
457 r2.in.lm_password = r->in.lm_password;
458 r2.in.lm_verifier = r->in.lm_verifier;
459 r2.in.password3 = NULL;
461 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
466 check that a password is sufficiently complex
468 static BOOL samdb_password_complexity_ok(const char *pass)
470 return check_password_quality(pass);
474 set the user password using plaintext, obeying any user or domain
475 password restrictions
477 note that this function doesn't actually store the result in the
478 database, it just fills in the "mod" structure with ldb modify
479 elements to setup the correct change when samdb_replace() is
480 called. This allows the caller to combine the change with other
481 changes (as is needed by some of the set user info levels)
483 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
484 const char *user_dn, const char *domain_dn,
485 struct ldb_message *mod,
486 const char *new_pass,
487 struct samr_Password *lmNewHash,
488 struct samr_Password *ntNewHash,
491 uint32_t *reject_reason)
493 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
494 "ntPwdHistory", "unicodePwd",
495 "lmPwdHash", "ntPwdHash", "badPwdCount",
497 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
498 "maxPwdAge", "minPwdAge",
499 "minPwdLength", "pwdLastSet", NULL };
500 const char *unicodePwd;
503 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
504 uint_t userAccountControl, badPwdCount;
505 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
506 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
507 struct samr_Password local_lmNewHash, local_ntNewHash;
508 int lmPwdHistory_len, ntPwdHistory_len;
509 struct ldb_message **res;
511 time_t now = time(NULL);
515 /* we need to know the time to compute password age */
516 unix_to_nt_time(&now_nt, now);
518 /* pull all the user parameters */
519 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
521 return NT_STATUS_INTERNAL_DB_CORRUPTION;
523 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
524 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
525 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
526 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
527 "lmPwdHistory", &lmPwdHistory);
528 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
529 "ntPwdHistory", &ntPwdHistory);
530 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
531 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
532 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
534 /* pull the domain parameters */
535 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
537 return NT_STATUS_INTERNAL_DB_CORRUPTION;
539 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
540 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
541 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
542 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
545 /* check the various password restrictions */
546 if (restrictions && minPwdLength > strlen_m(new_pass)) {
548 *reject_reason = SAMR_REJECT_TOO_SHORT;
550 return NT_STATUS_PASSWORD_RESTRICTION;
553 /* possibly check password complexity */
554 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
555 !samdb_password_complexity_ok(new_pass)) {
557 *reject_reason = SAMR_REJECT_COMPLEXITY;
559 return NT_STATUS_PASSWORD_RESTRICTION;
562 /* compute the new nt and lm hashes */
563 if (E_deshash(new_pass, local_lmNewHash.hash)) {
564 lmNewHash = &local_lmNewHash;
566 E_md4hash(new_pass, local_ntNewHash.hash);
567 ntNewHash = &local_ntNewHash;
570 if (restrictions && user_change) {
571 /* are all password changes disallowed? */
572 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
574 *reject_reason = SAMR_REJECT_OTHER;
576 return NT_STATUS_PASSWORD_RESTRICTION;
579 /* can this user change password? */
580 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
582 *reject_reason = SAMR_REJECT_OTHER;
584 return NT_STATUS_PASSWORD_RESTRICTION;
587 /* yes, this is a minus. The ages are in negative 100nsec units! */
588 if (pwdLastSet - minPwdAge > now_nt) {
590 *reject_reason = SAMR_REJECT_OTHER;
592 return NT_STATUS_PASSWORD_RESTRICTION;
595 /* check the immediately past password */
596 if (pwdHistoryLength > 0) {
597 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
599 *reject_reason = SAMR_REJECT_COMPLEXITY;
601 return NT_STATUS_PASSWORD_RESTRICTION;
603 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
605 *reject_reason = SAMR_REJECT_COMPLEXITY;
607 return NT_STATUS_PASSWORD_RESTRICTION;
611 /* check the password history */
612 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
613 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
615 if (pwdHistoryLength > 0) {
616 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
618 *reject_reason = SAMR_REJECT_COMPLEXITY;
620 return NT_STATUS_PASSWORD_RESTRICTION;
622 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
624 *reject_reason = SAMR_REJECT_COMPLEXITY;
626 return NT_STATUS_PASSWORD_RESTRICTION;
628 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
630 *reject_reason = SAMR_REJECT_COMPLEXITY;
632 return NT_STATUS_PASSWORD_RESTRICTION;
636 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
637 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
639 *reject_reason = SAMR_REJECT_COMPLEXITY;
641 return NT_STATUS_PASSWORD_RESTRICTION;
644 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
645 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
647 *reject_reason = SAMR_REJECT_COMPLEXITY;
649 return NT_STATUS_PASSWORD_RESTRICTION;
654 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
656 /* the password is acceptable. Start forming the new fields */
658 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
660 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
664 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
666 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
669 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
670 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
671 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
672 "unicodePwd", new_pass));
674 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
677 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
679 if (pwdHistoryLength == 0) {
680 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
681 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
685 /* store the password history */
686 new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
688 if (!new_lmPwdHistory) {
689 return NT_STATUS_NO_MEMORY;
691 new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
693 if (!new_ntPwdHistory) {
694 return NT_STATUS_NO_MEMORY;
696 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
697 new_lmPwdHistory[i+1] = lmPwdHistory[i];
699 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
700 new_ntPwdHistory[i+1] = ntPwdHistory[i];
703 /* Don't store 'long' passwords in the LM history,
704 but make sure to 'expire' one password off the other end */
706 new_lmPwdHistory[0] = *lmNewHash;
708 ZERO_STRUCT(new_lmPwdHistory[0]);
710 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
713 new_ntPwdHistory[0] = *ntNewHash;
715 ZERO_STRUCT(new_ntPwdHistory[0]);
717 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
719 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
724 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
732 set password via a samr_CryptPassword buffer
733 this will in the 'msg' with modify operations that will update the user
734 password when applied
736 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
738 const char *account_dn, const char *domain_dn,
740 struct ldb_message *msg,
741 struct samr_CryptPassword *pwbuf)
745 uint32_t new_pass_len;
746 DATA_BLOB session_key = data_blob(NULL, 0);
748 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
749 if (!NT_STATUS_IS_OK(nt_status)) {
753 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
755 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
756 &new_pass_len, STR_UNICODE)) {
757 DEBUG(3,("samr: failed to decode password buffer\n"));
758 return NT_STATUS_WRONG_PASSWORD;
761 /* set the password - samdb needs to know both the domain and user DNs,
762 so the domain password policy can be used */
763 return samdb_set_password(sam_ctx, mem_ctx,
764 account_dn, domain_dn,
767 False, /* This is a password set, not change */
768 True, /* run restriction tests */
774 set password via a samr_CryptPasswordEx buffer
775 this will in the 'msg' with modify operations that will update the user
776 password when applied
778 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
780 const char *account_dn, const char *domain_dn,
782 struct ldb_message *msg,
783 struct samr_CryptPasswordEx *pwbuf)
787 uint32_t new_pass_len;
788 DATA_BLOB co_session_key;
789 DATA_BLOB session_key = data_blob(NULL, 0);
790 struct MD5Context ctx;
792 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
793 if (!NT_STATUS_IS_OK(nt_status)) {
797 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
798 if (!co_session_key.data) {
799 return NT_STATUS_NO_MEMORY;
803 MD5Update(&ctx, &pwbuf->data[516], 16);
804 MD5Update(&ctx, session_key.data, session_key.length);
805 MD5Final(co_session_key.data, &ctx);
807 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
809 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
810 &new_pass_len, STR_UNICODE)) {
811 DEBUG(3,("samr: failed to decode password buffer\n"));
812 return NT_STATUS_WRONG_PASSWORD;
815 /* set the password - samdb needs to know both the domain and user DNs,
816 so the domain password policy can be used */
817 return samdb_set_password(sam_ctx, mem_ctx,
818 account_dn, domain_dn,
821 False, /* This is a password set, not change */
822 True, /* run restriction tests */