2 Unix SMB/CIFS implementation.
4 samr server password set/change handling
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "librpc/gen_ndr/ndr_samr.h"
26 #include "rpc_server/dcerpc_server.h"
27 #include "rpc_server/common/common.h"
28 #include "rpc_server/samr/dcesrv_samr.h"
29 #include "system/time.h"
30 #include "lib/crypto/crypto.h"
31 #include "lib/ldb/include/ldb.h"
33 #include "libcli/ldap/ldap.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "auth/auth.h"
38 samr_ChangePasswordUser
40 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
41 struct samr_ChangePasswordUser *r)
43 struct dcesrv_handle *h;
44 struct samr_account_state *a_state;
45 struct ldb_context *sam_ctx;
46 struct ldb_message **res, *msg;
48 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
49 struct samr_Password *lm_pwd, *nt_pwd;
50 NTSTATUS status = NT_STATUS_OK;
51 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
53 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
57 /* basic sanity checking on parameters. Do this before any database ops */
58 if (!r->in.lm_present || !r->in.nt_present ||
59 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
60 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
61 /* we should really handle a change with lm not
63 return NT_STATUS_INVALID_PARAMETER_MIX;
65 if (!r->in.cross1_present || !r->in.nt_cross) {
66 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
68 if (!r->in.cross2_present || !r->in.lm_cross) {
69 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
72 /* To change a password we need to open as system */
73 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
74 if (sam_ctx == NULL) {
75 return NT_STATUS_INVALID_SYSTEM_SERVICE;
78 ret = ldb_transaction_start(sam_ctx);
80 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
81 return NT_STATUS_INTERNAL_DB_CORRUPTION;
84 /* fetch the old hashes */
85 ret = gendb_search_dn(sam_ctx, mem_ctx,
86 a_state->account_dn, &res, attrs);
88 ldb_transaction_cancel(sam_ctx);
89 return NT_STATUS_WRONG_PASSWORD;
93 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
94 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
95 ldb_transaction_cancel(sam_ctx);
96 return NT_STATUS_WRONG_PASSWORD;
99 /* decrypt and check the new lm hash */
100 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
101 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
102 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
103 ldb_transaction_cancel(sam_ctx);
104 return NT_STATUS_WRONG_PASSWORD;
107 /* decrypt and check the new nt hash */
108 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
109 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
110 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
111 ldb_transaction_cancel(sam_ctx);
112 return NT_STATUS_WRONG_PASSWORD;
115 /* check the nt cross hash */
116 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
117 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
118 ldb_transaction_cancel(sam_ctx);
119 return NT_STATUS_WRONG_PASSWORD;
122 /* check the lm cross hash */
123 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
124 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
125 ldb_transaction_cancel(sam_ctx);
126 return NT_STATUS_WRONG_PASSWORD;
129 msg = ldb_msg_new(mem_ctx);
131 ldb_transaction_cancel(sam_ctx);
132 return NT_STATUS_NO_MEMORY;
135 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
137 ldb_transaction_cancel(sam_ctx);
138 return NT_STATUS_NO_MEMORY;
141 /* set the password on the user DN specified. This may fail
142 * due to password policies */
143 status = samdb_set_password(sam_ctx, mem_ctx,
144 a_state->account_dn, a_state->domain_state->domain_dn,
145 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
146 True, /* this is a user password change */
147 True, /* run restriction tests */
150 if (!NT_STATUS_IS_OK(status)) {
151 ldb_transaction_cancel(sam_ctx);
155 /* The above call only setup the modifications, this actually
156 * makes the write to the database. */
157 ret = samdb_replace(sam_ctx, mem_ctx, msg);
159 ldb_transaction_cancel(sam_ctx);
160 return NT_STATUS_UNSUCCESSFUL;
163 /* And this confirms it in a transaction commit */
164 ret = ldb_transaction_commit(sam_ctx);
166 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
167 ldb_dn_linearize(mem_ctx, a_state->account_dn),
168 ldb_errstring(sam_ctx)));
169 return NT_STATUS_INTERNAL_DB_CORRUPTION;
176 samr_OemChangePasswordUser2
178 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
179 struct samr_OemChangePasswordUser2 *r)
183 uint32_t new_pass_len;
184 struct samr_CryptPassword *pwbuf = r->in.password;
185 struct ldb_context *sam_ctx;
186 const struct ldb_dn *user_dn;
188 struct ldb_message **res, *mod;
189 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
190 struct samr_Password *lm_pwd;
191 DATA_BLOB lm_pwd_blob;
192 uint8_t new_lm_hash[16];
193 struct samr_Password lm_verifier;
196 return NT_STATUS_WRONG_PASSWORD;
199 /* To change a password we need to open as system */
200 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
201 if (sam_ctx == NULL) {
202 return NT_STATUS_INVALID_SYSTEM_SERVICE;
205 ret = ldb_transaction_start(sam_ctx);
207 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
208 return NT_STATUS_INTERNAL_DB_CORRUPTION;
211 /* we need the users dn and the domain dn (derived from the
212 user SID). We also need the current lm password hash in
213 order to decrypt the incoming password */
214 ret = gendb_search(sam_ctx,
215 mem_ctx, NULL, &res, attrs,
216 "(&(sAMAccountName=%s)(objectclass=user))",
217 r->in.account->string);
219 ldb_transaction_cancel(sam_ctx);
220 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
221 return NT_STATUS_WRONG_PASSWORD;
224 user_dn = res[0]->dn;
226 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
227 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
228 ldb_transaction_cancel(sam_ctx);
229 return NT_STATUS_WRONG_PASSWORD;
232 /* decrypt the password we have been given */
233 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
234 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
235 data_blob_free(&lm_pwd_blob);
237 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
238 &new_pass_len, STR_ASCII)) {
239 ldb_transaction_cancel(sam_ctx);
240 DEBUG(3,("samr: failed to decode password buffer\n"));
241 return NT_STATUS_WRONG_PASSWORD;
244 /* check LM verifier */
245 if (lm_pwd == NULL || r->in.hash == NULL) {
246 ldb_transaction_cancel(sam_ctx);
247 return NT_STATUS_WRONG_PASSWORD;
250 E_deshash(new_pass, new_lm_hash);
251 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
252 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
253 ldb_transaction_cancel(sam_ctx);
254 return NT_STATUS_WRONG_PASSWORD;
257 mod = ldb_msg_new(mem_ctx);
259 ldb_transaction_cancel(sam_ctx);
260 return NT_STATUS_NO_MEMORY;
263 mod->dn = ldb_dn_copy(mod, user_dn);
265 ldb_transaction_cancel(sam_ctx);
266 return NT_STATUS_NO_MEMORY;
269 /* set the password on the user DN specified. This may fail
270 * due to password policies */
271 status = samdb_set_password(sam_ctx, mem_ctx,
275 True, /* this is a user password change */
276 True, /* run restriction tests */
279 if (!NT_STATUS_IS_OK(status)) {
280 ldb_transaction_cancel(sam_ctx);
284 /* The above call only setup the modifications, this actually
285 * makes the write to the database. */
286 ret = samdb_replace(sam_ctx, mem_ctx, mod);
288 ldb_transaction_cancel(sam_ctx);
289 return NT_STATUS_UNSUCCESSFUL;
292 /* And this confirms it in a transaction commit */
293 ret = ldb_transaction_commit(sam_ctx);
295 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
296 ldb_dn_linearize(mem_ctx, user_dn),
297 ldb_errstring(sam_ctx)));
298 return NT_STATUS_INTERNAL_DB_CORRUPTION;
306 samr_ChangePasswordUser3
308 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
310 struct samr_ChangePasswordUser3 *r)
314 uint32_t new_pass_len;
315 struct ldb_context *sam_ctx = NULL;
316 const struct ldb_dn *user_dn;
318 struct ldb_message **res, *mod;
319 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
320 struct samr_Password *nt_pwd, *lm_pwd;
321 DATA_BLOB nt_pwd_blob;
322 struct samr_DomInfo1 *dominfo = NULL;
323 struct samr_ChangeReject *reject = NULL;
324 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
325 uint8_t new_nt_hash[16], new_lm_hash[16];
326 struct samr_Password nt_verifier, lm_verifier;
330 if (r->in.nt_password == NULL ||
331 r->in.nt_verifier == NULL) {
332 return NT_STATUS_INVALID_PARAMETER;
335 /* To change a password we need to open as system */
336 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
337 if (sam_ctx == NULL) {
338 return NT_STATUS_INVALID_SYSTEM_SERVICE;
341 ret = ldb_transaction_start(sam_ctx);
343 talloc_free(sam_ctx);
344 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
345 return NT_STATUS_INTERNAL_DB_CORRUPTION;
348 /* we need the users dn and the domain dn (derived from the
349 user SID). We also need the current lm and nt password hashes
350 in order to decrypt the incoming passwords */
351 ret = gendb_search(sam_ctx,
352 mem_ctx, NULL, &res, attrs,
353 "(&(sAMAccountName=%s)(objectclass=user))",
354 r->in.account->string);
356 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
357 status = NT_STATUS_WRONG_PASSWORD;
361 user_dn = res[0]->dn;
363 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
364 if (!NT_STATUS_IS_OK(status) ) {
369 status = NT_STATUS_WRONG_PASSWORD;
373 /* decrypt the password we have been given */
374 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
375 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
376 data_blob_free(&nt_pwd_blob);
378 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
379 &new_pass_len, STR_UNICODE)) {
380 DEBUG(3,("samr: failed to decode password buffer\n"));
381 status = NT_STATUS_WRONG_PASSWORD;
385 if (r->in.nt_verifier == NULL) {
386 status = NT_STATUS_WRONG_PASSWORD;
390 /* check NT verifier */
391 E_md4hash(new_pass, new_nt_hash);
392 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
393 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
394 status = NT_STATUS_WRONG_PASSWORD;
398 /* check LM verifier */
399 if (lm_pwd && r->in.lm_verifier != NULL) {
400 E_deshash(new_pass, new_lm_hash);
401 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
402 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
403 status = NT_STATUS_WRONG_PASSWORD;
409 mod = ldb_msg_new(mem_ctx);
411 return NT_STATUS_NO_MEMORY;
414 mod->dn = ldb_dn_copy(mod, user_dn);
416 status = NT_STATUS_NO_MEMORY;
420 /* set the password on the user DN specified. This may fail
421 * due to password policies */
422 status = samdb_set_password(sam_ctx, mem_ctx,
426 True, /* this is a user password change */
427 True, /* run restriction tests */
430 if (!NT_STATUS_IS_OK(status)) {
434 /* The above call only setup the modifications, this actually
435 * makes the write to the database. */
436 ret = samdb_replace(sam_ctx, mem_ctx, mod);
438 status = NT_STATUS_UNSUCCESSFUL;
442 /* And this confirms it in a transaction commit */
443 ret = ldb_transaction_commit(sam_ctx);
445 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
446 ldb_dn_linearize(mem_ctx, user_dn),
447 ldb_errstring(sam_ctx)));
448 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
455 ldb_transaction_cancel(sam_ctx);
456 talloc_free(sam_ctx);
458 reject = talloc(mem_ctx, struct samr_ChangeReject);
459 r->out.dominfo = dominfo;
460 r->out.reject = reject;
462 if (reject == NULL) {
465 ZERO_STRUCTP(reject);
467 reject->reason = reason;
474 samr_ChangePasswordUser2
476 easy - just a subset of samr_ChangePasswordUser3
478 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
479 struct samr_ChangePasswordUser2 *r)
481 struct samr_ChangePasswordUser3 r2;
483 r2.in.server = r->in.server;
484 r2.in.account = r->in.account;
485 r2.in.nt_password = r->in.nt_password;
486 r2.in.nt_verifier = r->in.nt_verifier;
487 r2.in.lm_change = r->in.lm_change;
488 r2.in.lm_password = r->in.lm_password;
489 r2.in.lm_verifier = r->in.lm_verifier;
490 r2.in.password3 = NULL;
492 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
497 check that a password is sufficiently complex
499 static BOOL samdb_password_complexity_ok(const char *pass)
501 return check_password_quality(pass);
505 set the user password using plaintext, obeying any user or domain
506 password restrictions
508 note that this function doesn't actually store the result in the
509 database, it just fills in the "mod" structure with ldb modify
510 elements to setup the correct change when samdb_replace() is
511 called. This allows the caller to combine the change with other
512 changes (as is needed by some of the set user info levels)
514 The caller should probably have a transaction wrapping this
516 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
517 const struct ldb_dn *user_dn,
518 const struct ldb_dn *domain_dn,
519 struct ldb_message *mod,
520 const char *new_pass,
521 struct samr_Password *lmNewHash,
522 struct samr_Password *ntNewHash,
525 enum samr_RejectReason *reject_reason,
526 struct samr_DomInfo1 **_dominfo)
528 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
529 "ntPwdHistory", "unicodePwd",
530 "lmPwdHash", "ntPwdHash", "badPwdCount",
532 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
533 "maxPwdAge", "minPwdAge",
534 "minPwdLength", "pwdLastSet", NULL };
535 const char *unicodePwd;
538 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
539 uint_t userAccountControl, badPwdCount;
540 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
541 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
542 struct samr_Password local_lmNewHash, local_ntNewHash;
543 int lmPwdHistory_len, ntPwdHistory_len;
545 struct dom_sid *domain_sid;
546 struct ldb_message **res;
548 time_t now = time(NULL);
552 /* we need to know the time to compute password age */
553 unix_to_nt_time(&now_nt, now);
555 /* pull all the user parameters */
556 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
558 return NT_STATUS_INTERNAL_DB_CORRUPTION;
560 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
561 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
562 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
563 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
564 "lmPwdHistory", &lmPwdHistory);
565 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
566 "ntPwdHistory", &ntPwdHistory);
567 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
568 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
569 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
570 kvno = samdb_result_uint(res[0], "msDS-KeyVersionNumber", 0);
573 /* pull the domain parameters */
574 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
576 return NT_STATUS_NO_SUCH_DOMAIN;
579 /* work out the domain sid, and pull the domain from there */
580 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
581 if (domain_sid == NULL) {
582 return NT_STATUS_INTERNAL_DB_CORRUPTION;
585 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
587 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
589 return NT_STATUS_NO_SUCH_DOMAIN;
593 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
594 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
595 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
596 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
599 struct samr_DomInfo1 *dominfo;
600 /* on failure we need to fill in the reject reasons */
601 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
602 if (dominfo == NULL) {
603 return NT_STATUS_NO_MEMORY;
605 dominfo->min_password_length = minPwdLength;
606 dominfo->password_properties = pwdProperties;
607 dominfo->password_history_length = pwdHistoryLength;
608 dominfo->max_password_age = minPwdAge;
609 dominfo->min_password_age = minPwdAge;
614 /* check the various password restrictions */
615 if (restrictions && minPwdLength > strlen_m(new_pass)) {
617 *reject_reason = SAMR_REJECT_TOO_SHORT;
619 return NT_STATUS_PASSWORD_RESTRICTION;
622 /* possibly check password complexity */
623 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
624 !samdb_password_complexity_ok(new_pass)) {
626 *reject_reason = SAMR_REJECT_COMPLEXITY;
628 return NT_STATUS_PASSWORD_RESTRICTION;
631 /* compute the new nt and lm hashes */
632 if (E_deshash(new_pass, local_lmNewHash.hash)) {
633 lmNewHash = &local_lmNewHash;
635 E_md4hash(new_pass, local_ntNewHash.hash);
636 ntNewHash = &local_ntNewHash;
639 if (restrictions && user_change) {
640 /* are all password changes disallowed? */
641 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
643 *reject_reason = SAMR_REJECT_OTHER;
645 return NT_STATUS_PASSWORD_RESTRICTION;
648 /* can this user change password? */
649 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
651 *reject_reason = SAMR_REJECT_OTHER;
653 return NT_STATUS_PASSWORD_RESTRICTION;
656 /* yes, this is a minus. The ages are in negative 100nsec units! */
657 if (pwdLastSet - minPwdAge > now_nt) {
659 *reject_reason = SAMR_REJECT_OTHER;
661 return NT_STATUS_PASSWORD_RESTRICTION;
664 /* check the immediately past password */
665 if (pwdHistoryLength > 0) {
666 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
668 *reject_reason = SAMR_REJECT_COMPLEXITY;
670 return NT_STATUS_PASSWORD_RESTRICTION;
672 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
674 *reject_reason = SAMR_REJECT_COMPLEXITY;
676 return NT_STATUS_PASSWORD_RESTRICTION;
680 /* check the password history */
681 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
682 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
684 if (pwdHistoryLength > 0) {
685 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
687 *reject_reason = SAMR_REJECT_COMPLEXITY;
689 return NT_STATUS_PASSWORD_RESTRICTION;
691 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
693 *reject_reason = SAMR_REJECT_COMPLEXITY;
695 return NT_STATUS_PASSWORD_RESTRICTION;
697 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
699 *reject_reason = SAMR_REJECT_COMPLEXITY;
701 return NT_STATUS_PASSWORD_RESTRICTION;
705 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
706 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
708 *reject_reason = SAMR_REJECT_COMPLEXITY;
710 return NT_STATUS_PASSWORD_RESTRICTION;
713 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
714 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
716 *reject_reason = SAMR_REJECT_COMPLEXITY;
718 return NT_STATUS_PASSWORD_RESTRICTION;
723 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
725 /* the password is acceptable. Start forming the new fields */
727 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
729 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
733 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
735 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
738 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
739 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
740 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
741 "unicodePwd", new_pass));
743 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
746 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
748 CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
750 if (pwdHistoryLength == 0) {
751 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
752 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
756 /* store the password history */
757 new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
759 if (!new_lmPwdHistory) {
760 return NT_STATUS_NO_MEMORY;
762 new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
764 if (!new_ntPwdHistory) {
765 return NT_STATUS_NO_MEMORY;
767 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
768 new_lmPwdHistory[i+1] = lmPwdHistory[i];
770 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
771 new_ntPwdHistory[i+1] = ntPwdHistory[i];
774 /* Don't store 'long' passwords in the LM history,
775 but make sure to 'expire' one password off the other end */
777 new_lmPwdHistory[0] = *lmNewHash;
779 ZERO_STRUCT(new_lmPwdHistory[0]);
781 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
784 new_ntPwdHistory[0] = *ntNewHash;
786 ZERO_STRUCT(new_ntPwdHistory[0]);
788 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
790 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
795 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
803 set password via a samr_CryptPassword buffer
804 this will in the 'msg' with modify operations that will update the user
805 password when applied
807 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
809 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
811 struct ldb_message *msg,
812 struct samr_CryptPassword *pwbuf)
816 uint32_t new_pass_len;
817 DATA_BLOB session_key = data_blob(NULL, 0);
819 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
820 if (!NT_STATUS_IS_OK(nt_status)) {
824 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
826 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
827 &new_pass_len, STR_UNICODE)) {
828 DEBUG(3,("samr: failed to decode password buffer\n"));
829 return NT_STATUS_WRONG_PASSWORD;
832 /* set the password - samdb needs to know both the domain and user DNs,
833 so the domain password policy can be used */
834 return samdb_set_password(sam_ctx, mem_ctx,
835 account_dn, domain_dn,
838 False, /* This is a password set, not change */
839 True, /* run restriction tests */
845 set password via a samr_CryptPasswordEx buffer
846 this will in the 'msg' with modify operations that will update the user
847 password when applied
849 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
850 struct ldb_context *sam_ctx,
851 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
853 struct ldb_message *msg,
854 struct samr_CryptPasswordEx *pwbuf)
858 uint32_t new_pass_len;
859 DATA_BLOB co_session_key;
860 DATA_BLOB session_key = data_blob(NULL, 0);
861 struct MD5Context ctx;
863 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
864 if (!NT_STATUS_IS_OK(nt_status)) {
868 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
869 if (!co_session_key.data) {
870 return NT_STATUS_NO_MEMORY;
874 MD5Update(&ctx, &pwbuf->data[516], 16);
875 MD5Update(&ctx, session_key.data, session_key.length);
876 MD5Final(co_session_key.data, &ctx);
878 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
880 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
881 &new_pass_len, STR_UNICODE)) {
882 DEBUG(3,("samr: failed to decode password buffer\n"));
883 return NT_STATUS_WRONG_PASSWORD;
886 /* set the password - samdb needs to know both the domain and user DNs,
887 so the domain password policy can be used */
888 return samdb_set_password(sam_ctx, mem_ctx,
889 account_dn, domain_dn,
892 False, /* This is a password set, not change */
893 True, /* run restriction tests */
898 set the user password using plaintext, obeying any user or domain
899 password restrictions
901 This wrapper function takes a SID as input, rather than a user DN,
902 and actually performs the password change
905 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
906 const struct dom_sid *user_sid,
907 const char *new_pass,
908 struct samr_Password *lmNewHash,
909 struct samr_Password *ntNewHash,
912 enum samr_RejectReason *reject_reason,
913 struct samr_DomInfo1 **_dominfo)
916 struct ldb_dn *user_dn;
917 struct ldb_message *msg;
920 ret = ldb_transaction_start(ctx);
922 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
923 return NT_STATUS_INTERNAL_DB_CORRUPTION;
926 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
927 "(&(objectSid=%s)(objectClass=user))",
928 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
930 ldb_transaction_cancel(ctx);
931 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
932 dom_sid_string(mem_ctx, user_sid)));
933 return NT_STATUS_NO_SUCH_USER;
936 msg = ldb_msg_new(mem_ctx);
938 ldb_transaction_cancel(ctx);
939 return NT_STATUS_NO_MEMORY;
942 msg->dn = ldb_dn_copy(msg, user_dn);
944 ldb_transaction_cancel(ctx);
945 return NT_STATUS_NO_MEMORY;
948 nt_status = samdb_set_password(ctx, mem_ctx,
951 lmNewHash, ntNewHash,
952 user_change, /* This is a password set, not change */
953 restrictions, /* run restriction tests */
954 reject_reason, _dominfo);
955 if (!NT_STATUS_IS_OK(nt_status)) {
956 ldb_transaction_cancel(ctx);
960 /* modify the samdb record */
961 ret = samdb_replace(ctx, mem_ctx, msg);
963 ldb_transaction_cancel(ctx);
964 return NT_STATUS_ACCESS_DENIED;
967 ret = ldb_transaction_commit(ctx);
969 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
970 ldb_dn_linearize(mem_ctx, msg->dn),
971 ldb_errstring(ctx)));
972 return NT_STATUS_INTERNAL_DB_CORRUPTION;