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"
36 samr_ChangePasswordUser
38 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
39 struct samr_ChangePasswordUser *r)
41 struct dcesrv_handle *h;
42 struct samr_account_state *a_state;
43 struct ldb_context *sam_ctx;
44 struct ldb_message **res, *msg;
46 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
47 struct samr_Password *lm_pwd, *nt_pwd;
48 NTSTATUS status = NT_STATUS_OK;
49 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
51 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
55 /* basic sanity checking on parameters. Do this before any database ops */
56 if (!r->in.lm_present || !r->in.nt_present ||
57 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
58 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
59 /* we should really handle a change with lm not
61 return NT_STATUS_INVALID_PARAMETER_MIX;
63 if (!r->in.cross1_present || !r->in.nt_cross) {
64 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
66 if (!r->in.cross2_present || !r->in.lm_cross) {
67 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
70 /* To change a password we need to open as system */
71 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
72 if (sam_ctx == NULL) {
73 return NT_STATUS_INVALID_SYSTEM_SERVICE;
76 ret = ldb_transaction_start(sam_ctx);
78 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
79 return NT_STATUS_TRANSACTION_ABORTED;
82 /* fetch the old hashes */
83 ret = gendb_search_dn(sam_ctx, mem_ctx,
84 a_state->account_dn, &res, attrs);
86 ldb_transaction_cancel(sam_ctx);
87 return NT_STATUS_WRONG_PASSWORD;
91 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
92 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
93 ldb_transaction_cancel(sam_ctx);
94 return NT_STATUS_WRONG_PASSWORD;
97 /* decrypt and check the new lm hash */
98 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
99 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
100 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
101 ldb_transaction_cancel(sam_ctx);
102 return NT_STATUS_WRONG_PASSWORD;
105 /* decrypt and check the new nt hash */
106 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
107 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
108 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
109 ldb_transaction_cancel(sam_ctx);
110 return NT_STATUS_WRONG_PASSWORD;
113 /* check the nt cross hash */
114 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
115 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
116 ldb_transaction_cancel(sam_ctx);
117 return NT_STATUS_WRONG_PASSWORD;
120 /* check the lm cross hash */
121 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
122 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
123 ldb_transaction_cancel(sam_ctx);
124 return NT_STATUS_WRONG_PASSWORD;
127 msg = ldb_msg_new(mem_ctx);
129 ldb_transaction_cancel(sam_ctx);
130 return NT_STATUS_NO_MEMORY;
133 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
135 ldb_transaction_cancel(sam_ctx);
136 return NT_STATUS_NO_MEMORY;
139 /* set the password on the user DN specified. This may fail
140 * due to password policies */
141 status = samdb_set_password(sam_ctx, mem_ctx,
142 a_state->account_dn, a_state->domain_state->domain_dn,
143 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
144 True, /* this is a user password change */
145 True, /* run restriction tests */
148 if (!NT_STATUS_IS_OK(status)) {
149 ldb_transaction_cancel(sam_ctx);
153 /* The above call only setup the modifications, this actually
154 * makes the write to the database. */
155 ret = samdb_replace(sam_ctx, mem_ctx, msg);
157 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
158 ldb_dn_linearize(mem_ctx, a_state->account_dn),
159 ldb_errstring(sam_ctx)));
160 ldb_transaction_cancel(sam_ctx);
161 return NT_STATUS_INTERNAL_DB_CORRUPTION;
164 /* And this confirms it in a transaction commit */
165 ret = ldb_transaction_commit(sam_ctx);
167 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
168 ldb_dn_linearize(mem_ctx, a_state->account_dn),
169 ldb_errstring(sam_ctx)));
170 return NT_STATUS_TRANSACTION_ABORTED;
177 samr_OemChangePasswordUser2
179 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
180 struct samr_OemChangePasswordUser2 *r)
184 uint32_t new_pass_len;
185 struct samr_CryptPassword *pwbuf = r->in.password;
186 struct ldb_context *sam_ctx;
187 const struct ldb_dn *user_dn;
189 struct ldb_message **res, *mod;
190 const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
191 struct samr_Password *lm_pwd;
192 DATA_BLOB lm_pwd_blob;
193 uint8_t new_lm_hash[16];
194 struct samr_Password lm_verifier;
197 return NT_STATUS_WRONG_PASSWORD;
200 /* To change a password we need to open as system */
201 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
202 if (sam_ctx == NULL) {
203 return NT_STATUS_INVALID_SYSTEM_SERVICE;
206 ret = ldb_transaction_start(sam_ctx);
208 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
209 return NT_STATUS_TRANSACTION_ABORTED;
212 /* we need the users dn and the domain dn (derived from the
213 user SID). We also need the current lm password hash in
214 order to decrypt the incoming password */
215 ret = gendb_search(sam_ctx,
216 mem_ctx, NULL, &res, attrs,
217 "(&(sAMAccountName=%s)(objectclass=user))",
218 r->in.account->string);
220 ldb_transaction_cancel(sam_ctx);
221 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
222 return NT_STATUS_WRONG_PASSWORD;
225 user_dn = res[0]->dn;
227 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
228 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
229 ldb_transaction_cancel(sam_ctx);
230 return NT_STATUS_WRONG_PASSWORD;
233 /* decrypt the password we have been given */
234 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
235 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
236 data_blob_free(&lm_pwd_blob);
238 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
239 &new_pass_len, STR_ASCII)) {
240 ldb_transaction_cancel(sam_ctx);
241 DEBUG(3,("samr: failed to decode password buffer\n"));
242 return NT_STATUS_WRONG_PASSWORD;
245 /* check LM verifier */
246 if (lm_pwd == NULL || r->in.hash == NULL) {
247 ldb_transaction_cancel(sam_ctx);
248 return NT_STATUS_WRONG_PASSWORD;
251 E_deshash(new_pass, new_lm_hash);
252 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
253 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
254 ldb_transaction_cancel(sam_ctx);
255 return NT_STATUS_WRONG_PASSWORD;
258 mod = ldb_msg_new(mem_ctx);
260 ldb_transaction_cancel(sam_ctx);
261 return NT_STATUS_NO_MEMORY;
264 mod->dn = ldb_dn_copy(mod, user_dn);
266 ldb_transaction_cancel(sam_ctx);
267 return NT_STATUS_NO_MEMORY;
270 /* set the password on the user DN specified. This may fail
271 * due to password policies */
272 status = samdb_set_password(sam_ctx, mem_ctx,
276 True, /* this is a user password change */
277 True, /* run restriction tests */
280 if (!NT_STATUS_IS_OK(status)) {
281 ldb_transaction_cancel(sam_ctx);
285 /* The above call only setup the modifications, this actually
286 * makes the write to the database. */
287 ret = samdb_replace(sam_ctx, mem_ctx, mod);
289 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
290 ldb_dn_linearize(mem_ctx, user_dn),
291 ldb_errstring(sam_ctx)));
292 ldb_transaction_cancel(sam_ctx);
293 return NT_STATUS_INTERNAL_DB_CORRUPTION;
296 /* And this confirms it in a transaction commit */
297 ret = ldb_transaction_commit(sam_ctx);
299 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
300 ldb_dn_linearize(mem_ctx, user_dn),
301 ldb_errstring(sam_ctx)));
302 return NT_STATUS_TRANSACTION_ABORTED;
310 samr_ChangePasswordUser3
312 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
314 struct samr_ChangePasswordUser3 *r)
318 uint32_t new_pass_len;
319 struct ldb_context *sam_ctx = NULL;
320 const struct ldb_dn *user_dn;
322 struct ldb_message **res, *mod;
323 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
324 struct samr_Password *nt_pwd, *lm_pwd;
325 DATA_BLOB nt_pwd_blob;
326 struct samr_DomInfo1 *dominfo = NULL;
327 struct samr_ChangeReject *reject = NULL;
328 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
329 uint8_t new_nt_hash[16], new_lm_hash[16];
330 struct samr_Password nt_verifier, lm_verifier;
334 if (r->in.nt_password == NULL ||
335 r->in.nt_verifier == NULL) {
336 return NT_STATUS_INVALID_PARAMETER;
339 /* To change a password we need to open as system */
340 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
341 if (sam_ctx == NULL) {
342 return NT_STATUS_INVALID_SYSTEM_SERVICE;
345 ret = ldb_transaction_start(sam_ctx);
347 talloc_free(sam_ctx);
348 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
349 return NT_STATUS_TRANSACTION_ABORTED;
352 /* we need the users dn and the domain dn (derived from the
353 user SID). We also need the current lm and nt password hashes
354 in order to decrypt the incoming passwords */
355 ret = gendb_search(sam_ctx,
356 mem_ctx, NULL, &res, attrs,
357 "(&(sAMAccountName=%s)(objectclass=user))",
358 r->in.account->string);
360 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
361 status = NT_STATUS_WRONG_PASSWORD;
365 user_dn = res[0]->dn;
367 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
368 if (!NT_STATUS_IS_OK(status) ) {
373 status = NT_STATUS_WRONG_PASSWORD;
377 /* decrypt the password we have been given */
378 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
379 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
380 data_blob_free(&nt_pwd_blob);
382 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
383 &new_pass_len, STR_UNICODE)) {
384 DEBUG(3,("samr: failed to decode password buffer\n"));
385 status = NT_STATUS_WRONG_PASSWORD;
389 if (r->in.nt_verifier == NULL) {
390 status = NT_STATUS_WRONG_PASSWORD;
394 /* check NT verifier */
395 E_md4hash(new_pass, new_nt_hash);
396 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
397 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
398 status = NT_STATUS_WRONG_PASSWORD;
402 /* check LM verifier */
403 if (lm_pwd && r->in.lm_verifier != NULL) {
404 E_deshash(new_pass, new_lm_hash);
405 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
406 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
407 status = NT_STATUS_WRONG_PASSWORD;
413 mod = ldb_msg_new(mem_ctx);
415 return NT_STATUS_NO_MEMORY;
418 mod->dn = ldb_dn_copy(mod, user_dn);
420 status = NT_STATUS_NO_MEMORY;
424 /* set the password on the user DN specified. This may fail
425 * due to password policies */
426 status = samdb_set_password(sam_ctx, mem_ctx,
430 True, /* this is a user password change */
431 True, /* run restriction tests */
434 if (!NT_STATUS_IS_OK(status)) {
438 /* The above call only setup the modifications, this actually
439 * makes the write to the database. */
440 ret = samdb_replace(sam_ctx, mem_ctx, mod);
442 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
443 ldb_dn_linearize(mem_ctx, user_dn),
444 ldb_errstring(sam_ctx)));
445 status = NT_STATUS_UNSUCCESSFUL;
449 /* And this confirms it in a transaction commit */
450 ret = ldb_transaction_commit(sam_ctx);
452 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
453 ldb_dn_linearize(mem_ctx, user_dn),
454 ldb_errstring(sam_ctx)));
455 status = NT_STATUS_TRANSACTION_ABORTED;
462 ldb_transaction_cancel(sam_ctx);
463 talloc_free(sam_ctx);
465 reject = talloc(mem_ctx, struct samr_ChangeReject);
466 r->out.dominfo = dominfo;
467 r->out.reject = reject;
469 if (reject == NULL) {
472 ZERO_STRUCTP(reject);
474 reject->reason = reason;
481 samr_ChangePasswordUser2
483 easy - just a subset of samr_ChangePasswordUser3
485 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
486 struct samr_ChangePasswordUser2 *r)
488 struct samr_ChangePasswordUser3 r2;
490 r2.in.server = r->in.server;
491 r2.in.account = r->in.account;
492 r2.in.nt_password = r->in.nt_password;
493 r2.in.nt_verifier = r->in.nt_verifier;
494 r2.in.lm_change = r->in.lm_change;
495 r2.in.lm_password = r->in.lm_password;
496 r2.in.lm_verifier = r->in.lm_verifier;
497 r2.in.password3 = NULL;
499 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
504 check that a password is sufficiently complex
506 static BOOL samdb_password_complexity_ok(const char *pass)
508 return check_password_quality(pass);
512 set the user password using plaintext, obeying any user or domain
513 password restrictions
515 note that this function doesn't actually store the result in the
516 database, it just fills in the "mod" structure with ldb modify
517 elements to setup the correct change when samdb_replace() is
518 called. This allows the caller to combine the change with other
519 changes (as is needed by some of the set user info levels)
521 The caller should probably have a transaction wrapping this
523 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
524 const struct ldb_dn *user_dn,
525 const struct ldb_dn *domain_dn,
526 struct ldb_message *mod,
527 const char *new_pass,
528 struct samr_Password *lmNewHash,
529 struct samr_Password *ntNewHash,
532 enum samr_RejectReason *reject_reason,
533 struct samr_DomInfo1 **_dominfo)
535 const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory",
537 "lmPwdHash", "ntPwdHash",
539 "pwdLastSet", NULL };
540 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
541 "maxPwdAge", "minPwdAge",
542 "minPwdLength", NULL };
545 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
546 uint_t userAccountControl;
547 struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
548 struct samr_Password local_lmNewHash, local_ntNewHash;
549 int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
550 struct dom_sid *domain_sid;
551 struct ldb_message **res;
553 time_t now = time(NULL);
557 /* we need to know the time to compute password age */
558 unix_to_nt_time(&now_nt, now);
560 /* pull all the user parameters */
561 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
563 return NT_STATUS_INTERNAL_DB_CORRUPTION;
565 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
566 sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
567 "sambaLMPwdHistory", &sambaLMPwdHistory);
568 sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
569 "sambaNTPwdHistory", &sambaNTPwdHistory);
570 lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
571 ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
572 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
575 /* pull the domain parameters */
576 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
578 return NT_STATUS_NO_SUCH_DOMAIN;
581 /* work out the domain sid, and pull the domain from there */
582 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
583 if (domain_sid == NULL) {
584 return NT_STATUS_INTERNAL_DB_CORRUPTION;
587 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
589 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
591 return NT_STATUS_NO_SUCH_DOMAIN;
595 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
596 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
597 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
598 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
601 struct samr_DomInfo1 *dominfo;
602 /* on failure we need to fill in the reject reasons */
603 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
604 if (dominfo == NULL) {
605 return NT_STATUS_NO_MEMORY;
607 dominfo->min_password_length = minPwdLength;
608 dominfo->password_properties = pwdProperties;
609 dominfo->password_history_length = pwdHistoryLength;
610 dominfo->max_password_age = minPwdAge;
611 dominfo->min_password_age = minPwdAge;
616 /* check the various password restrictions */
617 if (restrictions && minPwdLength > strlen_m(new_pass)) {
619 *reject_reason = SAMR_REJECT_TOO_SHORT;
621 return NT_STATUS_PASSWORD_RESTRICTION;
624 /* possibly check password complexity */
625 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
626 !samdb_password_complexity_ok(new_pass)) {
628 *reject_reason = SAMR_REJECT_COMPLEXITY;
630 return NT_STATUS_PASSWORD_RESTRICTION;
633 /* compute the new nt and lm hashes */
634 if (E_deshash(new_pass, local_lmNewHash.hash)) {
635 lmNewHash = &local_lmNewHash;
637 E_md4hash(new_pass, local_ntNewHash.hash);
638 ntNewHash = &local_ntNewHash;
641 if (restrictions && user_change) {
642 /* are all password changes disallowed? */
643 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
645 *reject_reason = SAMR_REJECT_OTHER;
647 return NT_STATUS_PASSWORD_RESTRICTION;
650 /* can this user change password? */
651 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
653 *reject_reason = SAMR_REJECT_OTHER;
655 return NT_STATUS_PASSWORD_RESTRICTION;
658 /* yes, this is a minus. The ages are in negative 100nsec units! */
659 if (pwdLastSet - minPwdAge > now_nt) {
661 *reject_reason = SAMR_REJECT_OTHER;
663 return NT_STATUS_PASSWORD_RESTRICTION;
666 /* check the immediately past password */
667 if (pwdHistoryLength > 0) {
668 if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
670 *reject_reason = SAMR_REJECT_COMPLEXITY;
672 return NT_STATUS_PASSWORD_RESTRICTION;
674 if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
676 *reject_reason = SAMR_REJECT_COMPLEXITY;
678 return NT_STATUS_PASSWORD_RESTRICTION;
682 /* check the password history */
683 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
684 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
686 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
687 if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
689 *reject_reason = SAMR_REJECT_COMPLEXITY;
691 return NT_STATUS_PASSWORD_RESTRICTION;
694 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
695 if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
697 *reject_reason = SAMR_REJECT_COMPLEXITY;
699 return NT_STATUS_PASSWORD_RESTRICTION;
704 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
706 /* the password is acceptable. Start forming the new fields */
708 /* if we know the cleartext, then only set it.
709 * Modules in ldb will set all the appropriate
711 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
712 "sambaPassword", new_pass));
714 /* We don't have the cleartext, so delete the old one
715 * and set what we have of the hashes */
716 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
719 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
721 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
725 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
727 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
735 set password via a samr_CryptPassword buffer
736 this will in the 'msg' with modify operations that will update the user
737 password when applied
739 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
741 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
743 struct ldb_message *msg,
744 struct samr_CryptPassword *pwbuf)
748 uint32_t new_pass_len;
749 DATA_BLOB session_key = data_blob(NULL, 0);
751 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
752 if (!NT_STATUS_IS_OK(nt_status)) {
756 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
758 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
759 &new_pass_len, STR_UNICODE)) {
760 DEBUG(3,("samr: failed to decode password buffer\n"));
761 return NT_STATUS_WRONG_PASSWORD;
764 /* set the password - samdb needs to know both the domain and user DNs,
765 so the domain password policy can be used */
766 return samdb_set_password(sam_ctx, mem_ctx,
767 account_dn, domain_dn,
770 False, /* This is a password set, not change */
771 True, /* run restriction tests */
777 set password via a samr_CryptPasswordEx buffer
778 this will in the 'msg' with modify operations that will update the user
779 password when applied
781 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
782 struct ldb_context *sam_ctx,
783 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
785 struct ldb_message *msg,
786 struct samr_CryptPasswordEx *pwbuf)
790 uint32_t new_pass_len;
791 DATA_BLOB co_session_key;
792 DATA_BLOB session_key = data_blob(NULL, 0);
793 struct MD5Context ctx;
795 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
796 if (!NT_STATUS_IS_OK(nt_status)) {
800 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
801 if (!co_session_key.data) {
802 return NT_STATUS_NO_MEMORY;
806 MD5Update(&ctx, &pwbuf->data[516], 16);
807 MD5Update(&ctx, session_key.data, session_key.length);
808 MD5Final(co_session_key.data, &ctx);
810 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
812 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
813 &new_pass_len, STR_UNICODE)) {
814 DEBUG(3,("samr: failed to decode password buffer\n"));
815 return NT_STATUS_WRONG_PASSWORD;
818 /* set the password - samdb needs to know both the domain and user DNs,
819 so the domain password policy can be used */
820 return samdb_set_password(sam_ctx, mem_ctx,
821 account_dn, domain_dn,
824 False, /* This is a password set, not change */
825 True, /* run restriction tests */
830 set the user password using plaintext, obeying any user or domain
831 password restrictions
833 This wrapper function takes a SID as input, rather than a user DN,
834 and actually performs the password change
837 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
838 const struct dom_sid *user_sid,
839 const char *new_pass,
840 struct samr_Password *lmNewHash,
841 struct samr_Password *ntNewHash,
844 enum samr_RejectReason *reject_reason,
845 struct samr_DomInfo1 **_dominfo)
848 struct ldb_dn *user_dn;
849 struct ldb_message *msg;
852 ret = ldb_transaction_start(ctx);
854 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
855 return NT_STATUS_TRANSACTION_ABORTED;
858 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
859 "(&(objectSid=%s)(objectClass=user))",
860 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
862 ldb_transaction_cancel(ctx);
863 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
864 dom_sid_string(mem_ctx, user_sid)));
865 return NT_STATUS_NO_SUCH_USER;
868 msg = ldb_msg_new(mem_ctx);
870 ldb_transaction_cancel(ctx);
871 return NT_STATUS_NO_MEMORY;
874 msg->dn = ldb_dn_copy(msg, user_dn);
876 ldb_transaction_cancel(ctx);
877 return NT_STATUS_NO_MEMORY;
880 nt_status = samdb_set_password(ctx, mem_ctx,
883 lmNewHash, ntNewHash,
884 user_change, /* This is a password set, not change */
885 restrictions, /* run restriction tests */
886 reject_reason, _dominfo);
887 if (!NT_STATUS_IS_OK(nt_status)) {
888 ldb_transaction_cancel(ctx);
892 /* modify the samdb record */
893 ret = samdb_replace(ctx, mem_ctx, msg);
895 ldb_transaction_cancel(ctx);
896 return NT_STATUS_ACCESS_DENIED;
899 ret = ldb_transaction_commit(ctx);
901 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
902 ldb_dn_linearize(mem_ctx, msg->dn),
903 ldb_errstring(ctx)));
904 return NT_STATUS_TRANSACTION_ABORTED;