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 "librpc/gen_ndr/ndr_security.h"
29 #include "system/time.h"
30 #include "lib/crypto/crypto.h"
32 #include "libcli/ldap/ldap.h"
33 #include "dsdb/samdb/samdb.h"
34 #include "auth/auth.h"
35 #include "rpc_server/samr/proto.h"
36 #include "libcli/auth/libcli_auth.h"
40 samr_ChangePasswordUser
42 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
43 struct samr_ChangePasswordUser *r)
45 struct dcesrv_handle *h;
46 struct samr_account_state *a_state;
47 struct ldb_context *sam_ctx;
48 struct ldb_message **res, *msg;
50 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
51 struct samr_Password *lm_pwd, *nt_pwd;
52 NTSTATUS status = NT_STATUS_OK;
53 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
55 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
59 /* basic sanity checking on parameters. Do this before any database ops */
60 if (!r->in.lm_present || !r->in.nt_present ||
61 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
62 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
63 /* we should really handle a change with lm not
65 return NT_STATUS_INVALID_PARAMETER_MIX;
67 if (!r->in.cross1_present || !r->in.nt_cross) {
68 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
70 if (!r->in.cross2_present || !r->in.lm_cross) {
71 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
74 /* To change a password we need to open as system */
75 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
76 if (sam_ctx == NULL) {
77 return NT_STATUS_INVALID_SYSTEM_SERVICE;
80 ret = ldb_transaction_start(sam_ctx);
82 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
83 return NT_STATUS_TRANSACTION_ABORTED;
86 /* fetch the old hashes */
87 ret = gendb_search_dn(sam_ctx, mem_ctx,
88 a_state->account_dn, &res, attrs);
90 ldb_transaction_cancel(sam_ctx);
91 return NT_STATUS_WRONG_PASSWORD;
95 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
96 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
97 ldb_transaction_cancel(sam_ctx);
98 return NT_STATUS_WRONG_PASSWORD;
101 /* decrypt and check the new lm hash */
102 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
103 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
104 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
105 ldb_transaction_cancel(sam_ctx);
106 return NT_STATUS_WRONG_PASSWORD;
109 /* decrypt and check the new nt hash */
110 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
111 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
112 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
113 ldb_transaction_cancel(sam_ctx);
114 return NT_STATUS_WRONG_PASSWORD;
117 /* check the nt cross hash */
118 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
119 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
120 ldb_transaction_cancel(sam_ctx);
121 return NT_STATUS_WRONG_PASSWORD;
124 /* check the lm cross hash */
125 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
126 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
127 ldb_transaction_cancel(sam_ctx);
128 return NT_STATUS_WRONG_PASSWORD;
131 msg = ldb_msg_new(mem_ctx);
133 ldb_transaction_cancel(sam_ctx);
134 return NT_STATUS_NO_MEMORY;
137 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
139 ldb_transaction_cancel(sam_ctx);
140 return NT_STATUS_NO_MEMORY;
143 /* set the password on the user DN specified. This may fail
144 * due to password policies */
145 status = samdb_set_password(sam_ctx, mem_ctx,
146 a_state->account_dn, a_state->domain_state->domain_dn,
147 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
148 True, /* this is a user password change */
149 True, /* run restriction tests */
152 if (!NT_STATUS_IS_OK(status)) {
153 ldb_transaction_cancel(sam_ctx);
157 /* The above call only setup the modifications, this actually
158 * makes the write to the database. */
159 ret = samdb_replace(sam_ctx, mem_ctx, msg);
161 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
162 ldb_dn_linearize(mem_ctx, a_state->account_dn),
163 ldb_errstring(sam_ctx)));
164 ldb_transaction_cancel(sam_ctx);
165 return NT_STATUS_INTERNAL_DB_CORRUPTION;
168 /* And this confirms it in a transaction commit */
169 ret = ldb_transaction_commit(sam_ctx);
171 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
172 ldb_dn_linearize(mem_ctx, a_state->account_dn),
173 ldb_errstring(sam_ctx)));
174 return NT_STATUS_TRANSACTION_ABORTED;
181 samr_OemChangePasswordUser2
183 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
184 struct samr_OemChangePasswordUser2 *r)
188 uint32_t new_pass_len;
189 struct samr_CryptPassword *pwbuf = r->in.password;
190 struct ldb_context *sam_ctx;
191 const struct ldb_dn *user_dn;
193 struct ldb_message **res, *mod;
194 const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
195 struct samr_Password *lm_pwd;
196 DATA_BLOB lm_pwd_blob;
197 uint8_t new_lm_hash[16];
198 struct samr_Password lm_verifier;
201 return NT_STATUS_WRONG_PASSWORD;
204 /* To change a password we need to open as system */
205 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
206 if (sam_ctx == NULL) {
207 return NT_STATUS_INVALID_SYSTEM_SERVICE;
210 ret = ldb_transaction_start(sam_ctx);
212 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
213 return NT_STATUS_TRANSACTION_ABORTED;
216 /* we need the users dn and the domain dn (derived from the
217 user SID). We also need the current lm password hash in
218 order to decrypt the incoming password */
219 ret = gendb_search(sam_ctx,
220 mem_ctx, NULL, &res, attrs,
221 "(&(sAMAccountName=%s)(objectclass=user))",
222 r->in.account->string);
224 ldb_transaction_cancel(sam_ctx);
225 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
226 return NT_STATUS_WRONG_PASSWORD;
229 user_dn = res[0]->dn;
231 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
232 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
233 ldb_transaction_cancel(sam_ctx);
234 return NT_STATUS_WRONG_PASSWORD;
237 /* decrypt the password we have been given */
238 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
239 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
240 data_blob_free(&lm_pwd_blob);
242 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
243 &new_pass_len, STR_ASCII)) {
244 ldb_transaction_cancel(sam_ctx);
245 DEBUG(3,("samr: failed to decode password buffer\n"));
246 return NT_STATUS_WRONG_PASSWORD;
249 /* check LM verifier */
250 if (lm_pwd == NULL || r->in.hash == NULL) {
251 ldb_transaction_cancel(sam_ctx);
252 return NT_STATUS_WRONG_PASSWORD;
255 E_deshash(new_pass, new_lm_hash);
256 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
257 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
258 ldb_transaction_cancel(sam_ctx);
259 return NT_STATUS_WRONG_PASSWORD;
262 mod = ldb_msg_new(mem_ctx);
264 ldb_transaction_cancel(sam_ctx);
265 return NT_STATUS_NO_MEMORY;
268 mod->dn = ldb_dn_copy(mod, user_dn);
270 ldb_transaction_cancel(sam_ctx);
271 return NT_STATUS_NO_MEMORY;
274 /* set the password on the user DN specified. This may fail
275 * due to password policies */
276 status = samdb_set_password(sam_ctx, mem_ctx,
280 True, /* this is a user password change */
281 True, /* run restriction tests */
284 if (!NT_STATUS_IS_OK(status)) {
285 ldb_transaction_cancel(sam_ctx);
289 /* The above call only setup the modifications, this actually
290 * makes the write to the database. */
291 ret = samdb_replace(sam_ctx, mem_ctx, mod);
293 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
294 ldb_dn_linearize(mem_ctx, user_dn),
295 ldb_errstring(sam_ctx)));
296 ldb_transaction_cancel(sam_ctx);
297 return NT_STATUS_INTERNAL_DB_CORRUPTION;
300 /* And this confirms it in a transaction commit */
301 ret = ldb_transaction_commit(sam_ctx);
303 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
304 ldb_dn_linearize(mem_ctx, user_dn),
305 ldb_errstring(sam_ctx)));
306 return NT_STATUS_TRANSACTION_ABORTED;
314 samr_ChangePasswordUser3
316 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
318 struct samr_ChangePasswordUser3 *r)
322 uint32_t new_pass_len;
323 struct ldb_context *sam_ctx = NULL;
324 const struct ldb_dn *user_dn;
326 struct ldb_message **res, *mod;
327 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
328 struct samr_Password *nt_pwd, *lm_pwd;
329 DATA_BLOB nt_pwd_blob;
330 struct samr_DomInfo1 *dominfo = NULL;
331 struct samr_ChangeReject *reject = NULL;
332 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
333 uint8_t new_nt_hash[16], new_lm_hash[16];
334 struct samr_Password nt_verifier, lm_verifier;
338 if (r->in.nt_password == NULL ||
339 r->in.nt_verifier == NULL) {
340 return NT_STATUS_INVALID_PARAMETER;
343 /* To change a password we need to open as system */
344 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
345 if (sam_ctx == NULL) {
346 return NT_STATUS_INVALID_SYSTEM_SERVICE;
349 ret = ldb_transaction_start(sam_ctx);
351 talloc_free(sam_ctx);
352 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
353 return NT_STATUS_TRANSACTION_ABORTED;
356 /* we need the users dn and the domain dn (derived from the
357 user SID). We also need the current lm and nt password hashes
358 in order to decrypt the incoming passwords */
359 ret = gendb_search(sam_ctx,
360 mem_ctx, NULL, &res, attrs,
361 "(&(sAMAccountName=%s)(objectclass=user))",
362 r->in.account->string);
364 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
365 status = NT_STATUS_WRONG_PASSWORD;
369 user_dn = res[0]->dn;
371 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
372 if (!NT_STATUS_IS_OK(status) ) {
377 status = NT_STATUS_WRONG_PASSWORD;
381 /* decrypt the password we have been given */
382 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
383 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
384 data_blob_free(&nt_pwd_blob);
386 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
387 &new_pass_len, STR_UNICODE)) {
388 DEBUG(3,("samr: failed to decode password buffer\n"));
389 status = NT_STATUS_WRONG_PASSWORD;
393 if (r->in.nt_verifier == NULL) {
394 status = NT_STATUS_WRONG_PASSWORD;
398 /* check NT verifier */
399 E_md4hash(new_pass, new_nt_hash);
400 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
401 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
402 status = NT_STATUS_WRONG_PASSWORD;
406 /* check LM verifier */
407 if (lm_pwd && r->in.lm_verifier != NULL) {
408 E_deshash(new_pass, new_lm_hash);
409 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
410 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
411 status = NT_STATUS_WRONG_PASSWORD;
417 mod = ldb_msg_new(mem_ctx);
419 return NT_STATUS_NO_MEMORY;
422 mod->dn = ldb_dn_copy(mod, user_dn);
424 status = NT_STATUS_NO_MEMORY;
428 /* set the password on the user DN specified. This may fail
429 * due to password policies */
430 status = samdb_set_password(sam_ctx, mem_ctx,
434 True, /* this is a user password change */
435 True, /* run restriction tests */
438 if (!NT_STATUS_IS_OK(status)) {
442 /* The above call only setup the modifications, this actually
443 * makes the write to the database. */
444 ret = samdb_replace(sam_ctx, mem_ctx, mod);
446 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
447 ldb_dn_linearize(mem_ctx, user_dn),
448 ldb_errstring(sam_ctx)));
449 status = NT_STATUS_UNSUCCESSFUL;
453 /* And this confirms it in a transaction commit */
454 ret = ldb_transaction_commit(sam_ctx);
456 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
457 ldb_dn_linearize(mem_ctx, user_dn),
458 ldb_errstring(sam_ctx)));
459 status = NT_STATUS_TRANSACTION_ABORTED;
466 ldb_transaction_cancel(sam_ctx);
467 talloc_free(sam_ctx);
469 reject = talloc(mem_ctx, struct samr_ChangeReject);
470 r->out.dominfo = dominfo;
471 r->out.reject = reject;
473 if (reject == NULL) {
476 ZERO_STRUCTP(reject);
478 reject->reason = reason;
485 samr_ChangePasswordUser2
487 easy - just a subset of samr_ChangePasswordUser3
489 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
490 struct samr_ChangePasswordUser2 *r)
492 struct samr_ChangePasswordUser3 r2;
494 r2.in.server = r->in.server;
495 r2.in.account = r->in.account;
496 r2.in.nt_password = r->in.nt_password;
497 r2.in.nt_verifier = r->in.nt_verifier;
498 r2.in.lm_change = r->in.lm_change;
499 r2.in.lm_password = r->in.lm_password;
500 r2.in.lm_verifier = r->in.lm_verifier;
501 r2.in.password3 = NULL;
503 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
508 check that a password is sufficiently complex
510 static BOOL samdb_password_complexity_ok(const char *pass)
512 return check_password_quality(pass);
516 set the user password using plaintext, obeying any user or domain
517 password restrictions
519 note that this function doesn't actually store the result in the
520 database, it just fills in the "mod" structure with ldb modify
521 elements to setup the correct change when samdb_replace() is
522 called. This allows the caller to combine the change with other
523 changes (as is needed by some of the set user info levels)
525 The caller should probably have a transaction wrapping this
527 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
528 const struct ldb_dn *user_dn,
529 const struct ldb_dn *domain_dn,
530 struct ldb_message *mod,
531 const char *new_pass,
532 struct samr_Password *lmNewHash,
533 struct samr_Password *ntNewHash,
536 enum samr_RejectReason *reject_reason,
537 struct samr_DomInfo1 **_dominfo)
539 const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory",
541 "lmPwdHash", "ntPwdHash",
543 "pwdLastSet", NULL };
544 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
545 "maxPwdAge", "minPwdAge",
546 "minPwdLength", NULL };
549 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
550 uint_t userAccountControl;
551 struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
552 struct samr_Password local_lmNewHash, local_ntNewHash;
553 int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
554 struct dom_sid *domain_sid;
555 struct ldb_message **res;
557 time_t now = time(NULL);
561 /* we need to know the time to compute password age */
562 unix_to_nt_time(&now_nt, now);
564 /* pull all the user parameters */
565 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
567 return NT_STATUS_INTERNAL_DB_CORRUPTION;
569 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
570 sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
571 "sambaLMPwdHistory", &sambaLMPwdHistory);
572 sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
573 "sambaNTPwdHistory", &sambaNTPwdHistory);
574 lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
575 ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
576 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
579 /* pull the domain parameters */
580 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
582 return NT_STATUS_NO_SUCH_DOMAIN;
585 /* work out the domain sid, and pull the domain from there */
586 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
587 if (domain_sid == NULL) {
588 return NT_STATUS_INTERNAL_DB_CORRUPTION;
591 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
593 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
595 return NT_STATUS_NO_SUCH_DOMAIN;
599 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
600 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
601 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
602 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
605 struct samr_DomInfo1 *dominfo;
606 /* on failure we need to fill in the reject reasons */
607 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
608 if (dominfo == NULL) {
609 return NT_STATUS_NO_MEMORY;
611 dominfo->min_password_length = minPwdLength;
612 dominfo->password_properties = pwdProperties;
613 dominfo->password_history_length = pwdHistoryLength;
614 dominfo->max_password_age = minPwdAge;
615 dominfo->min_password_age = minPwdAge;
620 /* check the various password restrictions */
621 if (restrictions && minPwdLength > strlen_m(new_pass)) {
623 *reject_reason = SAMR_REJECT_TOO_SHORT;
625 return NT_STATUS_PASSWORD_RESTRICTION;
628 /* possibly check password complexity */
629 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
630 !samdb_password_complexity_ok(new_pass)) {
632 *reject_reason = SAMR_REJECT_COMPLEXITY;
634 return NT_STATUS_PASSWORD_RESTRICTION;
637 /* compute the new nt and lm hashes */
638 if (E_deshash(new_pass, local_lmNewHash.hash)) {
639 lmNewHash = &local_lmNewHash;
641 E_md4hash(new_pass, local_ntNewHash.hash);
642 ntNewHash = &local_ntNewHash;
645 if (restrictions && user_change) {
646 /* are all password changes disallowed? */
647 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
649 *reject_reason = SAMR_REJECT_OTHER;
651 return NT_STATUS_PASSWORD_RESTRICTION;
654 /* can this user change password? */
655 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
657 *reject_reason = SAMR_REJECT_OTHER;
659 return NT_STATUS_PASSWORD_RESTRICTION;
662 /* yes, this is a minus. The ages are in negative 100nsec units! */
663 if (pwdLastSet - minPwdAge > now_nt) {
665 *reject_reason = SAMR_REJECT_OTHER;
667 return NT_STATUS_PASSWORD_RESTRICTION;
670 /* check the immediately past password */
671 if (pwdHistoryLength > 0) {
672 if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
674 *reject_reason = SAMR_REJECT_COMPLEXITY;
676 return NT_STATUS_PASSWORD_RESTRICTION;
678 if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
680 *reject_reason = SAMR_REJECT_COMPLEXITY;
682 return NT_STATUS_PASSWORD_RESTRICTION;
686 /* check the password history */
687 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
688 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
690 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
691 if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
693 *reject_reason = SAMR_REJECT_COMPLEXITY;
695 return NT_STATUS_PASSWORD_RESTRICTION;
698 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
699 if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
701 *reject_reason = SAMR_REJECT_COMPLEXITY;
703 return NT_STATUS_PASSWORD_RESTRICTION;
708 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
710 /* the password is acceptable. Start forming the new fields */
712 /* if we know the cleartext, then only set it.
713 * Modules in ldb will set all the appropriate
715 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
716 "sambaPassword", new_pass));
718 /* We don't have the cleartext, so delete the old one
719 * and set what we have of the hashes */
720 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
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"));
739 set password via a samr_CryptPassword buffer
740 this will in the 'msg' with modify operations that will update the user
741 password when applied
743 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
745 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
747 struct ldb_message *msg,
748 struct samr_CryptPassword *pwbuf)
752 uint32_t new_pass_len;
753 DATA_BLOB session_key = data_blob(NULL, 0);
755 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
756 if (!NT_STATUS_IS_OK(nt_status)) {
760 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
762 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
763 &new_pass_len, STR_UNICODE)) {
764 DEBUG(3,("samr: failed to decode password buffer\n"));
765 return NT_STATUS_WRONG_PASSWORD;
768 /* set the password - samdb needs to know both the domain and user DNs,
769 so the domain password policy can be used */
770 return samdb_set_password(sam_ctx, mem_ctx,
771 account_dn, domain_dn,
774 False, /* This is a password set, not change */
775 True, /* run restriction tests */
781 set password via a samr_CryptPasswordEx buffer
782 this will in the 'msg' with modify operations that will update the user
783 password when applied
785 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
786 struct ldb_context *sam_ctx,
787 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
789 struct ldb_message *msg,
790 struct samr_CryptPasswordEx *pwbuf)
794 uint32_t new_pass_len;
795 DATA_BLOB co_session_key;
796 DATA_BLOB session_key = data_blob(NULL, 0);
797 struct MD5Context ctx;
799 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
800 if (!NT_STATUS_IS_OK(nt_status)) {
804 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
805 if (!co_session_key.data) {
806 return NT_STATUS_NO_MEMORY;
810 MD5Update(&ctx, &pwbuf->data[516], 16);
811 MD5Update(&ctx, session_key.data, session_key.length);
812 MD5Final(co_session_key.data, &ctx);
814 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
816 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
817 &new_pass_len, STR_UNICODE)) {
818 DEBUG(3,("samr: failed to decode password buffer\n"));
819 return NT_STATUS_WRONG_PASSWORD;
822 /* set the password - samdb needs to know both the domain and user DNs,
823 so the domain password policy can be used */
824 return samdb_set_password(sam_ctx, mem_ctx,
825 account_dn, domain_dn,
828 False, /* This is a password set, not change */
829 True, /* run restriction tests */
834 set the user password using plaintext, obeying any user or domain
835 password restrictions
837 This wrapper function takes a SID as input, rather than a user DN,
838 and actually performs the password change
841 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
842 const struct dom_sid *user_sid,
843 const char *new_pass,
844 struct samr_Password *lmNewHash,
845 struct samr_Password *ntNewHash,
848 enum samr_RejectReason *reject_reason,
849 struct samr_DomInfo1 **_dominfo)
852 struct ldb_dn *user_dn;
853 struct ldb_message *msg;
856 ret = ldb_transaction_start(ctx);
858 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
859 return NT_STATUS_TRANSACTION_ABORTED;
862 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
863 "(&(objectSid=%s)(objectClass=user))",
864 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
866 ldb_transaction_cancel(ctx);
867 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
868 dom_sid_string(mem_ctx, user_sid)));
869 return NT_STATUS_NO_SUCH_USER;
872 msg = ldb_msg_new(mem_ctx);
874 ldb_transaction_cancel(ctx);
875 return NT_STATUS_NO_MEMORY;
878 msg->dn = ldb_dn_copy(msg, user_dn);
880 ldb_transaction_cancel(ctx);
881 return NT_STATUS_NO_MEMORY;
884 nt_status = samdb_set_password(ctx, mem_ctx,
887 lmNewHash, ntNewHash,
888 user_change, /* This is a password set, not change */
889 restrictions, /* run restriction tests */
890 reject_reason, _dominfo);
891 if (!NT_STATUS_IS_OK(nt_status)) {
892 ldb_transaction_cancel(ctx);
896 /* modify the samdb record */
897 ret = samdb_replace(ctx, mem_ctx, msg);
899 ldb_transaction_cancel(ctx);
900 return NT_STATUS_ACCESS_DENIED;
903 ret = ldb_transaction_commit(ctx);
905 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
906 ldb_dn_linearize(mem_ctx, msg->dn),
907 ldb_errstring(ctx)));
908 return NT_STATUS_TRANSACTION_ABORTED;