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"
35 samr_ChangePasswordUser
37 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
38 struct samr_ChangePasswordUser *r)
40 struct dcesrv_handle *h;
41 struct samr_account_state *a_state;
42 struct ldb_context *sam_ctx;
43 struct ldb_message **res, *msg;
45 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
46 struct samr_Password *lm_pwd, *nt_pwd;
47 NTSTATUS status = NT_STATUS_OK;
48 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
50 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
54 /* basic sanity checking on parameters. Do this before any database ops */
55 if (!r->in.lm_present || !r->in.nt_present ||
56 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
57 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
58 /* we should really handle a change with lm not
60 return NT_STATUS_INVALID_PARAMETER_MIX;
62 if (!r->in.cross1_present || !r->in.nt_cross) {
63 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
65 if (!r->in.cross2_present || !r->in.lm_cross) {
66 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
69 /* To change a password we need to open as system */
70 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
71 if (sam_ctx == NULL) {
72 return NT_STATUS_INVALID_SYSTEM_SERVICE;
75 ret = ldb_transaction_start(sam_ctx);
77 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
78 return NT_STATUS_INTERNAL_DB_CORRUPTION;
81 /* fetch the old hashes */
82 ret = gendb_search_dn(sam_ctx, mem_ctx,
83 a_state->account_dn, &res, attrs);
85 ldb_transaction_cancel(sam_ctx);
86 return NT_STATUS_WRONG_PASSWORD;
90 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
91 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
92 ldb_transaction_cancel(sam_ctx);
93 return NT_STATUS_WRONG_PASSWORD;
96 /* decrypt and check the new lm hash */
97 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
98 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
99 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
100 ldb_transaction_cancel(sam_ctx);
101 return NT_STATUS_WRONG_PASSWORD;
104 /* decrypt and check the new nt hash */
105 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
106 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
107 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
108 ldb_transaction_cancel(sam_ctx);
109 return NT_STATUS_WRONG_PASSWORD;
112 /* check the nt cross hash */
113 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
114 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
115 ldb_transaction_cancel(sam_ctx);
116 return NT_STATUS_WRONG_PASSWORD;
119 /* check the lm cross hash */
120 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
121 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
122 ldb_transaction_cancel(sam_ctx);
123 return NT_STATUS_WRONG_PASSWORD;
126 msg = ldb_msg_new(mem_ctx);
128 ldb_transaction_cancel(sam_ctx);
129 return NT_STATUS_NO_MEMORY;
132 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
134 ldb_transaction_cancel(sam_ctx);
135 return NT_STATUS_NO_MEMORY;
138 /* set the password on the user DN specified. This may fail
139 * due to password policies */
140 status = samdb_set_password(sam_ctx, mem_ctx,
141 a_state->account_dn, a_state->domain_state->domain_dn,
142 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
143 True, /* this is a user password change */
144 True, /* run restriction tests */
147 if (!NT_STATUS_IS_OK(status)) {
148 ldb_transaction_cancel(sam_ctx);
152 /* The above call only setup the modifications, this actually
153 * makes the write to the database. */
154 ret = samdb_replace(sam_ctx, mem_ctx, msg);
156 ldb_transaction_cancel(sam_ctx);
157 return NT_STATUS_UNSUCCESSFUL;
160 /* And this confirms it in a transaction commit */
161 ret = ldb_transaction_commit(sam_ctx);
163 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
164 ldb_dn_linearize(mem_ctx, a_state->account_dn),
165 ldb_errstring(sam_ctx)));
166 return NT_STATUS_INTERNAL_DB_CORRUPTION;
173 samr_OemChangePasswordUser2
175 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
176 struct samr_OemChangePasswordUser2 *r)
180 uint32_t new_pass_len;
181 struct samr_CryptPassword *pwbuf = r->in.password;
182 struct ldb_context *sam_ctx;
183 const struct ldb_dn *user_dn;
185 struct ldb_message **res, *mod;
186 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
187 struct samr_Password *lm_pwd;
188 DATA_BLOB lm_pwd_blob;
189 uint8_t new_lm_hash[16];
190 struct samr_Password lm_verifier;
193 return NT_STATUS_WRONG_PASSWORD;
196 /* To change a password we need to open as system */
197 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
198 if (sam_ctx == NULL) {
199 return NT_STATUS_INVALID_SYSTEM_SERVICE;
202 ret = ldb_transaction_start(sam_ctx);
204 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
205 return NT_STATUS_INTERNAL_DB_CORRUPTION;
208 /* we need the users dn and the domain dn (derived from the
209 user SID). We also need the current lm password hash in
210 order to decrypt the incoming password */
211 ret = gendb_search(sam_ctx,
212 mem_ctx, NULL, &res, attrs,
213 "(&(sAMAccountName=%s)(objectclass=user))",
214 r->in.account->string);
216 ldb_transaction_cancel(sam_ctx);
217 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
218 return NT_STATUS_WRONG_PASSWORD;
221 user_dn = res[0]->dn;
223 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
224 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
225 ldb_transaction_cancel(sam_ctx);
226 return NT_STATUS_WRONG_PASSWORD;
229 /* decrypt the password we have been given */
230 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
231 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
232 data_blob_free(&lm_pwd_blob);
234 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
235 &new_pass_len, STR_ASCII)) {
236 ldb_transaction_cancel(sam_ctx);
237 DEBUG(3,("samr: failed to decode password buffer\n"));
238 return NT_STATUS_WRONG_PASSWORD;
241 /* check LM verifier */
242 if (lm_pwd == NULL || r->in.hash == NULL) {
243 ldb_transaction_cancel(sam_ctx);
244 return NT_STATUS_WRONG_PASSWORD;
247 E_deshash(new_pass, new_lm_hash);
248 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
249 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
250 ldb_transaction_cancel(sam_ctx);
251 return NT_STATUS_WRONG_PASSWORD;
254 mod = ldb_msg_new(mem_ctx);
256 ldb_transaction_cancel(sam_ctx);
257 return NT_STATUS_NO_MEMORY;
260 mod->dn = ldb_dn_copy(mod, user_dn);
262 ldb_transaction_cancel(sam_ctx);
263 return NT_STATUS_NO_MEMORY;
266 /* set the password on the user DN specified. This may fail
267 * due to password policies */
268 status = samdb_set_password(sam_ctx, mem_ctx,
272 True, /* this is a user password change */
273 True, /* run restriction tests */
276 if (!NT_STATUS_IS_OK(status)) {
277 ldb_transaction_cancel(sam_ctx);
281 /* The above call only setup the modifications, this actually
282 * makes the write to the database. */
283 ret = samdb_replace(sam_ctx, mem_ctx, mod);
285 ldb_transaction_cancel(sam_ctx);
286 return NT_STATUS_UNSUCCESSFUL;
289 /* And this confirms it in a transaction commit */
290 ret = ldb_transaction_commit(sam_ctx);
292 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
293 ldb_dn_linearize(mem_ctx, user_dn),
294 ldb_errstring(sam_ctx)));
295 return NT_STATUS_INTERNAL_DB_CORRUPTION;
303 samr_ChangePasswordUser3
305 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
307 struct samr_ChangePasswordUser3 *r)
311 uint32_t new_pass_len;
312 struct ldb_context *sam_ctx = NULL;
313 const struct ldb_dn *user_dn;
315 struct ldb_message **res, *mod;
316 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
317 struct samr_Password *nt_pwd, *lm_pwd;
318 DATA_BLOB nt_pwd_blob;
319 struct samr_DomInfo1 *dominfo = NULL;
320 struct samr_ChangeReject *reject = NULL;
321 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
322 uint8_t new_nt_hash[16], new_lm_hash[16];
323 struct samr_Password nt_verifier, lm_verifier;
327 if (r->in.nt_password == NULL ||
328 r->in.nt_verifier == NULL) {
329 return NT_STATUS_INVALID_PARAMETER;
332 /* To change a password we need to open as system */
333 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
334 if (sam_ctx == NULL) {
335 return NT_STATUS_INVALID_SYSTEM_SERVICE;
338 ret = ldb_transaction_start(sam_ctx);
340 talloc_free(sam_ctx);
341 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
342 return NT_STATUS_INTERNAL_DB_CORRUPTION;
345 /* we need the users dn and the domain dn (derived from the
346 user SID). We also need the current lm and nt password hashes
347 in order to decrypt the incoming passwords */
348 ret = gendb_search(sam_ctx,
349 mem_ctx, NULL, &res, attrs,
350 "(&(sAMAccountName=%s)(objectclass=user))",
351 r->in.account->string);
353 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
354 status = NT_STATUS_WRONG_PASSWORD;
358 user_dn = res[0]->dn;
360 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
361 if (!NT_STATUS_IS_OK(status) ) {
366 status = NT_STATUS_WRONG_PASSWORD;
370 /* decrypt the password we have been given */
371 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
372 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
373 data_blob_free(&nt_pwd_blob);
375 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
376 &new_pass_len, STR_UNICODE)) {
377 DEBUG(3,("samr: failed to decode password buffer\n"));
378 status = NT_STATUS_WRONG_PASSWORD;
382 if (r->in.nt_verifier == NULL) {
383 status = NT_STATUS_WRONG_PASSWORD;
387 /* check NT verifier */
388 E_md4hash(new_pass, new_nt_hash);
389 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
390 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
391 status = NT_STATUS_WRONG_PASSWORD;
395 /* check LM verifier */
396 if (lm_pwd && r->in.lm_verifier != NULL) {
397 E_deshash(new_pass, new_lm_hash);
398 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
399 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
400 status = NT_STATUS_WRONG_PASSWORD;
406 mod = ldb_msg_new(mem_ctx);
408 return NT_STATUS_NO_MEMORY;
411 mod->dn = ldb_dn_copy(mod, user_dn);
413 status = NT_STATUS_NO_MEMORY;
417 /* set the password on the user DN specified. This may fail
418 * due to password policies */
419 status = samdb_set_password(sam_ctx, mem_ctx,
423 True, /* this is a user password change */
424 True, /* run restriction tests */
427 if (!NT_STATUS_IS_OK(status)) {
431 /* The above call only setup the modifications, this actually
432 * makes the write to the database. */
433 ret = samdb_replace(sam_ctx, mem_ctx, mod);
435 status = NT_STATUS_UNSUCCESSFUL;
439 /* And this confirms it in a transaction commit */
440 ret = ldb_transaction_commit(sam_ctx);
442 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
443 ldb_dn_linearize(mem_ctx, user_dn),
444 ldb_errstring(sam_ctx)));
445 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
452 ldb_transaction_cancel(sam_ctx);
453 talloc_free(sam_ctx);
455 reject = talloc(mem_ctx, struct samr_ChangeReject);
456 r->out.dominfo = dominfo;
457 r->out.reject = reject;
459 if (reject == NULL) {
462 ZERO_STRUCTP(reject);
464 reject->reason = reason;
471 samr_ChangePasswordUser2
473 easy - just a subset of samr_ChangePasswordUser3
475 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
476 struct samr_ChangePasswordUser2 *r)
478 struct samr_ChangePasswordUser3 r2;
480 r2.in.server = r->in.server;
481 r2.in.account = r->in.account;
482 r2.in.nt_password = r->in.nt_password;
483 r2.in.nt_verifier = r->in.nt_verifier;
484 r2.in.lm_change = r->in.lm_change;
485 r2.in.lm_password = r->in.lm_password;
486 r2.in.lm_verifier = r->in.lm_verifier;
487 r2.in.password3 = NULL;
489 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
494 check that a password is sufficiently complex
496 static BOOL samdb_password_complexity_ok(const char *pass)
498 return check_password_quality(pass);
502 set the user password using plaintext, obeying any user or domain
503 password restrictions
505 note that this function doesn't actually store the result in the
506 database, it just fills in the "mod" structure with ldb modify
507 elements to setup the correct change when samdb_replace() is
508 called. This allows the caller to combine the change with other
509 changes (as is needed by some of the set user info levels)
511 The caller should probably have a transaction wrapping this
513 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
514 const struct ldb_dn *user_dn,
515 const struct ldb_dn *domain_dn,
516 struct ldb_message *mod,
517 const char *new_pass,
518 struct samr_Password *lmNewHash,
519 struct samr_Password *ntNewHash,
522 enum samr_RejectReason *reject_reason,
523 struct samr_DomInfo1 **_dominfo)
525 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
526 "ntPwdHistory", "unicodePwd",
527 "lmPwdHash", "ntPwdHash", "badPwdCount",
529 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
530 "maxPwdAge", "minPwdAge",
531 "minPwdLength", "pwdLastSet", NULL };
532 const char *unicodePwd;
535 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
536 uint_t userAccountControl, badPwdCount;
537 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
538 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
539 struct samr_Password local_lmNewHash, local_ntNewHash;
540 int lmPwdHistory_len, ntPwdHistory_len;
542 struct dom_sid *domain_sid;
543 struct ldb_message **res;
545 time_t now = time(NULL);
549 /* we need to know the time to compute password age */
550 unix_to_nt_time(&now_nt, now);
552 /* pull all the user parameters */
553 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
555 return NT_STATUS_INTERNAL_DB_CORRUPTION;
557 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
558 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
559 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
560 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
561 "lmPwdHistory", &lmPwdHistory);
562 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
563 "ntPwdHistory", &ntPwdHistory);
564 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
565 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
566 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
567 kvno = samdb_result_uint(res[0], "msDS-KeyVersionNumber", 0);
570 /* pull the domain parameters */
571 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
573 return NT_STATUS_NO_SUCH_DOMAIN;
576 /* work out the domain sid, and pull the domain from there */
577 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
578 if (domain_sid == NULL) {
579 return NT_STATUS_INTERNAL_DB_CORRUPTION;
582 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
584 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
586 return NT_STATUS_NO_SUCH_DOMAIN;
590 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
591 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
592 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
593 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
596 struct samr_DomInfo1 *dominfo;
597 /* on failure we need to fill in the reject reasons */
598 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
599 if (dominfo == NULL) {
600 return NT_STATUS_NO_MEMORY;
602 dominfo->min_password_length = minPwdLength;
603 dominfo->password_properties = pwdProperties;
604 dominfo->password_history_length = pwdHistoryLength;
605 dominfo->max_password_age = minPwdAge;
606 dominfo->min_password_age = minPwdAge;
611 /* check the various password restrictions */
612 if (restrictions && minPwdLength > strlen_m(new_pass)) {
614 *reject_reason = SAMR_REJECT_TOO_SHORT;
616 return NT_STATUS_PASSWORD_RESTRICTION;
619 /* possibly check password complexity */
620 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
621 !samdb_password_complexity_ok(new_pass)) {
623 *reject_reason = SAMR_REJECT_COMPLEXITY;
625 return NT_STATUS_PASSWORD_RESTRICTION;
628 /* compute the new nt and lm hashes */
629 if (E_deshash(new_pass, local_lmNewHash.hash)) {
630 lmNewHash = &local_lmNewHash;
632 E_md4hash(new_pass, local_ntNewHash.hash);
633 ntNewHash = &local_ntNewHash;
636 if (restrictions && user_change) {
637 /* are all password changes disallowed? */
638 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
640 *reject_reason = SAMR_REJECT_OTHER;
642 return NT_STATUS_PASSWORD_RESTRICTION;
645 /* can this user change password? */
646 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
648 *reject_reason = SAMR_REJECT_OTHER;
650 return NT_STATUS_PASSWORD_RESTRICTION;
653 /* yes, this is a minus. The ages are in negative 100nsec units! */
654 if (pwdLastSet - minPwdAge > now_nt) {
656 *reject_reason = SAMR_REJECT_OTHER;
658 return NT_STATUS_PASSWORD_RESTRICTION;
661 /* check the immediately past password */
662 if (pwdHistoryLength > 0) {
663 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
665 *reject_reason = SAMR_REJECT_COMPLEXITY;
667 return NT_STATUS_PASSWORD_RESTRICTION;
669 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
671 *reject_reason = SAMR_REJECT_COMPLEXITY;
673 return NT_STATUS_PASSWORD_RESTRICTION;
677 /* check the password history */
678 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
679 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
681 if (pwdHistoryLength > 0) {
682 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
684 *reject_reason = SAMR_REJECT_COMPLEXITY;
686 return NT_STATUS_PASSWORD_RESTRICTION;
688 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
690 *reject_reason = SAMR_REJECT_COMPLEXITY;
692 return NT_STATUS_PASSWORD_RESTRICTION;
694 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
696 *reject_reason = SAMR_REJECT_COMPLEXITY;
698 return NT_STATUS_PASSWORD_RESTRICTION;
702 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
703 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
705 *reject_reason = SAMR_REJECT_COMPLEXITY;
707 return NT_STATUS_PASSWORD_RESTRICTION;
710 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
711 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
713 *reject_reason = SAMR_REJECT_COMPLEXITY;
715 return NT_STATUS_PASSWORD_RESTRICTION;
720 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
722 /* the password is acceptable. Start forming the new fields */
724 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
726 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
730 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
732 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
735 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
736 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
737 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
738 "unicodePwd", new_pass));
740 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
743 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
745 CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
747 if (pwdHistoryLength == 0) {
748 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
749 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
753 /* store the password history */
754 new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
756 if (!new_lmPwdHistory) {
757 return NT_STATUS_NO_MEMORY;
759 new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
761 if (!new_ntPwdHistory) {
762 return NT_STATUS_NO_MEMORY;
764 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
765 new_lmPwdHistory[i+1] = lmPwdHistory[i];
767 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
768 new_ntPwdHistory[i+1] = ntPwdHistory[i];
771 /* Don't store 'long' passwords in the LM history,
772 but make sure to 'expire' one password off the other end */
774 new_lmPwdHistory[0] = *lmNewHash;
776 ZERO_STRUCT(new_lmPwdHistory[0]);
778 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
781 new_ntPwdHistory[0] = *ntNewHash;
783 ZERO_STRUCT(new_ntPwdHistory[0]);
785 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
787 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
792 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
800 set password via a samr_CryptPassword buffer
801 this will in the 'msg' with modify operations that will update the user
802 password when applied
804 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
806 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
808 struct ldb_message *msg,
809 struct samr_CryptPassword *pwbuf)
813 uint32_t new_pass_len;
814 DATA_BLOB session_key = data_blob(NULL, 0);
816 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
817 if (!NT_STATUS_IS_OK(nt_status)) {
821 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
823 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
824 &new_pass_len, STR_UNICODE)) {
825 DEBUG(3,("samr: failed to decode password buffer\n"));
826 return NT_STATUS_WRONG_PASSWORD;
829 /* set the password - samdb needs to know both the domain and user DNs,
830 so the domain password policy can be used */
831 return samdb_set_password(sam_ctx, mem_ctx,
832 account_dn, domain_dn,
835 False, /* This is a password set, not change */
836 True, /* run restriction tests */
842 set password via a samr_CryptPasswordEx buffer
843 this will in the 'msg' with modify operations that will update the user
844 password when applied
846 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
847 struct ldb_context *sam_ctx,
848 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
850 struct ldb_message *msg,
851 struct samr_CryptPasswordEx *pwbuf)
855 uint32_t new_pass_len;
856 DATA_BLOB co_session_key;
857 DATA_BLOB session_key = data_blob(NULL, 0);
858 struct MD5Context ctx;
860 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
861 if (!NT_STATUS_IS_OK(nt_status)) {
865 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
866 if (!co_session_key.data) {
867 return NT_STATUS_NO_MEMORY;
871 MD5Update(&ctx, &pwbuf->data[516], 16);
872 MD5Update(&ctx, session_key.data, session_key.length);
873 MD5Final(co_session_key.data, &ctx);
875 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
877 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
878 &new_pass_len, STR_UNICODE)) {
879 DEBUG(3,("samr: failed to decode password buffer\n"));
880 return NT_STATUS_WRONG_PASSWORD;
883 /* set the password - samdb needs to know both the domain and user DNs,
884 so the domain password policy can be used */
885 return samdb_set_password(sam_ctx, mem_ctx,
886 account_dn, domain_dn,
889 False, /* This is a password set, not change */
890 True, /* run restriction tests */
895 set the user password using plaintext, obeying any user or domain
896 password restrictions
898 This wrapper function takes a SID as input, rather than a user DN,
899 and actually performs the password change
902 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
903 const struct dom_sid *user_sid,
904 const char *new_pass,
905 struct samr_Password *lmNewHash,
906 struct samr_Password *ntNewHash,
909 enum samr_RejectReason *reject_reason,
910 struct samr_DomInfo1 **_dominfo)
913 struct ldb_dn *user_dn;
914 struct ldb_message *msg;
917 ret = ldb_transaction_start(ctx);
919 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
920 return NT_STATUS_INTERNAL_DB_CORRUPTION;
923 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
924 "(&(objectSid=%s)(objectClass=user))",
925 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
927 ldb_transaction_cancel(ctx);
928 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
929 dom_sid_string(mem_ctx, user_sid)));
930 return NT_STATUS_NO_SUCH_USER;
933 msg = ldb_msg_new(mem_ctx);
935 ldb_transaction_cancel(ctx);
936 return NT_STATUS_NO_MEMORY;
939 msg->dn = ldb_dn_copy(msg, user_dn);
941 ldb_transaction_cancel(ctx);
942 return NT_STATUS_NO_MEMORY;
945 nt_status = samdb_set_password(ctx, mem_ctx,
948 lmNewHash, ntNewHash,
949 user_change, /* This is a password set, not change */
950 restrictions, /* run restriction tests */
951 reject_reason, _dominfo);
952 if (!NT_STATUS_IS_OK(nt_status)) {
953 ldb_transaction_cancel(ctx);
957 /* modify the samdb record */
958 ret = samdb_replace(ctx, mem_ctx, msg);
960 ldb_transaction_cancel(ctx);
961 return NT_STATUS_ACCESS_DENIED;
964 ret = ldb_transaction_commit(ctx);
966 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
967 ldb_dn_linearize(mem_ctx, msg->dn),
968 ldb_errstring(ctx)));
969 return NT_STATUS_INTERNAL_DB_CORRUPTION;