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 /* basic sanity checking on parameters. Do this before any database ops */
54 if (!r->in.lm_present || !r->in.nt_present ||
55 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
56 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
57 /* we should really handle a change with lm not
59 return NT_STATUS_INVALID_PARAMETER_MIX;
61 if (!r->in.cross1_present || !r->in.nt_cross) {
62 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
64 if (!r->in.cross2_present || !r->in.lm_cross) {
65 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
68 /* To change a password we need to open as system */
69 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
70 if (sam_ctx == NULL) {
71 return NT_STATUS_INVALID_SYSTEM_SERVICE;
74 ret = ldb_transaction_start(sam_ctx);
76 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
77 return NT_STATUS_INTERNAL_DB_CORRUPTION;
80 /* fetch the old hashes */
81 ret = gendb_search_dn(sam_ctx, mem_ctx,
82 a_state->account_dn, &res, attrs);
84 ldb_transaction_cancel(sam_ctx);
85 return NT_STATUS_WRONG_PASSWORD;
89 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
90 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
91 ldb_transaction_cancel(sam_ctx);
92 return NT_STATUS_WRONG_PASSWORD;
95 /* decrypt and check the new lm hash */
96 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
97 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
98 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
99 ldb_transaction_cancel(sam_ctx);
100 return NT_STATUS_WRONG_PASSWORD;
103 /* decrypt and check the new nt hash */
104 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
105 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
106 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
107 ldb_transaction_cancel(sam_ctx);
108 return NT_STATUS_WRONG_PASSWORD;
111 /* check the nt cross hash */
112 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
113 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
114 ldb_transaction_cancel(sam_ctx);
115 return NT_STATUS_WRONG_PASSWORD;
118 /* check the lm cross hash */
119 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
120 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
121 ldb_transaction_cancel(sam_ctx);
122 return NT_STATUS_WRONG_PASSWORD;
125 msg = ldb_msg_new(mem_ctx);
127 ldb_transaction_cancel(sam_ctx);
128 return NT_STATUS_NO_MEMORY;
131 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
133 ldb_transaction_cancel(sam_ctx);
134 return NT_STATUS_NO_MEMORY;
137 /* set the password on the user DN specified. This may fail
138 * due to password policies */
139 status = samdb_set_password(sam_ctx, mem_ctx,
140 a_state->account_dn, a_state->domain_state->domain_dn,
141 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
142 True, /* this is a user password change */
143 True, /* run restriction tests */
146 if (!NT_STATUS_IS_OK(status)) {
147 ldb_transaction_cancel(sam_ctx);
151 /* The above call only setup the modifications, this actually
152 * makes the write to the database. */
153 ret = samdb_replace(sam_ctx, mem_ctx, msg);
155 ldb_transaction_cancel(sam_ctx);
156 return NT_STATUS_UNSUCCESSFUL;
159 /* And this confirms it in a transaction commit */
160 ret = ldb_transaction_commit(sam_ctx);
162 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
163 ldb_dn_linearize(mem_ctx, a_state->account_dn),
164 ldb_errstring(sam_ctx)));
165 return NT_STATUS_INTERNAL_DB_CORRUPTION;
172 samr_OemChangePasswordUser2
174 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
175 struct samr_OemChangePasswordUser2 *r)
179 uint32_t new_pass_len;
180 struct samr_CryptPassword *pwbuf = r->in.password;
181 struct ldb_context *sam_ctx;
182 const struct ldb_dn *user_dn;
184 struct ldb_message **res, *mod;
185 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
186 struct samr_Password *lm_pwd;
187 DATA_BLOB lm_pwd_blob;
188 uint8_t new_lm_hash[16];
189 struct samr_Password lm_verifier;
192 return NT_STATUS_WRONG_PASSWORD;
195 /* To change a password we need to open as system */
196 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
197 if (sam_ctx == NULL) {
198 return NT_STATUS_INVALID_SYSTEM_SERVICE;
201 ret = ldb_transaction_start(sam_ctx);
203 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
204 return NT_STATUS_INTERNAL_DB_CORRUPTION;
207 /* we need the users dn and the domain dn (derived from the
208 user SID). We also need the current lm password hash in
209 order to decrypt the incoming password */
210 ret = gendb_search(sam_ctx,
211 mem_ctx, NULL, &res, attrs,
212 "(&(sAMAccountName=%s)(objectclass=user))",
213 r->in.account->string);
215 ldb_transaction_cancel(sam_ctx);
216 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
217 return NT_STATUS_WRONG_PASSWORD;
220 user_dn = res[0]->dn;
222 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
223 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
224 ldb_transaction_cancel(sam_ctx);
225 return NT_STATUS_WRONG_PASSWORD;
228 /* decrypt the password we have been given */
229 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
230 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
231 data_blob_free(&lm_pwd_blob);
233 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
234 &new_pass_len, STR_ASCII)) {
235 ldb_transaction_cancel(sam_ctx);
236 DEBUG(3,("samr: failed to decode password buffer\n"));
237 return NT_STATUS_WRONG_PASSWORD;
240 /* check LM verifier */
241 if (lm_pwd == NULL || r->in.hash == NULL) {
242 ldb_transaction_cancel(sam_ctx);
243 return NT_STATUS_WRONG_PASSWORD;
246 E_deshash(new_pass, new_lm_hash);
247 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
248 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
249 ldb_transaction_cancel(sam_ctx);
250 return NT_STATUS_WRONG_PASSWORD;
253 mod = ldb_msg_new(mem_ctx);
255 ldb_transaction_cancel(sam_ctx);
256 return NT_STATUS_NO_MEMORY;
259 mod->dn = ldb_dn_copy(mod, user_dn);
261 ldb_transaction_cancel(sam_ctx);
262 return NT_STATUS_NO_MEMORY;
265 /* set the password on the user DN specified. This may fail
266 * due to password policies */
267 status = samdb_set_password(sam_ctx, mem_ctx,
271 True, /* this is a user password change */
272 True, /* run restriction tests */
275 if (!NT_STATUS_IS_OK(status)) {
276 ldb_transaction_cancel(sam_ctx);
280 /* The above call only setup the modifications, this actually
281 * makes the write to the database. */
282 ret = samdb_replace(sam_ctx, mem_ctx, mod);
284 ldb_transaction_cancel(sam_ctx);
285 return NT_STATUS_UNSUCCESSFUL;
288 /* And this confirms it in a transaction commit */
289 ret = ldb_transaction_commit(sam_ctx);
291 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
292 ldb_dn_linearize(mem_ctx, user_dn),
293 ldb_errstring(sam_ctx)));
294 return NT_STATUS_INTERNAL_DB_CORRUPTION;
302 samr_ChangePasswordUser3
304 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
306 struct samr_ChangePasswordUser3 *r)
310 uint32_t new_pass_len;
311 struct ldb_context *sam_ctx = NULL;
312 const struct ldb_dn *user_dn;
314 struct ldb_message **res, *mod;
315 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
316 struct samr_Password *nt_pwd, *lm_pwd;
317 DATA_BLOB nt_pwd_blob;
318 struct samr_DomInfo1 *dominfo = NULL;
319 struct samr_ChangeReject *reject = NULL;
320 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
321 uint8_t new_nt_hash[16], new_lm_hash[16];
322 struct samr_Password nt_verifier, lm_verifier;
326 if (r->in.nt_password == NULL ||
327 r->in.nt_verifier == NULL) {
328 return NT_STATUS_INVALID_PARAMETER;
331 /* To change a password we need to open as system */
332 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
333 if (sam_ctx == NULL) {
334 return NT_STATUS_INVALID_SYSTEM_SERVICE;
337 ret = ldb_transaction_start(sam_ctx);
339 talloc_free(sam_ctx);
340 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
341 return NT_STATUS_INTERNAL_DB_CORRUPTION;
344 /* we need the users dn and the domain dn (derived from the
345 user SID). We also need the current lm and nt password hashes
346 in order to decrypt the incoming passwords */
347 ret = gendb_search(sam_ctx,
348 mem_ctx, NULL, &res, attrs,
349 "(&(sAMAccountName=%s)(objectclass=user))",
350 r->in.account->string);
352 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
353 status = NT_STATUS_WRONG_PASSWORD;
357 user_dn = res[0]->dn;
359 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
360 if (!NT_STATUS_IS_OK(status) ) {
365 status = NT_STATUS_WRONG_PASSWORD;
369 /* decrypt the password we have been given */
370 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
371 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
372 data_blob_free(&nt_pwd_blob);
374 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
375 &new_pass_len, STR_UNICODE)) {
376 DEBUG(3,("samr: failed to decode password buffer\n"));
377 status = NT_STATUS_WRONG_PASSWORD;
381 if (r->in.nt_verifier == NULL) {
382 status = NT_STATUS_WRONG_PASSWORD;
386 /* check NT verifier */
387 E_md4hash(new_pass, new_nt_hash);
388 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
389 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
390 status = NT_STATUS_WRONG_PASSWORD;
394 /* check LM verifier */
395 if (lm_pwd && r->in.lm_verifier != NULL) {
396 E_deshash(new_pass, new_lm_hash);
397 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
398 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
399 status = NT_STATUS_WRONG_PASSWORD;
405 mod = ldb_msg_new(mem_ctx);
407 return NT_STATUS_NO_MEMORY;
410 mod->dn = ldb_dn_copy(mod, user_dn);
412 status = NT_STATUS_NO_MEMORY;
416 /* set the password on the user DN specified. This may fail
417 * due to password policies */
418 status = samdb_set_password(sam_ctx, mem_ctx,
422 True, /* this is a user password change */
423 True, /* run restriction tests */
426 if (!NT_STATUS_IS_OK(status)) {
430 /* The above call only setup the modifications, this actually
431 * makes the write to the database. */
432 ret = samdb_replace(sam_ctx, mem_ctx, mod);
434 status = NT_STATUS_UNSUCCESSFUL;
438 /* And this confirms it in a transaction commit */
439 ret = ldb_transaction_commit(sam_ctx);
441 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
442 ldb_dn_linearize(mem_ctx, user_dn),
443 ldb_errstring(sam_ctx)));
444 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
451 ldb_transaction_cancel(sam_ctx);
452 talloc_free(sam_ctx);
454 reject = talloc(mem_ctx, struct samr_ChangeReject);
455 r->out.dominfo = dominfo;
456 r->out.reject = reject;
458 if (reject == NULL) {
461 ZERO_STRUCTP(reject);
463 reject->reason = reason;
470 samr_ChangePasswordUser2
472 easy - just a subset of samr_ChangePasswordUser3
474 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
475 struct samr_ChangePasswordUser2 *r)
477 struct samr_ChangePasswordUser3 r2;
479 r2.in.server = r->in.server;
480 r2.in.account = r->in.account;
481 r2.in.nt_password = r->in.nt_password;
482 r2.in.nt_verifier = r->in.nt_verifier;
483 r2.in.lm_change = r->in.lm_change;
484 r2.in.lm_password = r->in.lm_password;
485 r2.in.lm_verifier = r->in.lm_verifier;
486 r2.in.password3 = NULL;
488 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
493 check that a password is sufficiently complex
495 static BOOL samdb_password_complexity_ok(const char *pass)
497 return check_password_quality(pass);
501 set the user password using plaintext, obeying any user or domain
502 password restrictions
504 note that this function doesn't actually store the result in the
505 database, it just fills in the "mod" structure with ldb modify
506 elements to setup the correct change when samdb_replace() is
507 called. This allows the caller to combine the change with other
508 changes (as is needed by some of the set user info levels)
510 The caller should probably have a transaction wrapping this
512 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
513 const struct ldb_dn *user_dn,
514 const struct ldb_dn *domain_dn,
515 struct ldb_message *mod,
516 const char *new_pass,
517 struct samr_Password *lmNewHash,
518 struct samr_Password *ntNewHash,
521 enum samr_RejectReason *reject_reason,
522 struct samr_DomInfo1 **_dominfo)
524 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
525 "ntPwdHistory", "unicodePwd",
526 "lmPwdHash", "ntPwdHash", "badPwdCount",
528 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
529 "maxPwdAge", "minPwdAge",
530 "minPwdLength", "pwdLastSet", NULL };
531 const char *unicodePwd;
534 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
535 uint_t userAccountControl, badPwdCount;
536 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
537 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
538 struct samr_Password local_lmNewHash, local_ntNewHash;
539 int lmPwdHistory_len, ntPwdHistory_len;
541 struct dom_sid *domain_sid;
542 struct ldb_message **res;
544 time_t now = time(NULL);
548 /* we need to know the time to compute password age */
549 unix_to_nt_time(&now_nt, now);
551 /* pull all the user parameters */
552 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
554 return NT_STATUS_INTERNAL_DB_CORRUPTION;
556 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
557 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
558 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
559 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
560 "lmPwdHistory", &lmPwdHistory);
561 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
562 "ntPwdHistory", &ntPwdHistory);
563 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
564 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
565 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
566 kvno = samdb_result_uint(res[0], "msDS-KeyVersionNumber", 0);
569 /* pull the domain parameters */
570 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
572 return NT_STATUS_NO_SUCH_DOMAIN;
575 /* work out the domain sid, and pull the domain from there */
576 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
577 if (domain_sid == NULL) {
578 return NT_STATUS_INTERNAL_DB_CORRUPTION;
581 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
583 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
585 return NT_STATUS_NO_SUCH_DOMAIN;
589 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
590 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
591 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
592 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
595 struct samr_DomInfo1 *dominfo;
596 /* on failure we need to fill in the reject reasons */
597 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
598 if (dominfo == NULL) {
599 return NT_STATUS_NO_MEMORY;
601 dominfo->min_password_length = minPwdLength;
602 dominfo->password_properties = pwdProperties;
603 dominfo->password_history_length = pwdHistoryLength;
604 dominfo->max_password_age = minPwdAge;
605 dominfo->min_password_age = minPwdAge;
610 /* check the various password restrictions */
611 if (restrictions && minPwdLength > strlen_m(new_pass)) {
613 *reject_reason = SAMR_REJECT_TOO_SHORT;
615 return NT_STATUS_PASSWORD_RESTRICTION;
618 /* possibly check password complexity */
619 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
620 !samdb_password_complexity_ok(new_pass)) {
622 *reject_reason = SAMR_REJECT_COMPLEXITY;
624 return NT_STATUS_PASSWORD_RESTRICTION;
627 /* compute the new nt and lm hashes */
628 if (E_deshash(new_pass, local_lmNewHash.hash)) {
629 lmNewHash = &local_lmNewHash;
631 E_md4hash(new_pass, local_ntNewHash.hash);
632 ntNewHash = &local_ntNewHash;
635 if (restrictions && user_change) {
636 /* are all password changes disallowed? */
637 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
639 *reject_reason = SAMR_REJECT_OTHER;
641 return NT_STATUS_PASSWORD_RESTRICTION;
644 /* can this user change password? */
645 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
647 *reject_reason = SAMR_REJECT_OTHER;
649 return NT_STATUS_PASSWORD_RESTRICTION;
652 /* yes, this is a minus. The ages are in negative 100nsec units! */
653 if (pwdLastSet - minPwdAge > now_nt) {
655 *reject_reason = SAMR_REJECT_OTHER;
657 return NT_STATUS_PASSWORD_RESTRICTION;
660 /* check the immediately past password */
661 if (pwdHistoryLength > 0) {
662 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
664 *reject_reason = SAMR_REJECT_COMPLEXITY;
666 return NT_STATUS_PASSWORD_RESTRICTION;
668 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
670 *reject_reason = SAMR_REJECT_COMPLEXITY;
672 return NT_STATUS_PASSWORD_RESTRICTION;
676 /* check the password history */
677 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
678 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
680 if (pwdHistoryLength > 0) {
681 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
683 *reject_reason = SAMR_REJECT_COMPLEXITY;
685 return NT_STATUS_PASSWORD_RESTRICTION;
687 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
689 *reject_reason = SAMR_REJECT_COMPLEXITY;
691 return NT_STATUS_PASSWORD_RESTRICTION;
693 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
695 *reject_reason = SAMR_REJECT_COMPLEXITY;
697 return NT_STATUS_PASSWORD_RESTRICTION;
701 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
702 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
704 *reject_reason = SAMR_REJECT_COMPLEXITY;
706 return NT_STATUS_PASSWORD_RESTRICTION;
709 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
710 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
712 *reject_reason = SAMR_REJECT_COMPLEXITY;
714 return NT_STATUS_PASSWORD_RESTRICTION;
719 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
721 /* the password is acceptable. Start forming the new fields */
723 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
725 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
729 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
731 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
734 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
735 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
736 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
737 "unicodePwd", new_pass));
739 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
742 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
744 CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
746 if (pwdHistoryLength == 0) {
747 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
748 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
752 /* store the password history */
753 new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
755 if (!new_lmPwdHistory) {
756 return NT_STATUS_NO_MEMORY;
758 new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
760 if (!new_ntPwdHistory) {
761 return NT_STATUS_NO_MEMORY;
763 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
764 new_lmPwdHistory[i+1] = lmPwdHistory[i];
766 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
767 new_ntPwdHistory[i+1] = ntPwdHistory[i];
770 /* Don't store 'long' passwords in the LM history,
771 but make sure to 'expire' one password off the other end */
773 new_lmPwdHistory[0] = *lmNewHash;
775 ZERO_STRUCT(new_lmPwdHistory[0]);
777 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
780 new_ntPwdHistory[0] = *ntNewHash;
782 ZERO_STRUCT(new_ntPwdHistory[0]);
784 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
786 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
791 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
799 set password via a samr_CryptPassword buffer
800 this will in the 'msg' with modify operations that will update the user
801 password when applied
803 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
805 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
807 struct ldb_message *msg,
808 struct samr_CryptPassword *pwbuf)
812 uint32_t new_pass_len;
813 DATA_BLOB session_key = data_blob(NULL, 0);
815 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
816 if (!NT_STATUS_IS_OK(nt_status)) {
820 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
822 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
823 &new_pass_len, STR_UNICODE)) {
824 DEBUG(3,("samr: failed to decode password buffer\n"));
825 return NT_STATUS_WRONG_PASSWORD;
828 /* set the password - samdb needs to know both the domain and user DNs,
829 so the domain password policy can be used */
830 return samdb_set_password(sam_ctx, mem_ctx,
831 account_dn, domain_dn,
834 False, /* This is a password set, not change */
835 True, /* run restriction tests */
841 set password via a samr_CryptPasswordEx buffer
842 this will in the 'msg' with modify operations that will update the user
843 password when applied
845 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
846 struct ldb_context *sam_ctx,
847 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
849 struct ldb_message *msg,
850 struct samr_CryptPasswordEx *pwbuf)
854 uint32_t new_pass_len;
855 DATA_BLOB co_session_key;
856 DATA_BLOB session_key = data_blob(NULL, 0);
857 struct MD5Context ctx;
859 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
860 if (!NT_STATUS_IS_OK(nt_status)) {
864 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
865 if (!co_session_key.data) {
866 return NT_STATUS_NO_MEMORY;
870 MD5Update(&ctx, &pwbuf->data[516], 16);
871 MD5Update(&ctx, session_key.data, session_key.length);
872 MD5Final(co_session_key.data, &ctx);
874 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
876 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
877 &new_pass_len, STR_UNICODE)) {
878 DEBUG(3,("samr: failed to decode password buffer\n"));
879 return NT_STATUS_WRONG_PASSWORD;
882 /* set the password - samdb needs to know both the domain and user DNs,
883 so the domain password policy can be used */
884 return samdb_set_password(sam_ctx, mem_ctx,
885 account_dn, domain_dn,
888 False, /* This is a password set, not change */
889 True, /* run restriction tests */
894 set the user password using plaintext, obeying any user or domain
895 password restrictions
897 This wrapper function takes a SID as input, rather than a user DN,
898 and actually performs the password change
901 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
902 const struct dom_sid *user_sid,
903 const char *new_pass,
904 struct samr_Password *lmNewHash,
905 struct samr_Password *ntNewHash,
908 enum samr_RejectReason *reject_reason,
909 struct samr_DomInfo1 **_dominfo)
912 struct ldb_dn *user_dn;
913 struct ldb_message *msg;
916 ret = ldb_transaction_start(ctx);
918 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
919 return NT_STATUS_INTERNAL_DB_CORRUPTION;
922 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
923 "(&(objectSid=%s)(objectClass=user))",
924 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
926 ldb_transaction_cancel(ctx);
927 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
928 dom_sid_string(mem_ctx, user_sid)));
929 return NT_STATUS_NO_SUCH_USER;
932 msg = ldb_msg_new(mem_ctx);
934 ldb_transaction_cancel(ctx);
935 return NT_STATUS_NO_MEMORY;
938 msg->dn = ldb_dn_copy(msg, user_dn);
940 ldb_transaction_cancel(ctx);
941 return NT_STATUS_NO_MEMORY;
944 nt_status = samdb_set_password(ctx, mem_ctx,
947 lmNewHash, ntNewHash,
948 user_change, /* This is a password set, not change */
949 restrictions, /* run restriction tests */
950 reject_reason, _dominfo);
951 if (!NT_STATUS_IS_OK(nt_status)) {
952 ldb_transaction_cancel(ctx);
956 /* modify the samdb record */
957 ret = samdb_replace(ctx, mem_ctx, msg);
959 ldb_transaction_cancel(ctx);
960 return NT_STATUS_ACCESS_DENIED;
963 ret = ldb_transaction_commit(ctx);
965 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
966 ldb_dn_linearize(mem_ctx, msg->dn),
967 ldb_errstring(ctx)));
968 return NT_STATUS_INTERNAL_DB_CORRUPTION;