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 "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"
31 #include "libcli/ldap/ldap.h"
32 #include "dsdb/samdb/samdb.h"
33 #include "auth/auth.h"
34 #include "rpc_server/samr/proto.h"
35 #include "libcli/auth/libcli_auth.h"
39 samr_ChangePasswordUser
41 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
42 struct samr_ChangePasswordUser *r)
44 struct dcesrv_handle *h;
45 struct samr_account_state *a_state;
46 struct ldb_context *sam_ctx;
47 struct ldb_message **res, *msg;
49 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
50 struct samr_Password *lm_pwd, *nt_pwd;
51 NTSTATUS status = NT_STATUS_OK;
52 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
54 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
58 /* basic sanity checking on parameters. Do this before any database ops */
59 if (!r->in.lm_present || !r->in.nt_present ||
60 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
61 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
62 /* we should really handle a change with lm not
64 return NT_STATUS_INVALID_PARAMETER_MIX;
66 if (!r->in.cross1_present || !r->in.nt_cross) {
67 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
69 if (!r->in.cross2_present || !r->in.lm_cross) {
70 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
73 /* To change a password we need to open as system */
74 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
75 if (sam_ctx == NULL) {
76 return NT_STATUS_INVALID_SYSTEM_SERVICE;
79 ret = ldb_transaction_start(sam_ctx);
81 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
82 return NT_STATUS_TRANSACTION_ABORTED;
85 /* fetch the old hashes */
86 ret = gendb_search_dn(sam_ctx, mem_ctx,
87 a_state->account_dn, &res, attrs);
89 ldb_transaction_cancel(sam_ctx);
90 return NT_STATUS_WRONG_PASSWORD;
94 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
95 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
96 ldb_transaction_cancel(sam_ctx);
97 return NT_STATUS_WRONG_PASSWORD;
100 /* decrypt and check the new lm hash */
101 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
102 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
103 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
104 ldb_transaction_cancel(sam_ctx);
105 return NT_STATUS_WRONG_PASSWORD;
108 /* decrypt and check the new nt hash */
109 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
110 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
111 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
112 ldb_transaction_cancel(sam_ctx);
113 return NT_STATUS_WRONG_PASSWORD;
116 /* check the nt cross hash */
117 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
118 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
119 ldb_transaction_cancel(sam_ctx);
120 return NT_STATUS_WRONG_PASSWORD;
123 /* check the lm cross hash */
124 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
125 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
126 ldb_transaction_cancel(sam_ctx);
127 return NT_STATUS_WRONG_PASSWORD;
130 msg = ldb_msg_new(mem_ctx);
132 ldb_transaction_cancel(sam_ctx);
133 return NT_STATUS_NO_MEMORY;
136 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
138 ldb_transaction_cancel(sam_ctx);
139 return NT_STATUS_NO_MEMORY;
142 /* set the password on the user DN specified. This may fail
143 * due to password policies */
144 status = samdb_set_password(sam_ctx, mem_ctx,
145 a_state->account_dn, a_state->domain_state->domain_dn,
146 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
147 True, /* this is a user password change */
148 True, /* run restriction tests */
151 if (!NT_STATUS_IS_OK(status)) {
152 ldb_transaction_cancel(sam_ctx);
156 /* The above call only setup the modifications, this actually
157 * makes the write to the database. */
158 ret = samdb_replace(sam_ctx, mem_ctx, msg);
160 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
161 ldb_dn_linearize(mem_ctx, a_state->account_dn),
162 ldb_errstring(sam_ctx)));
163 ldb_transaction_cancel(sam_ctx);
164 return NT_STATUS_INTERNAL_DB_CORRUPTION;
167 /* And this confirms it in a transaction commit */
168 ret = ldb_transaction_commit(sam_ctx);
170 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
171 ldb_dn_linearize(mem_ctx, a_state->account_dn),
172 ldb_errstring(sam_ctx)));
173 return NT_STATUS_TRANSACTION_ABORTED;
180 samr_OemChangePasswordUser2
182 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
183 struct samr_OemChangePasswordUser2 *r)
187 uint32_t new_pass_len;
188 struct samr_CryptPassword *pwbuf = r->in.password;
189 struct ldb_context *sam_ctx;
190 const struct ldb_dn *user_dn;
192 struct ldb_message **res, *mod;
193 const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
194 struct samr_Password *lm_pwd;
195 DATA_BLOB lm_pwd_blob;
196 uint8_t new_lm_hash[16];
197 struct samr_Password lm_verifier;
200 return NT_STATUS_WRONG_PASSWORD;
203 /* To change a password we need to open as system */
204 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
205 if (sam_ctx == NULL) {
206 return NT_STATUS_INVALID_SYSTEM_SERVICE;
209 ret = ldb_transaction_start(sam_ctx);
211 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
212 return NT_STATUS_TRANSACTION_ABORTED;
215 /* we need the users dn and the domain dn (derived from the
216 user SID). We also need the current lm password hash in
217 order to decrypt the incoming password */
218 ret = gendb_search(sam_ctx,
219 mem_ctx, NULL, &res, attrs,
220 "(&(sAMAccountName=%s)(objectclass=user))",
221 r->in.account->string);
223 ldb_transaction_cancel(sam_ctx);
224 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
225 return NT_STATUS_WRONG_PASSWORD;
228 user_dn = res[0]->dn;
230 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
231 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
232 ldb_transaction_cancel(sam_ctx);
233 return NT_STATUS_WRONG_PASSWORD;
236 /* decrypt the password we have been given */
237 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
238 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
239 data_blob_free(&lm_pwd_blob);
241 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
242 &new_pass_len, STR_ASCII)) {
243 ldb_transaction_cancel(sam_ctx);
244 DEBUG(3,("samr: failed to decode password buffer\n"));
245 return NT_STATUS_WRONG_PASSWORD;
248 /* check LM verifier */
249 if (lm_pwd == NULL || r->in.hash == NULL) {
250 ldb_transaction_cancel(sam_ctx);
251 return NT_STATUS_WRONG_PASSWORD;
254 E_deshash(new_pass, new_lm_hash);
255 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
256 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
257 ldb_transaction_cancel(sam_ctx);
258 return NT_STATUS_WRONG_PASSWORD;
261 mod = ldb_msg_new(mem_ctx);
263 ldb_transaction_cancel(sam_ctx);
264 return NT_STATUS_NO_MEMORY;
267 mod->dn = ldb_dn_copy(mod, user_dn);
269 ldb_transaction_cancel(sam_ctx);
270 return NT_STATUS_NO_MEMORY;
273 /* set the password on the user DN specified. This may fail
274 * due to password policies */
275 status = samdb_set_password(sam_ctx, mem_ctx,
279 True, /* this is a user password change */
280 True, /* run restriction tests */
283 if (!NT_STATUS_IS_OK(status)) {
284 ldb_transaction_cancel(sam_ctx);
288 /* The above call only setup the modifications, this actually
289 * makes the write to the database. */
290 ret = samdb_replace(sam_ctx, mem_ctx, mod);
292 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
293 ldb_dn_linearize(mem_ctx, user_dn),
294 ldb_errstring(sam_ctx)));
295 ldb_transaction_cancel(sam_ctx);
296 return NT_STATUS_INTERNAL_DB_CORRUPTION;
299 /* And this confirms it in a transaction commit */
300 ret = ldb_transaction_commit(sam_ctx);
302 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
303 ldb_dn_linearize(mem_ctx, user_dn),
304 ldb_errstring(sam_ctx)));
305 return NT_STATUS_TRANSACTION_ABORTED;
313 samr_ChangePasswordUser3
315 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
317 struct samr_ChangePasswordUser3 *r)
321 uint32_t new_pass_len;
322 struct ldb_context *sam_ctx = NULL;
323 const struct ldb_dn *user_dn;
325 struct ldb_message **res, *mod;
326 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
327 struct samr_Password *nt_pwd, *lm_pwd;
328 DATA_BLOB nt_pwd_blob;
329 struct samr_DomInfo1 *dominfo = NULL;
330 struct samr_ChangeReject *reject = NULL;
331 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
332 uint8_t new_nt_hash[16], new_lm_hash[16];
333 struct samr_Password nt_verifier, lm_verifier;
337 if (r->in.nt_password == NULL ||
338 r->in.nt_verifier == NULL) {
339 return NT_STATUS_INVALID_PARAMETER;
342 /* To change a password we need to open as system */
343 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
344 if (sam_ctx == NULL) {
345 return NT_STATUS_INVALID_SYSTEM_SERVICE;
348 ret = ldb_transaction_start(sam_ctx);
350 talloc_free(sam_ctx);
351 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
352 return NT_STATUS_TRANSACTION_ABORTED;
355 /* we need the users dn and the domain dn (derived from the
356 user SID). We also need the current lm and nt password hashes
357 in order to decrypt the incoming passwords */
358 ret = gendb_search(sam_ctx,
359 mem_ctx, NULL, &res, attrs,
360 "(&(sAMAccountName=%s)(objectclass=user))",
361 r->in.account->string);
363 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
364 status = NT_STATUS_WRONG_PASSWORD;
368 user_dn = res[0]->dn;
370 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
371 if (!NT_STATUS_IS_OK(status) ) {
376 status = NT_STATUS_WRONG_PASSWORD;
380 /* decrypt the password we have been given */
381 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
382 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
383 data_blob_free(&nt_pwd_blob);
385 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
386 &new_pass_len, STR_UNICODE)) {
387 DEBUG(3,("samr: failed to decode password buffer\n"));
388 status = NT_STATUS_WRONG_PASSWORD;
392 if (r->in.nt_verifier == NULL) {
393 status = NT_STATUS_WRONG_PASSWORD;
397 /* check NT verifier */
398 E_md4hash(new_pass, new_nt_hash);
399 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
400 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
401 status = NT_STATUS_WRONG_PASSWORD;
405 /* check LM verifier */
406 if (lm_pwd && r->in.lm_verifier != NULL) {
407 E_deshash(new_pass, new_lm_hash);
408 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
409 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
410 status = NT_STATUS_WRONG_PASSWORD;
416 mod = ldb_msg_new(mem_ctx);
418 return NT_STATUS_NO_MEMORY;
421 mod->dn = ldb_dn_copy(mod, user_dn);
423 status = NT_STATUS_NO_MEMORY;
427 /* set the password on the user DN specified. This may fail
428 * due to password policies */
429 status = samdb_set_password(sam_ctx, mem_ctx,
433 True, /* this is a user password change */
434 True, /* run restriction tests */
437 if (!NT_STATUS_IS_OK(status)) {
441 /* The above call only setup the modifications, this actually
442 * makes the write to the database. */
443 ret = samdb_replace(sam_ctx, mem_ctx, mod);
445 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
446 ldb_dn_linearize(mem_ctx, user_dn),
447 ldb_errstring(sam_ctx)));
448 status = NT_STATUS_UNSUCCESSFUL;
452 /* And this confirms it in a transaction commit */
453 ret = ldb_transaction_commit(sam_ctx);
455 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
456 ldb_dn_linearize(mem_ctx, user_dn),
457 ldb_errstring(sam_ctx)));
458 status = NT_STATUS_TRANSACTION_ABORTED;
465 ldb_transaction_cancel(sam_ctx);
466 talloc_free(sam_ctx);
468 reject = talloc(mem_ctx, struct samr_ChangeReject);
469 r->out.dominfo = dominfo;
470 r->out.reject = reject;
472 if (reject == NULL) {
475 ZERO_STRUCTP(reject);
477 reject->reason = reason;
484 samr_ChangePasswordUser2
486 easy - just a subset of samr_ChangePasswordUser3
488 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
489 struct samr_ChangePasswordUser2 *r)
491 struct samr_ChangePasswordUser3 r2;
493 r2.in.server = r->in.server;
494 r2.in.account = r->in.account;
495 r2.in.nt_password = r->in.nt_password;
496 r2.in.nt_verifier = r->in.nt_verifier;
497 r2.in.lm_change = r->in.lm_change;
498 r2.in.lm_password = r->in.lm_password;
499 r2.in.lm_verifier = r->in.lm_verifier;
500 r2.in.password3 = NULL;
502 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
507 check that a password is sufficiently complex
509 static BOOL samdb_password_complexity_ok(const char *pass)
511 return check_password_quality(pass);
515 set the user password using plaintext, obeying any user or domain
516 password restrictions
518 note that this function doesn't actually store the result in the
519 database, it just fills in the "mod" structure with ldb modify
520 elements to setup the correct change when samdb_replace() is
521 called. This allows the caller to combine the change with other
522 changes (as is needed by some of the set user info levels)
524 The caller should probably have a transaction wrapping this
526 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
527 const struct ldb_dn *user_dn,
528 const struct ldb_dn *domain_dn,
529 struct ldb_message *mod,
530 const char *new_pass,
531 struct samr_Password *lmNewHash,
532 struct samr_Password *ntNewHash,
535 enum samr_RejectReason *reject_reason,
536 struct samr_DomInfo1 **_dominfo)
538 const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory",
540 "lmPwdHash", "ntPwdHash",
542 "pwdLastSet", NULL };
543 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
544 "maxPwdAge", "minPwdAge",
545 "minPwdLength", NULL };
548 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
549 uint_t userAccountControl;
550 struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
551 struct samr_Password local_lmNewHash, local_ntNewHash;
552 int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
553 struct dom_sid *domain_sid;
554 struct ldb_message **res;
556 time_t now = time(NULL);
560 /* we need to know the time to compute password age */
561 unix_to_nt_time(&now_nt, now);
563 /* pull all the user parameters */
564 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
566 return NT_STATUS_INTERNAL_DB_CORRUPTION;
568 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
569 sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
570 "sambaLMPwdHistory", &sambaLMPwdHistory);
571 sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
572 "sambaNTPwdHistory", &sambaNTPwdHistory);
573 lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
574 ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
575 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
578 /* pull the domain parameters */
579 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
581 return NT_STATUS_NO_SUCH_DOMAIN;
584 /* work out the domain sid, and pull the domain from there */
585 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
586 if (domain_sid == NULL) {
587 return NT_STATUS_INTERNAL_DB_CORRUPTION;
590 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
592 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
594 return NT_STATUS_NO_SUCH_DOMAIN;
598 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
599 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
600 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
601 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
604 struct samr_DomInfo1 *dominfo;
605 /* on failure we need to fill in the reject reasons */
606 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
607 if (dominfo == NULL) {
608 return NT_STATUS_NO_MEMORY;
610 dominfo->min_password_length = minPwdLength;
611 dominfo->password_properties = pwdProperties;
612 dominfo->password_history_length = pwdHistoryLength;
613 dominfo->max_password_age = minPwdAge;
614 dominfo->min_password_age = minPwdAge;
619 /* check the various password restrictions */
620 if (restrictions && minPwdLength > strlen_m(new_pass)) {
622 *reject_reason = SAMR_REJECT_TOO_SHORT;
624 return NT_STATUS_PASSWORD_RESTRICTION;
627 /* possibly check password complexity */
628 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
629 !samdb_password_complexity_ok(new_pass)) {
631 *reject_reason = SAMR_REJECT_COMPLEXITY;
633 return NT_STATUS_PASSWORD_RESTRICTION;
636 /* compute the new nt and lm hashes */
637 if (E_deshash(new_pass, local_lmNewHash.hash)) {
638 lmNewHash = &local_lmNewHash;
640 E_md4hash(new_pass, local_ntNewHash.hash);
641 ntNewHash = &local_ntNewHash;
644 if (restrictions && user_change) {
645 /* are all password changes disallowed? */
646 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
648 *reject_reason = SAMR_REJECT_OTHER;
650 return NT_STATUS_PASSWORD_RESTRICTION;
653 /* can this user change password? */
654 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
656 *reject_reason = SAMR_REJECT_OTHER;
658 return NT_STATUS_PASSWORD_RESTRICTION;
661 /* yes, this is a minus. The ages are in negative 100nsec units! */
662 if (pwdLastSet - minPwdAge > now_nt) {
664 *reject_reason = SAMR_REJECT_OTHER;
666 return NT_STATUS_PASSWORD_RESTRICTION;
669 /* check the immediately past password */
670 if (pwdHistoryLength > 0) {
671 if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
673 *reject_reason = SAMR_REJECT_COMPLEXITY;
675 return NT_STATUS_PASSWORD_RESTRICTION;
677 if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
679 *reject_reason = SAMR_REJECT_COMPLEXITY;
681 return NT_STATUS_PASSWORD_RESTRICTION;
685 /* check the password history */
686 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
687 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
689 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
690 if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
692 *reject_reason = SAMR_REJECT_COMPLEXITY;
694 return NT_STATUS_PASSWORD_RESTRICTION;
697 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
698 if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
700 *reject_reason = SAMR_REJECT_COMPLEXITY;
702 return NT_STATUS_PASSWORD_RESTRICTION;
707 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
709 /* the password is acceptable. Start forming the new fields */
711 /* if we know the cleartext, then only set it.
712 * Modules in ldb will set all the appropriate
714 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
715 "sambaPassword", new_pass));
717 /* We don't have the cleartext, so delete the old one
718 * and set what we have of the hashes */
719 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
722 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
724 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
728 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
730 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
738 set password via a samr_CryptPassword buffer
739 this will in the 'msg' with modify operations that will update the user
740 password when applied
742 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
744 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
746 struct ldb_message *msg,
747 struct samr_CryptPassword *pwbuf)
751 uint32_t new_pass_len;
752 DATA_BLOB session_key = data_blob(NULL, 0);
754 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
755 if (!NT_STATUS_IS_OK(nt_status)) {
759 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
761 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
762 &new_pass_len, STR_UNICODE)) {
763 DEBUG(3,("samr: failed to decode password buffer\n"));
764 return NT_STATUS_WRONG_PASSWORD;
767 /* set the password - samdb needs to know both the domain and user DNs,
768 so the domain password policy can be used */
769 return samdb_set_password(sam_ctx, mem_ctx,
770 account_dn, domain_dn,
773 False, /* This is a password set, not change */
774 True, /* run restriction tests */
780 set password via a samr_CryptPasswordEx buffer
781 this will in the 'msg' with modify operations that will update the user
782 password when applied
784 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
785 struct ldb_context *sam_ctx,
786 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
788 struct ldb_message *msg,
789 struct samr_CryptPasswordEx *pwbuf)
793 uint32_t new_pass_len;
794 DATA_BLOB co_session_key;
795 DATA_BLOB session_key = data_blob(NULL, 0);
796 struct MD5Context ctx;
798 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
799 if (!NT_STATUS_IS_OK(nt_status)) {
803 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
804 if (!co_session_key.data) {
805 return NT_STATUS_NO_MEMORY;
809 MD5Update(&ctx, &pwbuf->data[516], 16);
810 MD5Update(&ctx, session_key.data, session_key.length);
811 MD5Final(co_session_key.data, &ctx);
813 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
815 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
816 &new_pass_len, STR_UNICODE)) {
817 DEBUG(3,("samr: failed to decode password buffer\n"));
818 return NT_STATUS_WRONG_PASSWORD;
821 /* set the password - samdb needs to know both the domain and user DNs,
822 so the domain password policy can be used */
823 return samdb_set_password(sam_ctx, mem_ctx,
824 account_dn, domain_dn,
827 False, /* This is a password set, not change */
828 True, /* run restriction tests */
833 set the user password using plaintext, obeying any user or domain
834 password restrictions
836 This wrapper function takes a SID as input, rather than a user DN,
837 and actually performs the password change
840 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
841 const struct dom_sid *user_sid,
842 const char *new_pass,
843 struct samr_Password *lmNewHash,
844 struct samr_Password *ntNewHash,
847 enum samr_RejectReason *reject_reason,
848 struct samr_DomInfo1 **_dominfo)
851 struct ldb_dn *user_dn;
852 struct ldb_message *msg;
855 ret = ldb_transaction_start(ctx);
857 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
858 return NT_STATUS_TRANSACTION_ABORTED;
861 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
862 "(&(objectSid=%s)(objectClass=user))",
863 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
865 ldb_transaction_cancel(ctx);
866 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
867 dom_sid_string(mem_ctx, user_sid)));
868 return NT_STATUS_NO_SUCH_USER;
871 msg = ldb_msg_new(mem_ctx);
873 ldb_transaction_cancel(ctx);
874 return NT_STATUS_NO_MEMORY;
877 msg->dn = ldb_dn_copy(msg, user_dn);
879 ldb_transaction_cancel(ctx);
880 return NT_STATUS_NO_MEMORY;
883 nt_status = samdb_set_password(ctx, mem_ctx,
886 lmNewHash, ntNewHash,
887 user_change, /* This is a password set, not change */
888 restrictions, /* run restriction tests */
889 reject_reason, _dominfo);
890 if (!NT_STATUS_IS_OK(nt_status)) {
891 ldb_transaction_cancel(ctx);
895 /* modify the samdb record */
896 ret = samdb_replace(ctx, mem_ctx, msg);
898 ldb_transaction_cancel(ctx);
899 return NT_STATUS_ACCESS_DENIED;
902 ret = ldb_transaction_commit(ctx);
904 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
905 ldb_dn_linearize(mem_ctx, msg->dn),
906 ldb_errstring(ctx)));
907 return NT_STATUS_TRANSACTION_ABORTED;