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_context *sam_ctx;
42 struct ldb_message **res, *msg;
44 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
45 struct samr_Password *lm_pwd, *nt_pwd;
46 NTSTATUS status = NT_STATUS_OK;
47 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
49 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
53 /* To change a password we need to open as system */
54 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
55 if (sam_ctx == NULL) {
56 return NT_STATUS_INVALID_SYSTEM_SERVICE;
59 /* fetch the old hashes */
60 ret = gendb_search_dn(sam_ctx, mem_ctx,
61 a_state->account_dn, &res, attrs);
63 return NT_STATUS_INTERNAL_DB_CORRUPTION;
67 /* basic sanity checking on parameters */
68 if (!r->in.lm_present || !r->in.nt_present ||
69 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
70 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
71 /* we should really handle a change with lm not
73 return NT_STATUS_INVALID_PARAMETER_MIX;
75 if (!r->in.cross1_present || !r->in.nt_cross) {
76 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
78 if (!r->in.cross2_present || !r->in.lm_cross) {
79 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
82 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
83 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
84 return NT_STATUS_WRONG_PASSWORD;
87 /* decrypt and check the new lm hash */
88 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
89 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
90 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
91 return NT_STATUS_WRONG_PASSWORD;
94 /* decrypt and check the new nt hash */
95 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
96 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
97 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
98 return NT_STATUS_WRONG_PASSWORD;
101 /* check the nt cross hash */
102 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
103 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
104 return NT_STATUS_WRONG_PASSWORD;
107 /* check the lm cross hash */
108 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
109 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
110 return NT_STATUS_WRONG_PASSWORD;
113 msg = ldb_msg_new(mem_ctx);
115 return NT_STATUS_NO_MEMORY;
118 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
120 return NT_STATUS_NO_MEMORY;
123 status = samdb_set_password(sam_ctx, mem_ctx,
124 a_state->account_dn, a_state->domain_state->domain_dn,
125 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
126 True, /* this is a user password change */
127 True, /* run restriction tests */
130 if (!NT_STATUS_IS_OK(status)) {
134 /* modify the samdb record */
135 ret = samdb_replace(sam_ctx, mem_ctx, msg);
137 return NT_STATUS_UNSUCCESSFUL;
144 samr_OemChangePasswordUser2
146 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
147 struct samr_OemChangePasswordUser2 *r)
151 uint32_t new_pass_len;
152 struct samr_CryptPassword *pwbuf = r->in.password;
153 struct ldb_context *sam_ctx;
154 const struct ldb_dn *user_dn;
156 struct ldb_message **res, *mod;
157 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
158 struct samr_Password *lm_pwd;
159 DATA_BLOB lm_pwd_blob;
160 uint8_t new_lm_hash[16];
161 struct samr_Password lm_verifier;
164 return NT_STATUS_WRONG_PASSWORD;
167 /* To change a password we need to open as system */
168 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
169 if (sam_ctx == NULL) {
170 return NT_STATUS_INVALID_SYSTEM_SERVICE;
173 /* we need the users dn and the domain dn (derived from the
174 user SID). We also need the current lm password hash in
175 order to decrypt the incoming password */
176 ret = gendb_search(sam_ctx,
177 mem_ctx, NULL, &res, attrs,
178 "(&(sAMAccountName=%s)(objectclass=user))",
179 r->in.account->string);
181 return NT_STATUS_NO_SUCH_USER;
184 user_dn = res[0]->dn;
186 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
187 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
188 return NT_STATUS_WRONG_PASSWORD;
191 /* decrypt the password we have been given */
192 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
193 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
194 data_blob_free(&lm_pwd_blob);
196 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
197 &new_pass_len, STR_ASCII)) {
198 DEBUG(3,("samr: failed to decode password buffer\n"));
199 return NT_STATUS_WRONG_PASSWORD;
202 /* check LM verifier */
203 if (lm_pwd == NULL || r->in.hash == NULL) {
204 return NT_STATUS_WRONG_PASSWORD;
207 E_deshash(new_pass, new_lm_hash);
208 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
209 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
210 return NT_STATUS_WRONG_PASSWORD;
213 mod = ldb_msg_new(mem_ctx);
215 return NT_STATUS_NO_MEMORY;
218 mod->dn = ldb_dn_copy(mod, user_dn);
220 return NT_STATUS_NO_MEMORY;
223 /* set the password - samdb needs to know both the domain and user DNs,
224 so the domain password policy can be used */
225 status = samdb_set_password(sam_ctx, mem_ctx,
229 True, /* this is a user password change */
230 True, /* run restriction tests */
233 if (!NT_STATUS_IS_OK(status)) {
237 /* modify the samdb record */
238 ret = samdb_replace(sam_ctx, mem_ctx, mod);
240 return NT_STATUS_UNSUCCESSFUL;
248 samr_ChangePasswordUser3
250 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
252 struct samr_ChangePasswordUser3 *r)
256 uint32_t new_pass_len;
257 struct ldb_context *sam_ctx;
258 const struct ldb_dn *user_dn;
260 struct ldb_message **res, *mod;
261 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
262 struct samr_Password *nt_pwd, *lm_pwd;
263 DATA_BLOB nt_pwd_blob;
264 struct samr_DomInfo1 *dominfo;
265 struct samr_ChangeReject *reject;
267 uint8_t new_nt_hash[16], new_lm_hash[16];
268 struct samr_Password nt_verifier, lm_verifier;
272 if (r->in.nt_password == NULL ||
273 r->in.nt_verifier == NULL) {
274 status = NT_STATUS_INVALID_PARAMETER;
278 /* To change a password we need to open as system */
279 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
280 if (sam_ctx == NULL) {
281 return NT_STATUS_INVALID_SYSTEM_SERVICE;
285 /* we need the users dn and the domain dn (derived from the
286 user SID). We also need the current lm and nt password hashes
287 in order to decrypt the incoming passwords */
288 ret = gendb_search(sam_ctx,
289 mem_ctx, NULL, &res, attrs,
290 "(&(sAMAccountName=%s)(objectclass=user))",
291 r->in.account->string);
293 status = NT_STATUS_NO_SUCH_USER;
297 user_dn = res[0]->dn;
299 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
300 if (!NT_STATUS_IS_OK(status) ) {
305 status = NT_STATUS_WRONG_PASSWORD;
309 /* decrypt the password we have been given */
310 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
311 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
312 data_blob_free(&nt_pwd_blob);
314 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
315 &new_pass_len, STR_UNICODE)) {
316 DEBUG(3,("samr: failed to decode password buffer\n"));
317 status = NT_STATUS_WRONG_PASSWORD;
321 if (r->in.nt_verifier == NULL) {
322 status = NT_STATUS_WRONG_PASSWORD;
326 /* check NT verifier */
327 E_md4hash(new_pass, new_nt_hash);
328 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
329 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
330 status = NT_STATUS_WRONG_PASSWORD;
334 /* check LM verifier */
335 if (lm_pwd && r->in.lm_verifier != NULL) {
336 E_deshash(new_pass, new_lm_hash);
337 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
338 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
339 status = NT_STATUS_WRONG_PASSWORD;
345 mod = ldb_msg_new(mem_ctx);
347 return NT_STATUS_NO_MEMORY;
350 mod->dn = ldb_dn_copy(mod, user_dn);
352 status = NT_STATUS_NO_MEMORY;
356 /* set the password - samdb needs to know both the domain and user DNs,
357 so the domain password policy can be used */
358 status = samdb_set_password(sam_ctx, mem_ctx,
362 True, /* this is a user password change */
363 True, /* run restriction tests */
366 if (!NT_STATUS_IS_OK(status)) {
370 /* modify the samdb record */
371 ret = samdb_replace(sam_ctx, mem_ctx, mod);
373 status = NT_STATUS_UNSUCCESSFUL;
381 reject = talloc(mem_ctx, struct samr_ChangeReject);
382 if (reject == NULL) {
385 ZERO_STRUCTP(reject);
387 reject->reason = reason;
389 r->out.dominfo = dominfo;
390 r->out.reject = reject;
397 samr_ChangePasswordUser2
399 easy - just a subset of samr_ChangePasswordUser3
401 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
402 struct samr_ChangePasswordUser2 *r)
404 struct samr_ChangePasswordUser3 r2;
406 r2.in.server = r->in.server;
407 r2.in.account = r->in.account;
408 r2.in.nt_password = r->in.nt_password;
409 r2.in.nt_verifier = r->in.nt_verifier;
410 r2.in.lm_change = r->in.lm_change;
411 r2.in.lm_password = r->in.lm_password;
412 r2.in.lm_verifier = r->in.lm_verifier;
413 r2.in.password3 = NULL;
415 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
420 check that a password is sufficiently complex
422 static BOOL samdb_password_complexity_ok(const char *pass)
424 return check_password_quality(pass);
428 set the user password using plaintext, obeying any user or domain
429 password restrictions
431 note that this function doesn't actually store the result in the
432 database, it just fills in the "mod" structure with ldb modify
433 elements to setup the correct change when samdb_replace() is
434 called. This allows the caller to combine the change with other
435 changes (as is needed by some of the set user info levels)
437 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
438 const struct ldb_dn *user_dn,
439 const struct ldb_dn *domain_dn,
440 struct ldb_message *mod,
441 const char *new_pass,
442 struct samr_Password *lmNewHash,
443 struct samr_Password *ntNewHash,
446 uint32_t *reject_reason,
447 struct samr_DomInfo1 **_dominfo)
449 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
450 "ntPwdHistory", "unicodePwd",
451 "lmPwdHash", "ntPwdHash", "badPwdCount",
453 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
454 "maxPwdAge", "minPwdAge",
455 "minPwdLength", "pwdLastSet", NULL };
456 const char *unicodePwd;
459 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
460 uint_t userAccountControl, badPwdCount;
461 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
462 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
463 struct samr_Password local_lmNewHash, local_ntNewHash;
464 int lmPwdHistory_len, ntPwdHistory_len;
466 struct dom_sid *domain_sid;
467 struct ldb_message **res;
469 time_t now = time(NULL);
473 /* we need to know the time to compute password age */
474 unix_to_nt_time(&now_nt, now);
476 /* pull all the user parameters */
477 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
479 return NT_STATUS_INTERNAL_DB_CORRUPTION;
481 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
482 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
483 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
484 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
485 "lmPwdHistory", &lmPwdHistory);
486 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
487 "ntPwdHistory", &ntPwdHistory);
488 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
489 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
490 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
491 kvno = samdb_result_uint(res[0], "msDS-KeyVersionNumber", 0);
494 /* pull the domain parameters */
495 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
497 return NT_STATUS_NO_SUCH_DOMAIN;
500 /* work out the domain sid, and pull the domain from there */
501 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
502 if (domain_sid == NULL) {
503 return NT_STATUS_INTERNAL_DB_CORRUPTION;
506 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
508 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
510 return NT_STATUS_NO_SUCH_DOMAIN;
514 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
515 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
516 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
517 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
520 struct samr_DomInfo1 *dominfo;
521 /* on failure we need to fill in the reject reasons */
522 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
523 if (dominfo == NULL) {
524 return NT_STATUS_NO_MEMORY;
526 dominfo->min_password_length = minPwdLength;
527 dominfo->password_properties = pwdProperties;
528 dominfo->password_history_length = pwdHistoryLength;
529 dominfo->max_password_age = minPwdAge;
530 dominfo->min_password_age = minPwdAge;
535 /* check the various password restrictions */
536 if (restrictions && minPwdLength > strlen_m(new_pass)) {
538 *reject_reason = SAMR_REJECT_TOO_SHORT;
540 return NT_STATUS_PASSWORD_RESTRICTION;
543 /* possibly check password complexity */
544 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
545 !samdb_password_complexity_ok(new_pass)) {
547 *reject_reason = SAMR_REJECT_COMPLEXITY;
549 return NT_STATUS_PASSWORD_RESTRICTION;
552 /* compute the new nt and lm hashes */
553 if (E_deshash(new_pass, local_lmNewHash.hash)) {
554 lmNewHash = &local_lmNewHash;
556 E_md4hash(new_pass, local_ntNewHash.hash);
557 ntNewHash = &local_ntNewHash;
560 if (restrictions && user_change) {
561 /* are all password changes disallowed? */
562 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
564 *reject_reason = SAMR_REJECT_OTHER;
566 return NT_STATUS_PASSWORD_RESTRICTION;
569 /* can this user change password? */
570 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
572 *reject_reason = SAMR_REJECT_OTHER;
574 return NT_STATUS_PASSWORD_RESTRICTION;
577 /* yes, this is a minus. The ages are in negative 100nsec units! */
578 if (pwdLastSet - minPwdAge > now_nt) {
580 *reject_reason = SAMR_REJECT_OTHER;
582 return NT_STATUS_PASSWORD_RESTRICTION;
585 /* check the immediately past password */
586 if (pwdHistoryLength > 0) {
587 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
589 *reject_reason = SAMR_REJECT_COMPLEXITY;
591 return NT_STATUS_PASSWORD_RESTRICTION;
593 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
595 *reject_reason = SAMR_REJECT_COMPLEXITY;
597 return NT_STATUS_PASSWORD_RESTRICTION;
601 /* check the password history */
602 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
603 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
605 if (pwdHistoryLength > 0) {
606 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
608 *reject_reason = SAMR_REJECT_COMPLEXITY;
610 return NT_STATUS_PASSWORD_RESTRICTION;
612 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
614 *reject_reason = SAMR_REJECT_COMPLEXITY;
616 return NT_STATUS_PASSWORD_RESTRICTION;
618 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
620 *reject_reason = SAMR_REJECT_COMPLEXITY;
622 return NT_STATUS_PASSWORD_RESTRICTION;
626 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
627 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
629 *reject_reason = SAMR_REJECT_COMPLEXITY;
631 return NT_STATUS_PASSWORD_RESTRICTION;
634 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
635 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
637 *reject_reason = SAMR_REJECT_COMPLEXITY;
639 return NT_STATUS_PASSWORD_RESTRICTION;
644 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
646 /* the password is acceptable. Start forming the new fields */
648 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
650 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
654 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
656 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
659 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
660 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
661 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
662 "unicodePwd", new_pass));
664 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
667 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
669 CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
671 if (pwdHistoryLength == 0) {
672 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
673 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
677 /* store the password history */
678 new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
680 if (!new_lmPwdHistory) {
681 return NT_STATUS_NO_MEMORY;
683 new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
685 if (!new_ntPwdHistory) {
686 return NT_STATUS_NO_MEMORY;
688 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
689 new_lmPwdHistory[i+1] = lmPwdHistory[i];
691 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
692 new_ntPwdHistory[i+1] = ntPwdHistory[i];
695 /* Don't store 'long' passwords in the LM history,
696 but make sure to 'expire' one password off the other end */
698 new_lmPwdHistory[0] = *lmNewHash;
700 ZERO_STRUCT(new_lmPwdHistory[0]);
702 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
705 new_ntPwdHistory[0] = *ntNewHash;
707 ZERO_STRUCT(new_ntPwdHistory[0]);
709 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
711 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
716 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
724 set password via a samr_CryptPassword buffer
725 this will in the 'msg' with modify operations that will update the user
726 password when applied
728 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
730 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
732 struct ldb_message *msg,
733 struct samr_CryptPassword *pwbuf)
737 uint32_t new_pass_len;
738 DATA_BLOB session_key = data_blob(NULL, 0);
740 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
741 if (!NT_STATUS_IS_OK(nt_status)) {
745 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
747 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
748 &new_pass_len, STR_UNICODE)) {
749 DEBUG(3,("samr: failed to decode password buffer\n"));
750 return NT_STATUS_WRONG_PASSWORD;
753 /* set the password - samdb needs to know both the domain and user DNs,
754 so the domain password policy can be used */
755 return samdb_set_password(sam_ctx, mem_ctx,
756 account_dn, domain_dn,
759 False, /* This is a password set, not change */
760 True, /* run restriction tests */
766 set password via a samr_CryptPasswordEx buffer
767 this will in the 'msg' with modify operations that will update the user
768 password when applied
770 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
771 struct ldb_context *sam_ctx,
772 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
774 struct ldb_message *msg,
775 struct samr_CryptPasswordEx *pwbuf)
779 uint32_t new_pass_len;
780 DATA_BLOB co_session_key;
781 DATA_BLOB session_key = data_blob(NULL, 0);
782 struct MD5Context ctx;
784 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
785 if (!NT_STATUS_IS_OK(nt_status)) {
789 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
790 if (!co_session_key.data) {
791 return NT_STATUS_NO_MEMORY;
795 MD5Update(&ctx, &pwbuf->data[516], 16);
796 MD5Update(&ctx, session_key.data, session_key.length);
797 MD5Final(co_session_key.data, &ctx);
799 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
801 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
802 &new_pass_len, STR_UNICODE)) {
803 DEBUG(3,("samr: failed to decode password buffer\n"));
804 return NT_STATUS_WRONG_PASSWORD;
807 /* set the password - samdb needs to know both the domain and user DNs,
808 so the domain password policy can be used */
809 return samdb_set_password(sam_ctx, mem_ctx,
810 account_dn, domain_dn,
813 False, /* This is a password set, not change */
814 True, /* run restriction tests */
819 set the user password using plaintext, obeying any user or domain
820 password restrictions
822 This wrapper function takes a SID as input, rather than a user DN,
823 and actually performs the password change
826 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
827 const struct dom_sid *user_sid,
828 const char *new_pass,
829 struct samr_Password *lmNewHash,
830 struct samr_Password *ntNewHash,
833 uint32_t *reject_reason,
834 struct samr_DomInfo1 **_dominfo)
837 struct ldb_dn *user_dn;
838 struct ldb_message *msg;
842 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
843 "((objectSid=%s)(objectClass=user))",
844 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
846 return NT_STATUS_NO_SUCH_USER;
849 msg = ldb_msg_new(mem_ctx);
851 return NT_STATUS_NO_MEMORY;
854 msg->dn = ldb_dn_copy(msg, user_dn);
856 return NT_STATUS_NO_MEMORY;
859 nt_status = samdb_set_password(ctx, mem_ctx,
862 lmNewHash, ntNewHash,
863 user_change, /* This is a password set, not change */
864 restrictions, /* run restriction tests */
865 reject_reason, _dominfo);
866 if (!NT_STATUS_IS_OK(nt_status)) {
870 /* modify the samdb record */
871 ret = samdb_replace(ctx, mem_ctx, msg);
873 return NT_STATUS_ACCESS_DENIED;