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"
37 samr_ChangePasswordUser
39 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
40 struct samr_ChangePasswordUser *r)
42 struct dcesrv_handle *h;
43 struct samr_account_state *a_state;
44 struct ldb_context *sam_ctx;
45 struct ldb_message **res, *msg;
47 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
48 struct samr_Password *lm_pwd, *nt_pwd;
49 NTSTATUS status = NT_STATUS_OK;
50 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
52 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
56 /* basic sanity checking on parameters. Do this before any database ops */
57 if (!r->in.lm_present || !r->in.nt_present ||
58 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
59 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
60 /* we should really handle a change with lm not
62 return NT_STATUS_INVALID_PARAMETER_MIX;
64 if (!r->in.cross1_present || !r->in.nt_cross) {
65 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
67 if (!r->in.cross2_present || !r->in.lm_cross) {
68 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
71 /* To change a password we need to open as system */
72 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
73 if (sam_ctx == NULL) {
74 return NT_STATUS_INVALID_SYSTEM_SERVICE;
77 ret = ldb_transaction_start(sam_ctx);
79 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
80 return NT_STATUS_TRANSACTION_ABORTED;
83 /* fetch the old hashes */
84 ret = gendb_search_dn(sam_ctx, mem_ctx,
85 a_state->account_dn, &res, attrs);
87 ldb_transaction_cancel(sam_ctx);
88 return NT_STATUS_WRONG_PASSWORD;
92 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
93 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
94 ldb_transaction_cancel(sam_ctx);
95 return NT_STATUS_WRONG_PASSWORD;
98 /* decrypt and check the new lm hash */
99 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
100 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
101 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
102 ldb_transaction_cancel(sam_ctx);
103 return NT_STATUS_WRONG_PASSWORD;
106 /* decrypt and check the new nt hash */
107 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
108 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
109 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
110 ldb_transaction_cancel(sam_ctx);
111 return NT_STATUS_WRONG_PASSWORD;
114 /* check the nt cross hash */
115 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
116 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
117 ldb_transaction_cancel(sam_ctx);
118 return NT_STATUS_WRONG_PASSWORD;
121 /* check the lm cross hash */
122 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
123 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
124 ldb_transaction_cancel(sam_ctx);
125 return NT_STATUS_WRONG_PASSWORD;
128 msg = ldb_msg_new(mem_ctx);
130 ldb_transaction_cancel(sam_ctx);
131 return NT_STATUS_NO_MEMORY;
134 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
136 ldb_transaction_cancel(sam_ctx);
137 return NT_STATUS_NO_MEMORY;
140 /* set the password on the user DN specified. This may fail
141 * due to password policies */
142 status = samdb_set_password(sam_ctx, mem_ctx,
143 a_state->account_dn, a_state->domain_state->domain_dn,
144 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
145 True, /* this is a user password change */
146 True, /* run restriction tests */
149 if (!NT_STATUS_IS_OK(status)) {
150 ldb_transaction_cancel(sam_ctx);
154 /* The above call only setup the modifications, this actually
155 * makes the write to the database. */
156 ret = samdb_replace(sam_ctx, mem_ctx, msg);
158 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
159 ldb_dn_linearize(mem_ctx, a_state->account_dn),
160 ldb_errstring(sam_ctx)));
161 ldb_transaction_cancel(sam_ctx);
162 return NT_STATUS_INTERNAL_DB_CORRUPTION;
165 /* And this confirms it in a transaction commit */
166 ret = ldb_transaction_commit(sam_ctx);
168 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
169 ldb_dn_linearize(mem_ctx, a_state->account_dn),
170 ldb_errstring(sam_ctx)));
171 return NT_STATUS_TRANSACTION_ABORTED;
178 samr_OemChangePasswordUser2
180 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
181 struct samr_OemChangePasswordUser2 *r)
185 uint32_t new_pass_len;
186 struct samr_CryptPassword *pwbuf = r->in.password;
187 struct ldb_context *sam_ctx;
188 const struct ldb_dn *user_dn;
190 struct ldb_message **res, *mod;
191 const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
192 struct samr_Password *lm_pwd;
193 DATA_BLOB lm_pwd_blob;
194 uint8_t new_lm_hash[16];
195 struct samr_Password lm_verifier;
198 return NT_STATUS_WRONG_PASSWORD;
201 /* To change a password we need to open as system */
202 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
203 if (sam_ctx == NULL) {
204 return NT_STATUS_INVALID_SYSTEM_SERVICE;
207 ret = ldb_transaction_start(sam_ctx);
209 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
210 return NT_STATUS_TRANSACTION_ABORTED;
213 /* we need the users dn and the domain dn (derived from the
214 user SID). We also need the current lm password hash in
215 order to decrypt the incoming password */
216 ret = gendb_search(sam_ctx,
217 mem_ctx, NULL, &res, attrs,
218 "(&(sAMAccountName=%s)(objectclass=user))",
219 r->in.account->string);
221 ldb_transaction_cancel(sam_ctx);
222 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
223 return NT_STATUS_WRONG_PASSWORD;
226 user_dn = res[0]->dn;
228 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
229 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
230 ldb_transaction_cancel(sam_ctx);
231 return NT_STATUS_WRONG_PASSWORD;
234 /* decrypt the password we have been given */
235 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
236 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
237 data_blob_free(&lm_pwd_blob);
239 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
240 &new_pass_len, STR_ASCII)) {
241 ldb_transaction_cancel(sam_ctx);
242 DEBUG(3,("samr: failed to decode password buffer\n"));
243 return NT_STATUS_WRONG_PASSWORD;
246 /* check LM verifier */
247 if (lm_pwd == NULL || r->in.hash == NULL) {
248 ldb_transaction_cancel(sam_ctx);
249 return NT_STATUS_WRONG_PASSWORD;
252 E_deshash(new_pass, new_lm_hash);
253 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
254 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
255 ldb_transaction_cancel(sam_ctx);
256 return NT_STATUS_WRONG_PASSWORD;
259 mod = ldb_msg_new(mem_ctx);
261 ldb_transaction_cancel(sam_ctx);
262 return NT_STATUS_NO_MEMORY;
265 mod->dn = ldb_dn_copy(mod, user_dn);
267 ldb_transaction_cancel(sam_ctx);
268 return NT_STATUS_NO_MEMORY;
271 /* set the password on the user DN specified. This may fail
272 * due to password policies */
273 status = samdb_set_password(sam_ctx, mem_ctx,
277 True, /* this is a user password change */
278 True, /* run restriction tests */
281 if (!NT_STATUS_IS_OK(status)) {
282 ldb_transaction_cancel(sam_ctx);
286 /* The above call only setup the modifications, this actually
287 * makes the write to the database. */
288 ret = samdb_replace(sam_ctx, mem_ctx, mod);
290 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
291 ldb_dn_linearize(mem_ctx, user_dn),
292 ldb_errstring(sam_ctx)));
293 ldb_transaction_cancel(sam_ctx);
294 return NT_STATUS_INTERNAL_DB_CORRUPTION;
297 /* And this confirms it in a transaction commit */
298 ret = ldb_transaction_commit(sam_ctx);
300 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
301 ldb_dn_linearize(mem_ctx, user_dn),
302 ldb_errstring(sam_ctx)));
303 return NT_STATUS_TRANSACTION_ABORTED;
311 samr_ChangePasswordUser3
313 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
315 struct samr_ChangePasswordUser3 *r)
319 uint32_t new_pass_len;
320 struct ldb_context *sam_ctx = NULL;
321 const struct ldb_dn *user_dn;
323 struct ldb_message **res, *mod;
324 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
325 struct samr_Password *nt_pwd, *lm_pwd;
326 DATA_BLOB nt_pwd_blob;
327 struct samr_DomInfo1 *dominfo = NULL;
328 struct samr_ChangeReject *reject = NULL;
329 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
330 uint8_t new_nt_hash[16], new_lm_hash[16];
331 struct samr_Password nt_verifier, lm_verifier;
335 if (r->in.nt_password == NULL ||
336 r->in.nt_verifier == NULL) {
337 return NT_STATUS_INVALID_PARAMETER;
340 /* To change a password we need to open as system */
341 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
342 if (sam_ctx == NULL) {
343 return NT_STATUS_INVALID_SYSTEM_SERVICE;
346 ret = ldb_transaction_start(sam_ctx);
348 talloc_free(sam_ctx);
349 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
350 return NT_STATUS_TRANSACTION_ABORTED;
353 /* we need the users dn and the domain dn (derived from the
354 user SID). We also need the current lm and nt password hashes
355 in order to decrypt the incoming passwords */
356 ret = gendb_search(sam_ctx,
357 mem_ctx, NULL, &res, attrs,
358 "(&(sAMAccountName=%s)(objectclass=user))",
359 r->in.account->string);
361 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
362 status = NT_STATUS_WRONG_PASSWORD;
366 user_dn = res[0]->dn;
368 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
369 if (!NT_STATUS_IS_OK(status) ) {
374 status = NT_STATUS_WRONG_PASSWORD;
378 /* decrypt the password we have been given */
379 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
380 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
381 data_blob_free(&nt_pwd_blob);
383 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
384 &new_pass_len, STR_UNICODE)) {
385 DEBUG(3,("samr: failed to decode password buffer\n"));
386 status = NT_STATUS_WRONG_PASSWORD;
390 if (r->in.nt_verifier == NULL) {
391 status = NT_STATUS_WRONG_PASSWORD;
395 /* check NT verifier */
396 E_md4hash(new_pass, new_nt_hash);
397 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
398 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
399 status = NT_STATUS_WRONG_PASSWORD;
403 /* check LM verifier */
404 if (lm_pwd && r->in.lm_verifier != NULL) {
405 E_deshash(new_pass, new_lm_hash);
406 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
407 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
408 status = NT_STATUS_WRONG_PASSWORD;
414 mod = ldb_msg_new(mem_ctx);
416 return NT_STATUS_NO_MEMORY;
419 mod->dn = ldb_dn_copy(mod, user_dn);
421 status = NT_STATUS_NO_MEMORY;
425 /* set the password on the user DN specified. This may fail
426 * due to password policies */
427 status = samdb_set_password(sam_ctx, mem_ctx,
431 True, /* this is a user password change */
432 True, /* run restriction tests */
435 if (!NT_STATUS_IS_OK(status)) {
439 /* The above call only setup the modifications, this actually
440 * makes the write to the database. */
441 ret = samdb_replace(sam_ctx, mem_ctx, mod);
443 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
444 ldb_dn_linearize(mem_ctx, user_dn),
445 ldb_errstring(sam_ctx)));
446 status = NT_STATUS_UNSUCCESSFUL;
450 /* And this confirms it in a transaction commit */
451 ret = ldb_transaction_commit(sam_ctx);
453 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
454 ldb_dn_linearize(mem_ctx, user_dn),
455 ldb_errstring(sam_ctx)));
456 status = NT_STATUS_TRANSACTION_ABORTED;
463 ldb_transaction_cancel(sam_ctx);
464 talloc_free(sam_ctx);
466 reject = talloc(mem_ctx, struct samr_ChangeReject);
467 r->out.dominfo = dominfo;
468 r->out.reject = reject;
470 if (reject == NULL) {
473 ZERO_STRUCTP(reject);
475 reject->reason = reason;
482 samr_ChangePasswordUser2
484 easy - just a subset of samr_ChangePasswordUser3
486 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
487 struct samr_ChangePasswordUser2 *r)
489 struct samr_ChangePasswordUser3 r2;
491 r2.in.server = r->in.server;
492 r2.in.account = r->in.account;
493 r2.in.nt_password = r->in.nt_password;
494 r2.in.nt_verifier = r->in.nt_verifier;
495 r2.in.lm_change = r->in.lm_change;
496 r2.in.lm_password = r->in.lm_password;
497 r2.in.lm_verifier = r->in.lm_verifier;
498 r2.in.password3 = NULL;
500 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
505 check that a password is sufficiently complex
507 static BOOL samdb_password_complexity_ok(const char *pass)
509 return check_password_quality(pass);
513 set the user password using plaintext, obeying any user or domain
514 password restrictions
516 note that this function doesn't actually store the result in the
517 database, it just fills in the "mod" structure with ldb modify
518 elements to setup the correct change when samdb_replace() is
519 called. This allows the caller to combine the change with other
520 changes (as is needed by some of the set user info levels)
522 The caller should probably have a transaction wrapping this
524 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
525 const struct ldb_dn *user_dn,
526 const struct ldb_dn *domain_dn,
527 struct ldb_message *mod,
528 const char *new_pass,
529 struct samr_Password *lmNewHash,
530 struct samr_Password *ntNewHash,
533 enum samr_RejectReason *reject_reason,
534 struct samr_DomInfo1 **_dominfo)
536 const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory",
538 "lmPwdHash", "ntPwdHash",
540 "pwdLastSet", NULL };
541 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
542 "maxPwdAge", "minPwdAge",
543 "minPwdLength", NULL };
546 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
547 uint_t userAccountControl;
548 struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
549 struct samr_Password local_lmNewHash, local_ntNewHash;
550 int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
551 struct dom_sid *domain_sid;
552 struct ldb_message **res;
554 time_t now = time(NULL);
558 /* we need to know the time to compute password age */
559 unix_to_nt_time(&now_nt, now);
561 /* pull all the user parameters */
562 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
564 return NT_STATUS_INTERNAL_DB_CORRUPTION;
566 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
567 sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
568 "sambaLMPwdHistory", &sambaLMPwdHistory);
569 sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
570 "sambaNTPwdHistory", &sambaNTPwdHistory);
571 lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
572 ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
573 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
576 /* pull the domain parameters */
577 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
579 return NT_STATUS_NO_SUCH_DOMAIN;
582 /* work out the domain sid, and pull the domain from there */
583 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
584 if (domain_sid == NULL) {
585 return NT_STATUS_INTERNAL_DB_CORRUPTION;
588 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
590 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
592 return NT_STATUS_NO_SUCH_DOMAIN;
596 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
597 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
598 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
599 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
602 struct samr_DomInfo1 *dominfo;
603 /* on failure we need to fill in the reject reasons */
604 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
605 if (dominfo == NULL) {
606 return NT_STATUS_NO_MEMORY;
608 dominfo->min_password_length = minPwdLength;
609 dominfo->password_properties = pwdProperties;
610 dominfo->password_history_length = pwdHistoryLength;
611 dominfo->max_password_age = minPwdAge;
612 dominfo->min_password_age = minPwdAge;
617 /* check the various password restrictions */
618 if (restrictions && minPwdLength > strlen_m(new_pass)) {
620 *reject_reason = SAMR_REJECT_TOO_SHORT;
622 return NT_STATUS_PASSWORD_RESTRICTION;
625 /* possibly check password complexity */
626 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
627 !samdb_password_complexity_ok(new_pass)) {
629 *reject_reason = SAMR_REJECT_COMPLEXITY;
631 return NT_STATUS_PASSWORD_RESTRICTION;
634 /* compute the new nt and lm hashes */
635 if (E_deshash(new_pass, local_lmNewHash.hash)) {
636 lmNewHash = &local_lmNewHash;
638 E_md4hash(new_pass, local_ntNewHash.hash);
639 ntNewHash = &local_ntNewHash;
642 if (restrictions && user_change) {
643 /* are all password changes disallowed? */
644 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
646 *reject_reason = SAMR_REJECT_OTHER;
648 return NT_STATUS_PASSWORD_RESTRICTION;
651 /* can this user change password? */
652 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
654 *reject_reason = SAMR_REJECT_OTHER;
656 return NT_STATUS_PASSWORD_RESTRICTION;
659 /* yes, this is a minus. The ages are in negative 100nsec units! */
660 if (pwdLastSet - minPwdAge > now_nt) {
662 *reject_reason = SAMR_REJECT_OTHER;
664 return NT_STATUS_PASSWORD_RESTRICTION;
667 /* check the immediately past password */
668 if (pwdHistoryLength > 0) {
669 if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
671 *reject_reason = SAMR_REJECT_COMPLEXITY;
673 return NT_STATUS_PASSWORD_RESTRICTION;
675 if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
677 *reject_reason = SAMR_REJECT_COMPLEXITY;
679 return NT_STATUS_PASSWORD_RESTRICTION;
683 /* check the password history */
684 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
685 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
687 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
688 if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
690 *reject_reason = SAMR_REJECT_COMPLEXITY;
692 return NT_STATUS_PASSWORD_RESTRICTION;
695 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
696 if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
698 *reject_reason = SAMR_REJECT_COMPLEXITY;
700 return NT_STATUS_PASSWORD_RESTRICTION;
705 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
707 /* the password is acceptable. Start forming the new fields */
709 /* if we know the cleartext, then only set it.
710 * Modules in ldb will set all the appropriate
712 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
713 "sambaPassword", new_pass));
715 /* We don't have the cleartext, so delete the old one
716 * and set what we have of the hashes */
717 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
720 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
722 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
726 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
728 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
736 set password via a samr_CryptPassword buffer
737 this will in the 'msg' with modify operations that will update the user
738 password when applied
740 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
742 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
744 struct ldb_message *msg,
745 struct samr_CryptPassword *pwbuf)
749 uint32_t new_pass_len;
750 DATA_BLOB session_key = data_blob(NULL, 0);
752 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
753 if (!NT_STATUS_IS_OK(nt_status)) {
757 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
759 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
760 &new_pass_len, STR_UNICODE)) {
761 DEBUG(3,("samr: failed to decode password buffer\n"));
762 return NT_STATUS_WRONG_PASSWORD;
765 /* set the password - samdb needs to know both the domain and user DNs,
766 so the domain password policy can be used */
767 return samdb_set_password(sam_ctx, mem_ctx,
768 account_dn, domain_dn,
771 False, /* This is a password set, not change */
772 True, /* run restriction tests */
778 set password via a samr_CryptPasswordEx buffer
779 this will in the 'msg' with modify operations that will update the user
780 password when applied
782 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
783 struct ldb_context *sam_ctx,
784 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
786 struct ldb_message *msg,
787 struct samr_CryptPasswordEx *pwbuf)
791 uint32_t new_pass_len;
792 DATA_BLOB co_session_key;
793 DATA_BLOB session_key = data_blob(NULL, 0);
794 struct MD5Context ctx;
796 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
797 if (!NT_STATUS_IS_OK(nt_status)) {
801 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
802 if (!co_session_key.data) {
803 return NT_STATUS_NO_MEMORY;
807 MD5Update(&ctx, &pwbuf->data[516], 16);
808 MD5Update(&ctx, session_key.data, session_key.length);
809 MD5Final(co_session_key.data, &ctx);
811 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
813 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
814 &new_pass_len, STR_UNICODE)) {
815 DEBUG(3,("samr: failed to decode password buffer\n"));
816 return NT_STATUS_WRONG_PASSWORD;
819 /* set the password - samdb needs to know both the domain and user DNs,
820 so the domain password policy can be used */
821 return samdb_set_password(sam_ctx, mem_ctx,
822 account_dn, domain_dn,
825 False, /* This is a password set, not change */
826 True, /* run restriction tests */
831 set the user password using plaintext, obeying any user or domain
832 password restrictions
834 This wrapper function takes a SID as input, rather than a user DN,
835 and actually performs the password change
838 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
839 const struct dom_sid *user_sid,
840 const char *new_pass,
841 struct samr_Password *lmNewHash,
842 struct samr_Password *ntNewHash,
845 enum samr_RejectReason *reject_reason,
846 struct samr_DomInfo1 **_dominfo)
849 struct ldb_dn *user_dn;
850 struct ldb_message *msg;
853 ret = ldb_transaction_start(ctx);
855 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
856 return NT_STATUS_TRANSACTION_ABORTED;
859 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
860 "(&(objectSid=%s)(objectClass=user))",
861 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
863 ldb_transaction_cancel(ctx);
864 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
865 dom_sid_string(mem_ctx, user_sid)));
866 return NT_STATUS_NO_SUCH_USER;
869 msg = ldb_msg_new(mem_ctx);
871 ldb_transaction_cancel(ctx);
872 return NT_STATUS_NO_MEMORY;
875 msg->dn = ldb_dn_copy(msg, user_dn);
877 ldb_transaction_cancel(ctx);
878 return NT_STATUS_NO_MEMORY;
881 nt_status = samdb_set_password(ctx, mem_ctx,
884 lmNewHash, ntNewHash,
885 user_change, /* This is a password set, not change */
886 restrictions, /* run restriction tests */
887 reject_reason, _dominfo);
888 if (!NT_STATUS_IS_OK(nt_status)) {
889 ldb_transaction_cancel(ctx);
893 /* modify the samdb record */
894 ret = samdb_replace(ctx, mem_ctx, msg);
896 ldb_transaction_cancel(ctx);
897 return NT_STATUS_ACCESS_DENIED;
900 ret = ldb_transaction_commit(ctx);
902 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
903 ldb_dn_linearize(mem_ctx, msg->dn),
904 ldb_errstring(ctx)));
905 return NT_STATUS_TRANSACTION_ABORTED;